zenoh_result/
lib.rs

1//
2// Copyright (c) 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15//! ⚠️ WARNING ⚠️
16//!
17//! This crate is intended for Zenoh's internal use.
18//!
19//! [Click here for Zenoh's documentation](https://docs.rs/zenoh/latest/zenoh)
20#![cfg_attr(not(feature = "std"), no_std)]
21extern crate alloc;
22
23use core::fmt;
24
25use anyhow::Error as AnyError;
26
27#[cold]
28pub const fn cold() {}
29pub const fn likely(b: bool) -> bool {
30    if !b {
31        cold()
32    }
33    b
34}
35pub const fn unlikely(b: bool) -> bool {
36    if b {
37        cold()
38    }
39    b
40}
41
42#[cfg(not(feature = "std"))]
43use alloc::boxed::Box;
44#[cfg(not(feature = "std"))]
45use core::any::Any;
46
47// +-------+
48// | ERROR |
49// +-------+
50
51#[cfg(not(feature = "std"))]
52pub trait IError: fmt::Display + fmt::Debug + Any {}
53// FIXME: Until core::error::Error is stabilized, we rescue to our own implementation of an error trait
54// See tracking issue for most recent updates: https://github.com/rust-lang/rust/issues/103765
55
56#[cfg(not(feature = "std"))]
57impl dyn IError {
58    pub fn is<T: 'static>(&self) -> bool {
59        Any::type_id(self) == core::any::TypeId::of::<T>()
60    }
61    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
62        self.is::<T>()
63            .then(|| unsafe { &*(self as *const dyn IError as *const T) })
64    }
65}
66
67#[cfg(not(feature = "std"))]
68impl dyn IError + Send {
69    pub fn is<T: 'static>(&self) -> bool {
70        Any::type_id(self) == core::any::TypeId::of::<T>()
71    }
72    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
73        self.is::<T>()
74            .then(|| unsafe { &*(self as *const dyn IError as *const T) })
75    }
76}
77
78#[cfg(not(feature = "std"))]
79impl dyn IError + Send + Sync {
80    pub fn is<T: 'static>(&self) -> bool {
81        Any::type_id(self) == core::any::TypeId::of::<T>()
82    }
83    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
84        self.is::<T>()
85            .then(|| unsafe { &*(self as *const dyn IError as *const T) })
86    }
87}
88
89#[cfg(feature = "std")]
90pub use std::error::Error as IError;
91
92pub type Error = Box<dyn IError + Send + Sync + 'static>;
93
94// +---------+
95// | ZRESULT |
96// +---------+
97
98pub type ZResult<T> = core::result::Result<T, Error>;
99
100// +--------+
101// | ZERROR |
102// +--------+
103
104#[repr(transparent)]
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
106pub struct NegativeI8(i8);
107
108impl NegativeI8 {
109    pub const fn new(v: i8) -> Self {
110        if v >= 0 {
111            panic!("Non-negative value used in NegativeI8")
112        }
113        NegativeI8(v)
114    }
115    pub const fn get(self) -> i8 {
116        self.0
117    }
118    pub const MIN: Self = Self::new(i8::MIN);
119}
120
121pub struct ZError {
122    error: AnyError,
123    file: &'static str,
124    line: u32,
125    errno: NegativeI8,
126    source: Option<Error>,
127}
128
129unsafe impl Send for ZError {}
130unsafe impl Sync for ZError {}
131
132impl ZError {
133    pub fn new<E: Into<AnyError>>(
134        error: E,
135        file: &'static str,
136        line: u32,
137        errno: NegativeI8,
138    ) -> ZError {
139        ZError {
140            error: error.into(),
141            file,
142            line,
143            errno,
144            source: None,
145        }
146    }
147    pub fn set_source<S: Into<Error>>(mut self, source: S) -> Self {
148        self.source = Some(source.into());
149        self
150    }
151}
152
153#[cfg(feature = "std")]
154impl std::error::Error for ZError {
155    fn source(&self) -> Option<&'_ (dyn std::error::Error + 'static)> {
156        self.source
157            .as_ref()
158            .map(|r| unsafe { core::mem::transmute(r.as_ref()) })
159    }
160}
161
162#[cfg(not(feature = "std"))]
163impl IError for ZError {}
164
165impl fmt::Debug for ZError {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        fmt::Display::fmt(&self, f)
168    }
169}
170
171impl fmt::Display for ZError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        write!(f, "{} at {}:{}.", self.error, self.file, self.line)?;
174        if let Some(s) = &self.source {
175            write!(f, " - Caused by {}", *s)?;
176        }
177        Ok(())
178    }
179}
180
181#[cfg(not(feature = "std"))]
182impl From<ZError> for Error {
183    fn from(value: ZError) -> Self {
184        Box::new(value)
185    }
186}
187
188// +----------+
189// | SHMERROR |
190// +----------+
191
192#[derive(Debug)]
193pub struct ShmError(pub ZError);
194
195#[cfg(feature = "std")]
196impl std::error::Error for ShmError {
197    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
198        self.0.source()
199    }
200}
201
202#[cfg(not(feature = "std"))]
203impl IError for ShmError {}
204
205impl fmt::Display for ShmError {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        self.0.fmt(f)
208    }
209}
210
211#[cfg(not(feature = "std"))]
212impl From<ShmError> for Error {
213    fn from(value: ShmError) -> Self {
214        Box::new(value)
215    }
216}
217
218// +-------+
219// | ERRNO |
220// +-------+
221
222pub trait ErrNo {
223    fn errno(&self) -> NegativeI8;
224}
225
226impl ErrNo for ZError {
227    fn errno(&self) -> NegativeI8 {
228        self.errno
229    }
230}
231
232impl ErrNo for ShmError {
233    fn errno(&self) -> NegativeI8 {
234        self.0.errno
235    }
236}
237
238#[cfg(feature = "std")]
239impl ErrNo for dyn std::error::Error {
240    fn errno(&self) -> NegativeI8 {
241        match self.downcast_ref::<ZError>() {
242            Some(e) => e.errno(),
243            None => NegativeI8::new(i8::MIN),
244        }
245    }
246}
247
248#[cfg(not(feature = "std"))]
249impl ErrNo for dyn IError {
250    fn errno(&self) -> NegativeI8 {
251        match self.downcast_ref::<ZError>() {
252            Some(e) => e.errno(),
253            None => NegativeI8::new(i8::MIN),
254        }
255    }
256}
257
258#[cfg(feature = "std")]
259impl ErrNo for dyn std::error::Error + Send {
260    fn errno(&self) -> NegativeI8 {
261        match self.downcast_ref::<ZError>() {
262            Some(e) => e.errno(),
263            None => NegativeI8::new(i8::MIN),
264        }
265    }
266}
267
268#[cfg(not(feature = "std"))]
269impl ErrNo for dyn IError + Send {
270    fn errno(&self) -> NegativeI8 {
271        match self.downcast_ref::<ZError>() {
272            Some(e) => e.errno(),
273            None => NegativeI8::new(i8::MIN),
274        }
275    }
276}
277
278#[cfg(feature = "std")]
279impl ErrNo for dyn std::error::Error + Send + Sync {
280    fn errno(&self) -> NegativeI8 {
281        match self.downcast_ref::<ZError>() {
282            Some(e) => e.errno(),
283            None => NegativeI8::new(i8::MIN),
284        }
285    }
286}
287
288#[cfg(not(feature = "std"))]
289impl ErrNo for dyn IError + Send + Sync {
290    fn errno(&self) -> NegativeI8 {
291        match self.downcast_ref::<ZError>() {
292            Some(e) => e.errno(),
293            None => NegativeI8::new(i8::MIN),
294        }
295    }
296}
297
298// +--------+
299// | MACROS |
300// +--------+
301
302pub use anyhow::anyhow;
303#[macro_export]
304macro_rules! zerror {
305    (($errno:expr) $source: expr => $($t: tt)*) => {
306        $crate::ZError::new($crate::anyhow!($($t)*), file!(), line!(), $crate::NegativeI8::new($errno as i8)).set_source($source)
307    };
308    (($errno:expr) $t: literal) => {
309        $crate::ZError::new($crate::anyhow!($t), file!(), line!(), $crate::NegativeI8::new($errno as i8))
310    };
311    (($errno:expr) $t: expr) => {
312        $crate::ZError::new($t, file!(), line!(), $crate::NegativeI8::new($errno as i8))
313    };
314    (($errno:expr) $($t: tt)*) => {
315        $crate::ZError::new($crate::anyhow!($($t)*), file!(), line!(), $crate::NegativeI8::new($errno as i8))
316    };
317    ($source: expr => $($t: tt)*) => {
318        $crate::ZError::new($crate::anyhow!($($t)*), file!(), line!(), $crate::NegativeI8::MIN).set_source($source)
319    };
320    ($t: literal) => {
321        $crate::ZError::new($crate::anyhow!($t), file!(), line!(), $crate::NegativeI8::MIN)
322    };
323    ($t: expr) => {
324        $crate::ZError::new($t, file!(), line!(), $crate::NegativeI8::MIN)
325    };
326    ($($t: tt)*) => {
327        $crate::ZError::new($crate::anyhow!($($t)*), file!(), line!(), $crate::NegativeI8::MIN)
328    };
329}
330
331// @TODO: re-design ZError and macros
332// This macro is a shorthand for the creation of a ZError
333#[macro_export]
334macro_rules! bail{
335    ($($t: tt)*) => {
336        return Err($crate::zerror!($($t)*).into())
337    };
338}
339
340// This macro is a shorthand for the conversion of any Error into a ZError
341#[macro_export]
342macro_rules! to_zerror {
343    ($kind:ident, $descr:expr) => {
344        |e| Err(zerror!($kind, $descr, e))
345    };
346}