diff --git a/src/lib.rs b/src/lib.rs index a00066e..2ed7333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ extern crate num; pub use std::option::Option; pub use num::FromPrimitive; +pub use num::ToPrimitive; /// Helper macro for internal use by `enum_from_primitive!`. #[macro_export] @@ -73,6 +74,30 @@ macro_rules! enum_from_primitive_impl_ty { }; } +/// Helper macro for internal use by `enum_to_primitive!`. +#[macro_export] +macro_rules! enum_to_primitive_impl_ty { + ($meth:ident, $ty:ty, $name:ident) => { + #[allow(non_upper_case_globals, unused)] + fn $meth(&self) -> $crate::Option<$ty> { + let copy: $name = unsafe { ::std::mem::transmute_copy(self) }; + Some(copy as $ty) + } + }; +} + +/// Helper macro for internal use by `enum_to_primitive!`. +#[macro_export] +#[macro_use(enum_to_primitive_impl_ty)] +macro_rules! enum_to_primitive_impl { + ($name:ident) => { + impl $crate::ToPrimitive for $name { + enum_to_primitive_impl_ty! { to_i64, i64, $name } + enum_to_primitive_impl_ty! { to_u64, u64, $name } + } + }; +} + /// Helper macro for internal use by `enum_from_primitive!`. #[macro_export] #[macro_use(enum_from_primitive_impl_ty)] @@ -194,3 +219,122 @@ macro_rules! enum_from_primitive { enum_from_primitive_impl! { $name, $( $( $variant )+ )+ } }; } + + +/// Wrap this macro around an `enum` declaration to get an +/// automatically generated implementation of `num::FromPrimitive`. +#[macro_export] +#[macro_use(enum_primitive_impl)] +macro_rules! enum_primitive { + ( + $( #[$enum_attr:meta] )* + enum $name:ident { + $( $( #[$variant_attr:meta] )* $variant:ident ),+ $( = $discriminator:expr, $( $( #[$variant_two_attr:meta] )* $variant_two:ident ),+ )* + } + ) => { + $( #[$enum_attr] )* + enum $name { + $( $( #[$variant_attr] )* $variant ),+ $( = $discriminator, $( $( #[$variant_two_attr] )* $variant_two ),+ )* + } + enum_from_primitive_impl! { $name, $( $variant )+ $( $( $variant_two )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + enum $name:ident { + $( $( $( #[$variant_attr:meta] )* $variant:ident ),+ = $discriminator:expr ),* + } + ) => { + $( #[$enum_attr] )* + enum $name { + $( $( $( #[$variant_attr] )* $variant ),+ = $discriminator ),* + } + enum_from_primitive_impl! { $name, $( $( $variant )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + enum $name:ident { + $( $( #[$variant_attr:meta] )* $variant:ident ),+ $( = $discriminator:expr, $( $( #[$variant_two_attr:meta] )* $variant_two:ident ),+ )*, + } + ) => { + $( #[$enum_attr] )* + enum $name { + $( $( #[$variant_attr] )* $variant ),+ $( = $discriminator, $( $( #[$variant_two_attr] )* $variant_two ),+ )*, + } + enum_from_primitive_impl! { $name, $( $variant )+ $( $( $variant_two )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + enum $name:ident { + $( $( $( #[$variant_attr:meta] )* $variant:ident ),+ = $discriminator:expr ),+, + } + ) => { + $( #[$enum_attr] )* + enum $name { + $( $( $( #[$variant_attr] )* $variant ),+ = $discriminator ),+, + } + enum_from_primitive_impl! { $name, $( $( $variant )+ )+ } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + pub enum $name:ident { + $( $( #[$variant_attr:meta] )* $variant:ident ),+ $( = $discriminator:expr, $( $( #[$variant_two_attr:meta] )* $variant_two:ident ),+ )* + } + ) => { + $( #[$enum_attr] )* + pub enum $name { + $( $( #[$variant_attr] )* $variant ),+ $( = $discriminator, $( $( #[$variant_two_attr] )* $variant_two ),+ )* + } + enum_from_primitive_impl! { $name, $( $variant )+ $( $( $variant_two )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + pub enum $name:ident { + $( $( $( #[$variant_attr:meta] )* $variant:ident ),+ = $discriminator:expr ),* + } + ) => { + $( #[$enum_attr] )* + pub enum $name { + $( $( $( #[$variant_attr] )* $variant ),+ = $discriminator ),* + } + enum_from_primitive_impl! { $name, $( $( $variant )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + pub enum $name:ident { + $( $( #[$variant_attr:meta] )* $variant:ident ),+ $( = $discriminator:expr, $( $( #[$variant_two_attr:meta] )* $variant_two:ident ),+ )*, + } + ) => { + $( #[$enum_attr] )* + pub enum $name { + $( $( #[$variant_attr] )* $variant ),+ $( = $discriminator, $( $( #[$variant_two_attr] )* $variant_two ),+ )*, + } + enum_from_primitive_impl! { $name, $( $variant )+ $( $( $variant_two )+ )* } + enum_to_primitive_impl! { $name } + }; + + ( + $( #[$enum_attr:meta] )* + pub enum $name:ident { + $( $( $( #[$variant_attr:meta] )* $variant:ident ),+ = $discriminator:expr ),+, + } + ) => { + $( #[$enum_attr] )* + pub enum $name { + $( $( $( #[$variant_attr] )* $variant ),+ = $discriminator ),+, + } + enum_from_primitive_impl! { $name, $( $( $variant )+ )+ } + enum_to_primitive_impl! { $name } + }; +} diff --git a/tests/tests.rs b/tests/tests.rs index 4cdc3b4..2ddae0f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -233,3 +233,53 @@ fn documented() { assert_eq!(Documented::from_i32(17), Some(Documented::A)); assert_eq!(Documented::from_i32(91), None); } + +#[test] +fn to_primitive() { + use ep::ToPrimitive; + + mod top { + enum_primitive! { + #[derive(Debug, PartialEq)] + #[repr(u64)] + pub enum EnumU64 { + A = 0xdeadbeefdeadbeef, + B = 0x0, + } + } + + enum_primitive! { + #[derive(Debug, PartialEq)] + #[repr(u32)] + pub enum EnumU32 { + A = 0xdeadbeef, + B = 0x0, + } + } + + enum_primitive! { + #[derive(Debug, PartialEq)] + #[repr(u16)] + pub enum EnumU16 { + A = 0xdead, + B = 0x0, + } + } + + enum_primitive! { + #[derive(Debug, PartialEq)] + #[repr(u8)] + pub enum EnumU8 { + A = 0xde, + B = 0x0, + } + } + } + + println!("what {:?}", top::EnumU8::A.to_u8()); + + assert_eq!(0xdeadbeefdeadbeef, top::EnumU64::A.to_u64().unwrap()); + assert_eq!(0xdeadbeef, top::EnumU32::A.to_u32().unwrap()); + assert_eq!(0xdead, top::EnumU16::A.to_u16().unwrap()); + assert_eq!(0xde, top::EnumU8::A.to_u8().unwrap()); +}