aboutsummaryrefslogblamecommitdiffstats
path: root/filamento/src/presence.rs
blob: e35761cef49d23d7aa0ff6b760736a5e3208c7a7 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                            
                 
                                                            
 
                                               
                   
                           
                               

                               
                             

 











































                                                                   
                                
                    
                               

 
                       
                       


                     
 











                                             




                         
                                                   





                                                                          
                                                                               


                                           
                          


                                                                              



                                                    


         
 





                                             





                                                                              
                                                                               




                                           












                                                                        


         


                                                            


                                                                                         


         
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)),
        }
    }
}