#impl-block #generics #impl #type #traits

macro autogen

Autogen is a set of macros that allows you to automatically apply generics to impl blocks

6 releases (1 stable)

1.0.0 Nov 10, 2024
0.2.3 Aug 1, 2024
0.2.2 Jul 31, 2024
0.1.0 Jul 21, 2024

#287 in Procedural macros

Download history 456/week @ 2024-07-29 7/week @ 2024-08-05 4/week @ 2024-08-12 14/week @ 2024-09-23 2/week @ 2024-10-14 48/week @ 2024-11-04 64/week @ 2024-11-11

112 downloads per month

MIT license

42KB
788 lines

autogen

github crates.io docs.rs

Tired of repeating all the generics in every impl block or function?

Autogen is a set of macros that allows you to automatically apply generics to impl blocks and functions.

  • the #[register] macro registers the generics of a struct or enum, including lifetimes and the where clause.
  • the #[apply] macro applies the generics to an impl block or function.
#[autogen::register]
struct Struct<'a, T, R: ?Sized>
where
    T: PartialEq,
{
    x: T,
    y: &'a R,
}

// This will expand to impl<'a, T, R: ?Sized> Struct<'a, T, R> where T: PartialEq {}
#[autogen::apply]
impl Struct {}

// This will expand to impl<'a, T, T> Struct<'a, T, T> where T: PartialEq {}
#[autogen::apply(R = T)]
impl Struct {}

// This will expand to impl<'a, String, str> Struct<'a, String, str>
#[autogen::apply(T = String, R = str)]
impl Struct {}

Examples

#[autogen::register]
struct Struct<'a, T, R: ?Sized>
where
    T: PartialEq,
{
    x: T,
    y: &'a R,
}

#[autogen::apply]
impl Struct {
    fn x_equals(&self, other: &T) -> bool {
        &self.x == other
    }
    fn y(&self) -> &'a R {
        self.y
    }
}

let s = Struct { x: 1, y: "abc" };
assert!(s.x_equals(&1));
assert_eq!(s.y(), "abc");

#[autogen::apply]
impl TryFrom<Vec<Struct>> for Struct {
    type Error = String;

    fn try_from(vec: Vec<Struct>) -> Result<Struct, String> {
        let first: Option<Struct> = vec.into_iter().next();
        first.ok_or("empty".to_string())
    }
}

let vec = vec![s];
let s: Struct<'_, _, _> = vec.try_into().unwrap();
assert!(s.x_equals(&1));
assert_eq!(s.y(), "abc");

#[autogen::apply]
fn same_x(l: &Struct, r: &Struct) -> bool {
    l.x == r.x
}

let l = Struct { x: 2.1, y: &3 };
let r = Struct { x: 2.1, y: &7 };
assert!(same_x(&l, &r));

By default, the generics are registered with the struct/enum name, but you can provide a custom identifier. This can be useful if a type with the same name is already registered in another module.

#[autogen::register]
struct Name<T: PartialEq> {
    t: T,
}

#[autogen::apply]
impl Name {
    fn t(&self) -> &T {
        &self.t
    }
}

mod sub {
    use std::str::FromStr;

    #[autogen::register(CustomName)]
    pub struct Name<S: FromStr, X> {
        pub s: S,
        pub x: X,
    }

    #[autogen::apply(CustomName)]
    impl Name {
        pub fn new(string: &str, x: X) -> Result<Self, S::Err> {
            Ok(Name { s: string.parse()?, x })
        }
    }

    #[autogen::apply(id = CustomName, X = S)]
    impl Name {
        pub fn parse(string: &str) -> Result<Self, S::Err> {
            Ok(Name { s: string.parse()?, x: string.parse()? })
        }
    }
}

let s1 = Name { t: 64 };
assert_eq!(s1.t(), &64);

let s2 = sub::Name::<u32, i32>::new("123", -5).unwrap();
assert_eq!(s2.s, 123);
assert_eq!(s2.x, -5);

let s3 = sub::Name::<f64, f64>::parse("5.6").unwrap();
assert_eq!(s3.s, 5.6);
assert_eq!(s3.x, 5.6);

For more examples and details, please see the documentation

Dependencies

~270–720KB
~17K SLoC