Skip to main content

im_core/
error.rs

1use std::fmt;
2
3pub type ImResult<T> = Result<T, ImError>;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum ImError {
7    InvalidInput {
8        field: Option<String>,
9        message: String,
10    },
11    IdentityRequired,
12    IdentityNotFound {
13        selector: String,
14    },
15    DefaultIdentityMissing,
16    IdentityNotReady {
17        identity: String,
18        missing: Vec<String>,
19    },
20    AuthRequired,
21    SessionExpired,
22    PermissionDenied,
23    PeerNotFound {
24        peer: String,
25    },
26    GroupNotFound {
27        group: String,
28    },
29    MessageNotFound {
30        message_id: String,
31    },
32    TransportUnavailable {
33        detail: String,
34    },
35    UnsupportedCapability {
36        capability: String,
37    },
38    LocalStateUnavailable {
39        detail: String,
40    },
41    PathUnavailable {
42        path_kind: String,
43        detail: String,
44    },
45    CredentialFileUnreadable {
46        path_kind: String,
47        detail: String,
48    },
49    Service {
50        status_code: Option<u16>,
51        code: Option<String>,
52        message: String,
53        data: Option<serde_json::Value>,
54    },
55    Serialization {
56        detail: String,
57    },
58    Io {
59        detail: String,
60    },
61    Internal {
62        message: String,
63    },
64}
65
66impl ImError {
67    pub fn invalid_input(field: impl Into<Option<String>>, message: impl Into<String>) -> Self {
68        Self::InvalidInput {
69            field: field.into(),
70            message: message.into(),
71        }
72    }
73
74    pub fn unsupported(capability: impl Into<String>) -> Self {
75        Self::UnsupportedCapability {
76            capability: capability.into(),
77        }
78    }
79}
80
81impl fmt::Display for ImError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            Self::InvalidInput {
85                field: Some(field),
86                message,
87            } => write!(f, "invalid input for {field}: {message}"),
88            Self::InvalidInput {
89                field: None,
90                message,
91            } => write!(f, "invalid input: {message}"),
92            Self::IdentityRequired => f.write_str("identity is required"),
93            Self::IdentityNotFound { selector } => {
94                write!(f, "identity not found for selector {selector}")
95            }
96            Self::DefaultIdentityMissing => f.write_str("default identity is missing"),
97            Self::IdentityNotReady { identity, missing } => {
98                write!(
99                    f,
100                    "identity {identity} is not ready: {}",
101                    missing.join(", ")
102                )
103            }
104            Self::AuthRequired => f.write_str("authentication is required"),
105            Self::SessionExpired => f.write_str("session expired"),
106            Self::PermissionDenied => f.write_str("permission denied"),
107            Self::PeerNotFound { peer } => write!(f, "peer not found: {peer}"),
108            Self::GroupNotFound { group } => write!(f, "group not found: {group}"),
109            Self::MessageNotFound { message_id } => write!(f, "message not found: {message_id}"),
110            Self::TransportUnavailable { detail } => write!(f, "transport unavailable: {detail}"),
111            Self::UnsupportedCapability { capability } => {
112                write!(f, "unsupported capability: {capability}")
113            }
114            Self::LocalStateUnavailable { detail } => {
115                write!(f, "local state unavailable: {detail}")
116            }
117            Self::PathUnavailable { path_kind, detail } => {
118                write!(f, "{path_kind} path unavailable: {detail}")
119            }
120            Self::CredentialFileUnreadable { path_kind, detail } => {
121                write!(f, "{path_kind} credential file unreadable: {detail}")
122            }
123            Self::Service {
124                status_code,
125                code,
126                message,
127                data: _,
128            } => match (status_code, code) {
129                (Some(status), Some(code)) => {
130                    write!(f, "service error {status} ({code}): {message}")
131                }
132                (Some(status), None) => write!(f, "service error {status}: {message}"),
133                (None, Some(code)) => write!(f, "service error ({code}): {message}"),
134                (None, None) => write!(f, "service error: {message}"),
135            },
136            Self::Serialization { detail } => write!(f, "serialization error: {detail}"),
137            Self::Io { detail } => write!(f, "io error: {detail}"),
138            Self::Internal { message } => write!(f, "internal error: {message}"),
139        }
140    }
141}
142
143impl std::error::Error for ImError {}
144
145impl From<std::io::Error> for ImError {
146    fn from(value: std::io::Error) -> Self {
147        Self::Io {
148            detail: value.to_string(),
149        }
150    }
151}
152
153#[cfg(feature = "sqlite")]
154impl From<rusqlite::Error> for ImError {
155    fn from(value: rusqlite::Error) -> Self {
156        Self::LocalStateUnavailable {
157            detail: value.to_string(),
158        }
159    }
160}