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}