use chrono::{DateTime, Utc}; use sqlx::Sqlite; use stanza::{client::presence::String1024, xep_0203::Delay}; #[derive(Debug, Default, sqlx::FromRow, Clone)] pub struct Online { pub show: Option, #[sqlx(rename = "message")] pub status: Option, #[sqlx(skip)] pub priority: Option, } #[derive(Debug, Clone, Copy)] pub enum Show { Away, Chat, DoNotDisturb, ExtendedAway, } impl sqlx::Type for Show { fn type_info() -> ::TypeInfo { <&str as sqlx::Type>::type_info() } } impl sqlx::Decode<'_, Sqlite> for Show { fn decode( value: ::ValueRef<'_>, ) -> Result { let value = <&str as sqlx::Decode>::decode(value)?; match value { "away" => Ok(Self::Away), "chat" => Ok(Self::Chat), "do-not-disturb" => Ok(Self::DoNotDisturb), "extended-away" => Ok(Self::ExtendedAway), _ => unreachable!(), } } } impl sqlx::Encode<'_, Sqlite> for Show { fn encode_by_ref( &self, buf: &mut ::ArgumentBuffer<'_>, ) -> Result { let value = match self { Show::Away => "away", Show::Chat => "chat", Show::DoNotDisturb => "do-not-disturb", Show::ExtendedAway => "extended-away", }; <&str as sqlx::Encode>::encode(value, buf) } } #[derive(Debug, Default, Clone)] pub struct Offline { pub status: Option, } #[derive(Debug, Clone)] pub enum PresenceType { Online(Online), Offline(Offline), } #[derive(Debug, Clone)] pub struct Presence { pub timestamp: DateTime, pub presence: PresenceType, } impl Online { pub fn into_stanza( self, timestamp: Option>, ) -> stanza::client::presence::Presence { stanza::client::presence::Presence { from: None, id: None, to: None, r#type: None, lang: None, show: self.show.map(|show| match show { Show::Away => stanza::client::presence::Show::Away, Show::Chat => stanza::client::presence::Show::Chat, Show::DoNotDisturb => stanza::client::presence::Show::Dnd, Show::ExtendedAway => stanza::client::presence::Show::Xa, }), // TODO: enforce message length in status message status: self.status.map(|status| stanza::client::presence::Status { lang: None, status: String1024(status), }), priority: self .priority .map(|priority| stanza::client::presence::Priority(priority)), errors: Vec::new(), delay: timestamp.map(|timestamp| Delay { from: None, stamp: timestamp, }), } } } impl Offline { pub fn into_stanza( self, timestamp: Option>, ) -> stanza::client::presence::Presence { stanza::client::presence::Presence { from: None, id: None, to: None, r#type: Some(stanza::client::presence::PresenceType::Unavailable), lang: None, show: None, status: self.status.map(|status| stanza::client::presence::Status { lang: None, status: String1024(status), }), priority: None, errors: Vec::new(), delay: timestamp.map(|timestamp| Delay { from: None, stamp: timestamp, }), } } } impl From for stanza::client::presence::Presence { fn from(value: PresenceType) -> Self { match value { PresenceType::Online(online) => online.into_stanza(None), PresenceType::Offline(offline) => offline.into_stanza(None), } } } impl From for stanza::client::presence::Presence { fn from(value: Presence) -> Self { match value.presence { PresenceType::Online(online) => online.into_stanza(Some(value.timestamp)), PresenceType::Offline(offline) => offline.into_stanza(Some(value.timestamp)), } } }