aptos_iterable_derive/
lib.rs

1// Copyright (c) Aptos Foundation
2// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE
3
4//! A derive macro for implementing `IntoIterator` on newtype wrapper structs.
5//!
6//! This crate provides the [`Iterable`] derive macro, which automatically implements
7//! iteration traits for single-field tuple structs that wrap iterable collections.
8//!
9//! # Example
10//!
11//! ```rust
12//! use aptos_iterable_derive::Iterable;
13//! use std::collections::HashMap;
14//!
15//! #[derive(Iterable)]
16//! struct Config(HashMap<String, String>);
17//!
18//! let mut config = Config(HashMap::from([
19//!     ("key".to_string(), "value".to_string()),
20//! ]));
21//!
22//! // Use for-in loops directly
23//! for (k, v) in &config {
24//!     println!("{k}: {v}");
25//! }
26//!
27//! // Or use iterator methods
28//! for (k, v) in config.iter() {
29//!     println!("{k}: {v}");
30//! }
31//! ```
32//!
33//! See [`Iterable`] for detailed documentation on the generated implementations.
34#![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/// Derives `IntoIterator` implementations and iterator methods for single-field tuple structs.
42///
43/// Generates three `IntoIterator` implementations:
44/// - Owned: `for item in config`
45/// - Borrowed: `for item in &config`
46/// - Mutable: `for item in &mut config`
47///
48/// Also generates convenience methods:
49/// - `iter()` - returns an iterator over immutable references
50/// - `iter_mut()` - returns an iterator over mutable references
51///
52/// # Requirements
53///
54/// - Must be a tuple struct with exactly one field
55/// - The field must implement `IntoIterator`
56/// - For `iter()` and `iter_mut()`, the field should have these methods (like HashMap, Vec, etc.)
57#[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
67/// Expands the Iterable derive macro implementation.
68fn 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        // IntoIterator implementations
76        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        // Convenience methods for iteration
107        impl #impl_generics #name #ty_generics #where_clause {
108            /// Returns an iterator over the elements.
109            #[inline]
110            pub fn iter(&self) -> <&'_ #field_ty as IntoIterator>::IntoIter {
111                self.0.iter()
112            }
113
114            /// Returns an iterator that allows modifying each element.
115            #[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
125/// Validates and extracts the single field from a tuple struct.
126fn 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}