use darling::{FromAttributes, FromMeta};
use syn::Expr;
use crate::case::RenameRule;
#[derive(Debug, Default, FromMeta, PartialEq, Eq)]
#[darling(from_expr = RenameAll::from_expr)]
pub struct RenameAll {
#[darling(default)]
pub serialize: RenameRule,
#[darling(default)]
pub deserialize: RenameRule,
}
impl From<RenameRule> for RenameAll {
fn from(value: RenameRule) -> Self {
Self {
serialize: value,
deserialize: value,
}
}
}
impl RenameAll {
fn from_expr(expr: &Expr) -> darling::Result<Self> {
let Expr::Lit(lit) = expr else {
return Err(darling::Error::custom("Expected a string literal!"));
};
let rule = RenameRule::from_value(&lit.lit)?;
Ok(RenameAll::from(rule))
}
}
#[derive(Debug, FromMeta, PartialEq)]
#[darling(from_expr = |expr| Ok(SerdeDefault::Expr(expr.clone())))]
pub enum SerdeDefault {
#[darling(word, skip)]
UseTrait,
Expr(Expr),
}
#[derive(Debug, FromAttributes)]
#[darling(attributes(serde))]
pub struct ContainerAttributes {
pub rename: Option<String>,
#[darling(default)]
pub rename_all: RenameAll,
#[darling(default, rename = "rename_all_fields")]
pub rename_all_fields: RenameAll,
#[darling(default, rename = "deny_unknown_fields")]
pub _deny_unknown_fields: bool,
pub tag: Option<String>,
pub content: Option<String>,
#[darling(default)]
pub untagged: bool,
#[darling(default, rename = "bound")]
pub _bound: Option<String>,
#[darling(rename = "default")]
pub _default: Option<SerdeDefault>,
#[darling(rename = "remote")]
pub _remote: Option<String>,
#[darling(default)]
pub transparent: bool,
#[darling(default, rename = "from")]
pub _from: Option<String>,
#[darling(default, rename = "try_from")]
pub _try_from: Option<String>,
#[darling(default, rename = "into")]
pub _into: Option<String>,
#[darling(default, rename = "crate")]
pub _crate: Option<String>,
#[darling(default, rename = "expecting")]
pub _expecting: Option<String>,
#[darling(default)]
pub variant_identifier: bool,
#[darling(default)]
pub field_identifier: bool,
}
#[derive(Debug, FromAttributes)]
#[darling(attributes(serde))]
pub struct VariantAttributes {
#[darling(default)]
pub rename: Option<String>,
#[darling(multiple, rename = "alias")]
pub alias: Vec<String>,
#[darling(default, rename = "rename_all")]
pub rename_all: RenameAll,
#[darling(default, rename = "skip")]
pub skip: bool,
#[darling(default, rename = "skip_serializing")]
pub skip_serializing: bool,
#[darling(default, rename = "skip_deserializing")]
pub skip_deserializing: bool,
#[darling(rename = "serialize_with")]
pub _serialize_with: Option<String>,
#[darling(rename = "deserialize_with")]
pub _deserialize_with: Option<String>,
#[darling(rename = "with")]
pub with: Option<String>,
#[darling(rename = "bound")]
pub _bound: Option<String>,
#[darling(rename = "borrow")]
pub _borrow: Option<SerdeBorrow>,
#[darling(default)]
pub other: bool,
#[darling(default)]
pub untagged: bool,
}
#[derive(Debug, FromMeta, PartialEq)]
#[darling(from_expr = |expr| Ok(SerdeBorrow::Expr(expr.clone())))]
pub enum SerdeBorrow {
#[darling(word, skip)]
Default,
Expr(Expr),
}
#[derive(Debug, FromAttributes)]
#[darling(attributes(serde))]
pub struct FieldAttributes {
#[darling(default)]
pub rename: Option<String>,
#[darling(multiple)]
pub alias: Vec<String>,
#[darling(rename = "default")]
pub _default: Option<SerdeDefault>,
#[darling(default)]
pub flatten: bool,
#[darling(default)]
pub skip: bool,
#[darling(default)]
pub skip_serializing: bool,
#[darling(default)]
pub skip_deserializing: bool,
pub skip_serializing_if: Option<String>,
#[darling(rename = "serialize_with")]
pub _serialize_with: Option<String>,
#[darling(rename = "deserialize_with")]
pub _deserialize_with: Option<String>,
pub with: Option<String>,
#[darling(rename = "borrow")]
pub _borrow: Option<SerdeBorrow>,
#[darling(rename = "getter")]
pub _getter: Option<String>,
}
#[cfg(test)]
mod tests {
use crate::{
attributes::serde::{ContainerAttributes, RenameAll, SerdeDefault},
case::RenameRule,
};
use darling::FromAttributes;
use syn::DeriveInput;
#[test]
fn test_rename_all() {
let derive: DeriveInput = syn::parse_quote! {
#[serde(rename_all = "lowercase")]
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(input.rename_all, RenameAll::from(RenameRule::LowerCase));
let derive: DeriveInput = syn::parse_quote! {
#[serde(rename_all(serialize = "lowercase"))]
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(
input.rename_all,
RenameAll {
serialize: RenameRule::LowerCase,
deserialize: RenameRule::None
}
);
let derive: DeriveInput = syn::parse_quote! {
#[serde(rename_all(deserialize = "lowercase"))]
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(
input.rename_all,
RenameAll {
serialize: RenameRule::None,
deserialize: RenameRule::LowerCase
}
);
let derive: DeriveInput = syn::parse_quote! {
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(input.rename_all, RenameAll::from(RenameRule::None));
}
#[test]
fn test_default() {
let derive: DeriveInput = syn::parse_quote! {
#[serde(default)]
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(input._default, Some(SerdeDefault::UseTrait));
let derive: DeriveInput = syn::parse_quote! {
#[serde(default = "some::path")]
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert!(matches!(input._default, Some(SerdeDefault::Expr(_))));
let derive: DeriveInput = syn::parse_quote! {
struct Config {
field: String,
}
};
let input = ContainerAttributes::from_attributes(&derive.attrs).unwrap();
assert_eq!(input._default, None);
}
}