diff options
Diffstat (limited to 'filamento/src/chat.rs')
| -rw-r--r-- | filamento/src/chat.rs | 141 |
1 files changed, 91 insertions, 50 deletions
diff --git a/filamento/src/chat.rs b/filamento/src/chat.rs index 147c7f7..687da82 100644 --- a/filamento/src/chat.rs +++ b/filamento/src/chat.rs @@ -1,25 +1,61 @@ +// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::fmt::{Display, Write}; + use chrono::{DateTime, Utc}; -use jid::JID; -use sqlx::Sqlite; +use jid::{BareJID, JID}; +use rusqlite::{ + ToSql, + types::{FromSql, ToSqlOutput, Value}, +}; use uuid::Uuid; -#[derive(Debug, sqlx::FromRow, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "reactive_stores", derive(reactive_stores::Store))] pub struct Message { pub id: Uuid, - // does not contain full user information - #[sqlx(rename = "from_jid")] - // bare jid (for now) - pub from: JID, + /// jid of user currently tied to the original sender, updated by jid move event. original sender can be found within the source data. + pub from: BareJID, pub delivery: Option<Delivery>, pub timestamp: DateTime<Utc>, - // TODO: originally_from - // TODO: message edits - // TODO: message timestamp - #[sqlx(flatten)] + // TODO: message edits. message edits will need to include the edit and the source stanza that caused the edit. + /// original message sources (XMPP, imported, etc.). cannot change, but may be deleted or have information redacted. can be multiple because a message may be updated, have reactions appended to it, delivery receipt, user moved, etc. + pub source: Vec<Source>, pub body: Body, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Source { + XMPP(XMPPMessage), + // TODO: imported information + Imported, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct XMPPMessage { + // TODO: upon software updates, upgrade all message and xmppmessage structs with missed data from old XMPP raw messages. maybe just don't store parsed message in xmpp message, have it be a method + /// the raw data received from the xml stream + pub raw: String, + /// the timestamp the client received the stanza, or associated with the <delay> in the envelope. + pub timestamp: DateTime<Utc>, + /// if message was received in a carbon envolope, forwarded, or in an encrypted envelope, etc., the full xmpp message it came from is included here, linked. there could technically be multiple envelopes (same stanza received through multiple envelopes). + pub envelopes: Vec<XMPPMessage>, +} + +impl XMPPMessage { + // TODO: syncify + // pub async fn parsed(&self) -> stanza::client::message::Message { + // let reader = peanuts::Reader::new(peanuts::ReadableString(self.raw.to_string())); + // let message = reader.read().await?; + // message + // } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Delivery { Sending, Written, @@ -30,45 +66,47 @@ pub enum Delivery { Queued, } -impl sqlx::Type<Sqlite> for Delivery { - fn type_info() -> <Sqlite as sqlx::Database>::TypeInfo { - <&str as sqlx::Type<Sqlite>>::type_info() +impl Display for Delivery { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Delivery::Sending => f.write_str("sending"), + Delivery::Written => f.write_str("written"), + Delivery::Sent => f.write_str("sent"), + Delivery::Delivered => f.write_str("delivered"), + Delivery::Read => f.write_str("read"), + Delivery::Failed => f.write_str("failed"), + Delivery::Queued => f.write_str("queued"), + } } } -impl sqlx::Decode<'_, Sqlite> for Delivery { - fn decode( - value: <Sqlite as sqlx::Database>::ValueRef<'_>, - ) -> Result<Self, sqlx::error::BoxDynError> { - let value = <&str as sqlx::Decode<Sqlite>>::decode(value)?; - match value { - "sending" => Ok(Self::Sending), - "written" => Ok(Self::Written), - "sent" => Ok(Self::Sent), - "delivered" => Ok(Self::Delivered), - "read" => Ok(Self::Read), - "failed" => Ok(Self::Failed), - "queued" => Ok(Self::Queued), - _ => unreachable!(), - } +impl ToSql for Delivery { + fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> { + Ok(match self { + Delivery::Sending => ToSqlOutput::Owned(Value::Text("sending".to_string())), + Delivery::Written => ToSqlOutput::Owned(Value::Text("written".to_string())), + Delivery::Sent => ToSqlOutput::Owned(Value::Text("sent".to_string())), + Delivery::Delivered => ToSqlOutput::Owned(Value::Text("delivered".to_string())), + Delivery::Read => ToSqlOutput::Owned(Value::Text("read".to_string())), + Delivery::Failed => ToSqlOutput::Owned(Value::Text("failed".to_string())), + Delivery::Queued => ToSqlOutput::Owned(Value::Text("queued".to_string())), + }) } } -impl sqlx::Encode<'_, Sqlite> for Delivery { - fn encode_by_ref( - &self, - buf: &mut <Sqlite as sqlx::Database>::ArgumentBuffer<'_>, - ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> { - let value = match self { - Delivery::Sending => "sending", - Delivery::Written => "written", - Delivery::Sent => "sent", - Delivery::Delivered => "delivered", - Delivery::Read => "read", - Delivery::Failed => "failed", - Delivery::Queued => "queued", - }; - <&str as sqlx::Encode<Sqlite>>::encode(value, buf) +impl FromSql for Delivery { + fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> { + Ok(match value.as_str()? { + "sending" => Self::Sending, + "written" => Self::Written, + "sent" => Self::Sent, + "delivered" => Self::Delivered, + "read" => Self::Read, + "failed" => Self::Failed, + "queued" => Self::Queued, + // TODO: don't have these lol + value => panic!("unexpected subscription `{value}`"), + }) } } @@ -78,15 +116,18 @@ impl sqlx::Encode<'_, Sqlite> for Delivery { // Outside, // } -#[derive(Debug, sqlx::FromRow, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Body { // TODO: rich text, other contents, threads pub body: String, } -#[derive(sqlx::FromRow, Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "reactive_stores", derive(reactive_stores::Store))] pub struct Chat { - pub correspondent: JID, + pub correspondent: BareJID, pub have_chatted: bool, // pub unread_messages: i32, // pub latest_message: Message, @@ -98,13 +139,13 @@ pub struct Chat { pub enum ChatUpdate {} impl Chat { - pub fn new(correspondent: JID, have_chatted: bool) -> Self { + pub fn new(correspondent: BareJID, have_chatted: bool) -> Self { Self { correspondent, have_chatted, } } - pub fn correspondent(&self) -> &JID { + pub fn correspondent(&self) -> &BareJID { &self.correspondent } } |
