1use alloc::{borrow::Cow, boxed::Box, sync::Arc};
2use core::{
3 any::{Any, TypeId, type_name},
4 error::Error,
5 fmt::{Debug, Display},
6 ops::Deref,
7};
8
9use crate::cause::{Cause, CauseInner, Chain, UniDisplay};
10
11pub type UniResult<T, U> = Result<T, UniError<U>>;
15
16pub type SimpleResult<T> = Result<T, SimpleError>;
18
19pub type DynResult<T> = Result<T, DynError>;
21
22pub type SimpleError = UniError<()>;
24
25#[derive(Debug)]
31pub struct DynError(Box<dyn UniErrorOps + Send + Sync>);
32
33impl DynError {
34 pub(crate) fn new<E: UniErrorOps>(err: E) -> Self {
35 Self(Box::new(err))
36 }
37
38 pub fn downcast<T: UniKind>(self) -> Option<UniError<T>> {
40 let err: Box<dyn Any> = self.0;
41 err.downcast().ok().map(|err| *err)
42 }
43}
44
45impl Deref for DynError {
46 type Target = dyn UniErrorOps + Send + Sync;
47
48 fn deref(&self) -> &Self::Target {
49 &*self.0
50 }
51}
52
53pub trait UniKind: Debug + Any + Send + Sync {
57 fn value(&self) -> Cow<'static, str> {
60 Cow::Borrowed("")
61 }
62
63 fn context(&self) -> Option<Cow<'static, str>> {
65 None
66 }
67
68 fn code(&self) -> i32 {
70 -1
71 }
72
73 fn type_name(&self) -> &'static str {
75 type_name::<Self>()
76 }
77}
78
79impl dyn UniKind {
80 pub fn downcast_ref<T: UniKind>(&self) -> Option<&T> {
82 let err: &dyn Any = self;
83 err.downcast_ref()
84 }
85}
86
87impl UniKind for () {}
88
89#[derive(Debug)]
92pub(crate) struct UniErrorInner<T> {
93 kind: T,
94 context: Option<Cow<'static, str>>,
95 cause: Option<CauseInner>,
96}
97
98impl<T: UniKind> UniErrorInner<T> {
99 pub fn prev_cause<'e>(&'e self) -> Option<Cause<'e>> {
100 self.cause.as_ref().map(|inner| Cause::from_inner(inner))
101 }
102}
103
104impl<T: UniKind> Display for UniErrorInner<T> {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 if let Some(context) = &self.context {
107 write!(f, "{}", context)?;
108 }
109
110 if let Some(context) = self.kind.context() {
111 if self.context.is_some() {
112 write!(f, ": ")?;
113 }
114 write!(f, "{}", context)?;
115 }
116
117 if let Some(cause) = &self.prev_cause() {
118 if self.context.is_some() || self.kind.context().is_some() {
119 write!(f, ": ")?;
120 }
121 write!(f, "{}", cause)?;
122 }
123
124 Ok(())
125 }
126}
127
128impl<T: UniKind> Error for UniErrorInner<T> {
129 fn source(&self) -> Option<&(dyn Error + 'static)> {
130 match self.prev_cause() {
131 Some(Cause::UniError(err)) => Some(&***err),
132 Some(Cause::UniStdError(err)) => Some(err),
133 Some(Cause::StdError(err)) => Some(err),
134 Some(Cause::UniDisplay(_)) | None => None,
135 }
136 }
137}
138
139#[derive(Debug)]
143pub struct UniError<T> {
144 inner: Arc<UniErrorInner<T>>,
145}
146
147impl<T: UniKind + Default> UniError<T> {
148 pub fn from_context(context: impl Into<Cow<'static, str>>) -> Self {
150 Self::new(Default::default(), Some(context.into()), None)
151 }
152
153 pub fn from_boxed(error: Box<dyn Error + Send + Sync>) -> Self {
154 Self::new(
155 Default::default(),
156 None,
157 Some(CauseInner::from_boxed_error(error)),
158 )
159 }
160
161 pub fn from_kind_boxed(kind: T, error: Box<dyn Error + Send + Sync>) -> Self {
162 Self::new(kind, None, Some(CauseInner::from_boxed_error(error)))
163 }
164
165 pub fn from_kind_context_boxed(
166 kind: T,
167 context: impl Into<Cow<'static, str>>,
168 error: Box<dyn Error + Send + Sync>,
169 ) -> Self {
170 Self::new(
171 kind,
172 Some(context.into()),
173 Some(CauseInner::from_boxed_error(error)),
174 )
175 }
176}
177
178impl<T: UniKind> UniError<T> {
179 pub(crate) fn new(
180 kind: T,
181 context: Option<Cow<'static, str>>,
182 cause: Option<CauseInner>,
183 ) -> Self {
184 Self {
185 inner: Arc::new(UniErrorInner {
186 kind,
187 context,
188 cause,
189 }),
190 }
191 }
192
193 pub fn from_kind(kind: T) -> Self {
195 Self::new(kind, None, None)
196 }
197
198 pub fn from_kind_context(kind: T, context: impl Into<Cow<'static, str>>) -> Self {
200 Self::new(kind, Some(context.into()), None)
201 }
202
203 pub fn kind_ref(&self) -> &T {
205 &self.inner.kind
206 }
207}
208
209impl<T: Copy> UniError<T> {
210 pub fn kind(&self) -> T {
212 self.inner.kind
213 }
214}
215
216pub trait UniErrorOps: UniDisplay + Deref<Target = dyn Error + Send + Sync + 'static> {
218 fn kind_dyn_ref(&self) -> &dyn UniKind;
220
221 fn is_simple(&self) -> bool {
223 self.type_id() == TypeId::of::<SimpleError>()
224 }
225
226 fn prev_cause<'e>(&'e self) -> Option<Cause<'e>>;
228
229 fn chain(&self) -> Chain<'_>;
231
232 fn root_cause(&self) -> Option<Cause<'_>>;
235}
236
237impl dyn UniErrorOps + Send + Sync {
238 pub fn downcast_ref<T: UniKind>(&self) -> Option<&UniError<T>> {
240 let err: &dyn Any = self;
241 err.downcast_ref()
242 }
243}
244
245impl<T: UniKind> UniErrorOps for UniError<T> {
246 fn kind_dyn_ref(&self) -> &dyn UniKind {
247 self.kind_ref()
248 }
249
250 fn prev_cause<'e>(&'e self) -> Option<Cause<'e>> {
251 self.inner.prev_cause()
252 }
253
254 fn chain(&self) -> Chain<'_> {
255 Chain::new(self.prev_cause())
256 }
257
258 fn root_cause(&self) -> Option<Cause<'_>> {
259 let mut chain = self.chain();
260 let mut root = chain.next();
261
262 while let Some(next) = chain.next() {
263 root = Some(next);
264 }
265 root
266 }
267}
268
269impl<T: UniKind> Display for UniError<T> {
270 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
271 <UniErrorInner<T> as Display>::fmt(&self.inner, f)
272 }
273}
274
275impl<T: UniKind> Clone for UniError<T> {
277 fn clone(&self) -> Self {
278 Self {
279 inner: Arc::clone(&self.inner),
280 }
281 }
282}
283
284impl<T: UniKind + PartialEq + 'static> PartialEq for UniError<T> {
285 fn eq(&self, other: &Self) -> bool {
286 if self.inner.kind == other.inner.kind {
288 if self.is_simple() {
291 self.inner.context == other.inner.context
292 } else {
293 true
294 }
295 } else {
296 false
297 }
298 }
299}
300
301impl<T: UniKind> Deref for UniError<T> {
302 type Target = dyn Error + Sync + Send + 'static;
303
304 fn deref(&self) -> &Self::Target {
305 &self.inner
306 }
307}
308
309impl<T: UniKind> AsRef<dyn Error + Sync + Send> for UniError<T> {
310 fn as_ref(&self) -> &(dyn Error + Sync + Send + 'static) {
311 &**self
312 }
313}