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 { #[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), #[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 { #[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), } #[derive(Debug, Error, Clone)] pub enum AvatarUpdateError { #[error("could not save to disk: {0}")] FileStore(Fs::Err), #[error("could not fetch avatar data: {0}")] PEPError(#[from] CommandError), #[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> for RosterError { fn from(value: CommandError) -> 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); impl From for DatabaseError { fn from(e: sqlx::Error) -> Self { Self(Arc::new(e)) } } impl From 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), #[error("migration: {0}")] Migration(Arc), #[error("io: {0}")] Io(Arc), #[error("invalid path")] InvalidPath, } impl From for DatabaseOpenError { fn from(e: sqlx::migrate::MigrateError) -> Self { Self::Migration(Arc::new(e)) } } impl From 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), #[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), #[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 { #[error("disconnected")] Disconnected, #[error("image read: {0}")] Read(Arc), #[error("image: {0}")] Image(Arc), #[error("pep publish: {0}")] Publish(#[from] CommandError), #[error("bytes number conversion: {0}")] FromInt(#[from] TryFromIntError), #[error("could not save to disk")] FileStore(Fs::Err), } impl From for AvatarPublishError { fn from(value: std::io::Error) -> Self { Self::Read(Arc::new(value)) } } impl From for AvatarPublishError { fn from(value: ImageError) -> Self { Self::Image(Arc::new(value)) } }