use syn::spanned::Spanned;
use crate::case_conversion::RenameRule;
#[derive(Default)]
pub(crate) struct ContainerAttrs {
pub(crate) rename_all: Option<RenameRule>,
pub(crate) tag: Option<String>,
pub(crate) content: Option<String>,
pub(crate) untagged: bool,
pub(crate) deny_unknown_fields: bool,
pub(crate) transparent: bool,
}
#[derive(Default)]
pub(crate) struct FieldAttrs {
pub(crate) rename: Option<String>,
pub(crate) skip: bool,
pub(crate) default: bool,
pub(crate) default_fn: Option<String>,
pub(crate) with: Option<String>,
pub(crate) skip_saving_if: Option<String>,
pub(crate) flatten: bool,
pub(crate) transparent: bool,
pub(crate) alias: Option<String>,
}
#[derive(Default)]
pub(crate) struct VariantAttrs {
pub(crate) rename: Option<String>,
pub(crate) skip: bool,
}
pub(crate) const KNOWN_CONTAINER_ATTRS: &[&str] = &[
"rename_all",
"tag",
"content",
"untagged",
"deny_unknown_fields",
"transparent",
];
pub(crate) const KNOWN_FIELD_ATTRS: &[&str] = &[
"rename",
"skip",
"default",
"with",
"skip_saving_if",
"flatten",
"transparent",
"alias",
];
pub(crate) const KNOWN_VARIANT_ATTRS: &[&str] = &["rename", "skip"];
pub(crate) fn validate_container_attrs(attrs: &[syn::Attribute]) -> Option<syn::Error> {
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let mut error: Option<syn::Error> = None;
let _ = attr.parse_nested_meta(|meta| {
let attr_name = meta.path.get_ident().map(|i| i.to_string());
if let Some(name) = attr_name {
if !KNOWN_CONTAINER_ATTRS.contains(&name.as_str()) {
error = Some(syn::Error::new(
meta.path.span(),
format!(
"unknown automorph container attribute `{}`. \
Known attributes: {}",
name,
KNOWN_CONTAINER_ATTRS.join(", ")
),
));
}
}
if meta.input.peek(syn::Token![=]) {
let _ = meta.value().ok().and_then(|v| v.parse::<syn::Lit>().ok());
}
Ok(())
});
if let Some(e) = error {
return Some(e);
}
}
None
}
pub(crate) fn validate_field_attrs(attrs: &[syn::Attribute]) -> Option<syn::Error> {
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let mut error: Option<syn::Error> = None;
let _ = attr.parse_nested_meta(|meta| {
let attr_name = meta.path.get_ident().map(|i| i.to_string());
if let Some(ref name) = attr_name {
if !KNOWN_FIELD_ATTRS.contains(&name.as_str()) {
error = Some(syn::Error::new(
meta.path.span(),
format!(
"unknown automorph field attribute `{}`. \
Known attributes: {}",
name,
KNOWN_FIELD_ATTRS.join(", ")
),
));
}
if name == "flatten" {
}
}
if meta.input.peek(syn::Token![=]) {
let _ = meta.value().ok().and_then(|v| v.parse::<syn::Lit>().ok());
}
Ok(())
});
if let Some(e) = error {
return Some(e);
}
}
None
}
pub(crate) fn validate_variant_attrs(attrs: &[syn::Attribute]) -> Option<syn::Error> {
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let mut error: Option<syn::Error> = None;
let _ = attr.parse_nested_meta(|meta| {
let attr_name = meta.path.get_ident().map(|i| i.to_string());
if let Some(name) = attr_name {
if !KNOWN_VARIANT_ATTRS.contains(&name.as_str()) {
error = Some(syn::Error::new(
meta.path.span(),
format!(
"unknown automorph variant attribute `{}`. \
Known attributes: {}",
name,
KNOWN_VARIANT_ATTRS.join(", ")
),
));
}
}
if meta.input.peek(syn::Token![=]) {
let _ = meta.value().ok().and_then(|v| v.parse::<syn::Lit>().ok());
}
Ok(())
});
if let Some(e) = error {
return Some(e);
}
}
None
}
pub(crate) fn parse_container_attrs(attrs: &[syn::Attribute]) -> ContainerAttrs {
let mut result = ContainerAttrs::default();
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("rename_all") {
let value: syn::LitStr = meta.value()?.parse()?;
result.rename_all = match value.value().as_str() {
"camelCase" => Some(RenameRule::CamelCase),
"snake_case" => Some(RenameRule::SnakeCase),
"PascalCase" => Some(RenameRule::PascalCase),
"SCREAMING_SNAKE_CASE" => Some(RenameRule::ScreamingSnakeCase),
"kebab-case" => Some(RenameRule::KebabCase),
_ => None,
};
} else if meta.path.is_ident("tag") {
let value: syn::LitStr = meta.value()?.parse()?;
result.tag = Some(value.value());
} else if meta.path.is_ident("content") {
let value: syn::LitStr = meta.value()?.parse()?;
result.content = Some(value.value());
} else if meta.path.is_ident("untagged") {
result.untagged = true;
} else if meta.path.is_ident("deny_unknown_fields") {
result.deny_unknown_fields = true;
} else if meta.path.is_ident("transparent") {
result.transparent = true;
}
Ok(())
});
}
result
}
pub(crate) fn parse_field_attrs(attrs: &[syn::Attribute]) -> FieldAttrs {
let mut result = FieldAttrs::default();
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("rename") {
let value: syn::LitStr = meta.value()?.parse()?;
result.rename = Some(value.value());
} else if meta.path.is_ident("skip") {
result.skip = true;
} else if meta.path.is_ident("default") {
result.default = true;
if meta.input.peek(syn::Token![=]) {
let value: syn::LitStr = meta.value()?.parse()?;
result.default_fn = Some(value.value());
}
} else if meta.path.is_ident("with") {
let value: syn::LitStr = meta.value()?.parse()?;
result.with = Some(value.value());
} else if meta.path.is_ident("skip_saving_if") {
let value: syn::LitStr = meta.value()?.parse()?;
result.skip_saving_if = Some(value.value());
} else if meta.path.is_ident("flatten") {
result.flatten = true;
} else if meta.path.is_ident("transparent") {
result.transparent = true;
} else if meta.path.is_ident("alias") {
let value: syn::LitStr = meta.value()?.parse()?;
result.alias = Some(value.value());
}
Ok(())
});
}
result
}
pub(crate) fn parse_variant_attrs(attrs: &[syn::Attribute]) -> VariantAttrs {
let mut result = VariantAttrs::default();
for attr in attrs {
if !attr.path().is_ident("automorph") {
continue;
}
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("rename") {
let value: syn::LitStr = meta.value()?.parse()?;
result.rename = Some(value.value());
} else if meta.path.is_ident("skip") {
result.skip = true;
}
Ok(())
});
}
result
}
pub(crate) fn get_field_key(field: &syn::Field, container_attrs: &ContainerAttrs) -> String {
let field_attrs = parse_field_attrs(&field.attrs);
if let Some(rename) = field_attrs.rename {
return rename;
}
let name = field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_default();
if let Some(rule) = &container_attrs.rename_all {
rule.apply(&name)
} else {
name
}
}
pub(crate) fn get_variant_key(variant: &syn::Variant, container_attrs: &ContainerAttrs) -> String {
let variant_attrs = parse_variant_attrs(&variant.attrs);
if let Some(rename) = variant_attrs.rename {
return rename;
}
let name = variant.ident.to_string();
if let Some(rule) = &container_attrs.rename_all {
rule.apply(&name)
} else {
name
}
}