1#[macro_export]
45macro_rules! forward_error {
46 ($ty:ty) => {
47 impl ::std::error::Error for $ty {
48 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
49 Some(::core::ops::Deref::deref(&self.0))
50 }
51 }
52 };
53
54 ($ty:ty => $field:ident) => {
55 impl ::std::error::Error for $ty {
56 fn source(&self) -> Option<&(dyn ::std::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 ::std::error::Error for $ty {
92 fn source(&self) -> ::core::option::Option<&(dyn ::std::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 ::std::error::Error for $ty {
109 fn source(&self) -> ::core::option::Option<&(dyn ::std::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 ::std::error::Error for $ty {}
126 };
127
128 ($ty:ty) => {
129 $crate::impl_error_enum!($ty,);
130 };
131}
132
133#[cfg(test)]
134mod tests {
135 use alloc::string::String;
136 use std::error::Error as _;
137
138 #[test]
139 fn with_trailing_comma() {
140 #![allow(unused)]
141
142 #[derive(Debug)]
143 enum Foo {
144 Bar,
145 }
146
147 impl_display_enum!(Foo, Bar => "bar");
148 impl_error_enum!(Foo,);
149 }
150
151 #[test]
152 fn no_inner_data() {
153 #[derive(Debug)]
154 enum Foo {
155 Bar,
156 Baz,
157 }
158
159 impl_display_enum!(Foo, Bar => "bar", Baz => "qux");
160 impl_error_enum!(Foo);
161
162 assert!(Foo::Bar.source().is_none());
163 assert!(Foo::Baz.source().is_none());
164 }
165
166 #[test]
167 fn uniform_enum() {
168 #[derive(Debug)]
169 enum Foo {
170 Bar(String),
171 Baz(std::io::Error),
172 Qux(String, std::io::Error),
173 }
174
175 impl_display_enum!(
176 Foo,
177 Bar(desc) => "{desc}",
178 Baz(err) => "{err}",
179 Qux(desc, err) => "{desc}: {err}"
180 );
181 impl_error_enum!(Foo, Baz(err) => err, Qux(_desc, err) => err);
182
183 assert!(Foo::Bar(String::new()).source().is_none());
184
185 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
186 assert!(Foo::Baz(io_err).source().is_some());
187
188 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test");
189 assert!(Foo::Qux(String::new(), io_err).source().is_some());
190 }
191}