Skip to main content

workflow_core/
enums.rs

1//!
2//! Rust enum conversion utilities
3//!
4
5pub use workflow_core_macros::Describe;
6
7/// Enum trait used by the [`Describe`] derive macro
8pub trait Describe: Sized + 'static {
9    /// return a caption for the enum declared by the `#[caption=""]` attribute
10    fn caption() -> &'static str;
11    /// return all permutations of the enum as an iterator
12    fn iter() -> impl Iterator<Item = &'static Self>;
13    /// converts enum into an iterator
14    fn into_iter() -> impl Iterator<Item = Self>;
15    /// return `rust doc` text describing the enum value
16    fn rustdoc(&self) -> &'static str;
17    /// return `#[describe=""]` text describing the enum value
18    fn describe(&self) -> &'static str;
19    /// return enum value as a string without namespace (i.e. `Value`)
20    fn as_str(&self) -> &'static str;
21    /// return enum value as a string with namespace (i.e. `Enum::Value`)
22    fn as_str_ns(&self) -> &'static str;
23    /// get enum value from the value string without namespace (i.e. `Value`)
24    fn from_str(str: &str) -> Option<Self>;
25    /// get enum value from the value string with namespace (i.e. `Enum::Value`)
26    fn from_str_ns(str: &str) -> Option<Self>;
27}
28
29/// Error produced by the enum `try_from` macros
30#[derive(Clone, Debug, thiserror::Error)]
31#[allow(non_camel_case_types)]
32pub enum TryFromError {
33    /// A `u32` value (second field) is out of range for the named enum (first field).
34    #[error("value for enum `{0}` is out of range: {1}")]
35    u32(&'static str, u32),
36    /// A `u16` value (second field) is out of range for the named enum (first field).
37    #[error("value for enum `{0}` is out of range: {1}")]
38    u16(&'static str, u16),
39    /// A `u8` value (second field) is out of range for the named enum (first field).
40    #[error("value for enum `{0}` is out of range: {1}")]
41    u8(&'static str, u8),
42    /// A `usize` value (second field) is out of range for the named enum (first field).
43    #[error("value for enum `{0}` is out of range: {1}")]
44    usize(&'static str, usize),
45}
46
47///
48/// Associates u32 values to each enum value and declares
49/// a `TryFrom<u32>` implementation for this enum allowing
50/// a `try_from(u32)` to enum conversion.
51///
52/// Example:
53/// ```rust
54/// use workflow_core::enums::u32_try_from;
55///
56/// u32_try_from!{
57///     #[derive(Debug, Clone, PartialEq)]
58///     enum MyEnum {
59///         A,  // 0u32
60///         B,  // 1u32
61///         C,  // 2u32
62///     }
63/// }
64///
65/// let v1 = MyEnum::B;
66/// let n = v1.clone() as u32;
67/// let v2 = MyEnum::try_from(n).unwrap();
68/// assert_eq!(v1, v2);
69/// ```
70///
71#[macro_export]
72macro_rules! u32_try_from {
73        ($(#[$meta:meta])* $vis:vis enum $name:ident {
74        $($(#[$vmeta:meta])* $vname:ident $(= $val:expr_2021)?,)*
75    }) => {
76        $(#[$meta])*
77        $vis enum $name {
78            $($(#[$vmeta])* $vname $(= $val)?,)*
79        }
80
81        impl std::convert::TryFrom<u32> for $name {
82            type Error = workflow_core::enums::TryFromError;
83
84            fn try_from(v: u32) -> std::result::Result<Self, workflow_core::enums::TryFromError> {
85                match v {
86                    $(x if x == $name::$vname as u32 => Ok($name::$vname),)*
87                    _ => {
88                        Err(workflow_core::enums::TryFromError::u32(stringify!($name),v))
89                    },
90                }
91            }
92        }
93    }
94}
95
96pub use u32_try_from;
97
98///
99/// Associates u16 values to each enum value and declares
100/// a `TryFrom<u16>` implementation for this enum allowing
101/// a `try_from(u16)` to enum conversion.
102///
103/// Example:
104/// ```rust
105/// use workflow_core::enums::u16_try_from;
106///
107/// u16_try_from!{
108///     #[derive(Debug, Clone, PartialEq)]
109///     enum MyEnum {
110///         A,  // 0u16
111///         B,  // 1u16
112///         C,  // 2u16
113///     }
114/// }
115///
116/// let v1 = MyEnum::B;
117/// let n: u16 = v1.clone() as u16;
118/// let v2 = MyEnum::try_from(n).unwrap();
119/// assert_eq!(v1, v2);
120/// ```
121///
122#[macro_export]
123macro_rules! u16_try_from {
124    ($(#[$meta:meta])* $vis:vis enum $name:ident {
125    $($(#[$vmeta:meta])* $vname:ident $(= $val:expr_2021)?,)*
126    }) => {
127        $(#[$meta])*
128        $vis enum $name {
129            $($(#[$vmeta])* $vname $(= $val)?,)*
130        }
131
132        impl std::convert::TryFrom<u16> for $name {
133            type Error = workflow_core::enums::TryFromError;
134
135            fn try_from(v: u16) -> std::result::Result<Self, workflow_core::enums::TryFromError> {
136                match v {
137                    $(x if x == $name::$vname as u16 => Ok($name::$vname),)*
138                    _ => {
139                        Err(workflow_core::enums::TryFromError::u16(stringify!($name),v))
140                    },
141                }
142            }
143        }
144
145        impl std::convert::From<$name> for u16 {
146            fn from(v: $name) -> u16 {
147                v as u16
148            }
149        }
150    }
151}
152
153pub use u16_try_from;
154
155///
156///  Associates u8 values to each enum value and declares
157/// a `TryFrom<u8>` implementation for this enum allowing
158/// a `try_from(u8)` to enum conversion.
159///
160/// Example:
161/// ```rust
162/// use workflow_core::enums::u8_try_from;
163///
164/// u8_try_from!{
165///     #[derive(Debug, Clone, PartialEq)]
166///     enum MyEnum {
167///         A,  // 0u8
168///         B,  // 1u8
169///         C,  // 2u8
170///     }
171/// }
172///
173/// let v1 = MyEnum::B;
174/// let n: u8 = v1.clone() as u8;
175/// let v2 = MyEnum::try_from(n).unwrap();
176/// assert_eq!(v1, v2);
177/// ```
178///
179#[macro_export]
180macro_rules! u8_try_from {
181    ($(#[$meta:meta])* $vis:vis enum $name:ident {
182    $($(#[$vmeta:meta])* $vname:ident $(= $val:expr_2021)?,)*
183    }) => {
184        $(#[$meta])*
185        $vis enum $name {
186            $($(#[$vmeta])* $vname $(= $val)?,)*
187        }
188
189        impl std::convert::TryFrom<u8> for $name {
190            type Error = workflow_core::enums::TryFromError;
191
192            fn try_from(v: u8) -> std::result::Result<Self, workflow_core::enums::TryFromError> {
193                match v {
194                    $(x if x == $name::$vname as u8 => Ok($name::$vname),)*
195                    _ => {
196                        Err(workflow_core::enums::TryFromError::u8(stringify!($name),v))
197                    },
198                }
199            }
200        }
201    }
202}
203
204pub use u8_try_from;
205
206///
207///  Associates usize values to each enum value and declares
208/// a `TryFrom<usize>` implementation for this enum allowing
209/// a `try_from(usize)` to enum conversion.
210///
211/// Example:
212/// ```rust
213/// use workflow_core::enums::usize_try_from;
214///
215/// usize_try_from!{
216///     #[derive(Debug, Clone, PartialEq)]
217///     enum MyEnum {
218///         A,  // 0usize
219///         B,  // 1usize
220///         C,  // 2usize
221///     }
222/// }
223///
224/// let v1 = MyEnum::B;
225/// let n: usize = v1.clone() as usize;
226/// let v2 = MyEnum::try_from(n).unwrap();
227/// assert_eq!(v1, v2);
228/// ```
229///
230#[macro_export]
231macro_rules! usize_try_from {
232    ($(#[$meta:meta])* $vis:vis enum $name:ident {
233    $($(#[$vmeta:meta])* $vname:ident $(= $val:expr_2021)?,)*
234    }) => {
235        $(#[$meta])*
236        $vis enum $name {
237            $($(#[$vmeta])* $vname $(= $val)?,)*
238        }
239
240        impl std::convert::TryFrom<usize> for $name {
241            type Error = workflow_core::enums::TryFromError;
242
243            fn try_from(v: usize) -> std::result::Result<Self, workflow_core::enums::TryFromError> {
244                match v {
245                    $(x if x == $name::$vname as usize => Ok($name::$vname),)*
246                    _ => {
247                        Err(workflow_core::enums::TryFromError::usize(stringify!($name),v))
248                    },
249                }
250            }
251        }
252    }
253}
254
255pub use usize_try_from;