[go: up one dir, main page]

rancor/
lib.rs

1//! # rancor
2//!
3//! rancor provides scalable and efficient error handling without using type
4//! composition. This makes it best-suited for situations where:
5//!
6//! - Programmatic error introspection is not useful
7//! - Functions may error, but succeed most of the time
8//! - Errors should provide as much useful detail as possible when emitted
9//! - Use cases include both `no_std` and targets with support for `std`
10//!
11//! ## Features
12//!
13//! - `alloc`: Provides the [`BoxedError`] type. Enabled by default.
14
15#![deny(
16    future_incompatible,
17    missing_docs,
18    nonstandard_style,
19    unsafe_op_in_unsafe_fn,
20    unused,
21    warnings,
22    clippy::all,
23    clippy::missing_safety_doc,
24    clippy::undocumented_unsafe_blocks,
25    rustdoc::broken_intra_doc_links,
26    rustdoc::missing_crate_level_docs
27)]
28#![no_std]
29#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
30
31#[cfg(feature = "alloc")]
32extern crate alloc;
33
34#[cfg(feature = "alloc")]
35mod boxed_error;
36#[cfg(feature = "alloc")]
37mod thin_box;
38
39use core::{
40    error, fmt,
41    hint::unreachable_unchecked,
42    marker::PhantomData,
43    ops::{Deref, DerefMut},
44};
45
46/// An error type which can add additional "trace" information to itself.
47///
48/// Some functions only add additional context to errors created by other
49/// functions, rather than creating errors themselves. With generics, it's
50/// therefore possible to have a generic function which can produce errors with
51/// some type arguments but not with others. In these cases, `Trace` allows
52/// those functions to add context if an error can occur, and compile out the
53/// context if the error type is [`Infallible`] or [`Panic`].
54///
55/// # Example
56///
57/// ```
58/// use rancor::{ResultExt, Trace};
59///
60/// trait Print<E> {
61///     fn print(&self, message: &str) -> Result<(), E>;
62/// }
63///
64/// fn print_hello_world<T: Print<E>, E: Trace>(printer: &T) -> Result<(), E> {
65///     printer.print("hello").trace("failed to print hello")?;
66///     printer.print("world").trace("failed to print world")?;
67///     Ok(())
68/// }
69/// ```
70pub trait Trace: Sized + Send + Sync + 'static {
71    /// Adds an additional trace to this error, returning a new error.
72    fn trace<R>(self, trace: R) -> Self
73    where
74        R: fmt::Debug + fmt::Display + Send + Sync + 'static;
75}
76
77/// An error type which can be uniformly constructed from an [`Error`] and
78/// additional trace information.
79///
80/// # Example
81///
82/// ```
83/// use core::{error::Error, fmt};
84///
85/// use rancor::{fail, Source};
86///
87/// #[derive(Debug)]
88/// struct DivideByZeroError;
89///
90/// impl fmt::Display for DivideByZeroError {
91///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92///         write!(f, "attempted to divide by zero")
93///     }
94/// }
95///
96/// impl Error for DivideByZeroError {}
97///
98/// fn try_divide<E: Source>(a: i32, b: i32) -> Result<i32, E> {
99///     if b == 0 {
100///         fail!(DivideByZeroError);
101///     }
102///     Ok(a / b)
103/// }
104/// ```
105pub trait Source: Trace + error::Error {
106    /// Returns a new `Self` using the given [`Error`].
107    ///
108    /// Depending on the specific implementation, this may box the error,
109    /// immediately emit a diagnostic, or discard it and only remember that some
110    /// error occurred.
111    fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self;
112}
113
114/// A type with fallible operations that return its associated error type.
115///
116/// `Fallible` turns an error type parameter into an associated type of another
117/// parameter. You can equip an existing type with a `Fallible` implementation
118/// by wrapping it in a [`Strategy`].
119///
120/// # Example
121///
122/// ```
123/// use rancor::{Failure, Fallible, Strategy};
124///
125/// trait Operator<E = <Self as Fallible>::Error> {
126///     fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E>;
127/// }
128///
129/// impl<T: Operator<E> + ?Sized, E> Operator<E> for Strategy<T, E> {
130///     fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E> {
131///         T::operate(self, lhs, rhs)
132///     }
133/// }
134///
135/// struct Add;
136///
137/// impl<E> Operator<E> for Add {
138///     fn operate(&self, lhs: i32, rhs: i32) -> Result<i32, E> {
139///         Ok(lhs + rhs)
140///     }
141/// }
142///
143/// fn operate_one_one<T: Operator + Fallible>(
144///     operator: &T,
145/// ) -> Result<i32, T::Error> {
146///     operator.operate(1, 1)
147/// }
148///
149/// assert_eq!(
150///     operate_one_one(Strategy::<_, Failure>::wrap(&mut Add)),
151///     Ok(2)
152/// );
153/// ```
154pub trait Fallible {
155    /// The error type associated with this type's operations.
156    type Error;
157}
158
159/// Equips a type with a `Fallible` implementation that chooses `E` as its error
160/// type.
161///
162/// # Example
163///
164/// ```
165/// use rancor::{Failure, Fallible, Strategy};
166///
167/// trait Print<E = <Self as Fallible>::Error> {
168///     fn print(&self, message: &str) -> Result<(), E>;
169/// }
170///
171/// impl<T: Print<E> + ?Sized, E> Print<E> for Strategy<T, E> {
172///     fn print(&self, message: &str) -> Result<(), E> {
173///         T::print(self, message)
174///     }
175/// }
176///
177/// struct StdOut;
178///
179/// impl<E> Print<E> for StdOut {
180///     fn print(&self, message: &str) -> Result<(), E> {
181///         println!("{message}");
182///         Ok(())
183///     }
184/// }
185///
186/// Strategy::<_, Failure>::wrap(&mut StdOut)
187///     .print("hello world")
188///     .unwrap();
189/// ```
190#[repr(transparent)]
191pub struct Strategy<T: ?Sized, E> {
192    _error: PhantomData<E>,
193    inner: T,
194}
195
196impl<T: ?Sized, E> Fallible for Strategy<T, E> {
197    type Error = E;
198}
199
200impl<T: ?Sized, E> Strategy<T, E> {
201    /// Wraps the given mutable reference, returning a mutable reference to a
202    /// `Strategy`.
203    ///
204    /// ## Example
205    /// ```
206    /// use core::ops::Deref;
207    ///
208    /// use rancor::{Failure, Strategy};
209    /// fn test() {
210    ///     struct Inner {
211    ///         value: u64,
212    ///     }
213    ///
214    ///     let mut inner = Inner { value: 10 };
215    ///
216    ///     let inner_value_ptr = &inner.value as *const u64;
217    ///     let strategy: &mut Strategy<Inner, Failure> =
218    ///         Strategy::wrap(&mut inner);
219    ///     let strategy_value_ptr = (&strategy.deref().value) as *const u64;
220    ///     assert_eq!(inner_value_ptr, strategy_value_ptr);
221    ///     // Strategy wraps a type but does not change its memory layout.
222    /// }
223    ///
224    /// test();
225    /// ```
226    pub fn wrap(inner: &mut T) -> &mut Self {
227        // SAFETY: `Strategy` is `repr(transparent)` and so has the same layout
228        // as `T`. The input and output lifetimes are the same, so mutable
229        // aliasing rules will be upheld. Finally, because the inner `T` is the
230        // final element of `Strategy`, the pointer metadata of the two pointers
231        // will be the same.
232        unsafe { core::mem::transmute::<&mut T, &mut Self>(inner) }
233    }
234}
235
236impl<T: ?Sized, E> Deref for Strategy<T, E> {
237    type Target = T;
238
239    fn deref(&self) -> &Self::Target {
240        &self.inner
241    }
242}
243
244impl<T: ?Sized, E> DerefMut for Strategy<T, E> {
245    fn deref_mut(&mut self) -> &mut Self::Target {
246        &mut self.inner
247    }
248}
249
250/// Returns the given error from this function.
251///
252/// The current function must return a `Result<_, E>` where `E` implements
253/// [`Source`].
254///
255/// # Example
256///
257/// ```
258/// use core::{error::Error, fmt};
259///
260/// use rancor::{fail, Source};
261///
262/// #[derive(Debug)]
263/// struct DivideByZeroError;
264///
265/// impl fmt::Display for DivideByZeroError {
266///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267///         write!(f, "attempted to divide by zero")
268///     }
269/// }
270///
271/// impl Error for DivideByZeroError {}
272///
273/// fn divide<E: Source>(a: i32, b: i32) -> Result<i32, E> {
274///     if b == 0 {
275///         fail!(DivideByZeroError);
276///     }
277///     Ok(a / b)
278/// }
279/// ```
280#[macro_export]
281macro_rules! fail {
282    ($($x:tt)*) => {
283        return ::core::result::Result::Err($crate::Source::new($($x)*));
284    };
285}
286
287/// Helper methods for `Result`s.
288pub trait ResultExt<T, E> {
289    /// Returns a `Result` with this error type converted to `U`.
290    ///
291    /// # Example
292    ///
293    /// ```
294    /// use rancor::{Failure, ResultExt as _};
295    ///
296    /// let result = "1_000".parse::<i32>().into_error::<Failure>();
297    /// ```
298    fn into_error<U>(self) -> Result<T, U>
299    where
300        U: Source,
301        E: error::Error + Send + Sync + 'static;
302
303    /// Returns a `Result` with this error type converted to `U` and with an
304    /// additional `trace` message added.
305    ///
306    /// # Example
307    ///
308    /// ```
309    /// use rancor::{BoxedError, ResultExt as _};
310    ///
311    /// let result = "1_000"
312    ///     .parse::<i32>()
313    ///     .into_trace::<BoxedError, _>("while parsing 1_000");
314    /// ```
315    fn into_trace<U, R>(self, trace: R) -> Result<T, U>
316    where
317        U: Source,
318        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
319        E: error::Error + Send + Sync + 'static;
320
321    /// Returns a `Result` with this error type converted to `U` and with an
322    /// additional trace message added by evaluating the given function `f`. The
323    /// function is evaluated only if an error occurred.
324    ///
325    /// # Example
326    ///
327    /// ```
328    /// use rancor::{BoxedError, ResultExt as _};
329    ///
330    /// let input = "1_000";
331    /// let result =
332    ///     input
333    ///         .parse::<i32>()
334    ///         .into_with_trace::<BoxedError, _, _>(|| {
335    ///             format!("while parsing {input}")
336    ///         });
337    /// ```
338    fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
339    where
340        U: Source,
341        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
342        F: FnOnce() -> R,
343        E: error::Error + Send + Sync + 'static;
344
345    /// Adds an additional `trace` message to the error value of this type.
346    ///
347    /// # Example
348    ///
349    /// ```
350    /// use rancor::{BoxedError, ResultExt as _};
351    ///
352    /// let result = "1_000"
353    ///     .parse::<i32>()
354    ///     .into_error::<BoxedError>()
355    ///     .trace("while parsing 1_000");
356    /// ```
357    fn trace<R>(self, trace: R) -> Result<T, E>
358    where
359        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
360        E: Trace;
361
362    /// Adds an additional trace message to the error value of this type by
363    /// evaluating the given function `f`. The function is evaluated only if an
364    /// error occurred.
365    ///
366    /// # Example
367    ///
368    /// ```
369    /// use rancor::{BoxedError, ResultExt as _};
370    ///
371    /// let input = "1_000";
372    /// let result = input
373    ///     .parse::<i32>()
374    ///     .into_error::<BoxedError>()
375    ///     .with_trace(|| format!("while parsing {input}"));
376    /// ```
377    fn with_trace<R, F>(self, f: F) -> Result<T, E>
378    where
379        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
380        F: FnOnce() -> R,
381        E: Trace;
382
383    /// Safely unwraps a result that is always `Ok`.
384    ///
385    /// In order to call this method, the error type of this `Result` must be a
386    /// [`Never`] type.
387    ///
388    /// # Example
389    ///
390    /// ```
391    /// use rancor::{Infallible, ResultExt};
392    ///
393    /// let inner = Ok::<i32, Infallible>(10).always_ok();
394    /// ```
395    fn always_ok(self) -> T
396    where
397        E: Never;
398}
399
400impl<T, E> ResultExt<T, E> for Result<T, E> {
401    fn into_error<U>(self) -> Result<T, U>
402    where
403        U: Source,
404        E: error::Error + Send + Sync + 'static,
405    {
406        match self {
407            Ok(x) => Ok(x),
408            Err(e) => Err(U::new(e)),
409        }
410    }
411
412    fn into_trace<U, R>(self, trace: R) -> Result<T, U>
413    where
414        U: Source,
415        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
416        E: error::Error + Send + Sync + 'static,
417    {
418        match self {
419            Ok(x) => Ok(x),
420            Err(e) => Err(U::new(e).trace(trace)),
421        }
422    }
423
424    fn into_with_trace<U, R, F>(self, f: F) -> Result<T, U>
425    where
426        U: Source,
427        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
428        F: FnOnce() -> R,
429        E: error::Error + Send + Sync + 'static,
430    {
431        match self {
432            Ok(x) => Ok(x),
433            Err(e) => Err(U::new(e).trace(f())),
434        }
435    }
436
437    fn trace<R>(self, trace: R) -> Result<T, E>
438    where
439        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
440        E: Trace,
441    {
442        match self {
443            Ok(x) => Ok(x),
444            Err(e) => Err(e.trace(trace)),
445        }
446    }
447
448    fn with_trace<R, F>(self, f: F) -> Result<T, E>
449    where
450        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
451        F: FnOnce() -> R,
452        E: Trace,
453    {
454        match self {
455            Ok(x) => Ok(x),
456            Err(e) => Err(e.trace(f())),
457        }
458    }
459
460    fn always_ok(self) -> T
461    where
462        E: Never,
463    {
464        match self {
465            Ok(x) => x,
466            Err(e) => unreachable_checked(e),
467        }
468    }
469}
470
471/// Helper methods for `Option`s.
472pub trait OptionExt<T> {
473    /// Returns a `Result` with an error indicating that `Some` was expected but
474    /// `None` was found.
475    ///
476    /// # Example
477    ///
478    /// ```
479    /// use rancor::{Failure, OptionExt};
480    ///
481    /// let result = Some(10).into_error::<Failure>();
482    /// ```
483    fn into_error<E>(self) -> Result<T, E>
484    where
485        E: Source;
486
487    /// Returns a `Result` with an error indicating that `Some` was expected but
488    /// `None` was found, and with an additional `trace` message added.
489    ///
490    /// # Example
491    ///
492    /// ```
493    /// use rancor::{Failure, OptionExt};
494    ///
495    /// ##[rustfmt::skip]
496    /// let result = Some(10).
497    ///     into_trace::<Failure, _>("while converting Some(10)");
498    /// ```
499    fn into_trace<E, R>(self, trace: R) -> Result<T, E>
500    where
501        E: Source,
502        R: fmt::Debug + fmt::Display + Send + Sync + 'static;
503
504    /// Returns a `Result` with an error indicating that `Some` was expected but
505    /// `None` was found, and with an additional trace message added by
506    /// evaluating the given function `f`. The function is evaluated only if an
507    /// error occurred.
508    ///
509    /// # Example
510    ///
511    /// ```
512    /// use rancor::{Failure, OptionExt};
513    ///
514    /// let input = Some(10);
515    /// let result = input.into_with_trace::<Failure, _, _>(|| {
516    ///     format!("while converting {input:?}")
517    /// });
518    /// ```
519    fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
520    where
521        E: Source,
522        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
523        F: FnOnce() -> R;
524}
525
526/// A type that can never be produced.
527///
528/// Never types include the unstable `!` type, enums with no variants, or any
529/// type that always contains a never type (e.g. a struct with a `Never` field).
530///
531/// # Safety
532///
533/// It must be impossible to produce a value of this type.
534pub unsafe trait Never {}
535
536/// Consumes a `Never` type, returning a primitive `!`.
537///
538/// This is a safe version of [`unreachable_unchecked`] for `Never` types.
539///
540/// # Example
541///
542/// ```
543/// use rancor::{unreachable_checked, Infallible};
544///
545/// let result = Ok::<i32, Infallible>(10);
546/// match result {
547///     Ok(i) => println!("i"),
548///     Err(e) => unreachable_checked(e),
549/// }
550/// ```
551#[inline(always)]
552pub const fn unreachable_checked<T: Never>(_: T) -> ! {
553    // SAFETY: Types that implement `Never` cannot be constructed,
554    // so this is unreachable.
555    unsafe { unreachable_unchecked() }
556}
557
558#[derive(Debug)]
559struct NoneError;
560
561impl fmt::Display for NoneError {
562    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563        write!(f, "`Option` is `None`, expected `Some`")
564    }
565}
566
567impl error::Error for NoneError {}
568
569impl<T> OptionExt<T> for Option<T> {
570    fn into_error<E>(self) -> Result<T, E>
571    where
572        E: Source,
573    {
574        match self {
575            Some(x) => Ok(x),
576            None => Err(E::new(NoneError)),
577        }
578    }
579
580    fn into_trace<E, R>(self, trace: R) -> Result<T, E>
581    where
582        E: Source,
583        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
584    {
585        match self {
586            Some(x) => Ok(x),
587            None => Err(E::new(NoneError).trace(trace)),
588        }
589    }
590
591    fn into_with_trace<E, R, F>(self, f: F) -> Result<T, E>
592    where
593        E: Source,
594        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
595        F: FnOnce() -> R,
596    {
597        match self {
598            Some(x) => Ok(x),
599            None => Err(E::new(NoneError).trace(f())),
600        }
601    }
602}
603
604/// A re-export of `core::convert::Infallible`.
605pub use core::convert::Infallible;
606
607// SAFETY: `Infallible` is an enum with no variants, and so cannot be produced.
608unsafe impl Never for Infallible {}
609
610impl Trace for Infallible {
611    fn trace<R>(self, _: R) -> Self
612    where
613        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
614    {
615        match self {}
616    }
617}
618
619/// An error type that does not occupy any space, panicking on creation instead.
620///
621/// Because panicking occurs immediately upon creation, this error type will not
622/// print any additional trace information.
623#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
624pub enum Panic {}
625
626// SAFETY: `Panic` is an enum with no variants, and so cannot be produced.
627unsafe impl Never for Panic {}
628
629impl fmt::Display for Panic {
630    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
631        match *self {}
632    }
633}
634
635impl error::Error for Panic {}
636
637impl Trace for Panic {
638    fn trace<R>(self, _: R) -> Self
639    where
640        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
641    {
642        match self {}
643    }
644}
645
646impl Source for Panic {
647    fn new<T: fmt::Display>(error: T) -> Self {
648        panic!("created a new `Panic` from: {error}");
649    }
650}
651
652/// An error type that only preserves success or failure, throwing away any more
653/// detailed error messages.
654#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
655pub struct Failure;
656
657impl fmt::Display for Failure {
658    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
659        write!(f, "failed without error information")
660    }
661}
662
663impl error::Error for Failure {}
664
665impl Trace for Failure {
666    fn trace<R>(self, _: R) -> Self
667    where
668        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
669    {
670        self
671    }
672}
673
674impl Source for Failure {
675    fn new<T: error::Error + Send + Sync + 'static>(_: T) -> Self {
676        Self
677    }
678}
679
680#[cfg(feature = "alloc")]
681pub use boxed_error::BoxedError;
682
683#[cfg(all(debug_assertions, feature = "alloc"))]
684type ErrorType = BoxedError;
685#[cfg(not(all(debug_assertions, feature = "alloc")))]
686type ErrorType = Failure;
687
688/// A good general-purpose error type.
689///
690/// If `debug_assertions` and the `alloc` feature are enabled, then this error
691/// will have the same behavior as [`BoxedError`]. Otherwise, it will behave
692/// like [`Failure`].
693#[derive(Debug)]
694pub struct Error {
695    inner: ErrorType,
696}
697
698impl fmt::Display for Error {
699    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700        write!(f, "{}", self.inner)?;
701        #[cfg(not(all(debug_assertions, feature = "alloc")))]
702        write!(
703            f,
704            "; enable debug assertions and the `alloc` feature in rancor for \
705             error information"
706        )?;
707
708        Ok(())
709    }
710}
711
712impl error::Error for Error {
713    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
714        self.inner.source()
715    }
716}
717
718impl Trace for Error {
719    fn trace<R>(self, trace: R) -> Self
720    where
721        R: fmt::Debug + fmt::Display + Send + Sync + 'static,
722    {
723        Self {
724            inner: self.inner.trace(trace),
725        }
726    }
727}
728
729impl Source for Error {
730    fn new<T: error::Error + Send + Sync + 'static>(source: T) -> Self {
731        Self {
732            inner: ErrorType::new(source),
733        }
734    }
735}
736
737#[cfg(test)]
738mod test {
739    use super::*;
740
741    struct Inner {
742        value: u64,
743    }
744
745    #[test]
746    fn test_strategy() {
747        let mut inner = Inner { value: 10 };
748        let address = &inner.value as *const u64;
749        let strategy: &mut Strategy<Inner, Failure> =
750            Strategy::wrap(&mut inner);
751        let s_address = (&strategy.inner.value) as *const u64;
752        assert_eq!(address, s_address);
753
754        assert_eq!(strategy.value, 10);
755        strategy.value = 20;
756        assert_eq!(strategy.value, 20);
757        assert_eq!(inner.value, 20);
758    }
759}