diff --git a/lazy-supplements-core/src/data/entity/mod.rs b/lazy-supplements-core/src/data/entity/mod.rs index 5e38d96..c515558 100644 --- a/lazy-supplements-core/src/data/entity/mod.rs +++ b/lazy-supplements-core/src/data/entity/mod.rs @@ -1,11 +1,11 @@ -mod node; +mod trusted_peer; mod record_deletion; -pub use node::{ - ActiveModel as NodeActiveModel, - Column as NodeColumn, - Entity as NodeEntity, - Model as NodeModel, +pub use trusted_peer::{ + ActiveModel as TrustedPeerActiveModel, + Column as TrustedPeerColumn, + Entity as TrustedPeerEntity, + Model as TrustedPeerModel, }; pub use record_deletion::{ diff --git a/lazy-supplements-core/src/data/entity/node.rs b/lazy-supplements-core/src/data/entity/trusted_peer.rs similarity index 85% rename from lazy-supplements-core/src/data/entity/node.rs rename to lazy-supplements-core/src/data/entity/trusted_peer.rs index d932c8e..51647a6 100644 --- a/lazy-supplements-core/src/data/entity/node.rs +++ b/lazy-supplements-core/src/data/entity/trusted_peer.rs @@ -5,9 +5,11 @@ use sea_orm::entity::{ }; use serde::{Deserialize, Serialize}; +use crate::data::value::PeerIdValue; + #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)] -#[sea_orm(table_name = "node")] +#[sea_orm(table_name = "trusted_peer")] pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: Uuid, @@ -18,9 +20,10 @@ pub struct Model { #[sea_orm(indexed)] pub synced_at: Option, #[sea_orm(indexed)] - pub peer_id: String, + pub peer_id: PeerIdValue, #[sea_orm(column_type = "Text")] pub note: String, + pub is_prefered: bool, } #[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] @@ -46,14 +49,14 @@ mod tests { use super::*; - use libp2p::identity; + use libp2p::{identity, PeerId}; #[tokio::test] async fn check_insert_node() { let db = get_or_init_test_data_database().await; ActiveModel{ - peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()), + peer_id: Set(PeerIdValue::from(PeerId::random())), note: Set("test note".to_owned()), ..ActiveModel::new() }.insert(db).await.unwrap(); diff --git a/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs b/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs index c69bfbe..9fa0be0 100644 --- a/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs +++ b/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs @@ -8,20 +8,20 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - Node::up(manager).await?; + TrustedPeer::up(manager).await?; RecordDeletion::up(manager).await?; Ok(()) } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - Node::down(manager).await?; + TrustedPeer::down(manager).await?; RecordDeletion::down(manager).await?; Ok(()) } } #[derive(DeriveIden)] -enum Node { +enum TrustedPeer { Table, Id, CreatedAt, @@ -29,10 +29,11 @@ enum Node { SyncedAt, PeerId, Note, + IsPrefered, } #[async_trait::async_trait] -impl TableMigration for Node { +impl TableMigration for TrustedPeer { async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> { manager.create_table( Table::create() @@ -44,6 +45,7 @@ impl TableMigration for Node { .col(timestamp_null(Self::SyncedAt)) .col(string_len(Self::PeerId, 255)) .col(text(Self::Note)) + .col(boolean(Self::IsPrefered)) .to_owned() ).await?; Ok(()) diff --git a/lazy-supplements-core/src/data/mod.rs b/lazy-supplements-core/src/data/mod.rs index 25d0085..c1779c9 100644 --- a/lazy-supplements-core/src/data/mod.rs +++ b/lazy-supplements-core/src/data/mod.rs @@ -1,3 +1,4 @@ pub mod entity; pub mod migration; -pub mod value; \ No newline at end of file +pub mod syncable; +pub mod value; diff --git a/lazy-supplements-core/src/data/syncable.rs b/lazy-supplements-core/src/data/syncable.rs new file mode 100644 index 0000000..2e0a784 --- /dev/null +++ b/lazy-supplements-core/src/data/syncable.rs @@ -0,0 +1,64 @@ +use sea_orm::{*, prelude::*, query::*}; + +pub trait SyncableModel: ModelTrait { + type SyncableEntity: SyncableEntity; + fn get_updated_at(&self) -> DateTimeUtc; + fn get_uuid(&self) -> Uuid; +} + +pub trait SyncableEntity: EntityTrait< + Model = Self::SyncableModel, + ActiveModel = Self::SyncableActiveModel, + Column = Self::SyncableColumn, +>{ + type SyncableModel: SyncableModel + FromQueryResult; + type SyncableActiveModel: SyncableActiveModel; + type SyncableColumn: SyncableColumn; + + async fn get_updated_after(date: DateTimeUtc, db: &DatabaseConnection) -> Result::Model>, SyncableError> { + let result: Vec = ::find() + .filter(Self::SyncableColumn::updated_at().gte(date)) + .all(db) + .await.unwrap(); + Ok(result) + } + fn apply_updated(models: Vec<::Model>, db: &DatabaseConnection) { + todo!() + } +} + +pub trait SyncableActiveModel: ActiveModelTrait { + + type SyncableEntity: SyncableEntity; + fn get_uuid(&self) -> Option; + fn get_updated_at(&self) -> Option; + fn try_merge(&mut self, other: ::SyncableModel) -> Result<(), SyncableError> { + if self.get_uuid().ok_or(SyncableError::MissingField("uuid"))? != other.get_uuid() { + return Err(SyncableError::MismatchUuid) + } + if self.get_updated_at().ok_or(SyncableError::MissingField("updated_at"))? < other.get_updated_at() { + for column in <<::Entity as EntityTrait>::Column as Iterable>::iter() { + self.take(column).set_if_not_equals(other.get(column)); + } + } + Ok(()) + } + +} + +pub trait SyncableColumn: ColumnTrait { + fn is_uuid(&self) -> bool; + fn is_updated_at(&self) -> bool; + fn updated_at() -> Self; + fn should_not_sync(&self); +} + + +#[derive(Debug, thiserror::Error)] +pub enum SyncableError { + #[error("Invalid UUID")] + MismatchUuid, + #[error("mandatory field {0} is missing")] + MissingField(&'static str), + +} \ No newline at end of file diff --git a/lazy-supplements-core/src/data/value/peer_id.rs b/lazy-supplements-core/src/data/value/peer_id.rs index e28b5d4..b6558ca 100644 --- a/lazy-supplements-core/src/data/value/peer_id.rs +++ b/lazy-supplements-core/src/data/value/peer_id.rs @@ -4,9 +4,42 @@ use libp2p::PeerId; use sea_orm::{sea_query::ValueTypeErr, DbErr}; use serde::{Deserialize, Serialize}; +use crate::error::Error; + #[derive(Clone, Debug, PartialEq)] pub struct PeerIdValue(PeerId); +impl<'de> Deserialize<'de> for PeerIdValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de> { + Self::from_str(&String::deserialize(deserializer)?).or(Err(::custom("fail to parse PeerId"))) + + } +} + +impl Serialize for PeerIdValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + serializer.serialize_str(&self.0.to_string()) + } +} + +impl FromStr for PeerIdValue{ + type Err = libp2p::identity::ParseError; + + fn from_str(s: &str) -> Result { + Ok(Self(PeerId::from_str(s)?)) + } +} + +impl ToString for PeerIdValue { + fn to_string(&self) -> String { + self.0.to_string() + } +} + impl From for PeerIdValue { fn from(source: PeerId) -> Self { Self(source) @@ -65,4 +98,23 @@ impl sea_orm::sea_query::Nullable for PeerIdValue { fn null() -> sea_orm::Value { ::null() } -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use crate::tests::{test_cbor_serialize_deserialize, test_toml_serialize_deserialize}; + + use super::*; + + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] + struct PeerIdValueWrapper { + content: PeerIdValue + + } + #[test] + fn test_serialize_deserialize() { + let peer_id= PeerIdValueWrapper{content: PeerIdValue::from(PeerId::random())}; + let x = toml::to_string(&peer_id).unwrap(); + assert_eq!(peer_id.content, toml::from_str::(&x).unwrap().content) + } +}