enum_try_from/lib.rs
1//! Rust macros which create enums with `TryFrom` trait implementation.
2//!
3//! ## Examples
4//!
5//! ```rust
6//! use enum_try_from::impl_enum_try_from;
7//!
8//! impl_enum_try_from!(
9//! #[repr(u16)]
10//! #[derive(PartialEq, Eq, Debug)]
11//! enum MyEnum {
12//! Foo = 0,
13//! Bar = 1,
14//! Baz = 2,
15//! },
16//! u16,
17//! (),
18//! ()
19//! );
20//!
21//! fn main() {
22//! assert_eq!(MyEnum::try_from(0), Ok(MyEnum::Foo));
23//! assert_eq!(MyEnum::try_from(1), Ok(MyEnum::Bar));
24//! assert_eq!(MyEnum::try_from(2), Ok(MyEnum::Baz));
25//! assert_eq!(MyEnum::try_from(3), Err(()));
26//! }
27//! ```
28//!
29//! ```rust
30//! use enum_try_from::impl_enum_try_from;
31//! use thiserror::Error;
32//!
33//! #[derive(Error, Debug, PartialEq, Eq)]
34//! pub enum MyError {
35//! #[error("invalid value")]
36//! InvalidValue,
37//! }
38//!
39//! impl_enum_try_from!(
40//! #[repr(u16)]
41//! #[derive(PartialEq, Eq, Debug)]
42//! enum MyEnum {
43//! Foo = 0,
44//! Bar = 1,
45//! Baz = 2,
46//! },
47//! u16,
48//! MyError,
49//! MyError::InvalidValue
50//! );
51//! ```
52//!
53//! If the value provided to `try_from` should be converted from big endian:
54//!
55//! ```rust
56//! use enum_try_from::impl_enum_try_from_be;
57//!
58//! impl_enum_try_from_be!(
59//! #[repr(u16)]
60//! #[derive(PartialEq, Eq, Debug)]
61//! enum MyEnum {
62//! Foo = 0x1234,
63//! Bar = 0x5678,
64//! Baz = 0x9abc,
65//! },
66//! u16,
67//! (),
68//! ()
69//! );
70//!
71//! fn main() {
72//! assert_eq!(MyEnum::try_from(0x3412), Ok(MyEnum::Foo));
73//! assert_eq!(MyEnum::try_from(0x7856), Ok(MyEnum::Bar));
74//! assert_eq!(MyEnum::try_from(0xbc9a), Ok(MyEnum::Baz));
75//! assert_eq!(MyEnum::try_from(0xdef0), Err(()));
76//! }
77//! ```
78//!
79//! ## Why does it exist?
80//!
81//! Rust projects very often consume values as regular integers and then try to
82//! match them with enums. Doing so, requires implementing the `TryFrom` trait for
83//! enums. Example:
84//!
85//! ```rust
86//! #[repr(u16)]
87//! #[derive(PartialEq, Eq, Debug)]
88//! enum MyEnum {
89//! Foo = 0,
90//! Bar = 1,
91//! Baz = 2,
92//! }
93//!
94//! impl TryFrom<u16> for MyEnum {
95//! type Error = ();
96//!
97//! fn try_from(v: u16) -> Result<Self, Self::Error> {
98//! match v {
99//! 0 => Ok(MyEnum::Foo),
100//! 1 => Ok(MyEnum::Bar),
101//! 2 => Ok(MyEnum::Baz),
102//! _ => Err(()),
103//! }
104//! }
105//! }
106//!
107//! fn main() {
108//! assert_eq!(MyEnum::try_from(0), Ok(MyEnum::Foo));
109//! assert_eq!(MyEnum::try_from(1), Ok(MyEnum::Bar));
110//! assert_eq!(MyEnum::try_from(2), Ok(MyEnum::Baz));
111//! assert_eq!(MyEnum::try_from(3), Err(()));
112//! }
113//! ```
114//!
115//! It requires listing all enum variants multiple times and also requires writing
116//! down new variants multiple times when adding them to the existing enum.
117//!
118//! The goal of this crate is to avoid that and define an enum with variants only
119//! once.
120
121#![no_std]
122
123/// Macro which implements the `TryFrom` trait for the given enum and type.
124///
125/// The first argument is the enum to implement the trait for.
126///
127/// The second argument is the type to implement the trait for. Usually `i32` or
128/// `u32` would be the best choice. However, if you are providing any concrete
129/// primitive type in `repr` (i.e. `#[repr(u8)]`), then you should use the same
130/// type.
131///
132/// The third argument is the type of the error which should be returned if the
133/// value provided to `try_from` is not a valid variant of the enum.
134///
135/// The fourth argument is the concrete error value which should be returned if
136/// the value provided to `try_from` is not a valid variant of the enum.
137///
138/// # Examples
139///
140/// ```
141/// # use enum_try_from::impl_enum_try_from;
142/// impl_enum_try_from!(
143/// #[repr(u16)]
144/// #[derive(PartialEq, Eq, Debug)]
145/// enum MyEnum {
146/// Foo = 0,
147/// Bar = 1,
148/// Baz = 2,
149/// },
150/// u16,
151/// (),
152/// ()
153/// );
154///
155/// # fn main() {
156/// assert_eq!(MyEnum::try_from(0), Ok(MyEnum::Foo));
157/// assert_eq!(MyEnum::try_from(1), Ok(MyEnum::Bar));
158/// assert_eq!(MyEnum::try_from(2), Ok(MyEnum::Baz));
159/// assert_eq!(MyEnum::try_from(3), Err(()));
160/// # }
161/// ```
162///
163/// ```
164/// use thiserror::Error;
165/// # use enum_try_from::impl_enum_try_from;
166///
167/// #[derive(Error, Debug, PartialEq, Eq)]
168/// pub enum MyError {
169/// #[error("invalid value")]
170/// InvalidValue,
171/// }
172///
173/// impl_enum_try_from!(
174/// #[repr(u16)]
175/// #[derive(PartialEq, Eq, Debug)]
176/// enum MyEnum {
177/// Foo = 0,
178/// Bar = 1,
179/// Baz = 2,
180/// },
181/// u16,
182/// MyError,
183/// MyError::InvalidValue
184/// );
185/// ```
186#[macro_export]
187macro_rules! impl_enum_try_from {
188 ($(#[$meta:meta])* $vis:vis enum $name:ident {
189 $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
190 }, $type:ty, $err_ty:ty, $err:expr) => {
191 $(#[$meta])*
192 $vis enum $name {
193 $($(#[$vmeta])* $vname $(= $val)?,)*
194 }
195
196 impl TryFrom<$type> for $name {
197 type Error = $err_ty;
198
199 fn try_from(v: $type) -> Result<Self, Self::Error> {
200 match v {
201 $(x if x == $name::$vname as $type => Ok($name::$vname),)*
202 _ => Err($err),
203 }
204 }
205 }
206 }
207}
208
209/// Macro which implements the `TryFrom` trait for the given enum and type, with
210/// conversion of the input value from big endian.
211///
212/// The first argument is the enum to implement the trait for.
213///
214/// The second argument is the type to implement the trait for. Usually `i32` or
215/// `u32` would be the best choice. However, if you are providing any concrete
216/// primitive type in `repr` (i.e. `#[repr(u8)]`), then you should use the same
217/// type.
218///
219/// The third argument is the type of the error which should be returned if the
220/// value provided to `try_from` is not a valid variant of the enum.
221///
222/// The fourth argument is the concrete error value which should be returned if
223/// the value provided to `try_from` is not a valid variant of the enum.
224///
225/// # Examples
226///
227/// ```
228/// # use enum_try_from::impl_enum_try_from_be;
229/// impl_enum_try_from_be!(
230/// #[repr(u16)]
231/// #[derive(PartialEq, Eq, Debug)]
232/// enum MyEnum {
233/// Foo = 0x1234,
234/// Bar = 0x5678,
235/// Baz = 0x9abc,
236/// },
237/// u16,
238/// (),
239/// ()
240/// );
241///
242/// # fn main() {
243/// assert_eq!(MyEnum::try_from(0x3412), Ok(MyEnum::Foo));
244/// assert_eq!(MyEnum::try_from(0x7856), Ok(MyEnum::Bar));
245/// assert_eq!(MyEnum::try_from(0xbc9a), Ok(MyEnum::Baz));
246/// assert_eq!(MyEnum::try_from(0xdef0), Err(()));
247/// # }
248/// ```
249///
250/// ```
251/// use thiserror::Error;
252/// # use enum_try_from::impl_enum_try_from_be;
253///
254/// #[derive(Error, Debug, PartialEq, Eq)]
255/// pub enum MyError {
256/// #[error("invalid value")]
257/// InvalidValue,
258/// }
259///
260/// impl_enum_try_from_be!(
261/// #[repr(u16)]
262/// #[derive(PartialEq, Eq, Debug)]
263/// enum MyEnum {
264/// Foo = 0x1234,
265/// Bar = 0x5678,
266/// Baz = 0x9abc,
267/// },
268/// u16,
269/// MyError,
270/// MyError::InvalidValue
271/// );
272/// ```
273#[macro_export]
274macro_rules! impl_enum_try_from_be {
275 ($(#[$meta:meta])* $vis:vis enum $name:ident {
276 $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
277 }, $type:ty, $err_ty:ty, $err:expr) => {
278 $(#[$meta])*
279 $vis enum $name {
280 $($(#[$vmeta])* $vname $(= $val)?,)*
281 }
282
283 impl TryFrom<$type> for $name {
284 type Error = $err_ty;
285
286 fn try_from(v: $type) -> Result<Self, Self::Error> {
287 let v = <$type>::from_be(v);
288 match v {
289 $(x if x == $name::$vname as $type => Ok($name::$vname),)*
290 _ => Err($err),
291 }
292 }
293 }
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use crate::*;
300
301 #[test]
302 fn test_impl_enum_try_from() {
303 impl_enum_try_from!(
304 #[repr(u16)]
305 #[derive(PartialEq, Eq, Debug)]
306 enum Test {
307 Test = 0x1234,
308 Test2 = 0x5678,
309 },
310 u16,
311 (),
312 ()
313 );
314
315 assert_eq!(Test::try_from(0x1234), Ok(Test::Test));
316 assert_eq!(Test::try_from(0x5678), Ok(Test::Test2));
317 }
318
319 #[test]
320 fn test_impl_enum_try_from_be() {
321 impl_enum_try_from_be!(
322 #[repr(u16)]
323 #[derive(PartialEq, Eq, Debug)]
324 enum Test {
325 Test = 0x1234,
326 Test2 = 0x5678,
327 },
328 u16,
329 (),
330 ()
331 );
332
333 assert_eq!(Test::try_from(0x3412), Ok(Test::Test));
334 assert_eq!(Test::try_from(0x7856), Ok(Test::Test2));
335 }
336}