mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-25 07:09:59 -07:00
89 lines
2.7 KiB
Rust
89 lines
2.7 KiB
Rust
extern crate proc_macro;
|
|
|
|
use proc_macro::TokenStream;
|
|
use quote::quote;
|
|
use syn::{parse_macro_input, Data, DeriveInput, Fields};
|
|
|
|
/// The `Iterable` proc macro.
|
|
///
|
|
/// Deriving this macro for your struct will make it "iterable". An iterable struct allows you to iterate over its fields, returning a tuple containing the field name as a static string and a reference to the field's value as `dyn Any`.
|
|
///
|
|
/// # Limitations
|
|
///
|
|
/// - Only structs are supported, not enums or unions.
|
|
/// - Only structs with named fields are supported.
|
|
///
|
|
/// # Usage
|
|
///
|
|
/// Add the derive attribute (`#[derive(Iterable)]`) above your struct definition.
|
|
///
|
|
/// ```
|
|
/// use struct_iterable::Iterable;
|
|
///
|
|
/// #[derive(Iterable)]
|
|
/// struct MyStruct {
|
|
/// field1: i32,
|
|
/// field2: String,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// You can now call the `iter` method on instances of your struct to get an iterator over its fields:
|
|
///
|
|
/// ```
|
|
/// let my_instance = MyStruct {
|
|
/// field1: 42,
|
|
/// field2: "Hello, world!".to_string(),
|
|
/// };
|
|
///
|
|
/// for (field_name, field_value) in my_instance.iter() {
|
|
/// println!("{}: {:?}", field_name, field_value);
|
|
/// }
|
|
/// ```
|
|
#[proc_macro_derive(Iterable)]
|
|
pub fn derive_iterable(input: TokenStream) -> TokenStream {
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
let struct_name = input.ident;
|
|
let fields = match input.data {
|
|
Data::Struct(data_struct) => match data_struct.fields {
|
|
Fields::Named(fields_named) => fields_named.named,
|
|
_ => panic!("Only structs with named fields are supported"),
|
|
},
|
|
_ => panic!("Only structs are supported"),
|
|
};
|
|
|
|
let fields_iter = fields.iter().map(|field| {
|
|
let field_ident = &field.ident;
|
|
let field_name = field_ident.as_ref().unwrap().to_string();
|
|
quote! {
|
|
(#field_name, &(self.#field_ident) as &dyn std::any::Any)
|
|
}
|
|
});
|
|
|
|
let fields_iter_mut = fields.iter().map(|field| {
|
|
let field_ident = &field.ident;
|
|
let field_name = field_ident.as_ref().unwrap().to_string();
|
|
quote! {
|
|
(#field_name, &mut (self.#field_ident) as &mut dyn std::any::Any)
|
|
}
|
|
});
|
|
|
|
let expanded = quote! {
|
|
impl Iterable for #struct_name {
|
|
fn iter<'a>(&'a self) -> std::vec::IntoIter<(&'static str, &'a dyn std::any::Any)> {
|
|
vec![
|
|
#(#fields_iter),*
|
|
].into_iter()
|
|
}
|
|
|
|
fn iter_mut<'a>(&'a mut self) -> std::vec::IntoIter<(&'static str, &'a mut dyn std::any::Any)> {
|
|
vec![
|
|
#(#fields_iter_mut),*
|
|
].into_iter()
|
|
}
|
|
}
|
|
};
|
|
|
|
TokenStream::from(expanded)
|
|
}
|