Skip to content

Commit ce1ddca

Browse files
authored
Merge pull request #64 from ImJeremyHe/jh/enum-value
Add xml enum value derive macro
2 parents 344022b + 8bca559 commit ce1ddca

File tree

10 files changed

+269
-44
lines changed

10 files changed

+269
-44
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
authors = ["ImJeremyHe<[email protected]>"]
33
edition = "2018"
44
name = "xmlserde"
5-
version = "0.10.2"
5+
version = "0.11.0"
66
license = "MIT"
77
description = "useful tool for serializing and deserializing xml"
88
repository = "https://github.com/ImJeremyHe/xmlserde"
99
keywords = ["xml", "serde"]
1010
readme = "README.md"
1111

1212
[dependencies]
13-
quick-xml = {version = "0.37", features = ["serialize"]}
13+
quick-xml = { version = "0.37", features = ["serialize"] }
1414

1515
[dev-dependencies]
16-
xmlserde_derives = {path = "./derives", version = "0.10.2"}
16+
xmlserde_derives = { path = "./derives", version = "0.11.0" }

derives/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "xmlserde_derives"
3-
version = "0.10.2"
3+
version = "0.11.0"
44
description = "macros that help xmlserde serde the xml files"
55
authors = ["ImJeremyHe<[email protected]>"]
66
license = "MIT"
@@ -11,6 +11,6 @@ repository = "https://github.com/ImJeremyHe/xmlserde"
1111
proc-macro = true
1212

1313
[dependencies]
14-
syn = {version = "2.0.48", features = ["full"]}
14+
syn = { version = "2.0.48", features = ["full"] }
1515
quote = "1"
1616
proc-macro2 = "1.0.75"

derives/src/container.rs

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::symbol::{
22
DEFAULT, DENY_UNKNOWN, NAME, ROOT, SKIP_SERIALIZING, TYPE, VEC_SIZE, WITH_CUSTOM_NS, WITH_NS,
3-
XML_SERDE,
43
};
54
use proc_macro2::{Group, Span, TokenStream, TokenTree};
65
use syn::parse::{self, Parse};
@@ -10,6 +9,8 @@ use syn::Meta::Path;
109
use syn::Meta::{self, NameValue};
1110
use syn::Variant;
1211

12+
use crate::utils::{get_lit_byte_str, get_lit_str, get_xmlserde_meta_items};
13+
1314
pub struct Container<'a> {
1415
pub struct_fields: Vec<StructField<'a>>, // Struct fields
1516
pub enum_variants: Vec<EnumVariant<'a>>,
@@ -36,7 +37,7 @@ impl<'a> Container<'a> {
3637
self.struct_fields.iter().for_each(|f| f.validate());
3738
}
3839

39-
pub fn from_ast(item: &'a syn::DeriveInput, _derive: Derive) -> Container<'a> {
40+
pub fn from_ast(item: &'a syn::DeriveInput) -> Container<'a> {
4041
let mut with_ns = Option::<syn::LitByteStr>::None;
4142
let mut custom_ns = Vec::<(syn::LitByteStr, syn::LitByteStr)>::new();
4243
let mut root = Option::<syn::LitByteStr>::None;
@@ -341,40 +342,6 @@ pub enum EleType {
341342
UntaggedStruct,
342343
}
343344

344-
pub enum Derive {
345-
Serialize,
346-
Deserialize,
347-
}
348-
349-
fn get_xmlserde_meta_items(attr: &syn::Attribute) -> Result<Vec<syn::Meta>, ()> {
350-
if attr.path() != XML_SERDE {
351-
return Ok(Vec::new());
352-
}
353-
354-
match attr.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated) {
355-
Ok(meta) => Ok(meta.into_iter().collect()),
356-
Err(_) => Err(()),
357-
}
358-
}
359-
360-
fn get_lit_byte_str<'a>(expr: &syn::Expr) -> Result<&syn::LitByteStr, ()> {
361-
if let syn::Expr::Lit(lit) = expr {
362-
if let syn::Lit::ByteStr(l) = &lit.lit {
363-
return Ok(l);
364-
}
365-
}
366-
Err(())
367-
}
368-
369-
fn get_lit_str<'a>(lit: &syn::Expr) -> Result<&syn::LitStr, ()> {
370-
if let syn::Expr::Lit(lit) = lit {
371-
if let syn::Lit::Str(l) = &lit.lit {
372-
return Ok(&l);
373-
}
374-
}
375-
Err(())
376-
}
377-
378345
pub fn parse_lit_into_expr_path(value: &syn::Expr) -> Result<syn::ExprPath, ()> {
379346
let l = get_lit_str(value)?;
380347
parse_lit_str(l).map_err(|_| ())

derives/src/de.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use syn::DeriveInput;
33
use crate::container::{self, Container, EleType, FieldsSummary, Generic, StructField};
44

55
pub fn get_de_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
6-
let container = Container::from_ast(&input, container::Derive::Deserialize);
6+
let container = Container::from_ast(&input);
77
container.validate();
88
if container.is_enum() {
99
get_de_enum_impl_block(container)

derives/src/enum_value.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use syn::DeriveInput;
2+
use syn::Meta;
3+
4+
use crate::symbol::OTHER;
5+
use crate::symbol::{MAP, RENAME};
6+
use crate::utils::get_array_lit_str;
7+
use crate::utils::get_lit_str;
8+
use crate::utils::get_xmlserde_meta_items;
9+
10+
pub fn get_enum_value_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
11+
let data = match input.data {
12+
syn::Data::Enum(e) => e,
13+
_ => panic!("expect enum type"),
14+
};
15+
let ident = input.ident;
16+
let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl();
17+
let variants = data
18+
.variants
19+
.iter()
20+
.filter_map(|v| EnumValueVariant::from_variant(v))
21+
.collect::<Vec<_>>();
22+
23+
let ser_branches = variants.iter().map(get_ser_branch).collect::<Vec<_>>();
24+
let de_branches = variants.iter().map(get_de_branch).collect::<Vec<_>>();
25+
quote! {
26+
impl #impl_generics ::xmlserde::XmlValue for #ident #type_generics #where_clause {
27+
fn serialize(&self) -> String {
28+
match &self {
29+
#(#ser_branches),*
30+
}
31+
}
32+
fn deserialize(s: &str) -> Result<Self, String> {
33+
match s {
34+
#(#de_branches),*
35+
}
36+
}
37+
}
38+
}
39+
}
40+
41+
fn get_de_branch(var: &EnumValueVariant) -> proc_macro2::TokenStream {
42+
let ident = var.ident;
43+
if var.is_other {
44+
if let Some(field) = &var.is_other_field {
45+
let ty = &field.ty;
46+
return quote! {
47+
_ => Ok(Self::#ident(#ty::deserialize(s)?))
48+
};
49+
}
50+
}
51+
if !var.map.is_empty() {
52+
let values = var.map.iter().map(|s| quote! {#s => Ok(Self::#ident)});
53+
return quote! {
54+
#(#values),*
55+
};
56+
}
57+
if let Some(rename) = &var.rename {
58+
return quote! {
59+
#rename => Ok(Self::#ident)
60+
};
61+
}
62+
panic!("unexpected situation")
63+
}
64+
65+
fn get_ser_branch(var: &EnumValueVariant) -> proc_macro2::TokenStream {
66+
let ident = var.ident;
67+
if var.is_other {
68+
if let Some(field) = &var.is_other_field {
69+
let ty = &field.ty;
70+
return quote! {
71+
Self::#ident(v) => {
72+
<#ty as ::xmlserde::XmlValue>::serialize(v)
73+
}
74+
};
75+
}
76+
}
77+
if !var.map.is_empty() {
78+
let first = var.map.first().unwrap();
79+
return quote! {
80+
Self::#ident => #first.to_string()
81+
};
82+
}
83+
if let Some(rename) = &var.rename {
84+
return quote! {
85+
Self::#ident => #rename.to_string()
86+
};
87+
}
88+
89+
panic!("unexpected situation")
90+
}
91+
92+
struct EnumValueVariant<'a> {
93+
ident: &'a syn::Ident,
94+
is_other: bool,
95+
is_other_field: Option<syn::Field>,
96+
rename: Option<syn::LitStr>,
97+
map: Vec<syn::LitStr>,
98+
}
99+
100+
impl<'a> EnumValueVariant<'a> {
101+
pub fn from_variant(v: &'a syn::Variant) -> Option<Self> {
102+
for meta_item in v
103+
.attrs
104+
.iter()
105+
.flat_map(|attr| get_xmlserde_meta_items(attr))
106+
.flatten()
107+
{
108+
match meta_item {
109+
Meta::Path(path) if path == OTHER => {
110+
let field = match &v.fields {
111+
syn::Fields::Named(_) => panic!("unsupported named fields"),
112+
syn::Fields::Unnamed(fields_unnamed) => {
113+
fields_unnamed.unnamed.iter().next().cloned()
114+
}
115+
syn::Fields::Unit => None,
116+
};
117+
if field.is_none() {
118+
panic!("other field should not have no field!")
119+
}
120+
return Some(Self {
121+
rename: None,
122+
ident: &v.ident,
123+
is_other: true,
124+
is_other_field: field,
125+
map: Vec::new(),
126+
});
127+
}
128+
Meta::NameValue(m) if m.path == RENAME => {
129+
if let Ok(s) = get_lit_str(&m.value) {
130+
return Some(Self {
131+
rename: Some(s.clone()),
132+
ident: &v.ident,
133+
is_other: false,
134+
map: Vec::new(),
135+
is_other_field: None,
136+
});
137+
}
138+
panic!(r#"please use `#[rename = "..."]`"#);
139+
}
140+
Meta::NameValue(m) if m.path == MAP => {
141+
if let Ok(s) = get_array_lit_str(&m.value) {
142+
return Some(Self {
143+
rename: None,
144+
ident: &v.ident,
145+
is_other: false,
146+
map: s.into_iter().map(|s| s.clone()).collect(),
147+
is_other_field: None,
148+
});
149+
}
150+
panic!(r#"please use `#[map = ["..."]`"#);
151+
}
152+
_ => panic!("unexpected attribute"),
153+
}
154+
}
155+
None
156+
}
157+
}

derives/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ extern crate quote;
33

44
mod container;
55
mod de;
6+
mod enum_value;
67
mod ser;
78
mod symbol;
9+
mod utils;
810

911
use de::get_de_impl_block;
1012
use ser::get_ser_impl_block;
@@ -24,3 +26,9 @@ pub fn derive_xml_serialize(input: TokenStream) -> TokenStream {
2426
let input = parse_macro_input!(input as DeriveInput);
2527
get_ser_impl_block(input).into()
2628
}
29+
30+
#[proc_macro_derive(XmlEnumValue, attributes(xmlserde))]
31+
pub fn derive_xml_enum_value(input: TokenStream) -> TokenStream {
32+
let input = parse_macro_input!(input as DeriveInput);
33+
enum_value::get_enum_value_impl_block(input).into()
34+
}

derives/src/ser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use syn::DeriveInput;
22

3-
use crate::container::{Container, Derive, EleType, FieldsSummary, Generic, StructField};
3+
use crate::container::{Container, EleType, FieldsSummary, Generic, StructField};
44

55
pub fn get_ser_impl_block(input: DeriveInput) -> proc_macro2::TokenStream {
6-
let container = Container::from_ast(&input, Derive::Serialize);
6+
let container = Container::from_ast(&input);
77
container.validate();
88
if container.is_enum() {
99
get_ser_enum_impl_block(container)

derives/src/symbol.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing");
1515
pub const VEC_SIZE: Symbol = Symbol("vec_size");
1616
pub const DEFAULT: Symbol = Symbol("default");
1717

18+
pub const RENAME: Symbol = Symbol("rename");
19+
pub const MAP: Symbol = Symbol("map");
20+
pub const OTHER: Symbol = Symbol("other");
21+
1822
impl PartialEq<Symbol> for Ident {
1923
fn eq(&self, other: &Symbol) -> bool {
2024
self == other.0

derives/src/utils.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use crate::symbol::XML_SERDE;
2+
use syn::punctuated::Punctuated;
3+
use syn::token::Comma;
4+
use syn::Meta;
5+
6+
pub fn get_xmlserde_meta_items(attr: &syn::Attribute) -> Result<Vec<syn::Meta>, ()> {
7+
if attr.path() != XML_SERDE {
8+
return Ok(Vec::new());
9+
}
10+
11+
match attr.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated) {
12+
Ok(meta) => Ok(meta.into_iter().collect()),
13+
Err(_) => Err(()),
14+
}
15+
}
16+
17+
pub fn get_lit_byte_str<'a>(expr: &syn::Expr) -> Result<&syn::LitByteStr, ()> {
18+
if let syn::Expr::Lit(lit) = expr {
19+
if let syn::Lit::ByteStr(l) = &lit.lit {
20+
return Ok(l);
21+
}
22+
}
23+
Err(())
24+
}
25+
26+
pub fn get_lit_str<'a>(lit: &syn::Expr) -> Result<&syn::LitStr, ()> {
27+
if let syn::Expr::Lit(lit) = lit {
28+
if let syn::Lit::Str(l) = &lit.lit {
29+
return Ok(&l);
30+
}
31+
}
32+
Err(())
33+
}
34+
35+
pub fn get_array_lit_str<'a>(expr: &syn::Expr) -> Result<Vec<&syn::LitStr>, ()> {
36+
if let syn::Expr::Array(array) = expr {
37+
array.elems.iter().map(get_lit_str).collect()
38+
} else {
39+
Err(())
40+
}
41+
}

0 commit comments

Comments
 (0)