aptos_iterable_derive/
lib.rs1#![doc(html_logo_url = "https://geomi.dev/images/logos/icon/icon_4x.png")]
35#![doc(html_favicon_url = "https://geomi.dev/splash-assets-public/Favicon.svg")]
36
37use proc_macro::TokenStream;
38use quote::quote;
39use syn::{Data, DeriveInput, Error, Field, Fields, Result, parse_macro_input};
40
41#[proc_macro_derive(Iterable)]
58pub fn derive_iterable(input: TokenStream) -> TokenStream {
59 let input = parse_macro_input!(input as DeriveInput);
60
61 match expand_iterable(&input) {
62 Ok(tokens) => tokens,
63 Err(err) => err.to_compile_error().into(),
64 }
65}
66
67fn expand_iterable(input: &DeriveInput) -> Result<TokenStream> {
69 let field = extract_tuple_struct_value(input)?;
70 let name = &input.ident;
71 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
72 let field_ty = &field.ty;
73
74 let expanded = quote! {
75 impl #impl_generics IntoIterator for #name #ty_generics #where_clause {
77 type Item = <#field_ty as IntoIterator>::Item;
78 type IntoIter = <#field_ty as IntoIterator>::IntoIter;
79
80 #[inline]
81 fn into_iter(self) -> Self::IntoIter {
82 self.0.into_iter()
83 }
84 }
85
86 impl<'a, #impl_generics> IntoIterator for &'a #name #ty_generics #where_clause {
87 type Item = <&'a #field_ty as IntoIterator>::Item;
88 type IntoIter = <&'a #field_ty as IntoIterator>::IntoIter;
89
90 #[inline]
91 fn into_iter(self) -> Self::IntoIter {
92 (&self.0).into_iter()
93 }
94 }
95
96 impl<'a, #impl_generics> IntoIterator for &'a mut #name #ty_generics #where_clause {
97 type Item = <&'a mut #field_ty as IntoIterator>::Item;
98 type IntoIter = <&'a mut #field_ty as IntoIterator>::IntoIter;
99
100 #[inline]
101 fn into_iter(self) -> Self::IntoIter {
102 (&mut self.0).into_iter()
103 }
104 }
105
106 impl #impl_generics #name #ty_generics #where_clause {
108 #[inline]
110 pub fn iter(&self) -> <&'_ #field_ty as IntoIterator>::IntoIter {
111 self.0.iter()
112 }
113
114 #[inline]
116 pub fn iter_mut(&mut self) -> <&'_ mut #field_ty as IntoIterator>::IntoIter {
117 self.0.iter_mut()
118 }
119 }
120 };
121
122 Ok(TokenStream::from(expanded))
123}
124
125fn extract_tuple_struct_value(input: &DeriveInput) -> Result<&Field> {
127 let data_struct = match &input.data {
128 Data::Struct(s) => s,
129 _ => {
130 return Err(Error::new_spanned(
131 input,
132 "Iterable can only be derived for structs",
133 ));
134 },
135 };
136
137 match &data_struct.fields {
138 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Ok(&fields.unnamed[0]),
139 Fields::Unnamed(fields) => Err(Error::new_spanned(
140 fields,
141 format!(
142 "Iterable requires exactly one field, found {}",
143 fields.unnamed.len()
144 ),
145 )),
146 Fields::Named(fields) => Err(Error::new_spanned(
147 fields,
148 "Iterable can only be derived for tuple structs, not structs with named fields",
149 )),
150 Fields::Unit => Err(Error::new_spanned(
151 input,
152 "Iterable cannot be derived for unit structs",
153 )),
154 }
155}