Maraiah/maraiah/cenum.rs

121 lines
2.8 KiB
Rust
Raw Permalink Normal View History

2019-03-01 01:27:14 -08:00
#![doc(hidden)]
2019-06-17 22:40:39 -07:00
/// Creates an enumeration and function for converting a representation into the
/// enumeration type.
2019-03-01 01:27:14 -08:00
///
/// The syntax is similar to the `c_bitfield` macro, but each value has the
/// syntax `value = enumeration`. `enum` is used instead of `struct`.
2019-03-01 01:27:14 -08:00
///
2019-04-11 18:38:33 -07:00
/// This will generate an `enum $t` as well as implement `TryFrom` on it which
2019-03-04 22:05:01 -08:00
/// will return `Result<$t, ReprError>`.
2019-03-04 18:14:09 -08:00
///
/// # Examples
///
/// ```
2019-06-13 18:09:07 -07:00
/// use maraiah::{c_enum, err::ReprError};
2019-04-11 21:04:39 -07:00
/// use std::convert::TryFrom;
2019-03-04 18:14:09 -08:00
///
/// c_enum! {
2019-07-05 20:21:11 -07:00
/// enum MyEnum: u16 {
/// Zero = 0,
/// One = 1,
/// Two = 2
/// }
2019-03-04 18:14:09 -08:00
/// }
///
2019-03-18 09:22:10 -07:00
/// assert_eq!(MyEnum::try_from(0), Ok(MyEnum::Zero));
/// assert_eq!(MyEnum::try_from(1), Ok(MyEnum::One));
/// assert_eq!(MyEnum::try_from(2), Ok(MyEnum::Two));
/// assert_eq!(MyEnum::try_from(3), Err(ReprError::new("MyEnum", 3)));
/// assert_eq!(MyEnum::try_from(4), Err(ReprError::new("MyEnum", 4)));
/// assert_eq!(MyEnum::try_from(5), Err(ReprError::new("MyEnum", 5)));
2019-03-04 18:14:09 -08:00
/// ```
#[macro_export]
macro_rules! c_enum
{
2019-07-05 20:21:11 -07:00
(
$(#[$outer:meta])*
$vi:vis enum $t:ident: $ti:ident {
$(
$(#[$inner:meta])*
$en:ident = $va:expr
),+
$(,)?
}
) => {
$(#[$outer])*
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr($ti)]
$vi enum $t {
$(
$(#[$inner])*
$en = $va,
)+
}
2019-07-05 20:21:11 -07:00
#[allow(unused_qualifications)]
impl std::convert::TryFrom<$ti> for $t
{
type Error = $crate::err::ReprError;
2019-04-11 18:38:33 -07:00
2019-07-05 20:21:11 -07:00
/// Returns, if representable, the variant of `Self` from `n`.
fn try_from(n: $ti) -> Result<Self, Self::Error>
{
match n {
$($va => Ok($t::$en),)+
n => Err(Self::Error::new(stringify!($t), n))
}
}
}
2019-07-05 20:21:11 -07:00
#[allow(unused_qualifications)]
impl std::str::FromStr for $t
{
type Err = $crate::err::ParseEnumError;
2019-07-05 20:21:11 -07:00
fn from_str(s: &str) -> Result<Self, Self::Err>
{
match s {
$(
stringify!($en) => Ok($t::$en),
)+
_ => Err(Self::Err::new(stringify!($t)))
}
}
}
};
}
#[cfg(test)]
mod test
{
2019-07-05 20:21:11 -07:00
use crate::err::{ParseEnumError, ReprError};
use std::{convert::TryFrom, str::FromStr};
2019-07-05 20:21:11 -07:00
c_enum! {
enum TestEnum: u16 {
Zero = 0,
One = 1,
Two = 2,
}
}
2019-07-05 20:21:11 -07:00
#[test]
fn c_enum()
{
assert_eq!(TestEnum::try_from(0), Ok(TestEnum::Zero));
assert_eq!(TestEnum::try_from(1), Ok(TestEnum::One));
assert_eq!(TestEnum::try_from(2), Ok(TestEnum::Two));
assert_eq!(TestEnum::try_from(3), Err(ReprError::new("TestEnum", 3)));
assert_eq!(TestEnum::try_from(4), Err(ReprError::new("TestEnum", 4)));
assert_eq!(TestEnum::try_from(5), Err(ReprError::new("TestEnum", 5)));
assert_eq!(TestEnum::from_str("Zero"), Ok(TestEnum::Zero));
assert_eq!(TestEnum::from_str("One"), Ok(TestEnum::One));
assert_eq!(TestEnum::from_str("Two"), Ok(TestEnum::Two));
assert_eq!(TestEnum::from_str("Three"),
Err(ParseEnumError::new("TestEnum")));
}
}
// EOF