1use core::fmt::{Debug, Display};
2use std::{error::Error as StdError, io};
3
4use thiserror::Error as ThisError;
5
6pub trait Error: StdError + Sized {
8 fn custom<T>(msg: T) -> Self
10 where
11 T: Display;
12
13 fn context<C>(self, context: C) -> Self
15 where
16 C: Display;
17}
18
19pub trait Context: Sized {
21 type Ok;
22 type Error: Error;
23
24 fn with_context<C>(self, context: impl FnOnce() -> C) -> Result<Self::Ok, Self::Error>
26 where
27 C: Display;
28
29 #[inline]
31 fn context<C>(self, context: C) -> Result<Self::Ok, Self::Error>
32 where
33 C: Display,
34 {
35 self.with_context(move || context)
36 }
37}
38
39impl<T, E> Context for Result<T, E>
40where
41 E: Error,
42{
43 type Ok = T;
44 type Error = E;
45
46 #[inline]
47 fn with_context<C>(self, context: impl FnOnce() -> C) -> Result<T, E>
48 where
49 C: Display,
50 {
51 self.map_err(move |err| err.context(context()))
52 }
53}
54
55impl<T> Context for Option<T> {
56 type Ok = T;
57
58 type Error = StringError;
59
60 #[inline]
61 fn with_context<C>(self, context: impl FnOnce() -> C) -> Result<Self::Ok, Self::Error>
62 where
63 C: Display,
64 {
65 self.ok_or_else(context).map_err(Error::custom)
66 }
67}
68
69#[derive(Debug, ThisError)]
71#[error("{0}")]
72pub struct StringError(String);
73
74impl Error for StringError {
75 #[inline]
76 fn custom<T>(msg: T) -> Self
77 where
78 T: Display,
79 {
80 Self(msg.to_string())
81 }
82
83 #[inline]
84 fn context<C>(self, context: C) -> Self
85 where
86 C: Display,
87 {
88 Self(format!("{context}: {self}"))
89 }
90}
91
92impl AsRef<str> for StringError {
93 #[inline]
94 fn as_ref(&self) -> &str {
95 self.0.as_str()
96 }
97}
98
99impl Error for io::Error {
100 #[inline]
101 fn custom<T>(msg: T) -> Self
102 where
103 T: Display,
104 {
105 Self::other(msg.to_string())
106 }
107
108 #[inline]
109 fn context<C>(self, context: C) -> Self
110 where
111 C: Display,
112 {
113 Self::new(self.kind(), format!("{context}: {self}"))
114 }
115}