struct_iter/
lib.rs

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                // Explicitly define the type of the vector
56                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}