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>) -> 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 { 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 } } }; 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 for MyReferenceType<'a, _gp0> where &'a _gp0: ::my_crate::MyTrait { 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 { item: T, a: A, #[ignore] b: B, } }; let stream = derive_trait(r#struct, example_trait(None)); token_stream_eq!( stream, quote! { #[automatically_derived] impl ::my_crate::MyTrait for MyStruct 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 { 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 ::my_crate::MyTrait for MyStruct 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 { item: Box, } }; let stream = derive_trait(r#struct, example_trait(None)); token_stream_eq!( stream, quote! { #[automatically_derived] impl ::my_crate::MyTrait for MyStruct where Box : ::my_crate::MyTrait { fn method_one(&self) { let MyStruct { item: ref _0 } = self; do_thing(_0); } } } ) }