aboutsummaryrefslogtreecommitdiffstats
path: root/filamento/src/roster.rs
diff options
context:
space:
mode:
Diffstat (limited to 'filamento/src/roster.rs')
-rw-r--r--filamento/src/roster.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/filamento/src/roster.rs b/filamento/src/roster.rs
new file mode 100644
index 0000000..43c32f5
--- /dev/null
+++ b/filamento/src/roster.rs
@@ -0,0 +1,127 @@
+use std::collections::HashSet;
+
+use jid::JID;
+use sqlx::Sqlite;
+
+pub struct ContactUpdate {
+ pub name: Option<String>,
+ pub groups: HashSet<String>,
+}
+
+#[derive(Debug, sqlx::FromRow, Clone)]
+pub struct Contact {
+ // jid is the id used to reference everything, but not the primary key
+ pub user_jid: JID,
+ pub subscription: Subscription,
+ /// client user defined name
+ pub name: Option<String>,
+ // TODO: avatar, nickname
+ /// nickname picked by contact
+ // nickname: Option<String>,
+ #[sqlx(skip)]
+ pub groups: HashSet<String>,
+}
+
+#[derive(Debug, Clone)]
+pub enum Subscription {
+ None,
+ PendingOut,
+ PendingIn,
+ PendingInPendingOut,
+ OnlyOut,
+ OnlyIn,
+ OutPendingIn,
+ InPendingOut,
+ Buddy,
+ // TODO: perhaps don't need, just emit event to remove contact
+ // Remove,
+}
+
+impl sqlx::Type<Sqlite> for Subscription {
+ fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
+ <&str as sqlx::Type<Sqlite>>::type_info()
+ }
+}
+
+impl sqlx::Decode<'_, Sqlite> for Subscription {
+ fn decode(
+ value: <Sqlite as sqlx::Database>::ValueRef<'_>,
+ ) -> Result<Self, sqlx::error::BoxDynError> {
+ let value = <&str as sqlx::Decode<Sqlite>>::decode(value)?;
+ match value {
+ "none" => Ok(Self::None),
+ "pending-out" => Ok(Self::PendingOut),
+ "pending-in" => Ok(Self::PendingIn),
+ "pending-in-pending-out" => Ok(Self::PendingInPendingOut),
+ "only-out" => Ok(Self::OnlyOut),
+ "only-in" => Ok(Self::OnlyIn),
+ "out-pending-in" => Ok(Self::OutPendingIn),
+ "in-pending-out" => Ok(Self::InPendingOut),
+ "buddy" => Ok(Self::Buddy),
+ _ => panic!("unexpected subscription `{value}`"),
+ }
+ }
+}
+
+impl sqlx::Encode<'_, Sqlite> for Subscription {
+ fn encode_by_ref(
+ &self,
+ buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer<'_>,
+ ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
+ let value = match self {
+ Subscription::None => "none",
+ Subscription::PendingOut => "pending-out",
+ Subscription::PendingIn => "pending-in",
+ Subscription::PendingInPendingOut => "pending-in-pending-out",
+ Subscription::OnlyOut => "only-out",
+ Subscription::OnlyIn => "only-in",
+ Subscription::OutPendingIn => "out-pending-in",
+ Subscription::InPendingOut => "in-pending-out",
+ Subscription::Buddy => "buddy",
+ };
+ <&str as sqlx::Encode<Sqlite>>::encode(value, buf)
+ }
+}
+
+// none
+// >
+// >>
+// <
+// <<
+// ><
+// >><
+// ><<
+// >><<
+
+impl From<stanza::roster::Item> for Contact {
+ fn from(value: stanza::roster::Item) -> Self {
+ let subscription = match value.ask {
+ true => match value.subscription {
+ Some(s) => match s {
+ stanza::roster::Subscription::Both => Subscription::Buddy,
+ stanza::roster::Subscription::From => Subscription::InPendingOut,
+ stanza::roster::Subscription::None => Subscription::PendingOut,
+ stanza::roster::Subscription::Remove => Subscription::PendingOut,
+ stanza::roster::Subscription::To => Subscription::OnlyOut,
+ },
+ None => Subscription::PendingOut,
+ },
+ false => match value.subscription {
+ Some(s) => match s {
+ stanza::roster::Subscription::Both => Subscription::Buddy,
+ stanza::roster::Subscription::From => Subscription::OnlyIn,
+ stanza::roster::Subscription::None => Subscription::None,
+ stanza::roster::Subscription::Remove => Subscription::None,
+ stanza::roster::Subscription::To => Subscription::OnlyOut,
+ },
+ None => Subscription::None,
+ },
+ };
+ Contact {
+ user_jid: value.jid,
+ subscription,
+ name: value.name,
+ groups: HashSet::from_iter(value.groups.into_iter().filter_map(|group| group.0)),
+ }
+ }
+}