safina-select 0.1.4

Safe async select function, for awaiting multiple futures
Documentation
#![forbid(unsafe_code)]

use core::fmt::{Debug, Display, Formatter};

pub enum OptionAbcd<A, B, C, D> {
    A(A),
    B(B),
    C(C),
    D(D),
}

impl<T> OptionAbcd<T, T, T, T> {
    pub fn take(self) -> T {
        match self {
            OptionAbcd::A(value)
            | OptionAbcd::B(value)
            | OptionAbcd::C(value)
            | OptionAbcd::D(value) => value,
        }
    }
}

impl<A, B, C, D> OptionAbcd<A, B, C, D> {
    pub fn as_ref(&self) -> OptionAbcd<&A, &B, &C, &D> {
        match self {
            OptionAbcd::A(value) => OptionAbcd::A(value),
            OptionAbcd::B(value) => OptionAbcd::B(value),
            OptionAbcd::C(value) => OptionAbcd::C(value),
            OptionAbcd::D(value) => OptionAbcd::D(value),
        }
    }
    pub fn a(&self) -> Option<&A> {
        match self {
            OptionAbcd::A(value) => Some(value),
            _ => None,
        }
    }
    pub fn b(&self) -> Option<&B> {
        match self {
            OptionAbcd::B(value) => Some(value),
            _ => None,
        }
    }
    pub fn c(&self) -> Option<&C> {
        match self {
            OptionAbcd::C(value) => Some(value),
            _ => None,
        }
    }
    pub fn d(&self) -> Option<&D> {
        match self {
            OptionAbcd::D(value) => Some(value),
            _ => None,
        }
    }
}
impl<A: Debug, B: Debug, C: Debug, D: Debug> OptionAbcd<A, B, C, D> {
    /// # Panics
    /// Panics if `self` is not an `OptionABCD::A`.
    pub fn unwrap_a(self) -> A {
        match self {
            OptionAbcd::A(value) => value,
            _ => panic!("expected OptionABCD::A(_) but found {:?}", self),
        }
    }
    /// # Panics
    /// Panics if `self` is not an `OptionABCD::B`.
    pub fn unwrap_b(self) -> B {
        match self {
            OptionAbcd::B(value) => value,
            _ => panic!("expected OptionABCD::B(_) but found {:?}", self),
        }
    }
    /// # Panics
    /// Panics if `self` is not an `OptionABCD::C`.
    pub fn unwrap_c(self) -> C {
        match self {
            OptionAbcd::C(value) => value,
            _ => panic!("expected OptionABCD::C(_) but found {:?}", self),
        }
    }
    /// # Panics
    /// Panics if `self` is not an `OptionABCD::D`.
    pub fn unwrap_d(self) -> D {
        match self {
            OptionAbcd::D(value) => value,
            _ => panic!("expected OptionABCD::D(_) but found {:?}", self),
        }
    }
}

impl<A: Clone, B: Clone, C: Clone, D: Clone> Clone for OptionAbcd<A, B, C, D> {
    fn clone(&self) -> Self {
        match self {
            OptionAbcd::A(value) => OptionAbcd::A(value.clone()),
            OptionAbcd::B(value) => OptionAbcd::B(value.clone()),
            OptionAbcd::C(value) => OptionAbcd::C(value.clone()),
            OptionAbcd::D(value) => OptionAbcd::D(value.clone()),
        }
    }
}

impl<A: Debug, B: Debug, C: Debug, D: Debug> Debug for OptionAbcd<A, B, C, D> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            OptionAbcd::A(value) => write!(f, "OptionABCD::A({:?})", value),
            OptionAbcd::B(value) => write!(f, "OptionABCD::B({:?})", value),
            OptionAbcd::C(value) => write!(f, "OptionABCD::C({:?})", value),
            OptionAbcd::D(value) => write!(f, "OptionABCD::D({:?})", value),
        }
    }
}

impl<A: Display, B: Display, C: Display, D: Display> Display for OptionAbcd<A, B, C, D> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            OptionAbcd::A(value) => write!(f, "{}", value),
            OptionAbcd::B(value) => write!(f, "{}", value),
            OptionAbcd::C(value) => write!(f, "{}", value),
            OptionAbcd::D(value) => write!(f, "{}", value),
        }
    }
}

impl<A: PartialEq, B: PartialEq, C: PartialEq, D: PartialEq> PartialEq for OptionAbcd<A, B, C, D> {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (OptionAbcd::A(value), OptionAbcd::A(other)) if value == other => true,
            (OptionAbcd::B(value), OptionAbcd::B(other)) if value == other => true,
            (OptionAbcd::C(value), OptionAbcd::C(other)) if value == other => true,
            (OptionAbcd::D(value), OptionAbcd::D(other)) if value == other => true,
            _ => false,
        }
    }
}
impl<A: PartialEq, B: PartialEq, C: PartialEq, D: PartialEq> Eq for OptionAbcd<A, B, C, D> {}

#[cfg(test)]
mod tests {
    use super::*;

    #[allow(clippy::type_complexity)]
    fn test_values() -> (
        OptionAbcd<bool, u8, &'static str, usize>,
        OptionAbcd<bool, u8, &'static str, usize>,
        OptionAbcd<bool, u8, &'static str, usize>,
        OptionAbcd<bool, u8, &'static str, usize>,
    ) {
        (
            OptionAbcd::A(true),
            OptionAbcd::B(42),
            OptionAbcd::C("s1"),
            OptionAbcd::D(7_usize),
        )
    }

    #[test]
    fn test_as_ref() {
        let (a, b, c, d) = test_values();
        let _: OptionAbcd<&bool, &u8, &&'static str, &usize> = a.as_ref();
        let _: &u8 = b.as_ref().unwrap_b();
        let _: &&'static str = c.as_ref().unwrap_c();
        let _: &usize = d.as_ref().unwrap_d();
        assert!(*(a.as_ref().unwrap_a()));
        assert_eq!(42_u8, *(b.as_ref().unwrap_b()));
        assert_eq!("s1", *(c.as_ref().unwrap_c()));
        assert_eq!(7_usize, *(d.as_ref().unwrap_d()));
    }

    #[test]
    fn test_accessors() {
        let (a, b, c, d) = test_values();
        assert!(*(a.a().unwrap()));
        assert_eq!(None, a.b());
        assert_eq!(None, a.c());
        assert_eq!(None, a.d());

        assert_eq!(None, b.a());
        assert_eq!(42_u8, *(b.b().unwrap()));
        assert_eq!(None, b.c());
        assert_eq!(None, b.d());

        assert_eq!(None, c.a());
        assert_eq!(None, c.b());
        assert_eq!("s1", *(c.c().unwrap()));
        assert_eq!(None, c.d());

        assert_eq!(None, d.a());
        assert_eq!(None, d.b());
        assert_eq!(None, d.c());
        assert_eq!(7_usize, *(d.d().unwrap()));
    }

    #[test]
    fn test_debug() {
        let (a, b, c, d) = test_values();
        assert_eq!("OptionABCD::A(true)", format!("{:?}", a));
        assert_eq!("OptionABCD::B(42)", format!("{:?}", b));
        assert_eq!("OptionABCD::C(\"s1\")", format!("{:?}", c));
        assert_eq!("OptionABCD::D(7)", format!("{:?}", d));
    }

    #[test]
    fn test_display() {
        let (a, b, c, d) = test_values();
        assert_eq!("true", format!("{}", a));
        assert_eq!("42", format!("{}", b));
        assert_eq!("s1", format!("{}", c));
        assert_eq!("7", format!("{}", d));
    }

    #[test]
    fn test_eq() {
        let (a, b, c, d) = test_values();
        assert_eq!(OptionAbcd::A(true), a);
        assert_ne!(OptionAbcd::A(false), a);
        assert_eq!(OptionAbcd::B(42_u8), b);
        assert_ne!(OptionAbcd::B(2_u8), b);
        assert_eq!(OptionAbcd::C("s1"), c);
        assert_ne!(OptionAbcd::C("other"), c);
        assert_eq!(OptionAbcd::D(7_usize), d);
        assert_ne!(OptionAbcd::D(2_usize), d);
        assert_ne!(a, b);
        assert_ne!(a, c);
        assert_ne!(a, d);
        assert_ne!(b, c);
        assert_ne!(b, d);
        assert_ne!(c, d);
    }

    #[test]
    fn test_unwrap() {
        let (a, b, c, d) = test_values();
        let a_clone = a.clone();
        assert!(a_clone.unwrap_a());
        let a_clone = a.clone();
        assert_eq!(
            "expected OptionABCD::B(_) but found OptionABCD::A(true)",
            std::panic::catch_unwind(|| a_clone.unwrap_b())
                .unwrap_err()
                .downcast::<String>()
                .unwrap()
                .as_str()
        );
        let a_clone = a.clone();
        assert_eq!(
            "expected OptionABCD::C(_) but found OptionABCD::A(true)",
            std::panic::catch_unwind(|| a_clone.unwrap_c())
                .unwrap_err()
                .downcast::<String>()
                .unwrap()
                .as_str()
        );
        let a_clone = a.clone();
        assert_eq!(
            "expected OptionABCD::D(_) but found OptionABCD::A(true)",
            std::panic::catch_unwind(|| a_clone.unwrap_d())
                .unwrap_err()
                .downcast::<String>()
                .unwrap()
                .as_str()
        );

        let b_clone = b.clone();
        assert_eq!(
            "expected OptionABCD::A(_) but found OptionABCD::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());
        let b_clone = b.clone();
        std::panic::catch_unwind(|| b_clone.unwrap_c()).unwrap_err();
        let b_clone = b.clone();
        std::panic::catch_unwind(|| b_clone.unwrap_d()).unwrap_err();

        let c_clone = c.clone();
        std::panic::catch_unwind(|| c_clone.unwrap_a()).unwrap_err();
        let c_clone = c.clone();
        std::panic::catch_unwind(|| c_clone.unwrap_b()).unwrap_err();
        let c_clone = c.clone();
        assert_eq!("s1", c_clone.unwrap_c());
        let c_clone = c.clone();
        std::panic::catch_unwind(|| c_clone.unwrap_d()).unwrap_err();

        let d_clone = d.clone();
        std::panic::catch_unwind(|| d_clone.unwrap_a()).unwrap_err();
        let d_clone = d.clone();
        std::panic::catch_unwind(|| d_clone.unwrap_b()).unwrap_err();
        let d_clone = d.clone();
        std::panic::catch_unwind(|| d_clone.unwrap_c()).unwrap_err();
        let d_clone = d.clone();
        assert_eq!(7_usize, d_clone.unwrap_d());
    }

    #[test]
    fn test_take() {
        let same_a: OptionAbcd<u8, u8, u8, u8> = OptionAbcd::A(42_u8);
        let same_b: OptionAbcd<u8, u8, u8, u8> = OptionAbcd::B(42_u8);
        let same_c: OptionAbcd<u8, u8, u8, u8> = OptionAbcd::C(42_u8);
        let same_d: OptionAbcd<u8, u8, u8, u8> = OptionAbcd::C(42_u8);
        assert_eq!(42_u8, same_a.take());
        assert_eq!(42_u8, same_b.take());
        assert_eq!(42_u8, same_c.take());
        assert_eq!(42_u8, same_d.take());
    }
}