#![forbid(unsafe_code)]
mod parse;
mod validate;
mod codegen;
use proc_macro::TokenStream;
use syn::parse_macro_input;
use darling::FromDeriveInput;
#[proc_macro_derive(DeepMerge, attributes(merge))]
pub fn derive_deep_merge(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::DeriveInput);
let merge_input = match parse::MergeInput::from_derive_input(&input) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let struct_policy = match parse::parse_struct_policy(&merge_input.attrs) {
Ok(p) => p,
Err(e) => return TokenStream::from(e.write_errors()),
};
if let Some(ref policy) = struct_policy {
let diagnostics = validate::validate_struct_policy(policy);
if !diagnostics.is_empty() {
return TokenStream::from(diagnostics.into_iter().next().unwrap().write_errors());
}
}
let output = match merge_input.data {
darling::ast::Data::Struct(ref fields) => {
let fields: Vec<_> = fields.fields.clone();
let analyzer = validate::ConstraintAnalyzer::new();
for field in &fields {
let config = parse::parse_field_typed_policies(field);
let diagnostics = validate::validate_field_config(field, &config, &analyzer);
if !diagnostics.is_empty() {
return TokenStream::from(diagnostics.into_iter().next().unwrap().write_errors());
}
}
codegen::generate_struct_impl(
&merge_input.ident,
&merge_input.generics,
&fields,
struct_policy.as_ref(),
merge_input.generics.where_clause.as_ref(),
)
}
darling::ast::Data::Enum(_) => {
codegen::generate_enum_impl(
&merge_input.ident,
&merge_input.generics,
merge_input.generics.where_clause.as_ref(),
)
}
};
TokenStream::from(output)
}