#![doc(hidden)]

/// Creates an enumeration and function for converting a representation into
/// the enumeration type.
///
/// The syntax is similar to the `bitflags` macro, but each value has the
/// syntax `value => enumeration`. `enum` is used instead of `struct`.
///
/// This will generate an `enum $t` as well as a function `$t::from_repr` which
/// will return `Result<$t, ReprError>`.
///
/// # Examples
///
/// ```
/// use maraiah::{c_enum, durandal::err::ReprError};
///
/// c_enum! {
///    #[derive(Debug)]
///    enum MyEnum: u16
///    {
///       0 => Zero,
///       1 => One,
///       2 => Two,
///    }
/// }
///
/// assert_eq!(MyEnum::from_repr(0), Ok(MyEnum::Zero));
/// assert_eq!(MyEnum::from_repr(1), Ok(MyEnum::One));
/// assert_eq!(MyEnum::from_repr(2), Ok(MyEnum::Two));
/// assert_eq!(MyEnum::from_repr(3), Err(ReprError::new(3)));
/// assert_eq!(MyEnum::from_repr(4), Err(ReprError::new(4)));
/// assert_eq!(MyEnum::from_repr(5), Err(ReprError::new(5)));
/// ```
#[macro_export]
macro_rules! c_enum
{
   (
      $(#[$outer:meta])*
      $vi:vis enum $t:ident: $ti:ty
      {
         $($(#[$inner:meta])* $va:expr => $en:ident,)+
      }
   ) => {
      $(#[$outer])*
      #[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
      $vi enum $t
      {
         $($en,)+
      }

      impl $t
      {
         /// Returns, if representable, the variant of `Self` from `n`.
         $vi fn from_repr(n: $ti) -> Result<Self, ReprError>
         {
            match n {
               $($(#[$inner])* $va => Ok($t::$en),)+
               n => Err(ReprError::new(n))
            }
         }
      }
   };
}

#[cfg(test)]
mod test
{
   use crate::durandal::err::ReprError;

   c_enum! {
      #[derive(Debug)]
      enum TestEnum: u16
      {
         0 => Zero,
         1 => One,
         2 => Two,
      }
   }

   #[test]
   fn c_enum_should_be_ok()
   {
      assert_eq!(TestEnum::from_repr(0), Ok(TestEnum::Zero));
      assert_eq!(TestEnum::from_repr(1), Ok(TestEnum::One));
      assert_eq!(TestEnum::from_repr(2), Ok(TestEnum::Two));
      assert_eq!(TestEnum::from_repr(3), Err(ReprError::new(3)));
      assert_eq!(TestEnum::from_repr(4), Err(ReprError::new(4)));
      assert_eq!(TestEnum::from_repr(5), Err(ReprError::new(5)));
   }

   #[test]
   #[should_panic]
   fn c_enum_should_error() {TestEnum::from_repr(3).unwrap();}
}

// EOF
