1#[macro_export]
64macro_rules! impl_enum_from {
65 (<$($generic:ident),+> in $($rest:tt)+) => {
66 $crate::impl_enum_from!(@impl [$($generic),+] $($rest)+);
67 };
68
69 (<const $($rest:tt)+) => {
70 $crate::__impl_more_parse_generics!(@parse impl_enum_from [] [] const $($rest)+);
71 };
72
73 (<$generic:ident, $($rest:tt)+) => {
74 $crate::__impl_more_parse_generics!(@parse impl_enum_from [] [] $generic, $($rest)+);
75 };
76
77 (@impl [$($generic:tt)+] $from:ty => $($target:tt)+) => {
78 $crate::impl_enum_from!(@parse [<$($generic)+>] [$from] [] $($target)+);
79 };
80
81 ($from:ty => $($target:tt)+) => {
82 $crate::impl_enum_from!(@parse [] [$from] [] $($target)+);
83 };
84
85 (@parse [$($generic:tt)*] [$from:ty] [$($enum:tt)+]
86 :: $variant:ident $(,)?) => {
87 impl $($generic)* ::core::convert::From<$from>
88 for $($enum)+
89 {
90 fn from(value: $from) -> Self {
91 Self::$variant(value)
92 }
93 }
94 };
95
96 (@parse [$($generic:tt)*] [$from:ty] [$($enum:tt)+]
97 :: $variant:ident { $field:ident $(,)? } $(,)?) => {
98 impl $($generic)* ::core::convert::From<$from>
99 for $($enum)+
100 {
101 fn from(value: $from) -> Self {
102 Self::$variant { $field: value }
103 }
104 }
105 };
106
107 (@parse [$($generic:tt)*] [$from:ty] [$($path:tt)*]
108 $next:tt $($rest:tt)+) => {
109 $crate::impl_enum_from!(@parse [$($generic)*] [$from]
110 [$($path)* $next] $($rest)+);
111 };
112}
113
114#[cfg(test)]
115mod tests {
116 #[derive(Debug, PartialEq)]
117 enum Value<T> {
118 Some(T),
119 }
120
121 impl_enum_from!(u32 => crate::enum_from::tests::Value<u32>::Some);
122
123 #[test]
124 fn error_conversion_with_question_mark() {
125 enum Error {
126 Io(std::io::Error),
127 Parse(core::num::ParseIntError),
128 }
129
130 impl_enum_from!(std::io::Error => Error::Io);
131 impl_enum_from!(core::num::ParseIntError => Error::Parse,);
132
133 fn fail() -> Result<(), Error> {
134 Err(std::io::Error::from(std::io::ErrorKind::NotFound))?;
135 Ok(())
136 }
137
138 match fail() {
139 Err(Error::Io(error)) => assert_eq!(error.kind(), std::io::ErrorKind::NotFound),
140 _ => panic!("expected an IO error"),
141 }
142
143 let source = "invalid".parse::<u32>().unwrap_err();
144 match Error::from(source.clone()) {
145 Error::Parse(error) => assert_eq!(error, source),
146 _ => panic!("expected a parse error"),
147 }
148 }
149
150 #[test]
151 fn generic_enum_with_module_path() {
152 mod errors {
153 #[derive(Debug, PartialEq)]
154 pub enum Error<T, U> {
155 Code(u32),
156 Other((T, U)),
157 }
158 }
159
160 impl_enum_from!(<T, U> in u32 => errors::Error<T, U>::Code);
161 impl_enum_from!(<T, U> in (T, U) => errors::Error<T, U>::Other,);
162
163 assert_eq!(errors::Error::<u8, bool>::from(42), errors::Error::Code(42));
164 assert_eq!(
165 errors::Error::from((7_u8, true)),
166 errors::Error::Other((7, true))
167 );
168 }
169
170 #[test]
171 fn named_error_conversion_with_question_mark() {
172 enum Error {
173 Io { source: std::io::Error },
174 }
175
176 impl_enum_from!(std::io::Error => Error::Io { source });
177
178 fn fail() -> Result<(), Error> {
179 Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied))?;
180 Ok(())
181 }
182
183 match fail() {
184 Err(Error::Io { source }) => {
185 assert_eq!(source.kind(), std::io::ErrorKind::PermissionDenied);
186 }
187 _ => panic!("expected an IO error"),
188 }
189 }
190
191 #[test]
192 fn named_generic_enum_with_module_path() {
193 mod errors {
194 #[derive(Debug, PartialEq)]
195 pub enum Error<T> {
196 Other { source: T },
197 }
198 }
199
200 impl_enum_from!(<T> in T => errors::Error<T>::Other { source, },);
201
202 assert_eq!(
203 errors::Error::from(42_u32),
204 errors::Error::Other { source: 42 }
205 );
206 }
207
208 #[test]
209 fn concrete_generic_arguments_with_crate_path() {
210 assert_eq!(Value::<u32>::from(42), Value::Some(42));
211 }
212}