derive_builder_apply_to 0.1.0

A procedural macro for generating apply_to methods for builder patterns
Documentation
//! A procedural macro for generating `apply_to` methods for builder patterns.
//! 
//! This crate provides the `ApplyTo` derive macro that generates an `apply_to` method
//! for builder structs, allowing them to selectively update target structs with only
//! the fields that have been set.

extern crate proc_macro;
use proc_macro::TokenStream;

use syn::{parse_macro_input, Data, DeriveInput, Fields};
use quote::quote;

/// Derive macro for generating an `apply_to` method on builder structs.
/// 
/// The generated method takes a mutable reference to the target struct and
/// applies only the fields that have been set in the builder (i.e., are `Some`).
/// Returns `true` if any fields were changed, `false` otherwise.
/// 
/// # Example
/// 
/// ```rust
/// use derive_builder_apply_to::ApplyTo;
/// 
/// #[derive(ApplyTo, Default)]
/// struct Person {
///     name: String,
///     age: u32,
/// }
/// 
/// let mut person = Person::default();
/// let builder = PersonBuilder::default()
///     .name("Alice".to_string());
/// 
/// let changed = builder.apply_to(&mut person);
/// assert!(changed);
/// assert_eq!(person.name, "Alice");
/// ```
#[proc_macro_derive(ApplyTo)]
pub fn derive_apply_to(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let builder_name = syn::Ident::new(&format!("{}Builder", name), name.span());

    let apply_fields = match &input.data {
        Data::Struct(data_struct) => match &data_struct.fields {
            Fields::Named(fields) => fields
                .named
                .iter()
                .map(|field| {
                    let field_name = &field.ident;
                    quote! {
                        if let Some(value) = self.#field_name {
                            if target.#field_name != value {
                                target.#field_name = value;
                                changed = true;
                            }
                        }
                    }
                })
                .collect::<Vec<_>>(),
            _ => panic!("Only named fields supported"),
        },
        _ => panic!("Only structs supported"),
    };

    let expanded = quote! {
        impl #builder_name {
            pub fn apply_to(self, target: &mut #name) -> bool {
                let mut changed = false;
                #(#apply_fields)*
                changed
            }
        }
    };

    TokenStream::from(expanded)
}