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

                        
             
                                                                         
                                        



                                     

                            

                              
                               






                                                         
                               








                                                   
                                


                                          
                                          
                                          
                                              


                                      
                              





                                                     





                                                      


                              
                                          



                                                     

                                               















                                                 

























                                                      
                                
                                       




                                                                           

                                                      

                                                   










                                                                         


                              



                                
                                       














                                                      
                         












                                                
                               
                                               















                                                                                                         

                                     

                                            

                                        


                              







                                























                                                              







                                                 







                                                      

                              
                   











                                                    






                                                        




                                    
                                            




                                      





















                                              





                                                     






                                  




                                  



























                                                                     
use std::{num::TryFromIntError, string::FromUtf8Error, sync::Arc};

use base64::DecodeError;
use image::ImageError;
use jid::JID;
use lampada::error::{ActorError, ConnectionError, ReadError, WriteError};
use stanza::client::{Stanza, iq::Query};
use thiserror::Error;

pub use lampada::error::CommandError;

use crate::files::FileStore;

// for the client logic impl
#[derive(Debug, Error, Clone)]
pub enum Error<Fs: FileStore> {
    #[error("core error: {0}")]
    Connection(#[from] ConnectionError),
    #[error("received unrecognized/unsupported content")]
    UnrecognizedContent,
    // TODO: include content
    // UnrecognizedContent(peanuts::element::Content),
    #[error("iq receive error: {0}")]
    Iq(#[from] IqProcessError),
    // TODO: change to Connecting(ConnectingError)
    #[error("connecting: {0}")]
    Connecting(#[from] ConnectionJobError),
    #[error("presence: {0}")]
    Presence(#[from] PresenceError),
    #[error("set status: {0}")]
    SetStatus(#[from] StatusError),
    // TODO: have different ones for get/update/set
    #[error("roster: {0}")]
    Roster(#[from] RosterError),
    #[error("stream error: {0}")]
    Stream(#[from] stanza::stream::Error),
    #[error("message send error: {0}")]
    MessageSend(#[from] MessageSendError),
    #[error("message receive error: {0}")]
    MessageRecv(#[from] MessageRecvError<Fs>),
    #[error("subscripbe error: {0}")]
    Subscribe(#[from] SubscribeError),
    #[error("publish error: {0}")]
    Publish(#[from] PEPError),
}

#[derive(Debug, Error, Clone)]
pub enum MessageSendError {
    #[error("could not add to message history: {0}")]
    MessageHistory(#[from] DatabaseError),
    #[error("could not mark chat as chatted: {0}")]
    MarkChatAsChatted(DatabaseError),
    #[error("could not get client user details: {0}")]
    GetUserDetails(DatabaseError),
    #[error("writing message to connection: {0}")]
    Write(#[from] WriteError),
}

#[derive(Debug, Error, Clone)]
pub enum MessageRecvError<Fs: FileStore> {
    #[error("could not add to message history: {0}")]
    MessageHistory(#[from] DatabaseError),
    #[error("missing from")]
    MissingFrom,
    #[error("could not update user nick: {0}")]
    NickUpdate(DatabaseError),
    #[error("could not update user avatar: {0}")]
    AvatarUpdate(#[from] AvatarUpdateError<Fs>),
}

#[derive(Debug, Error, Clone)]
pub enum AvatarUpdateError<Fs: FileStore> {
    #[error("could not save to disk: {0}")]
    FileStore(Fs::Err),
    #[error("could not fetch avatar data: {0}")]
    PEPError(#[from] CommandError<PEPError>),
    #[error("base64 decode: {0}")]
    Base64(#[from] DecodeError),
    #[error("pep node missing avatar data")]
    MissingData,
    #[error("database: {0}")]
    Database(#[from] DatabaseError),
}

#[derive(Debug, Error, Clone)]
pub enum StatusError {
    #[error("cache: {0}")]
    Cache(#[from] DatabaseError),
    #[error("stream write: {0}")]
    Write(#[from] WriteError),
}

#[derive(Debug, Clone, Error)]
pub enum ConnectionJobError {
    // #[error("connection failed: {0}")]
    // ConnectionFailed(#[from] luz::Error),
    #[error("failed roster retreival: {0}")]
    RosterRetreival(#[from] RosterError),
    #[error("failed to send available presence: {0}")]
    SendPresence(#[from] WriteError),
    #[error("cached status: {0}")]
    StatusCacheError(#[from] DatabaseError),
}

#[derive(Debug, Error, Clone)]
pub enum RosterError {
    #[error("cache: {0}")]
    Cache(#[from] DatabaseError),
    #[error("iq response: {0}")]
    IqResponse(#[from] IqRequestError),
    #[error("stream write: {0}")]
    Write(#[from] WriteError),
    // TODO: display for stanza, to show as xml, same for read error types.
    #[error("unexpected reply: {0:?}")]
    UnexpectedStanza(Stanza),
    #[error("stanza error: {0}")]
    StanzaError(#[from] stanza::client::error::Error),
    #[error("could not reply to roster push: {0}")]
    PushReply(WriteError),
    #[error("actor error: {0}")]
    Actor(ActorError),
}

impl From<CommandError<RosterError>> for RosterError {
    fn from(value: CommandError<RosterError>) -> Self {
        match value {
            CommandError::Actor(actor_error) => Self::Actor(actor_error),
            CommandError::Error(e) => e,
        }
    }
}

#[derive(Debug, Error, Clone)]
pub enum DiscoError {
    #[error("write error: {0}")]
    Write(#[from] WriteError),
    #[error("iq response: {0}")]
    IqResponse(#[from] IqRequestError),
    #[error("reply from incorrect entity: {0}")]
    IncorrectEntity(JID),
    #[error("unexpected reply: {0:?}")]
    UnexpectedStanza(Stanza),
    #[error("stanza error: {0}")]
    StanzaError(#[from] stanza::client::error::Error),
    #[error("disco result missing query item")]
    MissingQuery,
    #[error("disco error missing error")]
    MissingError,
    #[error("received mismatched query")]
    MismatchedQuery(Query),
}

#[derive(Debug, Error, Clone)]
pub enum IqRequestError {
    #[error("sending request: {0}")]
    Write(#[from] WriteError),
    #[error("receiving expected response: {0}")]
    Read(#[from] ReadError),
}

#[derive(Debug, Error, Clone)]
pub enum ResponseError {
    #[error("no matching id: {0}")]
    NoMatchingId(String),
}

#[derive(Debug, Error, Clone)]
#[error("database error: {0}")]
pub struct DatabaseError(pub Arc<sqlx::Error>);

impl From<sqlx::Error> for DatabaseError {
    fn from(e: sqlx::Error) -> Self {
        Self(Arc::new(e))
    }
}

impl From<sqlx::Error> for DatabaseOpenError {
    fn from(e: sqlx::Error) -> Self {
        Self::Error(Arc::new(e))
    }
}

#[derive(Debug, Error, Clone)]
// TODO: should probably have all iq query related errors here, including read, write, stanza error, etc.
pub enum IqError {
    #[error("writing response: {0}")]
    WriteError(#[from] WriteError),
    #[error("receiving response: `{0}`")]
    ReceivedResponse(#[from] ResponseError),
    #[error("incorrect addressee: {0}")]
    IncorrectAddressee(jid::JID),
}

#[derive(Debug, Error, Clone)]
pub enum IqProcessError {
    #[error("iq error")]
    Iq(#[from] IqError),
    #[error("roster push")]
    Roster(#[from] RosterError),
}

#[derive(Debug, Error, Clone)]
pub enum DatabaseOpenError {
    #[error("error: {0}")]
    Error(Arc<sqlx::Error>),
    #[error("migration: {0}")]
    Migration(Arc<sqlx::migrate::MigrateError>),
    #[error("io: {0}")]
    Io(Arc<tokio::io::Error>),
    #[error("invalid path")]
    InvalidPath,
}

impl From<sqlx::migrate::MigrateError> for DatabaseOpenError {
    fn from(e: sqlx::migrate::MigrateError) -> Self {
        Self::Migration(Arc::new(e))
    }
}

impl From<tokio::io::Error> for DatabaseOpenError {
    fn from(e: tokio::io::Error) -> Self {
        Self::Io(Arc::new(e))
    }
}

#[derive(Debug, Error, Clone)]
pub enum SubscribeError {
    #[error("write: {0}")]
    Write(#[from] WriteError),
    #[error("fetching client user details: {0}")]
    Database(#[from] DatabaseError),
}

#[derive(Debug, Error, Clone)]
pub enum PresenceError {
    #[error("unsupported")]
    Unsupported,
    #[error("missing from")]
    MissingFrom,
    #[error("stanza error: {0}")]
    StanzaError(#[from] stanza::client::error::Error),
}

#[derive(Debug, Error, Clone)]
pub enum PEPError {
    #[error("received mismatched query")]
    MismatchedQuery(Query),
    #[error("missing query")]
    MissingQuery,
    #[error("stanza errors: {0:?}")]
    StanzaErrors(Vec<stanza::client::error::Error>),
    #[error("reply from incorrect entity: {0}")]
    IncorrectEntity(JID),
    #[error("unexpected stanza: {0:?}")]
    UnexpectedStanza(Stanza),
    #[error("iq response: {0}")]
    IqResponse(#[from] IqRequestError),
    #[error("missing pep item")]
    MissingItem,
    #[error("incorrect item id: expected {0}, got {1}")]
    IncorrectItemID(String, String),
    #[error("unsupported pep item")]
    UnsupportedItem,
    // TODO: should the item be in the error?
}

#[derive(Debug, Error, Clone)]
pub enum NickError {
    #[error("publishing nick: {0}")]
    Publish(#[from] CommandError<PEPError>),
    #[error("updating database: {0}")]
    Database(#[from] DatabaseError),
    #[error("disconnected")]
    Disconnected,
}

#[derive(Debug, Error, Clone)]
pub enum CapsDecodeError {
    #[error("base64 decode: {0}")]
    Base64Decode(#[from] base64::DecodeError),
    #[error("utf8: {0}")]
    UTF8(#[from] FromUtf8Error),
    #[error("missing features")]
    MissingFeatures,
    #[error("missing identities")]
    MissingIdentities,
    #[error("missing identity category")]
    MissingIdentityCategory,
    #[error("missing identity type")]
    MissingIdentityType,
    #[error("missing identity language")]
    MissingIdentityLang,
    #[error("missing identity name")]
    MissingIdentityName,
}

#[derive(Debug, Error, Clone)]
pub enum CapsEncodeError {
    #[error("invalid data form in disco extensions")]
    InvalidDataForm,
}

#[derive(Debug, Error, Clone)]
pub enum HashNodeConversionError {
    #[error("no prefix")]
    NoPrefix,
    #[error("missing period")]
    MissingPeriod,
}

#[derive(Debug, Error, Clone)]
pub enum CapsNodeConversionError {
    #[error("missing hashtag")]
    MissingHashtag,
}

#[derive(Debug, Error, Clone)]
pub enum AvatarPublishError<Fs: FileStore> {
    #[error("disconnected")]
    Disconnected,
    #[error("image read: {0}")]
    Read(Arc<std::io::Error>),
    #[error("image: {0}")]
    Image(Arc<ImageError>),
    #[error("pep publish: {0}")]
    Publish(#[from] CommandError<PEPError>),
    #[error("bytes number conversion: {0}")]
    FromInt(#[from] TryFromIntError),
    #[error("could not save to disk")]
    FileStore(Fs::Err),
}

impl<Fs: FileStore> From<std::io::Error> for AvatarPublishError<Fs> {
    fn from(value: std::io::Error) -> Self {
        Self::Read(Arc::new(value))
    }
}

impl<Fs: FileStore> From<ImageError> for AvatarPublishError<Fs> {
    fn from(value: ImageError) -> Self {
        Self::Image(Arc::new(value))
    }
}