xcept/
lib.rs

1use crate::context::SingleErrorStorage;
2use std::hint::unreachable_unchecked;
3use std::marker::PhantomData;
4
5pub mod context;
6pub mod multihandler;
7
8pub use multihandler::builder;
9pub use multihandler::try_or_handle;
10
11/// Marker trait for error compatible types
12///
13/// This is blanket implemented for all types that satisfies it.
14pub trait Error: 'static {}
15
16impl<T: 'static> Error for T {}
17
18/// The main result type
19///
20/// Unlike `std::result::Result` this `Result` can only hold a value, or an error flag. The error,
21/// if one has occurred will be set directly at the handling scope.
22///
23pub struct Result<T> {
24    value: core::result::Result<T, u32>,
25    _not_send: PhantomData<*mut ()>,
26}
27
28impl<T> Result<T> {
29    /// Create a new `Result` holding `value`
30    ///
31    /// # Arguments
32    ///
33    /// * `value`: The value held by the result.
34    ///
35    /// returns: Result<T>
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// let x = xcept::Result::new(10);
41    /// assert_eq!(x.unwrap(), 10);
42    /// ```
43    #[inline]
44    pub fn new(value: T) -> Self {
45        Self {
46            value: Ok(value),
47            _not_send: PhantomData,
48        }
49    }
50
51    /// Create a new `Result` holding an error, identified by `id`
52    ///
53    /// This should generally not be used, but for can be useful for those wishing to implement
54    /// custom `try_handle` functions.
55    ///
56    /// # Arguments
57    ///
58    /// * `id`: The ID of the error it holds
59    ///
60    /// returns: `Result<T>`.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// let res = xcept::Result::new_with_error_id(1);
66    /// assert_eq!(res.error_id().unwrap(), 1);
67    /// ```
68    ///
69    #[inline]
70    pub fn new_with_error_id(id: u32) -> Self {
71        Self {
72            value: Err(id),
73            _not_send: PhantomData,
74        }
75    }
76
77    /// Create a new `Result` with an error indication.
78    ///
79    /// The error is not held within `Result`, but is directly assigned to the nearest handler,
80    /// if one is found. If no handler is found the error is dropped.
81    ///
82    /// # Arguments
83    ///
84    /// * `err`: The error to report.
85    ///
86    /// returns: Result<T>
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// let err: xcept::Result<i32> = xcept::Result::new_error("Error");
92    /// assert!(!err.is_ok());
93    /// assert!(err.is_error());
94    /// ```
95    #[inline]
96    pub fn new_error<E: Error>(err: E) -> Self {
97        let id = context::push_error(err);
98        Self {
99            value: Err(id),
100            _not_send: PhantomData,
101        }
102    }
103
104    /// Test if a `Result` contains a value.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// let err: xcept::Result<i32> = xcept::Result::new_error("Error");
110    /// assert!(!err.is_ok());
111    /// assert!(err.is_error());
112    /// ```
113    #[inline]
114    pub fn is_ok(&self) -> bool {
115        self.value.is_ok()
116    }
117
118    /// Convert the `Result` to an `Option<T>`
119    #[inline]
120    pub fn ok(self) -> Option<T> {
121        self.value.ok()
122    }
123
124    /// Test if a `Result` contains an error.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// let err: xcept::Result<i32> = xcept::Result::new_error("Error");
130    /// assert!(!err.is_ok());
131    /// assert!(err.is_error());
132    /// ```
133    #[inline]
134    pub fn is_error(&self) -> bool {
135        self.value.is_err()
136    }
137
138    /// Unwrap the `Result` to a value, panicking if `Result` holds an error.
139    ///
140    /// # Panics
141    ///
142    /// If the `Result` doesn't contain a value we panic instead.
143    #[inline]
144    pub fn unwrap(self) -> T {
145        self.value.unwrap()
146    }
147
148    /// Unchecked unwrap
149    ///
150    /// # Safety
151    ///
152    /// If `result.is_ok()` returns `false` this will result in *undefined behaviour*.
153    #[inline]
154    pub unsafe fn unwrap_unchecked(self) -> T {
155        self.value.unwrap_unchecked()
156    }
157
158    /// Get the ID of the error that was set when `Result` was created.
159    #[inline]
160    pub fn error_id(&self) -> Option<u32> {
161        match &self.value {
162            Ok(_) => None,
163            Err(x) => Some(*x),
164        }
165    }
166
167    /// Unchecked getter of the ID of the error that was set when `Result` was created.
168    ///
169    /// # Safety
170    ///
171    /// If `result.is_error()` returns `false` this will result in *undefined behaviour*.
172    #[inline]
173    pub unsafe fn unchecked_error_id(&self) -> u32 {
174        match &self.value {
175            Err(x) => *x,
176            _ => unreachable_unchecked(),
177        }
178    }
179}
180
181impl<T> From<T> for Result<T> {
182    #[inline]
183    fn from(v: T) -> Self {
184        Self::new(v)
185    }
186}
187
188impl<T, E: Error> From<std::result::Result<T, E>> for Result<T> {
189    #[inline]
190    fn from(val: std::result::Result<T, E>) -> Self {
191        match val {
192            Ok(v) => Self::new(v),
193            Err(e) => Self::new_error(e),
194        }
195    }
196}
197
198/// Try to execute a function, and try to handle an error, if one occurs.
199///
200/// Use [`try_or_handle`] to handle multiple error types.
201///
202/// # Arguments
203///
204/// * `func`: The function to execute
205/// * `handler`: Handle a single error
206///
207/// returns: Result<T>
208///
209/// # Examples
210///
211/// ```
212/// fn to_int(string: &str) -> xcept::Result<i32> {
213///     string.parse().into()
214/// }
215///
216/// type ErrorT = <i32 as std::str::FromStr>::Err;
217/// let res = xcept::try_or_handle_one(|| to_int("abc"), |_err: ErrorT| xcept::Result::new(-1));
218/// assert_eq!(res.unwrap(), -1);
219///
220/// let res = xcept::try_or_handle_one(|| to_int("10"), |_err: ErrorT| xcept::Result::new(-1));
221/// assert_eq!(res.unwrap(), 10);
222/// ```
223#[inline]
224pub fn try_or_handle_one<F, H, T, E>(func: F, handler: H) -> Result<T>
225where
226    F: FnOnce() -> Result<T>,
227    H: FnOnce(E) -> Result<T>,
228    E: Error,
229{
230    let mut error_storage: crate::context::SingleErrorStorage<E> = SingleErrorStorage::default();
231    let mut scope = context::ScopeNode::new(&mut error_storage);
232    // Safety: scope is kept alive, guard is dropped before `scope` is used again
233    let guard = unsafe { context::push_handling_scope(&mut scope) };
234    let res = func();
235    drop(guard);
236    if res.is_error() {
237        // Safety: res.is_error() is true
238        unsafe { error_storage.unchecked_try_handle(res, handler) }
239    } else {
240        res
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use std::cell::RefCell;
247
248    #[test]
249    fn try_or_handle_one() {
250        fn func() -> crate::Result<i32> {
251            crate::Result::new_error(true)
252        }
253
254        let x = crate::try_or_handle_one(func, |_e: bool| 1.into()).unwrap();
255        assert_eq!(x, 1);
256
257        fn two_errors() -> crate::Result<i32> {
258            crate::Result::<i32>::new_error(true);
259            crate::Result::new_error(10)
260        }
261
262        let mut called = false;
263        let x = crate::try_or_handle_one(two_errors, |_: bool| {
264            called = true;
265            2.into()
266        });
267
268        assert!(!called);
269        assert!(x.is_error());
270    }
271
272    #[test]
273    fn error_without_scopes() {
274        let res: crate::Result<i32> = crate::Result::new_error(true);
275        assert!(res.is_error());
276    }
277
278    #[test]
279    fn multi_handlers() {
280        fn handler1(e: i32) -> crate::Result<i32> {
281            (e * 2).into()
282        }
283        fn handler2(_e: &str) -> crate::Result<i32> {
284            (-1).into()
285        }
286        fn handler3(_e: bool) -> crate::Result<i32> {
287            crate::Result::new_error("Bool not supported!")
288        }
289        let handlers = crate::multihandler::builder(handler1)
290            .handle(handler2)
291            .handle(handler3)
292            .build();
293
294        let res = crate::try_or_handle(|| 10.into(), handlers.clone());
295
296        assert_eq!(res.unwrap(), 10);
297
298        let res = crate::try_or_handle(|| crate::Result::new_error(15), handlers.clone());
299
300        assert_eq!(res.unwrap(), 30);
301
302        let res = crate::try_or_handle(
303            || crate::Result::new_error("should be -1"),
304            handlers.clone(),
305        );
306
307        assert_eq!(res.unwrap(), -1);
308
309        let res = crate::try_or_handle(|| crate::Result::new_error(false), handlers.clone());
310
311        assert_eq!(res.is_error(), true);
312    }
313
314    #[test]
315    fn multi_handlers_with_refs() {
316        let which = RefCell::new(0);
317        let handlers = crate::multihandler::builder(|_: i32| {
318            *which.borrow_mut() = 1;
319            crate::Result::new(1)
320        })
321        .handle(|_: &str| {
322            *which.borrow_mut() = 2;
323            crate::Result::new(2)
324        })
325        .build();
326
327        let res = crate::try_or_handle(|| crate::Result::new_error(0), handlers.clone());
328        assert_eq!(res.unwrap(), 1);
329        assert_eq!(*which.borrow(), 1);
330
331        let res = crate::try_or_handle(|| crate::Result::new_error("hello"), handlers.clone());
332        assert_eq!(res.unwrap(), 2);
333        assert_eq!(*which.borrow(), 2);
334    }
335}