use ::std::convert::{TryFrom, TryInto}; use ::num_enum::TryFromPrimitive; // Guard against https://github.com/illicitonion/num_enum/issues/27 mod alloc {} mod core {} mod num_enum {} mod std {} #[test] fn simple() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero, One, Two, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let three: Result = 3u8.try_into(); assert_eq!( three.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `3`" ); } #[test] fn even() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero = 0, Two = 2, Four = 4, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!( one.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `1`" ); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(Enum::Two)); let three: Result = 3u8.try_into(); assert_eq!( three.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `3`" ); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); } #[test] fn skipped_value() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero, One, Three = 3, Four, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!( two.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); let three: Result = 3u8.try_into(); assert_eq!(three, Ok(Enum::Three)); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); } #[test] fn wrong_order() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Four = 4, Three = 3, Zero = 0, One, // Zero + 1 } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!( two.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); let three: Result = 3u8.try_into(); assert_eq!(three, Ok(Enum::Three)); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); } #[test] fn negative_values() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(i8)] enum Enum { MinusTwo = -2, MinusOne = -1, Zero = 0, One = 1, Two = 2, } let minus_two: Result = (-2i8).try_into(); assert_eq!(minus_two, Ok(Enum::MinusTwo)); let minus_one: Result = (-1i8).try_into(); assert_eq!(minus_one, Ok(Enum::MinusOne)); let zero: Result = 0i8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1i8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2i8.try_into(); assert_eq!(two, Ok(Enum::Two)); } #[test] fn discriminant_expressions() { const ONE: u8 = 1; #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero, One = ONE, Two, Four = 4u8, Five, Six = ONE + ONE + 2u8 + 2, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(Enum::Two)); let three: Result = 3u8.try_into(); assert_eq!( three.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `3`", ); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); let five: Result = 5u8.try_into(); assert_eq!(five, Ok(Enum::Five)); let six: Result = 6u8.try_into(); assert_eq!(six, Ok(Enum::Six)); } #[cfg(feature = "complex-expressions")] mod complex { use num_enum::TryFromPrimitive; use std::convert::TryInto; const ONE: u8 = 1; #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero, One = ONE, Two, Four = 4u8, Five, Six = ONE + ONE + 2u8 + 2, Seven = (7, 2).0, } #[test] fn different_values() { let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(Enum::Two)); let three: Result = 3u8.try_into(); assert_eq!( three.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `3`", ); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); let five: Result = 5u8.try_into(); assert_eq!(five, Ok(Enum::Five)); let six: Result = 6u8.try_into(); assert_eq!(six, Ok(Enum::Six)); let seven: Result = 7u8.try_into(); assert_eq!(seven, Ok(Enum::Seven)); } #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum EnumWithExclusiveRange { Zero = 0, #[num_enum(alternatives = [2..4])] OneOrTwoOrThree, } #[test] fn different_values_with_exclusive_range() { let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(EnumWithExclusiveRange::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); let three: Result = 3u8.try_into(); assert_eq!(three, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); let four: Result = 4u8.try_into(); assert_eq!( four.unwrap_err().to_string(), "No discriminant in enum `EnumWithExclusiveRange` matches the value `4`", ); } #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum EnumWithInclusiveRange { Zero = 0, #[num_enum(alternatives = [2..=3])] OneOrTwoOrThree, } #[test] fn different_values_with_inclusive_range() { let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(EnumWithInclusiveRange::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); let three: Result = 3u8.try_into(); assert_eq!(three, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); let four: Result = 4u8.try_into(); assert_eq!( four.unwrap_err().to_string(), "No discriminant in enum `EnumWithInclusiveRange` matches the value `4`", ); } } #[test] fn missing_trailing_comma() { #[rustfmt::skip] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero, One } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!( two.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); } #[test] fn ignores_extra_attributes() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[allow(unused)] #[repr(u8)] enum Enum { Zero, #[allow(unused)] One, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!( two.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); } #[test] fn visibility_is_fine() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] pub(crate) enum Enum { Zero, One, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!( two.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); } #[test] fn error_variant_is_allowed() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] pub enum Enum { Ok, Error, } let ok: Result = 0u8.try_into(); assert_eq!(ok, Ok(Enum::Ok)); let err: Result = 1u8.try_into(); assert_eq!(err, Ok(Enum::Error)); let unknown: Result = 2u8.try_into(); assert_eq!( unknown.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `2`" ); } #[test] fn alternative_values() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(i8)] enum Enum { Zero = 0, #[num_enum(alternatives = [-1, 2, 3])] OneTwoThreeOrMinusOne = 1, } let minus_one: Result = (-1i8).try_into(); assert_eq!(minus_one, Ok(Enum::OneTwoThreeOrMinusOne)); let zero: Result = 0i8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1i8.try_into(); assert_eq!(one, Ok(Enum::OneTwoThreeOrMinusOne)); let two: Result = 2i8.try_into(); assert_eq!(two, Ok(Enum::OneTwoThreeOrMinusOne)); let three: Result = 3i8.try_into(); assert_eq!(three, Ok(Enum::OneTwoThreeOrMinusOne)); let four: Result = 4i8.try_into(); assert_eq!( four.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `4`" ); } #[test] fn default_value() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { Zero = 0, One = 1, #[num_enum(default)] Other = 2, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(Enum::Other)); let max_value: Result = u8::max_value().try_into(); assert_eq!( max_value.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `255`" ); } #[test] fn alternative_values_and_default_value() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { #[num_enum(default)] Zero = 0, One = 1, #[num_enum(alternatives = [3])] TwoOrThree = 2, Four = 4, } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(Enum::Zero)); let one: Result = 1u8.try_into(); assert_eq!(one, Ok(Enum::One)); let two: Result = 2u8.try_into(); assert_eq!(two, Ok(Enum::TwoOrThree)); let three: Result = 3u8.try_into(); assert_eq!(three, Ok(Enum::TwoOrThree)); let four: Result = 4u8.try_into(); assert_eq!(four, Ok(Enum::Four)); let five: Result = 5u8.try_into(); assert_eq!( five.unwrap_err().to_string(), "No discriminant in enum `Enum` matches the value `5`" ); } #[test] fn try_from_primitive_number() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] enum Enum { #[num_enum(default)] Whatever = 0, } // #[derive(FromPrimitive)] generates implementations for the following traits: // // - `TryFromPrimitive` // - `TryFrom` let try_from_primitive = Enum::try_from_primitive(0_u8); assert_eq!(try_from_primitive, Ok(Enum::Whatever)); let try_from = Enum::try_from(0_u8); assert_eq!(try_from, Ok(Enum::Whatever)); } #[test] fn custom_error() { #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[num_enum(error_type(name = CustomError, constructor = CustomError::new))] #[repr(u8)] enum FirstNumber { Zero, One, Two, } #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[num_enum(error_type(constructor = CustomError::new, name = CustomError))] #[repr(u8)] enum SecondNumber { Zero, One, Two, } #[derive(Debug, PartialEq, Eq)] struct CustomError { bad_value: u8, } impl CustomError { fn new(value: u8) -> CustomError { CustomError { bad_value: value } } } let zero: Result = 0u8.try_into(); assert_eq!(zero, Ok(FirstNumber::Zero)); let three: Result = 3u8.try_into(); assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 }); let three: Result = 3u8.try_into(); assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 }); } // #[derive(FromPrimitive)] generates implementations for the following traits: // // - `FromPrimitive` // - `From` // - `TryFromPrimitive` // - `TryFrom`