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#[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#[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}