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}