use chrono::{DateTime, Utc}; use rusqlite::{ ToSql, types::{FromSql, ToSqlOutput, Value}, }; use stanza::{client::presence::String1024, xep_0203::Delay}; use crate::caps; #[derive(Debug, Default, Clone)] pub struct Online { pub show: Option, pub status: Option, pub priority: Option, } #[derive(Debug, Clone, Copy)] pub enum Show { Away, Chat, DoNotDisturb, ExtendedAway, } impl ToSql for Show { fn to_sql(&self) -> rusqlite::Result> { Ok(match self { Show::Away => ToSqlOutput::Owned(Value::Text("away".to_string())), Show::Chat => ToSqlOutput::Owned(Value::Text("chat".to_string())), Show::DoNotDisturb => ToSqlOutput::Owned(Value::Text("do-not-disturb".to_string())), Show::ExtendedAway => ToSqlOutput::Owned(Value::Text("extended-away".to_string())), }) } } impl FromSql for Show { fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { Ok(match value.as_str()? { "away" => Self::Away, "chat" => Self::Chat, "do-not-disturb" => Self::DoNotDisturb, "extended-away" => Self::ExtendedAway, // TODO: horrible value => panic!("unexpected {value}"), }) } } #[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 { 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)), delay: timestamp.map(|timestamp| Delay { from: None, stamp: timestamp, }), c: Some(caps::c()), ..Default::default() } } } impl Offline { pub fn into_stanza( self, timestamp: Option>, ) -> stanza::client::presence::Presence { stanza::client::presence::Presence { r#type: Some(stanza::client::presence::PresenceType::Unavailable), status: self.status.map(|status| stanza::client::presence::Status { lang: None, status: String1024(status), }), delay: timestamp.map(|timestamp| Delay { from: None, stamp: timestamp, }), c: Some(caps::c()), ..Default::default() } } } 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)), } } }