settings_macros.rs

  1use proc_macro::TokenStream;
  2use quote::quote;
  3use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
  4
  5/// Derives the `MergeFrom` trait for a struct.
  6///
  7/// This macro automatically implements `MergeFrom` by calling `merge_from`
  8/// on all fields in the struct. For `Option<T>` fields, it merges by taking
  9/// the `other` value when `self` is `None`. For other types, it recursively
 10/// calls `merge_from` on the field.
 11///
 12/// # Example
 13///
 14/// ```ignore
 15/// #[derive(Clone, MergeFrom)]
 16/// struct MySettings {
 17///     field1: Option<String>,
 18///     field2: SomeOtherSettings,
 19/// }
 20/// ```
 21#[proc_macro_derive(MergeFrom)]
 22pub fn derive_merge_from(input: TokenStream) -> TokenStream {
 23    let input = parse_macro_input!(input as DeriveInput);
 24
 25    let name = &input.ident;
 26    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 27
 28    let merge_body = match &input.data {
 29        Data::Struct(data_struct) => match &data_struct.fields {
 30            Fields::Named(fields) => {
 31                let field_merges = fields.named.iter().map(|field| {
 32                    let field_name = &field.ident;
 33                    let field_type = &field.ty;
 34
 35                    if is_option_type(field_type) {
 36                        // For Option<T> fields, merge by taking the other value if self is None
 37                        quote! {
 38                            if let Some(other_value) = other.#field_name.as_ref() {
 39                                if self.#field_name.is_none() {
 40                                    self.#field_name = Some(other_value.clone());
 41                                } else if let Some(self_value) = self.#field_name.as_mut() {
 42                                    self_value.merge_from(Some(other_value));
 43                                }
 44                            }
 45                        }
 46                    } else {
 47                        // For non-Option fields, recursively call merge_from
 48                        quote! {
 49                            self.#field_name.merge_from(Some(&other.#field_name));
 50                        }
 51                    }
 52                });
 53
 54                quote! {
 55                    if let Some(other) = other {
 56                        #(#field_merges)*
 57                    }
 58                }
 59            }
 60            Fields::Unnamed(fields) => {
 61                let field_merges = fields.unnamed.iter().enumerate().map(|(i, field)| {
 62                    let field_index = syn::Index::from(i);
 63                    let field_type = &field.ty;
 64
 65                    if is_option_type(field_type) {
 66                        // For Option<T> fields, merge by taking the other value if self is None
 67                        quote! {
 68                            if let Some(other_value) = other.#field_index.as_ref() {
 69                                if self.#field_index.is_none() {
 70                                    self.#field_index = Some(other_value.clone());
 71                                } else if let Some(self_value) = self.#field_index.as_mut() {
 72                                    self_value.merge_from(Some(other_value));
 73                                }
 74                            }
 75                        }
 76                    } else {
 77                        // For non-Option fields, recursively call merge_from
 78                        quote! {
 79                            self.#field_index.merge_from(Some(&other.#field_index));
 80                        }
 81                    }
 82                });
 83
 84                quote! {
 85                    if let Some(other) = other {
 86                        #(#field_merges)*
 87                    }
 88                }
 89            }
 90            Fields::Unit => {
 91                quote! {
 92                    // No fields to merge for unit structs
 93                }
 94            }
 95        },
 96        Data::Enum(_) => {
 97            quote! {
 98               if let Some(other) = other {
 99                   *self = other.clone();
100               }
101            }
102        }
103        Data::Union(_) => {
104            panic!("MergeFrom cannot be derived for unions");
105        }
106    };
107
108    let expanded = quote! {
109        impl #impl_generics crate::merge_from::MergeFrom for #name #ty_generics #where_clause {
110            fn merge_from(&mut self, other: ::core::option::Option<&Self>) {
111                use crate::merge_from::MergeFrom as _;
112                #merge_body
113            }
114        }
115    };
116
117    TokenStream::from(expanded)
118}
119
120/// Check if a type is `Option<T>`
121fn is_option_type(ty: &Type) -> bool {
122    match ty {
123        Type::Path(type_path) => {
124            if let Some(segment) = type_path.path.segments.last() {
125                segment.ident == "Option"
126            } else {
127                false
128            }
129        }
130        _ => false,
131    }
132}