diff --git a/maraiah/cbitfield.rs b/maraiah/cbitfield.rs index 6abe235..4b3729c 100644 --- a/maraiah/cbitfield.rs +++ b/maraiah/cbitfield.rs @@ -1,5 +1,6 @@ #![doc(hidden)] +/// A wrapper for `bitflags!` which adds a `FromStr` implementation. #[macro_export] macro_rules! c_bitfield { @@ -22,6 +23,65 @@ macro_rules! c_bitfield )+ } } + + #[allow(unused_qualifications)] + impl std::str::FromStr for $t + { + type Err = $crate::err::ParseFlagError; + + fn from_str(s: &str) -> Result + { + let mut flags = $t::empty(); + + for s in s.split('|') { + match s { + $( + stringify!($f) => flags.insert($t::$f), + )+ + "(none)" => (), + _ => return Err(Self::Err::new(stringify!($t))) + } + } + + Ok(flags) + } + } + } +} + +#[cfg(test)] +mod test +{ + use crate::err::ParseFlagError; + use std::str::FromStr; + + c_bitfield! { + pub struct TestFlag: u16 { + ZERO = 0, + ONE = 1, + TWO = 2, + } + } + + #[test] + fn c_bitfield() + { + assert_eq!(TestFlag::from_bits(0), Some(TestFlag::empty())); + assert_eq!(TestFlag::from_bits(1), Some(TestFlag::ZERO)); + assert_eq!(TestFlag::from_bits(2), Some(TestFlag::ONE)); + assert_eq!(TestFlag::from_bits(4), Some(TestFlag::TWO)); + assert_eq!(TestFlag::from_bits(8), None); + assert_eq!(TestFlag::from_str("(none)"), Ok(TestFlag::empty())); + assert_eq!(TestFlag::from_str("ZERO"), Ok(TestFlag::ZERO)); + assert_eq!(TestFlag::from_str("ONE"), Ok(TestFlag::ONE)); + assert_eq!(TestFlag::from_str("TWO"), Ok(TestFlag::TWO)); + assert_eq!(TestFlag::from_str("ZERO|ONE|TWO"), Ok(TestFlag::all())); + assert_eq!(TestFlag::from_str("TWO|ZERO|ONE"), Ok(TestFlag::all())); + assert_eq!(TestFlag::from_str("ONE|ONE|ONE"), Ok(TestFlag::ONE)); + assert_eq!(TestFlag::from_str("(none)|(none)"), Ok(TestFlag::empty())); + assert_eq!(TestFlag::from_str("(none)|ONE"), Ok(TestFlag::ONE)); + assert_eq!(TestFlag::from_str("THREE"), + Err(ParseFlagError::new("TestFlag"))); } } diff --git a/maraiah/cenum.rs b/maraiah/cenum.rs index 57e0d37..848e8b6 100644 --- a/maraiah/cenum.rs +++ b/maraiah/cenum.rs @@ -76,7 +76,9 @@ macro_rules! c_enum fn from_str(s: &str) -> Result { match s { - $(stringify!($en) => Ok($t::$en),)+ + $( + stringify!($en) => Ok($t::$en), + )+ _ => Err(Self::Err::new(stringify!($t))) } } diff --git a/maraiah/err.rs b/maraiah/err.rs index 8dd9fe6..0b96835 100644 --- a/maraiah/err.rs +++ b/maraiah/err.rs @@ -71,6 +71,24 @@ impl ParseEnumError } } +impl ParseFlagError +{ + /// Returns an `Error` with a message for flag parsing errata. + /// + /// # Examples + /// + /// ``` + /// use maraiah::err::ParseFlagError; + /// + /// assert_eq!(format!("{}", ParseFlagError::new("TypeName")), + /// "could not parse TypeName"); + /// ``` + pub fn new(t: &'static str) -> Self + { + Self(t) + } +} + impl ReprError { /// Returns an `Error` with a message for representation errata. @@ -91,6 +109,7 @@ impl ReprError impl Fail for ErrMsg {} impl Fail for ParseEnumError {} +impl Fail for ParseFlagError {} impl Fail for ReprError {} impl fmt::Display for ParseEnumError @@ -101,6 +120,14 @@ impl fmt::Display for ParseEnumError } } +impl fmt::Display for ParseFlagError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "could not parse {}", self.0) + } +} + impl fmt::Display for ReprError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result @@ -124,6 +151,10 @@ struct ErrMsg(&'static str); #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ParseEnumError(&'static str); +/// A parser error for a bit field. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ParseFlagError(&'static str); + /// A representation error for an integer. #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ReprError(&'static str, i64);