algtype 0.1.0

Conversion between data and its representation, like Haskell Generic
Documentation
use algtype::{Generic, One, Product, Repr, Sum, Zero};
use proptest::{arbitrary::Arbitrary, proptest};
use proptest_derive::Arbitrary;
use std::fmt::Debug;

#[derive(Clone, Generic, Debug, PartialEq, Arbitrary)]
struct Unit;

#[derive(Clone, Generic, Debug, PartialEq, Arbitrary)]
struct Tuple<T>(T, Unit, Enum<T>);

#[derive(Clone, Generic, Debug, PartialEq, Arbitrary)]
struct Struct<T: Arbitrary, I: Iterator> {
    a: T,
    b: Unit,
    c: Enum<T>,
    d: I::Item,
}

#[derive(Clone, Generic, Debug, PartialEq, Arbitrary)]
enum Enum<T> {
    Unit,
    TupleUnit(),
    Tuple(T, Unit),
    StructUnit {},
    Struct { a: T, b: i32 },
}

#[derive(Clone, Generic, Debug, PartialEq)]
enum Empty {}

#[derive(Clone, Generic, Debug, PartialEq)]
enum Ref<'a> {
    No(Empty),
    Ref(&'a str),
}

fn check<T: Generic<Repr = R> + PartialEq + Debug + Clone + Arbitrary, R: Repr>()
where
    for<'a> R::Ref<'a>: PartialEq + Debug,
    for<'a> R::Mut<'a>: PartialEq + Debug,
{
    proptest!(|(x: T)| assert_refl(x));
}

fn assert_refl<T: Generic<Repr = R> + PartialEq + Debug + Clone, R: Repr>(mut x: T)
where
    for<'a> R::Ref<'a>: PartialEq + Debug,
    for<'a> R::Mut<'a>: PartialEq + Debug,
{
    let mut repr = x.clone().into_repr();
    assert_eq!(x.as_repr(), repr.as_ref());
    assert_eq!(x.as_mut_repr(), repr.as_mut_ref());
    assert_eq!(x, T::from_repr(repr));
}

#[test]
fn test() {
    check::<Unit, Sum<One, Zero>>();
    check::<Tuple<_>, Sum<Product<bool, Product<Unit, Product<Enum<bool>, One>>>, Zero>>();
    check::<
        Struct<_, std::ops::Range<_>>,
        Sum<Product<i32, Product<Unit, Product<Enum<i32>, Product<i32, One>>>>, Zero>,
    >();
    check::<
        Enum<()>,
        Sum<
            One,
            Sum<
                One,
                Sum<
                    Product<(), Product<Unit, One>>,
                    Sum<One, Sum<Product<(), Product<i32, One>>, Zero>>,
                >,
            >,
        >,
    >();

    assert_refl::<Ref, Sum<Product<Empty, One>, Sum<Product<&str, One>, Zero>>>(Ref::Ref(""));
}