1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 parse::{Parse, Parser},
5 parse_macro_input, Data, DeriveInput, Fields, LitStr, TypePath,
6};
7
8#[proc_macro_derive(StructIter, attributes(iter))]
9pub fn iterable_struct(input: TokenStream) -> TokenStream {
10 let input = parse_macro_input!(input as DeriveInput);
11 let name = input.ident;
12
13 let mut trait_path = syn::parse_str::<TypePath>("std::fmt::Debug").unwrap();
14 let res = input.attrs.iter().try_for_each(|attr| {
15 if attr.path().is_ident("iter") {
16 attr.parse_nested_meta(|meta| {
17 if meta.path.is_ident("trait") {
18 let value = meta.value()?;
19 let s: LitStr = value.parse()?;
20
21 trait_path = s.parse::<TypePath>()?;
22 Ok(())
23 } else {
24 Err(meta.error("Unsupported attribute"))
25 }
26 })
27 } else {
28 Ok(())
29 }
30 });
31
32 res.unwrap();
33
34 let fields = match &input.data {
35 Data::Struct(data_struct) => match data_struct.clone().fields {
36 Fields::Named(fields) => &fields.clone().named,
37 _ => panic!("Only named fields are supported"),
38 },
39 _ => panic!("Only structs are supported"),
40 };
41
42 let field_names: Vec<_> = fields
43 .iter()
44 .map(|f| f.ident.as_ref().expect("Field must have a name"))
45 .collect();
46
47 let expanded = quote! {
48
49 impl<'a> IntoIterator for &'a #name {
50 type Item = &'a dyn #trait_path;
51 type IntoIter = std::vec::IntoIter<&'a dyn #trait_path>;
52
53
54 fn into_iter(self) -> Self::IntoIter {
55 vec![
57 #(
58 &self.#field_names as &dyn #trait_path
59 ),*
60 ].into_iter()
61 }
62 }
63
64
65
66
67 impl IntoIterator for #name
68 {
69 type Item = std::boxed::Box<dyn #trait_path>;
70 type IntoIter = std::vec::IntoIter<std::boxed::Box<dyn #trait_path>>;
71
72 fn into_iter(self) -> Self::IntoIter {
73 let vec = vec![
74 #(
75 std::boxed::Box::new(self.#field_names) as std::boxed::Box<dyn #trait_path>
76 ),* ];
77 vec.into_iter()
78 }
79 }
80 };
81
82 TokenStream::from(expanded)
83}