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<Show>,
#[sqlx(rename = "message")]
pub status: Option<String>,
#[sqlx(skip)]
pub priority: Option<i8>,
}
#[derive(Debug, Clone, Copy)]
pub enum Show {
Away,
Chat,
DoNotDisturb,
ExtendedAway,
}
impl sqlx::Type<Sqlite> for Show {
fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo {
<&str as sqlx::Type<Sqlite>>::type_info()
}
}
impl sqlx::Decode<'_, Sqlite> for Show {
fn decode(
value: <Sqlite as sqlx::Database>::ValueRef<'_>,
) -> Result<Self, sqlx::error::BoxDynError> {
let value = <&str as sqlx::Decode<Sqlite>>::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 <Sqlite as sqlx::Database>::ArgumentBuffer<'_>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
let value = match self {
Show::Away => "away",
Show::Chat => "chat",
Show::DoNotDisturb => "do-not-disturb",
Show::ExtendedAway => "extended-away",
};
<&str as sqlx::Encode<Sqlite>>::encode(value, buf)
}
}
#[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 {
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<DateTime<Utc>>,
) -> 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<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)),
}
}
}