#[macro_export]
macro_rules! case_mapping {
(
$ty:ident,
$value:ty as $read:ty,
$($variant:ident <=> $stated:expr),* $(,)?
) => {
$crate::paste::paste! {
#[doc = concat!("States what each `", stringify!($ty), "` maps to, and reads it back.")]
pub mod [<$ty:snake>] {
#[allow(unused_imports)]
use super::*;
pub const ALL: &[super::$ty] = &[$(super::$ty::$variant),*];
#[must_use]
pub fn to(case: super::$ty) -> $value {
match case {
$(super::$ty::$variant => $stated,)*
}
}
#[must_use]
pub fn from(value: $read) -> Option<super::$ty> {
$(
if value == $stated {
return Some(super::$ty::$variant);
}
)*
None
}
}
}
};
(
$ty:ident,
$value:ty,
$($variant:ident <=> $stated:expr),* $(,)?
) => {
$crate::case_mapping! {
$ty, $value as $value,
$($variant <=> $stated),*
}
};
}
#[macro_export]
macro_rules! case_mapping_partial {
(
$ty:ident,
$value:ty as $read:ty,
$($variant:ident <=> $stated:expr),* $(,)?
) => {
$crate::paste::paste! {
#[doc = concat!("States what some `", stringify!($ty), "` cases map to, and reads them back.")]
pub mod [<$ty:snake>] {
#[allow(unused_imports)]
use super::*;
pub const ALL: &[super::$ty] = &[$(super::$ty::$variant),*];
#[must_use]
#[allow(unreachable_patterns)]
pub fn to(case: super::$ty) -> Option<$value> {
match case {
$(super::$ty::$variant => Some($stated),)*
_ => None,
}
}
#[must_use]
pub fn from(value: $read) -> Option<super::$ty> {
$(
if value == $stated {
return Some(super::$ty::$variant);
}
)*
None
}
}
}
};
(
$ty:ident,
$value:ty,
$($variant:ident <=> $stated:expr),* $(,)?
) => {
$crate::case_mapping_partial! {
$ty, $value as $value,
$($variant <=> $stated),*
}
};
}
#[cfg(test)]
mod tests {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SoundLevel {
Loud,
Quiet,
Unstated,
}
case_mapping! {
SoundLevel, &'static str as &str,
Loud <=> "loud",
Quiet <=> "quiet",
Unstated <=> "unstated",
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Loudness {
Loud,
Quiet,
Unstated,
}
case_mapping_partial! {
Loudness, u8,
Loud <=> 2,
Quiet <=> 1,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
Plus,
Ident(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Operator {
Add,
}
case_mapping! {
Operator, Token as Token,
Add <=> Token::Plus,
}
#[test]
fn a_mapped_type_may_own_what_it_carries() {
assert_eq!(operator::to(Operator::Add), Token::Plus);
assert_eq!(operator::from(Token::Plus), Some(Operator::Add));
assert_eq!(operator::from(Token::Ident("x".to_owned())), None);
assert_eq!(operator::ALL, &[Operator::Add]);
}
#[test]
fn a_mapping_is_named_after_the_type_it_maps() {
assert_eq!(sound_level::to(SoundLevel::Loud), "loud");
assert_eq!(sound_level::from("quiet"), Some(SoundLevel::Quiet));
assert_eq!(sound_level::from("nothing anybody would call a sound"), None);
assert_eq!(sound_level::ALL, &[SoundLevel::Loud, SoundLevel::Quiet, SoundLevel::Unstated]);
}
#[test]
fn a_partial_mapping_states_nothing_for_a_case_it_does_not_name() {
assert_eq!(loudness::to(Loudness::Loud), Some(2));
assert_eq!(loudness::to(Loudness::Unstated), None);
assert_eq!(loudness::from(1), Some(Loudness::Quiet));
assert_eq!(loudness::ALL, &[Loudness::Loud, Loudness::Quiet]);
}
}