use serde_rename_rule::RenameRule;
use syn::{Attribute, Expr, ExprLit, Lit};
#[derive(Debug)]
pub struct SerdeParser;
impl SerdeParser {
pub fn new() -> Self {
Self
}
pub fn parse_struct_serde_attrs(&self, attrs: &[Attribute]) -> SerdeStructAttributes {
let mut result = SerdeStructAttributes {
rename_all: None,
tag: None,
content: None,
};
for attr in attrs {
if attr.path().is_ident("serde") {
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("rename_all") {
if let Some(value) = parse_string_value(&meta)? {
result.rename_all = RenameRule::from_rename_all_str(&value).ok();
}
} else if meta.path.is_ident("tag") {
result.tag = parse_string_value(&meta)?;
} else if meta.path.is_ident("content") {
result.content = parse_string_value(&meta)?;
}
Ok(())
});
}
}
result
}
pub fn parse_field_serde_attrs(&self, attrs: &[Attribute]) -> SerdeFieldAttributes {
let mut result = SerdeFieldAttributes {
rename: None,
skip: false,
};
for attr in attrs {
if attr.path().is_ident("serde") {
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("rename") {
result.rename = parse_string_value(&meta)?;
} else if meta.path.is_ident("skip") {
result.skip = true;
}
Ok(())
});
}
}
result
}
}
fn parse_string_value(meta: &syn::meta::ParseNestedMeta) -> syn::Result<Option<String>> {
let expr: Expr = meta.value()?.parse()?;
if let Expr::Lit(ExprLit {
lit: Lit::Str(lit_str),
..
}) = expr
{
Ok(Some(lit_str.value()))
} else {
Ok(None)
}
}
impl Default for SerdeParser {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Default, Clone)]
pub struct SerdeStructAttributes {
pub rename_all: Option<RenameRule>,
pub tag: Option<String>,
pub content: Option<String>,
}
#[derive(Debug, Default, Clone)]
pub struct SerdeFieldAttributes {
pub rename: Option<String>,
pub skip: bool,
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_parse_struct_serde_attrs_with_rename_all_camel_case() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "camelCase")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_some());
assert!(matches!(result.rename_all.unwrap(), RenameRule::CamelCase));
}
#[test]
fn test_parse_struct_serde_attrs_with_rename_all_snake_case() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "snake_case")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_some());
assert!(matches!(result.rename_all.unwrap(), RenameRule::SnakeCase));
}
#[test]
fn test_parse_struct_serde_attrs_with_rename_all_pascal_case() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "PascalCase")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_some());
assert!(matches!(result.rename_all.unwrap(), RenameRule::PascalCase));
}
#[test]
fn test_parse_struct_serde_attrs_with_rename_all_screaming_snake_case() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> =
vec![parse_quote!(#[serde(rename_all = "SCREAMING_SNAKE_CASE")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_some());
assert!(matches!(
result.rename_all.unwrap(),
RenameRule::ScreamingSnakeCase
));
}
#[test]
fn test_parse_struct_serde_attrs_with_rename_all_kebab_case() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "kebab-case")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_some());
assert!(matches!(result.rename_all.unwrap(), RenameRule::KebabCase));
}
#[test]
fn test_parse_struct_serde_attrs_no_serde() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(result.rename_all.is_none());
assert!(result.tag.is_none());
assert!(result.content.is_none());
}
#[test]
fn test_parse_struct_serde_attrs_with_tag() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "type")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert_eq!(result.tag, Some("type".to_string()));
}
#[test]
fn test_parse_struct_serde_attrs_with_custom_tag() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "kind")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert_eq!(result.tag, Some("kind".to_string()));
}
#[test]
fn test_parse_struct_serde_attrs_with_content() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(content = "data")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert_eq!(result.content, Some("data".to_string()));
}
#[test]
fn test_parse_struct_serde_attrs_with_tag_and_content() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "kind", content = "data")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert_eq!(result.tag, Some("kind".to_string()));
assert_eq!(result.content, Some("data".to_string()));
}
#[test]
fn test_parse_struct_serde_attrs_with_all_attributes() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> =
vec![parse_quote!(#[serde(rename_all = "camelCase", tag = "type", content = "value")])];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
assert_eq!(result.tag, Some("type".to_string()));
assert_eq!(result.content, Some("value".to_string()));
}
#[test]
fn test_parse_field_serde_attrs_with_rename() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "customName")])];
let result = parser.parse_field_serde_attrs(&attrs);
assert_eq!(result.rename, Some("customName".to_string()));
assert!(!result.skip);
}
#[test]
fn test_parse_field_serde_attrs_with_skip() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip)])];
let result = parser.parse_field_serde_attrs(&attrs);
assert!(result.skip);
assert!(result.rename.is_none());
}
#[test]
fn test_parse_field_serde_attrs_skip_serializing_not_skip() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip_serializing)])];
let result = parser.parse_field_serde_attrs(&attrs);
assert!(!result.skip);
}
#[test]
fn test_parse_field_serde_attrs_skip_deserializing_not_skip() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip_deserializing)])];
let result = parser.parse_field_serde_attrs(&attrs);
assert!(!result.skip);
}
#[test]
fn test_parse_field_serde_attrs_multiple_attributes() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![
parse_quote!(#[serde(rename = "id")]),
parse_quote!(#[derive(Debug)]),
];
let result = parser.parse_field_serde_attrs(&attrs);
assert_eq!(result.rename, Some("id".to_string()));
}
#[test]
fn test_parse_field_serde_attrs_rename_and_skip() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "id", skip)])];
let result = parser.parse_field_serde_attrs(&attrs);
assert_eq!(result.rename, Some("id".to_string()));
assert!(result.skip);
}
#[test]
fn test_parse_field_serde_attrs_no_serde() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
let result = parser.parse_field_serde_attrs(&attrs);
assert!(result.rename.is_none());
assert!(!result.skip);
}
#[test]
fn test_parse_field_serde_attrs_empty() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![];
let result = parser.parse_field_serde_attrs(&attrs);
assert!(result.rename.is_none());
assert!(!result.skip);
}
#[test]
fn test_default_impl() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "test")])];
let result = parser.parse_field_serde_attrs(&attrs);
assert_eq!(result.rename, Some("test".to_string()));
}
#[test]
fn test_parse_struct_serde_attrs_ignores_other_attributes() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![parse_quote!(
#[serde(rename_all = "camelCase", deny_unknown_fields)]
)];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
}
#[test]
fn test_parse_field_serde_attrs_ignores_other_attributes() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![
parse_quote!(#[serde(rename = "id", default, skip_serializing_if = "Option::is_none")]),
];
let result = parser.parse_field_serde_attrs(&attrs);
assert_eq!(result.rename, Some("id".to_string()));
}
#[test]
fn test_parse_multiple_serde_attributes() {
let parser = SerdeParser::new();
let attrs: Vec<Attribute> = vec![
parse_quote!(#[serde(rename_all = "camelCase")]),
parse_quote!(#[serde(tag = "type")]),
];
let result = parser.parse_struct_serde_attrs(&attrs);
assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
assert_eq!(result.tag, Some("type".to_string()));
}
}