syn-helpers 0.5.0

Framework for building derive macros
Documentation
use proc_macro2::Span;
use quote::quote;
use syn::{parse_quote, DeriveInput, GenericParam, Ident, Stmt};
use syn_helpers::{
    derive_trait, Constructable, FieldMut, HasAttributes, NamedOrUnnamedFieldMut, Trait, TraitItem,
    TypeOfSelf,
};

macro_rules! token_stream_eq {
    ($a:expr, $b:expr) => {
        assert_eq!($a.to_string(), $b.to_string())
    };
}

fn example_trait(generic_parameters: Option<Vec<GenericParam>>) -> Trait {
    Trait {
        name: parse_quote!(::my_crate::MyTrait),
        generic_parameters,
        items: vec![TraitItem::new_method(
            Ident::new("method_one", Span::call_site()),
            None,
            TypeOfSelf::Reference,
            Vec::default(),
            None,
            |mut item| {
                item.map_constructable(|mut constructable| {
                    Ok(constructable
                        .get_fields_mut()
                        .fields_iterator_mut()
                        .flat_map(|mut field: NamedOrUnnamedFieldMut| -> Option<Stmt> {
                            if field
                                .get_attributes()
                                .iter()
                                .any(|attr| attr.path().is_ident("ignore"))
                            {
                                None
                            } else {
                                let reference = field.get_reference();
                                Some(parse_quote!(do_thing(#reference);))
                            }
                        })
                        .collect())
                })
            },
        )],
    }
}

#[test]
fn derives_fields_on_struct() {
    let r#struct: DeriveInput = parse_quote! {
        struct X {
            a: String,
            b: i32
        }
    };

    let stream = derive_trait(r#struct, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl ::my_crate::MyTrait for X {
                fn method_one(&self) {
                    let X { a: ref _0, b: ref _1 } = self;
                    do_thing(_0);
                    do_thing(_1);
                }
            }
        }
    )
}

#[test]
fn derives_fields_on_enum() {
    let r#enum: DeriveInput = parse_quote! {
        enum X {
            A(i32, bool),
            B {
                items: std::collections::HashSet<u8>
            }
        }
    };

    let stream = derive_trait(r#enum, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl ::my_crate::MyTrait for X {
                fn method_one(&self) {
                    match self {
                        X::A(ref _0, ref _1) => {
                            do_thing(_0);
                            do_thing(_1);
                        }
                        X::B { items: ref _0 } => {
                            do_thing(_0);
                        }
                    }
                }
            }
        }
    )
}

#[test]
fn derives_fields_on_input_with_generics() {
    let r#enum: DeriveInput = parse_quote! {
        struct MyReferenceType<'a, T>(&'a T);
    };
    let stream = derive_trait(r#enum, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl<'a, T> ::my_crate::MyTrait for MyReferenceType<'a, T>
            where &'a T: ::my_crate::MyTrait {
                fn method_one(&self) {
                    let MyReferenceType(ref _0) = self;
                    do_thing(*_0);
                }
            }
        }
    )
}

#[test]
fn derives_fields_on_trait_with_generic_collision() {
    let r#enum: DeriveInput = parse_quote! {
        struct MyReferenceType<'a, T>(&'a T);
    };

    let trait_generic_parameters = vec![GenericParam::Type(
        Ident::new("T", Span::call_site()).into(),
    )];

    let stream = derive_trait(r#enum, example_trait(Some(trait_generic_parameters)));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl<'a, T, _gp0> ::my_crate::MyTrait<T> for MyReferenceType<'a, _gp0>
            where &'a _gp0: ::my_crate::MyTrait<T> {
                fn method_one(&self) {
                    let MyReferenceType(ref _0) = self;
                    do_thing(*_0);
                }
            }
        }
    )
}

#[test]
fn derive_add_where_clause() {
    let r#struct: DeriveInput = parse_quote! {
        struct MyStruct<T> {
            item: T,
            a: A,
            #[ignore]
            b: B,
        }
    };

    let stream = derive_trait(r#struct, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl<T> ::my_crate::MyTrait for MyStruct<T>
            where T: ::my_crate::MyTrait {
                fn method_one(&self) {
                    let MyStruct { item: ref _0, a: ref _1, b : _ } = self;
                    do_thing(_0);
                    do_thing(_1);
                }
            }
        }
    )
}

#[test]
fn derive_add_where_clause_to_associated_item() {
    let r#struct: DeriveInput = parse_quote! {
        struct MyStruct<T: OtherTrait> {
            item: T::X,
            a: A,
            #[ignore]
            b: B,
        }
    };

    let stream = derive_trait(r#struct, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl<T: OtherTrait> ::my_crate::MyTrait for MyStruct<T>
            where T::X : ::my_crate::MyTrait {
                fn method_one(&self) {
                    let MyStruct { item: ref _0, a: ref _1, b : _ } = self;
                    do_thing(_0);
                    do_thing(_1);
                }
            }
        }
    )
}

#[test]
fn derive_add_where_clause_to_parameterized_item() {
    let r#struct: DeriveInput = parse_quote! {
        struct MyStruct<T: OtherTrait> {
            item: Box<T>,
        }
    };

    let stream = derive_trait(r#struct, example_trait(None));

    token_stream_eq!(
        stream,
        quote! {
            #[automatically_derived]
            impl<T: OtherTrait> ::my_crate::MyTrait for MyStruct<T>
            where Box<T> : ::my_crate::MyTrait {
                fn method_one(&self) {
                    let MyStruct { item: ref _0 } = self;
                    do_thing(_0);
                }
            }
        }
    )
}