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}