1#[macro_export]
46macro_rules! errormake {
51 ($structname:ident) => {
52 #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
53 struct $structname<T: ?Sized + 'static> {
54 source: Option<Box<T>>,
55 description: Option<String>,
56 }
57
58 errormake!(impl $structname);
59 };
60 ($(#[$meta:meta])* pub $structname:ident) => {
61 $(#[$meta])*
62 #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
63 pub struct $structname<T: ?Sized + 'static> {
64 source: Option<Box<T>>,
65 description: Option<String>,
66 }
67
68 errormake!(impl $structname);
69 };
70 (impl $structname:ident) => {
71 #[allow(dead_code)]
72 impl $structname<std::convert::Infallible> {
73 pub fn new() -> $structname<std::convert::Infallible> {
79 $structname {
80 source: None,
81 description: None,
82 }
83 }
84
85 pub fn with_description(description: String) -> $structname<std::convert::Infallible> {
87 $structname {
88 source: None,
89 description: Some(description),
90 }
91 }
92 }
93
94 #[allow(dead_code)]
95 impl<T: 'static> $structname<T> {
96 pub fn with_source(source: T) -> $structname<T> {
98 $structname {
99 source: Some(Box::new(source)),
100 description: None,
101 }
102 }
103
104 pub fn with_source_and_description(source: T, description: String) -> $structname<T> {
106 $structname {
107 source: Some(Box::new(source)),
108 description: Some(description),
109 }
110 }
111 }
112
113 #[allow(dead_code)]
114 impl<T: ?Sized + 'static> $structname<T> {
115 pub fn with_optional_data(
118 source: Option<Box<T>>,
119 description: Option<String>,
120 ) -> $structname<T> {
121 $structname {
122 source,
123 description,
124 }
125 }
126 }
127
128 #[allow(dead_code)]
129 impl<T: std::error::Error + 'static> $structname<T> {
130 pub fn into_dynamic(self) -> $structname<dyn std::error::Error + 'static> {
133 $structname {
134 source: self.source.map(|source| source as Box<dyn std::error::Error + 'static>),
135 description: self.description,
136 }
137 }
138 }
139
140 impl<T: std::fmt::Display + ?Sized + 'static> std::fmt::Display for $structname<T> {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match &self.source {
143 Some(source) => write!(
144 f,
145 "{}\n\nThe above error caused the following error:\n\n",
146 source
147 )?,
148 None => {}
149 }
150 write!(
151 f,
152 concat!(stringify!($structname), ": {}"),
153 match self.description.as_ref() {
154 Some(description) => description,
155 None => "No description provided",
156 }
157 )?;
158 Ok(())
159 }
160 }
161
162 impl<T> std::error::Error for $structname<T>
163 where T: std::error::Error + 'static
164 {
165 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
166 self.source
167 .as_ref()
168 .map(|err| err.as_ref() as &(dyn std::error::Error + 'static))
169 }
170 }
171
172 impl std::error::Error for $structname<dyn std::error::Error + 'static> {
173 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
174 self.source
175 .as_ref()
176 .map(|err| err.as_ref())
177 }
178 }
179 };
180}
181
182errormake!(#[doc="An example of an error struct made by `errormake`"] pub ExampleErrorStruct);
183
184#[cfg(test)]
185mod tests {
186 use super::errormake;
187 use std::error::Error;
188
189 errormake!(TestingError);
190 errormake!(pub PublicTestingError);
191
192 #[test]
193 fn test_stable() {
194 let error1 = TestingError::new();
195 assert_eq!(
196 "TestingError: No description provided",
197 format!("{}", error1)
198 );
199 assert!(error1.source().is_none());
200 let error2 = TestingError::with_description(String::from("Custom error message"));
201 assert_eq!("TestingError: Custom error message", format!("{}", error2));
202 assert!(error2.source().is_none());
203 let error3 = TestingError::with_source(Box::new(error2));
204 assert_eq!("TestingError: Custom error message\n\nThe above error caused the following error:\n\nTestingError: No description provided", format!("{}", error3));
205 assert!(error3.source().is_some());
206 let error4 = TestingError::with_source_and_description(
207 Box::new(TestingError::with_description(String::from(
208 "Custom error message",
209 ))),
210 String::from("Another message"),
211 );
212 assert_eq!("TestingError: Custom error message\n\nThe above error caused the following error:\n\nTestingError: Another message", format!("{}", error4));
213 assert!(error4.source().is_some());
214 }
215
216 #[test]
217 fn test_derives() {
218 let error1 = TestingError::new();
219 assert_eq!(error1, error1.clone());
220 let error2 = TestingError::with_source(error1.clone());
221 assert_eq!(error2, error2.clone());
222 assert_eq!(error2, error2);
223 let error3 =
224 TestingError::with_source_and_description(error1.clone(), String::from("description"));
225 assert_ne!(error3, error2);
226 let error4 = TestingError::with_description(String::from("description"));
227 assert_ne!(error1, error4);
228 }
229
230 #[test]
231 fn test_dynamic() {
232 let error = TestingError::new();
234 let error = TestingError::with_source(error).into_dynamic();
235 assert!(error.source().is_some());
236 let box_error: Box<dyn Error + 'static> = Box::new(error);
237 let error = TestingError::with_optional_data(Some(box_error), None);
238 assert!(error.source().is_some());
239 }
240}