radiate_error/
lib.rs

1use std::{
2    borrow::Cow,
3    fmt::{self, Display, Formatter},
4    ops::Deref,
5};
6use thiserror::Error;
7
8#[derive(Error, Debug, Clone, PartialEq, Eq)]
9pub enum RadiateError {
10    #[error("Invalid configuration: {0}")]
11    InvalidConfig(ErrString),
12
13    #[error("Invalid parameter: {0}")]
14    InvalidParameter(ErrString),
15
16    #[error("Invalid data: {0}")]
17    EngineError(ErrString),
18
19    #[error("Multiple Errors: {:?}", _0)]
20    Multiple(Vec<RadiateError>),
21}
22
23impl RadiateError {
24    pub fn with_context(self, message: impl Into<String>) -> ErrorContext {
25        ErrorContext::new(message).with_source(self)
26    }
27}
28
29impl From<ErrorContext> for RadiateError {
30    fn from(ctx: ErrorContext) -> Self {
31        RadiateError::EngineError(ctx.to_string().into())
32    }
33}
34
35pub type RadiateResult<T> = Result<T, RadiateError>;
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ErrString(Cow<'static, str>);
39
40impl AsRef<str> for ErrString {
41    fn as_ref(&self) -> &str {
42        &self.0
43    }
44}
45
46impl Deref for ErrString {
47    type Target = str;
48
49    fn deref(&self) -> &Self::Target {
50        &self.0
51    }
52}
53
54impl Display for ErrString {
55    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
56        write!(f, "{}", self.0)
57    }
58}
59
60impl<T> From<T> for ErrString
61where
62    T: Into<Cow<'static, str>>,
63{
64    fn from(value: T) -> Self {
65        Self(value.into())
66    }
67}
68
69pub trait IntoRadiateError<T> {
70    fn into_radiate_error(self) -> RadiateResult<T>;
71}
72
73impl<T, E> IntoRadiateError<T> for Result<T, E>
74where
75    E: Into<RadiateError>,
76{
77    fn into_radiate_error(self) -> RadiateResult<T> {
78        self.map_err(Into::into)
79    }
80}
81
82/// Error context for adding additional information to errors
83#[derive(Debug)]
84pub struct ErrorContext {
85    message: String,
86    source: Option<Box<dyn std::error::Error + Send + Sync>>,
87}
88
89impl ErrorContext {
90    pub fn new(message: impl Into<String>) -> Self {
91        Self {
92            message: message.into(),
93            source: None,
94        }
95    }
96
97    pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
98        self.source = Some(Box::new(source));
99        self
100    }
101}
102
103impl fmt::Display for ErrorContext {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(f, "{}", self.message)?;
106        if let Some(source) = &self.source {
107            write!(f, "\nCaused by: {}", source)?;
108        }
109        Ok(())
110    }
111}
112
113impl std::error::Error for ErrorContext {
114    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
115        self.source.as_ref().map(|s| s.as_ref() as _)
116    }
117}
118
119// ==== radiate-errors macro support ====
120#[doc(hidden)]
121pub mod __private {
122    #[doc(hidden)]
123    #[inline]
124    #[cold]
125    #[must_use]
126    pub fn must_use<E>(error: E) -> E {
127        error
128    }
129}
130
131#[macro_export]
132macro_rules! radiate_err {
133    ($variant:ident: $fmt:literal $(, $arg:expr)* $(,)?) => {
134        $crate::__private::must_use(
135            $crate::RadiateError::$variant(format!($fmt, $($arg),*).into())
136        )
137    };
138    ($variant:ident: $err:expr $(,)?) => {
139        $crate::__private::must_use(
140            $crate::RadiateError::$variant($err.into())
141        )
142    };
143}
144
145#[macro_export]
146macro_rules! radiate_bail {
147    ($($tt:tt)+) => {
148        return Err($crate::radiate_err!($($tt)+))
149    };
150}