use syn::Attribute;
use crate::serde_attrs::model::{SerdeFieldAttrs, SerdeFieldContext};
use crate::syntax::{consume_unused_meta, parse_string_value, unraw};
use super::rename_meta::parse_rename_meta;
pub(crate) fn parse_serde_field_attrs(
attributes: &[Attribute],
field_ident: &syn::Ident,
context: SerdeFieldContext,
) -> syn::Result<SerdeFieldAttrs> {
let base_name = unraw(field_ident);
let mut rename_serialize = None;
let mut rename_deserialize = None;
let mut aliases = Vec::new();
let mut flatten = false;
let mut skip_metadata = false;
let mut has_default = context.default_fields;
for attribute in attributes {
if !attribute.path().is_ident("serde") {
continue;
}
attribute.parse_nested_meta(|meta| {
if meta.path.is_ident("rename") {
parse_rename_meta(meta, &mut rename_serialize, &mut rename_deserialize)?;
return Ok(());
}
if meta.path.is_ident("alias") {
aliases.push(parse_string_value(meta)?);
return Ok(());
}
if meta.path.is_ident("flatten") {
flatten = true;
return Ok(());
}
if meta.path.is_ident("default") {
has_default = true;
consume_unused_meta(meta)?;
return Ok(());
}
if meta.path.is_ident("skip") || meta.path.is_ident("skip_deserializing") {
skip_metadata = true;
return Ok(());
}
consume_unused_meta(meta)?;
Ok(())
})?;
}
let has_explicit_rename = rename_serialize.is_some() || rename_deserialize.is_some();
let canonical_name = rename_serialize
.or_else(|| {
context
.rename_serialize
.map(|rule| rule.apply_to_field(&base_name))
})
.unwrap_or_else(|| base_name.clone());
let deserialize_name = rename_deserialize
.or_else(|| {
context
.rename_deserialize
.map(|rule| rule.apply_to_field(&base_name))
})
.unwrap_or_else(|| base_name.clone());
if deserialize_name != canonical_name {
aliases.push(deserialize_name);
}
if flatten && (!aliases.is_empty() || has_explicit_rename) {
return Err(syn::Error::new_spanned(
field_ident,
"flattened fields cannot use serde rename or alias attributes",
));
}
aliases.retain(|alias| alias != &canonical_name);
aliases.sort();
aliases.dedup();
Ok(SerdeFieldAttrs {
canonical_name,
aliases,
flatten,
skip_metadata,
has_default,
})
}
pub(crate) fn has_field_naming_attrs(attributes: &[Attribute]) -> syn::Result<bool> {
let mut has_naming = false;
for attribute in attributes {
if !attribute.path().is_ident("serde") {
continue;
}
attribute.parse_nested_meta(|meta| {
if meta.path.is_ident("rename")
|| meta.path.is_ident("alias")
|| meta.path.is_ident("flatten")
|| meta.path.is_ident("default")
{
has_naming = true;
}
consume_unused_meta(meta)?;
Ok(())
})?;
}
Ok(has_naming)
}