1#[macro_export]
45macro_rules! forward_error {
46 ($ty:ty) => {
47 impl ::core::error::Error for $ty {
48 fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
49 Some(::core::ops::Deref::deref(&self.0))
50 }
51 }
52 };
53
54 ($ty:ty => $field:ident) => {
55 impl ::core::error::Error for $ty {
56 fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> {
57 Some(::core::ops::Deref::deref(&self.$field))
58 }
59 }
60 };
61}
62
63#[macro_export]
89macro_rules! impl_error_enum {
90 ($ty:ty: $($variant:ident ($($inner:ident),+) => $source:expr),+ ,) => {
91 impl ::core::error::Error for $ty {
92 fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
93 match self {
94 $(
95 Self::$variant($($inner),+) => ::core::option::Option::Some($source),
96 )*
97 _ => ::core::option::Option::None,
98 }
99 }
100 }
101 };
102
103 ($ty:ty: $($variant:ident ($($inner:ident),+) => $source:expr),+) => {
104 $crate::impl_error_enum!($ty: $($variant ($($inner),+) => $source),+ ,);
105 };
106
107 ($ty:ty: $($variant:ident { $($inner:ident),+ } => $source:expr),+ ,) => {
108 impl ::core::error::Error for $ty {
109 fn source(&self) -> ::core::option::Option<&(dyn ::core::error::Error + 'static)> {
110 match self {
111 $(
112 Self::$variant { $($inner),+ } => ::core::option::Option::Some($source),
113 )*
114 _ => ::core::option::Option::None,
115 }
116 }
117 }
118 };
119
120 ($ty:ty: $($variant:ident { $($inner:ident),+ } => $source:expr),+) => {
121 $crate::impl_error_enum!($ty: $($variant { $($inner),+ } => $source),+ ,);
122 };
123
124 ($ty:ty) => {
125 impl ::core::error::Error for $ty {}
126 };
127}
128
129#[macro_export]
145macro_rules! impl_leaf_error {
146 ($ty:ty) => {
147 impl ::core::error::Error for $ty {}
148 };
149}
150
151#[cfg(test)]
152#[rustversion::since(1.81)]
153mod tests {
154 use alloc::string::String;
155 use core::error::Error as _;
156
157 #[test]
158 fn with_trailing_comma() {
159 #![allow(unused)]
160
161 #[derive(Debug)]
162 enum Foo {
163 Bar,
164 }
165
166 impl_display_enum!(Foo: Bar => "bar");
167 impl_leaf_error!(Foo);
168 }
169
170 #[test]
171 fn no_inner_data() {
172 #[derive(Debug)]
173 enum Foo {
174 Bar,
175 Baz,
176 }
177
178 impl_display_enum!(Foo: Bar => "bar", Baz => "qux");
179 impl_error_enum!(Foo);
180
181 assert!(Foo::Bar.source().is_none());
182 assert!(Foo::Baz.source().is_none());
183 }
184
185 #[test]
186 fn named_variant() {
187 #[derive(Debug)]
188 enum Foo {
189 Bar { source: std::io::Error },
190 Baz,
191 }
192
193 impl_display!(Foo: "foo");
194 impl_error_enum!(Foo: Bar { source } => source);
195
196 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
197 assert!(Foo::Bar { source: io_err }.source().is_some());
198 assert!(Foo::Baz.source().is_none());
199 }
200
201 #[test]
202 fn named_variant_with_trailing_comma() {
203 #[derive(Debug)]
204 enum Foo {
205 Bar { source: std::io::Error },
206 Baz,
207 }
208
209 impl_display!(Foo: "foo");
210 impl_error_enum!(Foo: Bar { source } => source,);
211
212 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
213 assert!(Foo::Bar { source: io_err }.source().is_some());
214 assert!(Foo::Baz.source().is_none());
215 }
216
217 #[test]
218 fn uniform_enum() {
219 #[derive(Debug)]
220 enum Foo {
221 Bar(String),
222 Baz(std::io::Error),
223 Qux(String, std::io::Error),
224 }
225
226 impl_display_enum! {
227 Foo:
228 Bar(desc) => "{desc}",
229 Baz(err) => "{err}",
230 Qux(desc, err) => "{desc}: {err}"
231 };
232 impl_error_enum! {
233 Foo:
234 Baz(err) => err,
235 Qux(_desc, err) => err
236 };
237
238 assert!(Foo::Bar(String::new()).source().is_none());
239
240 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
241 assert!(Foo::Baz(io_err).source().is_some());
242
243 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
244 assert!(Foo::Qux(String::new(), io_err).source().is_some());
245 }
246}