autogen 1.0.1

Autogen is a set of macros that allows you to automatically apply generics to impl blocks.
Documentation
#![allow(non_local_definitions)]
use std::fmt::{Debug, Display};

#[autogen::register]
struct Struct<X: Debug, Y: Debug> {
    x: X,
    y: Y,
}

#[autogen::apply]
impl Struct {}

trait Trait {
    fn type_of(&self) -> String;
}

#[test]
fn regular() {
    #[autogen::apply]
    impl Trait for Struct {
        fn type_of(&self) -> String {
            format!("regular {:?}, {:?}", self.x, self.y)
        }
    }

    let s = Struct { x: 3, y: 5.2 };
    assert_eq!(s.type_of(), "regular 3, 5.2");
}

#[test]
fn reference() {
    #[autogen::apply]
    impl Trait for &Struct {
        fn type_of(&self) -> String {
            format!("reference {:?}, {:?}", self.x, self.y)
        }
    }

    let s = Struct { x: 3, y: 5.2 };
    assert_eq!((&&s).type_of(), "reference 3, 5.2");
}

#[test]
fn array() {
    #[autogen::apply]
    impl Trait for [Struct; 1] {
        fn type_of(&self) -> String {
            format!("array {:?}, {:?}", self[0].x, self[0].y)
        }
    }

    let array = [Struct { x: 3, y: 5.2 }];
    assert_eq!(array.type_of(), "array 3, 5.2");
}

#[test]
fn slice() {
    #[autogen::apply]
    impl Trait for [Struct] {
        fn type_of(&self) -> String {
            format!("slice {:?}, {:?}", self[0].x, self[0].y)
        }
    }

    let array = [Struct { x: 3, y: 5.2 }];
    assert_eq!(array[..].type_of(), "slice 3, 5.2");
}

#[test]
fn pointer() {
    #[autogen::apply]
    impl Trait for *const Struct {
        fn type_of(&self) -> String {
            "pointer".to_string()
        }
    }

    let s = Struct { x: 3, y: 5.2 };
    let pointer = &s as *const Struct<_, _>;
    assert_eq!(pointer.type_of(), "pointer");
}

#[test]
fn tuple() {
    #[autogen::apply]
    impl Trait for (&'static str, Struct, Struct, Struct<f64, String>) {
        fn type_of(&self) -> String {
            format!(
                "tuple {}, {:?}, {:?}, {:?}, {:?}, {}, {}",
                self.0, self.1.x, self.1.y, self.2.x, self.2.y, self.3.x, self.3.y
            )
        }
    }

    let s1 = Struct { x: 3, y: 5.2 };
    let s2 = Struct { x: 4, y: 7.6 };
    let s3 = Struct {
        x: 4.2,
        y: "b".to_string(),
    };
    let tuple = ("a", s1, s2, s3);
    assert_eq!(tuple.type_of(), "tuple a, 3, 5.2, 4, 7.6, 4.2, b");
}

#[test]
fn generic_arg() {
    #[autogen::apply]
    impl Trait for Result<Struct, String> {
        fn type_of(&self) -> String {
            match self {
                Ok(s) => format!("ok {:?}, {:?}", s.x, s.y),
                Err(e) => format!("error {e}"),
            }
        }
    }

    let s = Struct { x: 3, y: 5.2 };
    let ok: Result<_, String> = Ok(s);
    let err: Result<Struct<i32, f64>, String> = Err("oops".to_string());
    assert_eq!(ok.type_of(), "ok 3, 5.2");
    assert_eq!(err.type_of(), "error oops");
}

#[test]
fn combo() {
    #[autogen::apply]
    impl Trait for [([Option<&Struct>; 1], Struct, String)] {
        fn type_of(&self) -> String {
            format!(
                "crazy {:?}, {:?}, {:?}, {:?} {}",
                self[0].0[0].unwrap().x,
                self[0].0[0].unwrap().y,
                self[0].1.x,
                self[0].1.y,
                self[0].2
            )
        }
    }
    let s1 = Struct { x: 3, y: 5.2 };
    let s2 = Struct { x: 4, y: 7.6 };
    let crazy = [([Some(&s1)], s2, "****".to_string())];
    assert_eq!(crazy[..].type_of(), "crazy 3, 5.2, 4, 7.6 ****");
}

#[test]
fn custom_id() {
    #[autogen::register(StructX)]
    struct Struct<X: Display> {
        x: X,
    }

    #[autogen::apply(StructX)]
    impl Trait for Struct {
        fn type_of(&self) -> String {
            format!("custom {}", self.x)
        }
    }
    let s = Struct { x: "x" };
    assert_eq!(s.type_of(), "custom x");
}

#[test]
fn custom_with_multiple_types() {
    #[autogen::register(StructY)]
    struct Struct<Y: Debug> {
        y: Y,
    }

    #[autogen::apply(id = StructY)]
    impl Trait for (Struct, String) {
        fn type_of(&self) -> String {
            format!("custom {:?} {}", self.0.y, self.1)
        }
    }
    let s = Struct { y: 0 };
    let tuple = (s, "y".to_string());
    assert_eq!(tuple.type_of(), "custom 0 y");
}

#[test]
fn custom_with_multiple_registered_types() {
    #[autogen::register]
    struct Struct2<Z: Display> {
        z: Z,
    }

    #[autogen::apply(Struct)]
    impl Trait for (Struct, Struct2<String>) {
        fn type_of(&self) -> String {
            format!("custom {:?} {:?} {}", self.0.x, self.0.y, self.1.z)
        }
    }
    let s1 = Struct { x: 1, y: 2 };
    let s2 = Struct2 { z: "z".to_string() };
    let tuple = (s1, s2);
    assert_eq!(tuple.type_of(), "custom 1 2 z");
}

#[test]
fn fn_arg() {
    #[autogen::apply]
    impl Struct {
        fn combine_as_string(&self, other: &Struct) -> String {
            format!("{:?} {:?} - {:?} {:?}", self.x, self.y, other.x, other.y)
        }
        fn combine<'a>(&'a self, other: &'a Struct) -> Vec<&'a Struct> {
            let v: Vec<&'a Struct> = [self, other].into_iter().collect();
            v
        }
    }

    let a = Struct { x: 1, y: 2 };
    let b = Struct { x: 3, y: 4 };
    let c = a.combine(&b);
    assert_eq!(a.combine_as_string(&b), "1 2 - 3 4");
    assert_eq!(c[0].x, a.x);
    assert_eq!(c[0].y, a.y);
    assert_eq!(c[1].x, b.x);
    assert_eq!(c[1].y, b.y);
}