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<Show>,
pub status: Option<String>,
pub priority: Option<i8>,
}
#[derive(Debug, Clone, Copy)]
pub enum Show {
Away,
Chat,
DoNotDisturb,
ExtendedAway,
}
impl ToSql for Show {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
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<Self> {
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<String>,
}
#[derive(Debug, Clone)]
pub enum PresenceType {
Online(Online),
Offline(Offline),
}
#[derive(Debug, Clone)]
pub struct Presence {
pub timestamp: DateTime<Utc>,
pub presence: PresenceType,
}
impl Online {
pub fn into_stanza(
self,
timestamp: Option<DateTime<Utc>>,
) -> 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<DateTime<Utc>>,
) -> 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<PresenceType> 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<Presence> 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)),
}
}
}