1use atrium_api::xrpc::error::XrpcErrorKind;
3use atrium_api::xrpc::http::StatusCode;
4use atrium_api::xrpc::Error as XrpcError;
5use std::fmt::Debug;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
10pub enum Error {
11 #[error("not logged in")]
12 NotLoggedIn,
13 #[error("invalid AT URI")]
14 InvalidAtUri,
15 #[error("xrpc response error: {0}")]
16 Xrpc(Box<GenericXrpcError>),
17 #[error("loading config error: {0}")]
18 ConfigLoad(Box<dyn std::error::Error + Send + Sync + 'static>),
19 #[error("saving config error: {0}")]
20 ConfigSave(Box<dyn std::error::Error + Send + Sync + 'static>),
21 #[error(transparent)]
22 ApiType(#[from] atrium_api::error::Error),
23 #[error(transparent)]
24 Moderation(#[from] crate::moderation::Error),
25}
26
27#[derive(Error, Debug)]
29pub enum GenericXrpcError {
30 Response { status: StatusCode, error: Option<String> },
31 Other(String),
32}
33
34impl std::fmt::Display for GenericXrpcError {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 Self::Response { status, error } => {
38 write!(f, "{}", status.as_str())?;
39 let Some(error) = &error else {
40 return Ok(());
41 };
42 if !error.is_empty() {
43 write!(f, " {error}")?;
44 }
45 }
46 Self::Other(s) => {
47 write!(f, "{s}")?;
48 }
49 }
50 Ok(())
51 }
52}
53
54impl<E> From<XrpcError<E>> for Error
55where
56 E: Debug,
57{
58 fn from(err: XrpcError<E>) -> Self {
59 if let XrpcError::XrpcResponse(e) = err {
60 Self::Xrpc(Box::new(GenericXrpcError::Response {
61 status: e.status,
62 error: e.error.map(|e| match e {
63 XrpcErrorKind::Custom(_) => String::from("custom error"),
64 XrpcErrorKind::Undefined(res) => res.to_string(),
65 }),
66 }))
67 } else {
68 Self::Xrpc(Box::new(GenericXrpcError::Other(format!("{err:?}"))))
69 }
70 }
71}
72
73pub type Result<T> = core::result::Result<T, Error>;