#![forbid(unsafe_code)]
use core::fmt::{Debug, Display, Formatter};
pub enum OptionAb<A, B> {
A(A),
B(B),
}
impl<T> OptionAb<T, T> {
pub fn take(self) -> T {
match self {
OptionAb::A(value) | OptionAb::B(value) => value,
}
}
}
impl<A, B> OptionAb<A, B> {
pub fn as_ref(&self) -> OptionAb<&A, &B> {
match self {
OptionAb::A(value) => OptionAb::A(value),
OptionAb::B(value) => OptionAb::B(value),
}
}
pub fn a(&self) -> Option<&A> {
match self {
OptionAb::A(value) => Some(value),
OptionAb::B(_) => None,
}
}
pub fn b(&self) -> Option<&B> {
match self {
OptionAb::A(_) => None,
OptionAb::B(value) => Some(value),
}
}
}
impl<A: Debug, B: Debug> OptionAb<A, B> {
pub fn unwrap_a(self) -> A {
match self {
OptionAb::A(value) => value,
OptionAb::B(_) => panic!("expected OptionAB::A(_) but found {:?}", self),
}
}
pub fn unwrap_b(self) -> B {
match self {
OptionAb::A(_) => panic!("expected OptionAB::B(_) but found {:?}", self),
OptionAb::B(value) => value,
}
}
}
impl<A: Clone, B: Clone> Clone for OptionAb<A, B> {
fn clone(&self) -> Self {
match self {
OptionAb::A(value) => OptionAb::A(value.clone()),
OptionAb::B(value) => OptionAb::B(value.clone()),
}
}
}
impl<A: Debug, B: Debug> Debug for OptionAb<A, B> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
OptionAb::A(value) => write!(f, "OptionAB::A({:?})", value),
OptionAb::B(value) => write!(f, "OptionAB::B({:?})", value),
}
}
}
impl<A: Display, B: Display> Display for OptionAb<A, B> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
OptionAb::A(value) => write!(f, "{}", value),
OptionAb::B(value) => write!(f, "{}", value),
}
}
}
impl<A: PartialEq, B: PartialEq> PartialEq for OptionAb<A, B> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(OptionAb::A(value), OptionAb::A(other)) if value == other => true,
(OptionAb::B(value), OptionAb::B(other)) if value == other => true,
_ => false,
}
}
}
impl<A: PartialEq, B: PartialEq> Eq for OptionAb<A, B> {}
#[cfg(test)]
mod tests {
use super::*;
fn test_values() -> (OptionAb<bool, u8>, OptionAb<bool, u8>) {
(OptionAb::A(true), OptionAb::B(42))
}
#[test]
fn test_as_ref() {
let (a, b) = test_values();
let _: OptionAb<&bool, &u8> = a.as_ref();
let _: &u8 = b.as_ref().unwrap_b();
assert!(*(a.as_ref().unwrap_a()));
assert_eq!(42_u8, *(b.as_ref().unwrap_b()));
}
#[test]
fn test_accessors() {
let (a, b) = test_values();
assert!(*(a.a().unwrap()));
assert_eq!(None, a.b());
assert_eq!(None, b.a());
assert_eq!(42_u8, *(b.b().unwrap()));
}
#[test]
fn test_debug() {
let (a, b) = test_values();
assert_eq!("OptionAB::A(true)", format!("{:?}", a));
assert_eq!("OptionAB::B(42)", format!("{:?}", b));
}
#[test]
fn test_display() {
let (a, b) = test_values();
assert_eq!("true", format!("{}", a));
assert_eq!("42", format!("{}", b));
}
#[test]
fn test_eq() {
let (a, b) = test_values();
assert_eq!(OptionAb::A(true), a);
assert_ne!(OptionAb::A(false), a);
assert_eq!(OptionAb::B(42_u8), b);
assert_ne!(OptionAb::B(2_u8), b);
assert_ne!(a, b);
}
#[allow(clippy::redundant_clone)]
#[test]
fn test_unwrap() {
let (a, b) = test_values();
let a_clone = a.clone();
assert!(a_clone.unwrap_a());
let a_clone = a.clone();
assert_eq!(
"expected OptionAB::B(_) but found OptionAB::A(true)",
std::panic::catch_unwind(|| a_clone.unwrap_b())
.unwrap_err()
.downcast::<String>()
.unwrap()
.as_str()
);
let b_clone = b.clone();
assert_eq!(
"expected OptionAB::A(_) but found OptionAB::B(42)",
std::panic::catch_unwind(|| b_clone.unwrap_a())
.unwrap_err()
.downcast::<String>()
.unwrap()
.as_str()
);
let b_clone = b.clone();
assert_eq!(42_u8, b_clone.unwrap_b());
}
#[test]
fn test_take() {
let same_a: OptionAb<u8, u8> = OptionAb::A(42_u8);
let same_b: OptionAb<u8, u8> = OptionAb::B(42_u8);
assert_eq!(42_u8, same_a.take());
assert_eq!(42_u8, same_b.take());
}
}