From a8a8d2968b2a3ecaa639ad6b78bf0e74756272a9 Mon Sep 17 00:00:00 2001 From: "Philip (a-0)" <@ph:a-0.me> Date: Sat, 10 Feb 2024 20:48:58 +0100 Subject: [PATCH] Added peer families to `StateDB`. Relates to #5 --- ubisync-lib/src/types/family.rs | 20 +++ ubisync-lib/src/types/family_id.rs | 11 ++ ubisync-lib/src/types/mod.rs | 6 + ubisync-lib/src/types/peer_id.rs | 2 +- ubisync/src/state/database/collections/mod.rs | 4 +- .../database/collections/peer_families.rs | 144 ++++++++++++++++++ .../src/state/database/collections/peers.rs | 14 +- 7 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 ubisync-lib/src/types/family.rs create mode 100644 ubisync-lib/src/types/family_id.rs create mode 100644 ubisync/src/state/database/collections/peer_families.rs diff --git a/ubisync-lib/src/types/family.rs b/ubisync-lib/src/types/family.rs new file mode 100644 index 0000000..056a158 --- /dev/null +++ b/ubisync-lib/src/types/family.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +use super::{FamilyId, PeerId}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct Family { + pub id: FamilyId, + pub name: Option, + pub members: Vec, +} + +impl Family { + pub fn new(id: FamilyId, name: Option, members: Vec) -> Self { + Family { + id, + name, + members, + } + } +} diff --git a/ubisync-lib/src/types/family_id.rs b/ubisync-lib/src/types/family_id.rs new file mode 100644 index 0000000..4432c7b --- /dev/null +++ b/ubisync-lib/src/types/family_id.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize, Clone, Debug, Default, Ord, PartialOrd, PartialEq, Eq, Hash)] +pub struct FamilyId(Uuid); + +impl FamilyId { + pub fn new() -> Self { + FamilyId { 0: Uuid::new_v4() } + } +} \ No newline at end of file diff --git a/ubisync-lib/src/types/mod.rs b/ubisync-lib/src/types/mod.rs index d1d6af7..8475e64 100644 --- a/ubisync-lib/src/types/mod.rs +++ b/ubisync-lib/src/types/mod.rs @@ -10,6 +10,12 @@ pub use element_id::ElementId; mod element; pub use element::Element; +mod family_id; +pub use family_id::FamilyId; + +mod family; +pub use family::Family; + mod message_id; pub use message_id::MessageId; diff --git a/ubisync-lib/src/types/peer_id.rs b/ubisync-lib/src/types/peer_id.rs index 2095119..37df5cb 100644 --- a/ubisync-lib/src/types/peer_id.rs +++ b/ubisync-lib/src/types/peer_id.rs @@ -2,7 +2,7 @@ use anyhow::bail; use i2p::net::{I2pAddr, I2pSocketAddr, ToI2pSocketAddrs}; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PeerId { i2p_addr: I2pSocketAddr, } diff --git a/ubisync/src/state/database/collections/mod.rs b/ubisync/src/state/database/collections/mod.rs index 80ea251..a18973c 100644 --- a/ubisync/src/state/database/collections/mod.rs +++ b/ubisync/src/state/database/collections/mod.rs @@ -2,18 +2,20 @@ use bonsaidb::core::schema::Schema; use apps::DbApp; use elements::DbElement; +use peer_families::DbPeerFamily; use peers::DbPeer; use pot_memberships::DbPotMembership; use pots::DbPot; mod apps; mod elements; +mod peer_families; mod peers; mod pot_memberships; mod pots; #[derive(Schema, Debug)] -#[schema(name = "ubisync", collections = [DbElement, DbPotMembership, DbApp, DbPot, DbPeer])] +#[schema(name = "ubisync", collections = [DbElement, DbPotMembership, DbApp, DbPot, DbPeer, DbPeerFamily])] pub struct UbisyncSchema; #[cfg(test)] diff --git a/ubisync/src/state/database/collections/peer_families.rs b/ubisync/src/state/database/collections/peer_families.rs new file mode 100644 index 0000000..182dfdd --- /dev/null +++ b/ubisync/src/state/database/collections/peer_families.rs @@ -0,0 +1,144 @@ +use std::collections::HashSet; + +use anyhow::{anyhow, Error}; +use bonsaidb::core::schema::{Collection, SerializedCollection}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use ubisync_lib::types::{Family, FamilyId, PeerId}; + +use crate::state::database::{as_key::AsKey, StateDB}; + +#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)] +#[collection(name = "peer-families", views = [])] +pub(super) struct DbPeerFamily { + #[natural_id] + pub(super) id: AsKey, + pub(super) name: Option, + pub(super) members: HashSet, +} + +impl From for Family { + fn from(value: DbPeerFamily) -> Self { + Family { + id: (*value.id).clone(), + name: value.name, + members: value.members.iter().map(|p| p.clone()).collect_vec(), + } + } +} + +impl StateDB { + pub fn add_peer_family( + &self, + id: FamilyId, + name: Option, + initial_members: Vec, + ) -> anyhow::Result<()> { + DbPeerFamily::push( + DbPeerFamily { + id: AsKey::new(id), + name, + members: HashSet::from_iter(initial_members), + }, + &self.db, + ) + .map(|_| ()) + .map_err(|e| anyhow!(e)) + } + + pub fn add_peer_to_family(&self, peer: PeerId, family: FamilyId) -> anyhow::Result<()> { + DbPeerFamily::get(&AsKey::new(family), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find peer family"))? + .modify(&self.db, |doc| { + doc.contents.members.insert(peer.clone()); + }) + .map_err(|e| anyhow!(e)) + } + + pub fn set_peer_family_name( + &self, + family: FamilyId, + name: Option, + ) -> anyhow::Result<()> { + DbPeerFamily::get(&AsKey::new(family), &self.db) + .map_err(|e| anyhow!(e))? + .ok_or(Error::msg("Could not find peer family"))? + .modify(&self.db, |doc| { + doc.contents.name = name.clone(); + }) + .map_err(|e| anyhow!(e)) + } + + pub fn get_peer_family(&self, id: FamilyId) -> anyhow::Result> { + DbPeerFamily::get(&AsKey::new(id), &self.db) + .map(|doc_opt| doc_opt.map(|doc| doc.contents.into())) + .map_err(|e| anyhow!(e)) + } +} + +#[cfg(test)] +mod tests { + use ubisync_lib::types::{Family, FamilyId, PeerId}; + + use crate::state::database::StateDB; + + #[test] + fn add_get() { + let db = StateDB::init(None); + let family_id = FamilyId::new(); + let peer_id = PeerId::default(); + + db.add_peer_family( + family_id.clone(), + Some("My family name".to_string()), + vec![peer_id.clone()], + ) + .unwrap(); + let retrieved_family = db.get_peer_family(family_id.clone()).unwrap(); + + assert_eq!( + retrieved_family, + Some(Family::new( + family_id, + Some("My family name".to_string()), + vec![peer_id] + )) + ) + } + + #[test] + fn set_name() { + let db = StateDB::init(None); + let family_id = FamilyId::new(); + let peer_id = PeerId::default(); + + db.add_peer_family( + family_id.clone(), + Some("My family name".to_string()), + vec![peer_id.clone()], + ) + .unwrap(); + + assert_eq!( + db.get_peer_family(family_id.clone()).unwrap(), + Some(Family::new( + family_id.clone(), + Some("My family name".to_string()), + vec![peer_id.clone()] + )) + ); + + db.set_peer_family_name(family_id.clone(), Some("New family name".to_string())) + .unwrap(); + + assert_eq!( + db.get_peer_family(family_id.clone()).unwrap(), + Some(Family::new( + family_id, + Some("New family name".to_string()), + vec![peer_id] + )) + ); + } +} diff --git a/ubisync/src/state/database/collections/peers.rs b/ubisync/src/state/database/collections/peers.rs index c670ce0..0f3193a 100644 --- a/ubisync/src/state/database/collections/peers.rs +++ b/ubisync/src/state/database/collections/peers.rs @@ -2,10 +2,7 @@ use anyhow::{anyhow, Error}; use bonsaidb::core::schema::{Collection, SerializedCollection}; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use ubisync_lib::{ - peer::Peer, - types::{self, PeerId}, -}; +use ubisync_lib::{peer::Peer, types::PeerId}; use crate::state::database::{as_key::AsKey, StateDB}; @@ -13,7 +10,7 @@ use crate::state::database::{as_key::AsKey, StateDB}; #[collection(name = "peers", views = [])] pub(super) struct DbPeer { #[natural_id] - pub(super) id: AsKey, + pub(super) id: AsKey, pub(super) name: Option, } @@ -45,7 +42,12 @@ impl StateDB { pub fn get_all_peers(&self) -> anyhow::Result> { DbPeer::all(&self.db) .query() - .map(|peers| peers.iter().map(|p| p.contents.clone().into()).collect_vec()) + .map(|peers| { + peers + .iter() + .map(|p| p.contents.clone().into()) + .collect_vec() + }) .map_err(|e| anyhow!(e)) }