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