#[macro_export]
macro_rules! bijective_enum_map {
{ $enum_ty:ty, $into:ty, $from:ty, $($body:tt)* } => {
$crate::__impl_from_enum! { $enum_ty, $into, $($body)* }
$crate::__impl_enum_from! { $enum_ty, $from, $($body)* }
};
{ $enum_ty:ty, $into:ty, $from:ty } => {
$crate::__impl_from_enum! { $enum_ty, $into }
$crate::__impl_enum_from! { $enum_ty, $from }
};
{ $enum_ty:ty, $both:ty, $($body:tt)* } => {
$crate::__impl_from_enum! { $enum_ty, $both, $($body)* }
$crate::__impl_enum_from! { $enum_ty, $both, $($body)* }
};
{ $enum_ty:ty, $both:ty } => {
$crate::__impl_from_enum! { $enum_ty, $both }
$crate::__impl_enum_from! { $enum_ty, $both }
};
}
#[cfg(test)]
mod tests {
use crate::bijective_enum_map;
#[test]
fn empty_both_specified() {
#[derive(Debug, PartialEq, Eq)]
enum Empty {}
enum AnotherEmpty {}
bijective_enum_map! { Empty, AnotherEmpty, AnotherEmpty }
fn _new_empty() -> Empty {
panic!()
}
fn _new_another_empty() -> AnotherEmpty {
AnotherEmpty::from(_new_empty())
}
fn _round_trip() -> Empty {
Empty::from(_new_another_empty())
}
}
#[test]
fn empty_one_specified() {
#[derive(Debug, PartialEq, Eq)]
enum Empty {}
enum AnotherEmpty {}
bijective_enum_map! { Empty, AnotherEmpty, }
fn _new_empty() -> Empty {
panic!()
}
fn _new_another_empty() -> AnotherEmpty {
AnotherEmpty::from(_new_empty())
}
fn _round_trip() -> Empty {
Empty::from(_new_another_empty())
}
}
#[test]
fn nonempty_both_specified() {
#[derive(Debug, PartialEq, Eq)]
enum Trivial {
Num(u8),
}
bijective_enum_map! {Trivial, u8, u8, Num(num) <=> num}
assert_eq!(Trivial::from(2_u8), Trivial::Num(2));
assert_eq!(u8::from(Trivial::Num(3)), 3);
}
#[test]
fn nonempty_one_specified() {
#[derive(Debug, PartialEq, Eq)]
enum Trivial {
Num(u8),
}
bijective_enum_map! {Trivial, u8, Num(num) <=> num}
assert_eq!(Trivial::from(2_u8), Trivial::Num(2));
assert_eq!(u8::from(Trivial::Num(3)), 3);
}
#[test]
fn nonempty_enums() {
#[derive(Debug, PartialEq, Eq)]
enum Enum {
One,
Two,
Three,
}
#[derive(Debug, PartialEq, Eq)]
enum Other {
Uno,
Dos,
Tres,
}
bijective_enum_map! {
Enum, Other, Other,
One <=> Other::Uno,
Two <=> Other::Dos,
Three <=> Other::Tres,
}
assert_eq!(Other::from(Enum::Three), Other::Tres);
assert_eq!(Enum::from(Other::Uno), Enum::One);
}
#[test]
fn trailing_commas() {
enum Empty {}
enum AnotherEmpty {}
enum YetAnotherEmpty {}
enum AFourthEmpty {}
enum Trivial {
Num(u8),
}
enum Trivial2 {
Num(u16),
}
enum Trivial3 {
Num(i8),
}
enum Trivial4 {
Num(i16),
}
bijective_enum_map!(Empty, AnotherEmpty, AnotherEmpty);
bijective_enum_map! { Empty, YetAnotherEmpty };
bijective_enum_map! {
AnotherEmpty, YetAnotherEmpty, YetAnotherEmpty,
};
bijective_enum_map! { Empty, AFourthEmpty, };
bijective_enum_map!(Trivial, u8, u8, Num(num) <=> num);
bijective_enum_map! { Trivial2, u16, Num(num) <=> num };
bijective_enum_map! {
Trivial3, i8, i8, Num(num) <=> num,
};
bijective_enum_map! { Trivial4, i16, Num(num) <=> num, };
}
#[test]
fn non_unit_variant() {
#[derive(Debug, PartialEq)]
enum Thing {
Player {
name: &'static str,
hp: u32,
strength: f32,
},
PhysicalObject {
name: &'static str,
hp: u32,
},
Spell {
name: &'static str,
strength: f32,
},
Marker(&'static str),
HardcodedMarker(&'static str, u32),
Unknown(Stuff),
}
type Stuff = (u8, Option<&'static str>, Option<u32>, Option<f32>);
bijective_enum_map! {
Thing, Stuff,
Player { name, hp, strength } <=> (0, Some(name), Some(hp), Some(strength)),
PhysicalObject { name, hp } <=> (1, Some(name), Some(hp), None),
Spell { name, strength } <=> (2, Some(name), None, Some(strength)),
Marker(name) <=> (3, Some(name), None, None),
HardcodedMarker(name, id) <=> (4, Some(name), Some(id), None),
Unknown(stuff) <=> stuff,
}
assert_eq!(
Stuff::from(Thing::Player { name: "person", hp: 2, strength: 1.5 }),
(0, Some("person"), Some(2), Some(1.5)),
);
assert_eq!(
Stuff::from(Thing::Marker("place")),
(3, Some("place"), None, None),
);
assert_eq!(
Thing::from((1_u8, Some("object"), Some(100_u32), None)),
Thing::PhysicalObject { name: "object", hp: 100 },
);
assert_eq!(
Thing::from((1_u8, Some("object"), Some(100_u32), Some(1e30))),
Thing::Unknown((1_u8, Some("object"), Some(100_u32), Some(1e30))),
);
}
#[test]
fn intentionally_non_surjective() {
#[derive(Debug, PartialEq, Eq)]
enum Enum {
One,
Two,
Three,
}
#[derive(Debug, PartialEq, Eq)]
enum Other {
Uno,
Dos,
Tres,
Cuatro,
}
#[allow(warnings)]
{
bijective_enum_map! {
Enum, Other, Other,
One <=> Other::Uno,
Two <=> Other::Dos,
Three <=> Other::Tres,
Three <=> Other::Cuatro,
}
}
assert_eq!(Other::from(Enum::Three), Other::Tres);
assert_eq!(Enum::from(Other::Uno), Enum::One);
assert_eq!(Enum::from(Other::Cuatro), Enum::Three);
}
#[test]
fn intentionally_non_injective() {
#[derive(Debug, PartialEq, Eq)]
enum Enum {
One,
Two,
Three,
}
#[derive(Debug, PartialEq, Eq)]
enum Other {
Uno,
Dos,
Tres,
Cuatro,
}
#[allow(warnings)]
{
bijective_enum_map! {
Other, Enum,
Uno <=> Enum::One,
Dos <=> Enum::Two,
Tres <=> Enum::Three,
Cuatro <=> Enum::Three,
}
}
assert_eq!(Other::from(Enum::Three), Other::Tres);
assert_eq!(Enum::from(Other::Uno), Enum::One);
assert_eq!(Enum::from(Other::Cuatro), Enum::Three);
}
}
#[cfg(doctest)]
pub mod compile_fail_tests {
pub fn _nonempty_but_nothing_provided() {}
pub fn _nonempty_but_not_enough_provided() {}
pub fn _nonempty_not_injective_warning() {}
pub fn _nonempty_not_surjective_warning() {}
pub fn _nonempty_to_enum_not_injective_warning() {}
pub fn _missing_comma() {}
pub fn not_missing_comma() {}
}