use ident_case::RenameRule;
use crate::ast::{Data, Fields, Style};
use crate::codegen;
use crate::codegen::PostfixTransform;
use crate::error::Accumulator;
use crate::options::{DefaultExpression, InputField, InputVariant, ParseAttribute, ParseData};
use crate::{Error, FromMeta, Result};
#[derive(Debug, Clone)]
pub struct Core {
pub ident: syn::Ident,
pub generics: syn::Generics,
pub default: Option<DefaultExpression>,
pub rename_rule: RenameRule,
pub post_transform: Option<codegen::PostfixTransform>,
pub data: Data<InputVariant, InputField>,
pub bound: Option<Vec<syn::WherePredicate>>,
pub allow_unknown_fields: Option<bool>,
}
impl Core {
pub fn start(di: &syn::DeriveInput) -> Result<Self> {
Ok(Core {
ident: di.ident.clone(),
generics: di.generics.clone(),
data: Data::try_empty_from(&di.data)?,
default: Default::default(),
rename_rule: if let syn::Data::Enum(_) = di.data {
RenameRule::SnakeCase
} else {
Default::default()
},
post_transform: Default::default(),
bound: Default::default(),
allow_unknown_fields: Default::default(),
})
}
fn as_codegen_default(&self) -> Option<codegen::DefaultExpression<'_>> {
self.default.as_ref().map(|expr| match *expr {
DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
DefaultExpression::Inherit => {
panic!("DefaultExpression::Inherit is not valid at container level")
}
DefaultExpression::Trait { span } => codegen::DefaultExpression::Trait { span },
})
}
}
impl ParseAttribute for Core {
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
let path = mi.path();
if path.is_ident("default") {
if self.default.is_some() {
return Err(Error::duplicate_field("default").with_span(mi));
}
self.default = FromMeta::from_meta(mi)?;
} else if path.is_ident("rename_all") {
self.rename_rule = FromMeta::from_meta(mi)?;
} else if path.is_ident("map") || path.is_ident("and_then") {
let transformer = path.get_ident().unwrap().clone();
if let Some(post_transform) = &self.post_transform {
if transformer == post_transform.transformer {
return Err(Error::duplicate_field(&transformer.to_string()).with_span(mi));
} else {
return Err(Error::custom(format!(
"Options `{}` and `{}` are mutually exclusive",
transformer, post_transform.transformer
))
.with_span(mi));
}
}
self.post_transform =
Some(PostfixTransform::new(transformer, FromMeta::from_meta(mi)?));
} else if path.is_ident("bound") {
self.bound = FromMeta::from_meta(mi)?;
} else if path.is_ident("allow_unknown_fields") {
if self.allow_unknown_fields.is_some() {
return Err(Error::duplicate_field("allow_unknown_fields").with_span(mi));
}
self.allow_unknown_fields = FromMeta::from_meta(mi)?;
} else {
return Err(Error::unknown_field_path(path).with_span(mi));
}
Ok(())
}
}
impl ParseData for Core {
fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
let v = InputVariant::from_variant(variant, Some(self))?;
match self.data {
Data::Enum(ref mut variants) => {
variants.push(v);
Ok(())
}
Data::Struct(_) => panic!("Core::parse_variant should never be called for a struct"),
}
}
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
let f = InputField::from_field(field, Some(self))?;
match self.data {
Data::Struct(Fields {
style: Style::Unit, ..
}) => panic!("Core::parse_field should not be called on unit"),
Data::Struct(Fields { ref mut fields, .. }) => {
fields.push(f);
Ok(())
}
Data::Enum(_) => panic!("Core::parse_field should never be called for an enum"),
}
}
fn validate_body(&self, errors: &mut Accumulator) {
if let Data::Struct(fields) = &self.data {
let flatten_targets: Vec<_> = fields
.iter()
.filter_map(|field| {
if field.flatten.is_present() {
Some(field.flatten)
} else {
None
}
})
.collect();
if flatten_targets.len() > 1 {
for flatten in flatten_targets {
errors.push(
Error::custom("`#[darling(flatten)]` can only be applied to one field")
.with_span(&flatten.span()),
);
}
}
}
}
}
impl<'a> From<&'a Core> for codegen::TraitImpl<'a> {
fn from(v: &'a Core) -> Self {
codegen::TraitImpl {
ident: &v.ident,
generics: &v.generics,
data: v
.data
.as_ref()
.map_struct_fields(InputField::as_codegen_field)
.map_enum_variants(|variant| variant.as_codegen_variant(&v.ident)),
default: v.as_codegen_default(),
post_transform: v.post_transform.as_ref(),
allow_unknown_fields: v.allow_unknown_fields.unwrap_or_default(),
}
}
}