#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Debug, Formatter};
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Choice<L, R> {
L(L),
R(R),
}
pub use Choice::{L, R};
impl<A, B> Choice<A, B> {
pub fn new(choice: A) -> Self {
L(choice)
}
pub fn or<L>(self) -> Choice<L, Choice<A, B>> {
R(self)
}
}
impl<A> Choice<A, Never> {
pub fn get(&self) -> &A {
match self {
L(l) => l,
R(_) => unreachable!(),
}
}
}
impl<A, B> Display for Choice<A, B> where A: Display, B: Display {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
L(v) => v.fmt(f),
R(v) => v.fmt(f),
}
}
}
impl<A, B> Debug for Choice<A, B> where A: Debug, B: Debug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
L(v) => v.fmt(f),
R(v) => v.fmt(f),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Never { }
impl Display for Never {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
unreachable!()
}
}
#[macro_export]
macro_rules! choice {
[$t:ty] => ($crate::Choice<$t, $crate::Never>);
[$t1:ty, $($t2:ty),+] => ($crate::Choice<$t1, choice![$($t2),+]>);
(0 <- $x:expr) => ($crate::Choice::new($x));
(1 <- $x:expr) => (choice!(0 <- $x).or());
(2 <- $x:expr) => (choice!(1 <- $x).or());
(3 <- $x:expr) => (choice!(2 <- $x).or());
(4 <- $x:expr) => (choice!(3 <- $x).or());
(5 <- $x:expr) => (choice!(4 <- $x).or());
(6 <- $x:expr) => (choice!(5 <- $x).or());
(7 <- $x:expr) => (choice!(6 <- $x).or());
(8 <- $x:expr) => (choice!(7 <- $x).or());
(9 <- $x:expr) => (choice!(8 <- $x).or());
(10 <- $x:expr) => (choice!(9 <- $x).or());
(11 <- $x:expr) => (choice!(10 <- $x).or());
(0 -> !) => (compile_error!("Index 0 cannot be uninhabited."));
(1 -> !) => ($crate::R(_));
(2 -> !) => ($crate::R(choice!(1 -> !)));
(3 -> !) => ($crate::R(choice!(2 -> !)));
(4 -> !) => ($crate::R(choice!(3 -> !)));
(5 -> !) => ($crate::R(choice!(4 -> !)));
(6 -> !) => ($crate::R(choice!(5 -> !)));
(7 -> !) => ($crate::R(choice!(6 -> !)));
(8 -> !) => ($crate::R(choice!(7 -> !)));
(9 -> !) => ($crate::R(choice!(8 -> !)));
(10 -> !) => ($crate::R(choice!(9 -> !)));
(11 -> !) => ($crate::R(choice!(10 -> !)));
(12 -> !) => ($crate::R(choice!(11 -> !)));
(0 -> $v:ident) => ($crate::L($v));
(1 -> $v:ident) => ($crate::R(choice!(0 -> $v)));
(2 -> $v:ident) => ($crate::R(choice!(1 -> $v)));
(3 -> $v:ident) => ($crate::R(choice!(2 -> $v)));
(4 -> $v:ident) => ($crate::R(choice!(3 -> $v)));
(5 -> $v:ident) => ($crate::R(choice!(4 -> $v)));
(6 -> $v:ident) => ($crate::R(choice!(5 -> $v)));
(7 -> $v:ident) => ($crate::R(choice!(6 -> $v)));
(8 -> $v:ident) => ($crate::R(choice!(7 -> $v)));
(9 -> $v:ident) => ($crate::R(choice!(8 -> $v)));
(10 -> $v:ident) => ($crate::R(choice!(9 -> $v)));
(11 -> $v:ident) => ($crate::R(choice!(10 -> $v)));
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn can_compare() {
let c1: choice![&'static str, char] = choice!(0 <- "a");
let c2: choice![&'static str, char] = choice!(0 <- "b");
let c3: choice![&'static str, char] = choice!(1 <- 'a');
let c4: choice![&'static str, char] = choice!(1 <- 'b');
assert!(c1 < c2);
assert!(c2 < c3); assert!(c3 < c4);
assert_eq!(c1, choice!(0 <- "a"));
assert_eq!(c2, choice!(0 <- "b"));
assert_eq!(c3, choice!(1 <- 'a'));
assert_eq!(c4, choice!(1 <- 'b'));
let c1: choice![char, char] = choice!(0 <- 'a');
let c2: choice![char, char] = choice!(1 <- 'a');
assert_ne!(c1, c2);
}
#[test]
fn can_debug() {
let c1: choice![&'static str, char] = choice!(0 <- "a");
let c2: choice![&'static str, char] = choice!(1 <- 'b');
assert_eq!(format!("{:?}", c1), "\"a\"");
assert_eq!(format!("{:?}", c2), "'b'");
}
#[test]
fn can_display() {
let c1: choice![&'static str, char] = choice!(0 <- "a");
let c2: choice![&'static str, char] = choice!(1 <- 'b');
assert_eq!(format!("{}", c1), "a");
assert_eq!(format!("{}", c2), "b");
}
#[test]
fn can_destructure() {
let c1: choice![&'static str, char] = choice!(0 <- "a");
if let choice!(0 -> v) = c1 {
assert_eq!(v, "a");
} else {
panic!("Expected match.");
}
if let choice!(1 -> _v) = c1 {
panic!("Unexpected match.");
}
let c2: choice![&'static str, char] = choice!(1 <- 'b');
match c2 {
choice!(0 -> _v) => {
panic!("Unexpected match.");
}
choice!(1 -> v) => {
assert_eq!(v, 'b');
}
choice!(2 -> !) => {
unreachable!();
}
}
}
#[test]
fn smoke_test() {
let choices: Vec<choice![u8, char, &'static str, String]> = vec![
choice!(0 <- 1),
choice!(1 <- '2'),
choice!(2 <- "3"),
choice!(3 <- "4".to_string()),
];
assert_eq!(
format!("{:?}", choices),
r#"[1, '2', "3", "4"]"#);
assert_eq!(choices, vec![
Choice::new(1),
Choice::new('2').or(),
Choice::new("3").or().or(),
Choice::new("4".to_string()).or().or().or(),
]);
assert_ne!(choices, vec![
choice!(0 <- 1),
choice!(1 <- '2'),
choice!(2 <- "three"),
choice!(3 <- "4".to_string()),
]);
#[derive(Debug, Eq, PartialEq)] struct A;
#[derive(Debug, Eq, PartialEq)] struct B;
#[derive(Debug, Eq, PartialEq)] struct C;
#[derive(Debug, Eq, PartialEq)] struct D;
#[derive(Debug, Eq, PartialEq)] struct E;
#[derive(Debug, Eq, PartialEq)] struct F;
#[derive(Debug, Eq, PartialEq)] struct G;
#[derive(Debug, Eq, PartialEq)] struct H;
#[derive(Debug, Eq, PartialEq)] struct I;
#[derive(Debug, Eq, PartialEq)] struct J;
#[derive(Debug, Eq, PartialEq)] struct K;
#[derive(Debug, Eq, PartialEq)] struct L;
let v: Vec<choice![A, B, C, D, E, F, G, H, I, J, K, L]> = vec![
choice!(0 <- A),
choice!(1 <- B),
choice!(2 <- C),
choice!(3 <- D),
choice!(4 <- E),
choice!(5 <- F),
choice!(6 <- G),
choice!(7 <- H),
choice!(8 <- I),
choice!(9 <- J),
choice!(10 <- K),
choice!(11 <- L),
];
for (i, c) in v.into_iter().enumerate() {
match c {
choice!(0 -> v) => {
assert_eq!(i, 0);
assert_eq!(v, A);
}
choice!(1 -> v) => {
assert_eq!(i, 1);
assert_eq!(v, B);
}
choice!(2 -> v) => {
assert_eq!(i, 2);
assert_eq!(v, C);
}
choice!(3 -> v) => {
assert_eq!(i, 3);
assert_eq!(v, D);
}
choice!(4 -> v) => {
assert_eq!(i, 4);
assert_eq!(v, E);
}
choice!(5 -> v) => {
assert_eq!(i, 5);
assert_eq!(v, F);
}
choice!(6 -> v) => {
assert_eq!(i, 6);
assert_eq!(v, G);
}
choice!(7 -> v) => {
assert_eq!(i, 7);
assert_eq!(v, H);
}
choice!(8 -> v) => {
assert_eq!(i, 8);
assert_eq!(v, I);
}
choice!(9 -> v) => {
assert_eq!(i, 9);
assert_eq!(v, J);
}
choice!(10 -> v) => {
assert_eq!(i, 10);
assert_eq!(v, K);
}
choice!(11 -> v) => {
assert_eq!(i, 11);
assert_eq!(v, L);
}
choice!(12 -> !) => {
unreachable!();
}
}
}
}
}