Skip to main content

veilid_core/veilid_api/
error.rs

1use super::*;
2
3#[allow(unused_macros)]
4#[macro_export]
5macro_rules! apibail_not_initialized {
6    () => {
7        return Err(VeilidAPIError::not_initialized())
8    };
9}
10
11#[allow(unused_macros)]
12#[macro_export]
13macro_rules! apibail_timeout {
14    () => {
15        return Err(VeilidAPIError::timeout())
16    };
17}
18
19#[allow(unused_macros)]
20#[macro_export]
21macro_rules! apibail_try_again {
22    ($x:expr) => {
23        return Err(VeilidAPIError::try_again($x))
24    };
25    ($fmt:literal, $($args:tt)*) => {
26        return Err(VeilidAPIError::try_again( format!($fmt, $($args)*) ))
27    };
28}
29
30#[allow(unused_macros)]
31#[macro_export]
32macro_rules! apibail_generic {
33    ($x:expr) => {
34        return Err(VeilidAPIError::generic($x))
35    };
36    ($fmt:literal, $($args:tt)*) => {
37        return Err(VeilidAPIError::generic( format!($fmt, $($args)*) ))
38    };
39}
40
41#[allow(unused_macros)]
42#[macro_export]
43macro_rules! apibail_internal {
44    ($x:expr) => {
45        return Err(VeilidAPIError::internal($x))
46    };
47    ($fmt:literal, $($args:tt)*) => {
48        return Err(VeilidAPIError::internal( format!($fmt, $($args)*) ))
49    };
50}
51
52#[allow(unused_macros)]
53#[macro_export]
54macro_rules! apibail_parse_error {
55    ($x:expr, $y:expr) => {
56        return Err(VeilidAPIError::parse_error($x, $y))
57    };
58}
59
60#[allow(unused_macros)]
61#[macro_export]
62macro_rules! apibail_missing_argument {
63    ($x:expr, $y:expr) => {
64        return Err(VeilidAPIError::missing_argument($x, $y))
65    };
66}
67
68#[allow(unused_macros)]
69#[macro_export]
70macro_rules! apibail_invalid_argument {
71    ($x:expr, $y:expr, $z:expr) => {
72        return Err(VeilidAPIError::invalid_argument($x, $y, $z))
73    };
74}
75
76#[allow(unused_macros)]
77#[macro_export]
78macro_rules! apibail_no_connection {
79    ($x:expr) => {
80        return Err(VeilidAPIError::no_connection($x))
81    };
82    ($fmt:literal, $($args: tt)* ) => {
83        return Err(VeilidAPIError::no_connection( format!($fmt, arg $($args)*) ))
84    };
85
86}
87
88#[allow(unused_macros)]
89#[macro_export]
90macro_rules! apibail_key_not_found {
91    ($x:expr) => {
92        return Err(VeilidAPIError::key_not_found($x))
93    };
94}
95
96#[allow(unused_macros)]
97#[macro_export]
98macro_rules! apibail_invalid_target {
99    ($x:expr) => {
100        return Err(VeilidAPIError::invalid_target($x))
101    };
102}
103
104#[allow(unused_macros)]
105#[macro_export]
106macro_rules! apibail_route_not_found {
107    ($x:expr) => {
108        return Err(VeilidAPIError::route_not_found($x))
109    };
110}
111
112#[allow(unused_macros)]
113#[macro_export]
114macro_rules! apibail_already_initialized {
115    () => {
116        return Err(VeilidAPIError::already_initialized())
117    };
118}
119
120#[derive(
121    ThisError, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Serialize, Deserialize, JsonSchema,
122)]
123#[cfg_attr(
124    all(target_arch = "wasm32", target_os = "unknown"),
125    derive(Tsify),
126    tsify(into_wasm_abi)
127)]
128#[serde(tag = "kind")]
129#[must_use]
130pub enum VeilidAPIError {
131    #[error("Not initialized")]
132    NotInitialized,
133    #[error("Already initialized")]
134    AlreadyInitialized,
135    #[error("Timeout")]
136    Timeout,
137    #[error("TryAgain: {message}")]
138    TryAgain { message: String },
139    #[error("Shutdown")]
140    Shutdown,
141    #[error("Invalid target: {message}")]
142    InvalidTarget { message: String },
143    #[error("No connection: {message}")]
144    NoConnection { message: String },
145    #[error("Key not found: {key}")]
146    KeyNotFound {
147        #[schemars(with = "String")]
148        key: OpaqueRecordKey,
149    },
150    #[error("Internal: {message}")]
151    Internal { message: String },
152    #[error("Unimplemented: {message}")]
153    Unimplemented { message: String },
154    #[error("Parse error: '{message}' with value '{value}'")]
155    ParseError { message: String, value: String },
156    #[error("Invalid argument: '{context}' for '{argument}' with value '{value}'")]
157    InvalidArgument {
158        context: String,
159        argument: String,
160        value: String,
161    },
162    #[error("Missing argument: '{context}' for '{argument}'")]
163    MissingArgument { context: String, argument: String },
164    #[error("Generic: {message}")]
165    Generic { message: String },
166}
167
168impl VeilidAPIError {
169    pub fn not_initialized() -> Self {
170        Self::NotInitialized
171    }
172    pub fn already_initialized() -> Self {
173        Self::AlreadyInitialized
174    }
175    pub fn timeout() -> Self {
176        Self::Timeout
177    }
178    pub fn try_again<T: ToString>(msg: T) -> Self {
179        Self::TryAgain {
180            message: msg.to_string(),
181        }
182    }
183    pub fn shutdown() -> Self {
184        Self::Shutdown
185    }
186    pub fn invalid_target<T: ToString>(msg: T) -> Self {
187        Self::InvalidTarget {
188            message: msg.to_string(),
189        }
190    }
191    pub fn no_connection<T: ToString>(msg: T) -> Self {
192        Self::NoConnection {
193            message: msg.to_string(),
194        }
195    }
196    pub fn key_not_found(key: OpaqueRecordKey) -> Self {
197        Self::KeyNotFound { key }
198    }
199    pub fn internal<T: ToString>(msg: T) -> Self {
200        Self::Internal {
201            message: msg.to_string(),
202        }
203    }
204    pub fn unimplemented<T: ToString>(msg: T) -> Self {
205        Self::Unimplemented {
206            message: msg.to_string(),
207        }
208    }
209    pub fn parse_error<T: ToString, S: ToString>(msg: T, value: S) -> Self {
210        Self::ParseError {
211            message: msg.to_string(),
212            value: value.to_string(),
213        }
214    }
215    pub fn invalid_argument<T: ToString, S: ToString, R: ToString>(
216        context: T,
217        argument: S,
218        value: R,
219    ) -> Self {
220        Self::InvalidArgument {
221            context: context.to_string(),
222            argument: argument.to_string(),
223            value: value.to_string(),
224        }
225    }
226    pub fn missing_argument<T: ToString, S: ToString>(context: T, argument: S) -> Self {
227        Self::MissingArgument {
228            context: context.to_string(),
229            argument: argument.to_string(),
230        }
231    }
232    pub fn generic<T: ToString>(msg: T) -> Self {
233        Self::Generic {
234            message: msg.to_string(),
235        }
236    }
237
238    pub(crate) fn from_network_result<T>(nr: NetworkResult<T>) -> Result<T, Self> {
239        match nr {
240            NetworkResult::Timeout => Err(VeilidAPIError::timeout()),
241            NetworkResult::ServiceUnavailable(m) => Err(VeilidAPIError::invalid_target(m)),
242            NetworkResult::NoConnection(m) => Err(VeilidAPIError::no_connection(m)),
243            NetworkResult::AlreadyExists(m) => {
244                Err(VeilidAPIError::generic(format!("Already exists: {}", m)))
245            }
246            NetworkResult::InvalidMessage(m) => {
247                Err(VeilidAPIError::parse_error("Invalid message", m))
248            }
249            NetworkResult::Value(v) => Ok(v),
250        }
251    }
252
253    pub(crate) fn log_level(&self) -> Level {
254        match self {
255            VeilidAPIError::NotInitialized
256            | VeilidAPIError::AlreadyInitialized
257            | VeilidAPIError::InvalidTarget { message: _ }
258            | VeilidAPIError::Internal { message: _ }
259            | VeilidAPIError::Generic { message: _ }
260            | VeilidAPIError::ParseError {
261                message: _,
262                value: _,
263            }
264            | VeilidAPIError::InvalidArgument {
265                context: _,
266                argument: _,
267                value: _,
268            }
269            | VeilidAPIError::MissingArgument {
270                context: _,
271                argument: _,
272            }
273            | VeilidAPIError::Shutdown => Level::ERROR,
274
275            VeilidAPIError::NoConnection { message: _ }
276            | VeilidAPIError::KeyNotFound { key: _ }
277            | VeilidAPIError::Unimplemented { message: _ } => Level::WARN,
278
279            VeilidAPIError::Timeout | VeilidAPIError::TryAgain { message: _ } => Level::DEBUG,
280        }
281    }
282}
283
284pub type VeilidAPIResult<T> = Result<T, VeilidAPIError>;
285
286pub trait OkVeilidAPIResult<T> {
287    fn ok_try_again(self) -> VeilidAPIResult<Option<T>>;
288    fn ok_try_again_timeout(self) -> VeilidAPIResult<Option<T>>;
289}
290
291impl<T> OkVeilidAPIResult<T> for VeilidAPIResult<Option<T>> {
292    fn ok_try_again(self) -> VeilidAPIResult<Option<T>> {
293        match self {
294            Ok(v) => Ok(v),
295            Err(VeilidAPIError::TryAgain { message: _ }) => Ok(None),
296            Err(e) => Err(e),
297        }
298    }
299    fn ok_try_again_timeout(self) -> VeilidAPIResult<Option<T>> {
300        match self {
301            Ok(v) => Ok(v),
302            Err(VeilidAPIError::TryAgain { message: _ }) => Ok(None),
303            Err(VeilidAPIError::Timeout) => Ok(None),
304            Err(e) => Err(e),
305        }
306    }
307}
308
309impl From<std::io::Error> for VeilidAPIError {
310    fn from(e: std::io::Error) -> Self {
311        match e.kind() {
312            std::io::ErrorKind::TimedOut => VeilidAPIError::timeout(),
313            std::io::ErrorKind::ConnectionRefused => VeilidAPIError::no_connection(e.to_string()),
314            std::io::ErrorKind::ConnectionReset => VeilidAPIError::no_connection(e.to_string()),
315            // #[cfg(feature = "io_error_more")]
316            // std::io::ErrorKind::HostUnreachable => VeilidAPIError::no_connection(e.to_string()),
317            // #[cfg(feature = "io_error_more")]
318            // std::io::ErrorKind::NetworkUnreachable => VeilidAPIError::no_connection(e.to_string()),
319            std::io::ErrorKind::ConnectionAborted => VeilidAPIError::no_connection(e.to_string()),
320            std::io::ErrorKind::NotConnected => VeilidAPIError::no_connection(e.to_string()),
321            std::io::ErrorKind::AddrInUse => VeilidAPIError::no_connection(e.to_string()),
322            std::io::ErrorKind::AddrNotAvailable => VeilidAPIError::no_connection(e.to_string()),
323            // #[cfg(feature = "io_error_more")]
324            // std::io::ErrorKind::NetworkDown => VeilidAPIError::no_connection(e.to_string()),
325            // #[cfg(feature = "io_error_more")]
326            // std::io::ErrorKind::ReadOnlyFilesystem => VeilidAPIError::internal(e.to_string()),
327            // #[cfg(feature = "io_error_more")]
328            // std::io::ErrorKind::NotSeekable => VeilidAPIError::internal(e.to_string()),
329            // #[cfg(feature = "io_error_more")]
330            // std::io::ErrorKind::FilesystemQuotaExceeded => VeilidAPIError::internal(e.to_string()),
331            // #[cfg(feature = "io_error_more")]
332            // std::io::ErrorKind::Deadlock => VeilidAPIError::internal(e.to_string()),
333            std::io::ErrorKind::Unsupported => VeilidAPIError::internal(e.to_string()),
334            std::io::ErrorKind::OutOfMemory => VeilidAPIError::internal(e.to_string()),
335            _ => VeilidAPIError::generic(e.to_string()),
336        }
337    }
338}