use std::collections::HashSet; use jid::JID; use sqlx::Sqlite; pub struct ContactUpdate { pub name: Option, pub groups: HashSet, } #[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, // TODO: avatar, nickname /// nickname picked by contact // nickname: Option, #[sqlx(skip)] pub groups: HashSet, } #[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 for Subscription { fn type_info() -> ::TypeInfo { <&str as sqlx::Type>::type_info() } } impl sqlx::Decode<'_, Sqlite> for Subscription { fn decode( value: ::ValueRef<'_>, ) -> Result { let value = <&str as sqlx::Decode>::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 ::ArgumentBuffer<'_>, ) -> Result { 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>::encode(value, buf) } } // none // > // >> // < // << // >< // >>< // ><< // >><< impl From 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)), } } }