| 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let three: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!( |
| one.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `1`" |
| ); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(Enum::Two)); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!( |
| three.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `3`" |
| ); |
| |
| let four: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!( |
| two.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `2`" |
| ); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!(three, Ok(Enum::Three)); |
| |
| let four: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!( |
| two.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `2`" |
| ); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!(three, Ok(Enum::Three)); |
| |
| let four: Result<Enum, _> = 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<Enum, _> = (-2i8).try_into(); |
| assert_eq!(minus_two, Ok(Enum::MinusTwo)); |
| |
| let minus_one: Result<Enum, _> = (-1i8).try_into(); |
| assert_eq!(minus_one, Ok(Enum::MinusOne)); |
| |
| let zero: Result<Enum, _> = 0i8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1i8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(Enum::Two)); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!( |
| three.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `3`", |
| ); |
| |
| let four: Result<Enum, _> = 4u8.try_into(); |
| assert_eq!(four, Ok(Enum::Four)); |
| |
| let five: Result<Enum, _> = 5u8.try_into(); |
| assert_eq!(five, Ok(Enum::Five)); |
| |
| let six: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(Enum::Two)); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!( |
| three.unwrap_err().to_string(), |
| "No discriminant in enum `Enum` matches the value `3`", |
| ); |
| |
| let four: Result<Enum, _> = 4u8.try_into(); |
| assert_eq!(four, Ok(Enum::Four)); |
| |
| let five: Result<Enum, _> = 5u8.try_into(); |
| assert_eq!(five, Ok(Enum::Five)); |
| |
| let six: Result<Enum, _> = 6u8.try_into(); |
| assert_eq!(six, Ok(Enum::Six)); |
| |
| let seven: Result<Enum, _> = 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<EnumWithExclusiveRange, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(EnumWithExclusiveRange::Zero)); |
| |
| let one: Result<EnumWithExclusiveRange, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); |
| |
| let two: Result<EnumWithExclusiveRange, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); |
| |
| let three: Result<EnumWithExclusiveRange, _> = 3u8.try_into(); |
| assert_eq!(three, Ok(EnumWithExclusiveRange::OneOrTwoOrThree)); |
| |
| let four: Result<EnumWithExclusiveRange, _> = 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<EnumWithInclusiveRange, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(EnumWithInclusiveRange::Zero)); |
| |
| let one: Result<EnumWithInclusiveRange, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); |
| |
| let two: Result<EnumWithInclusiveRange, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); |
| |
| let three: Result<EnumWithInclusiveRange, _> = 3u8.try_into(); |
| assert_eq!(three, Ok(EnumWithInclusiveRange::OneOrTwoOrThree)); |
| |
| let four: Result<EnumWithInclusiveRange, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(ok, Ok(Enum::Ok)); |
| |
| let err: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(err, Ok(Enum::Error)); |
| |
| let unknown: Result<Enum, _> = 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<Enum, _> = (-1i8).try_into(); |
| assert_eq!(minus_one, Ok(Enum::OneTwoThreeOrMinusOne)); |
| |
| let zero: Result<Enum, _> = 0i8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1i8.try_into(); |
| assert_eq!(one, Ok(Enum::OneTwoThreeOrMinusOne)); |
| |
| let two: Result<Enum, _> = 2i8.try_into(); |
| assert_eq!(two, Ok(Enum::OneTwoThreeOrMinusOne)); |
| |
| let three: Result<Enum, _> = 3i8.try_into(); |
| assert_eq!(three, Ok(Enum::OneTwoThreeOrMinusOne)); |
| |
| let four: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(Enum::Other)); |
| |
| let max_value: Result<Enum, _> = 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<Enum, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(Enum::Zero)); |
| |
| let one: Result<Enum, _> = 1u8.try_into(); |
| assert_eq!(one, Ok(Enum::One)); |
| |
| let two: Result<Enum, _> = 2u8.try_into(); |
| assert_eq!(two, Ok(Enum::TwoOrThree)); |
| |
| let three: Result<Enum, _> = 3u8.try_into(); |
| assert_eq!(three, Ok(Enum::TwoOrThree)); |
| |
| let four: Result<Enum, _> = 4u8.try_into(); |
| assert_eq!(four, Ok(Enum::Four)); |
| |
| let five: Result<Enum, _> = 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<T>` |
| // - `TryFrom<T>` |
| |
| 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<FirstNumber, _> = 0u8.try_into(); |
| assert_eq!(zero, Ok(FirstNumber::Zero)); |
| |
| let three: Result<FirstNumber, _> = 3u8.try_into(); |
| assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 }); |
| |
| let three: Result<SecondNumber, _> = 3u8.try_into(); |
| assert_eq!(three.unwrap_err(), CustomError { bad_value: 3u8 }); |
| } |
| |
| // #[derive(FromPrimitive)] generates implementations for the following traits: |
| // |
| // - `FromPrimitive<T>` |
| // - `From<T>` |
| // - `TryFromPrimitive<T>` |
| // - `TryFrom<T>` |