1use quote::quote;
2use syn::{parse_macro_input, DeriveInput};
3
4mod de;
5mod options;
6use options::{
7 XmlityFieldAttributeDeriveOpts, XmlityFieldElementDeriveOpts, XmlityFieldGroupDeriveOpts,
8 XmlityRootAttributeDeriveOpts, XmlityRootElementDeriveOpts, XmlityRootGroupDeriveOpts,
9 XmlityRootValueDeriveOpts,
10};
11mod ser;
12mod utils;
13
14#[proc_macro_derive(Serialize, attributes(xelement, xattribute, xgroup, xvalue))]
16pub fn derive_serialize_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
17 let ast = parse_macro_input!(item as DeriveInput);
18 let element_opts = XmlityRootElementDeriveOpts::parse(&ast).expect("Wrong options");
19 let value_opts = XmlityRootValueDeriveOpts::parse(&ast).expect("Wrong options");
20
21 ser::derive_element_serialize_fn(ast, element_opts.as_ref(), value_opts.as_ref())
22 .expect("Wrong options")
23 .into()
24}
25
26#[proc_macro_derive(SerializeAttribute, attributes(xattribute))]
28pub fn derive_serialize_attribute_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
29 let ast = parse_macro_input!(item as DeriveInput);
30 let opts = XmlityRootAttributeDeriveOpts::parse(&ast)
31 .expect("Wrong options")
32 .unwrap_or_default();
33
34 ser::derive_attribute_serialize_fn(ast, opts).into()
35}
36
37#[proc_macro_derive(Deserialize, attributes(xelement, xattribute, xgroup, xvalue))]
39pub fn derive_deserialize_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
40 let ast = parse_macro_input!(item as DeriveInput);
41 let element_opts = XmlityRootElementDeriveOpts::parse(&ast).expect("Wrong options");
42 let attribute_opts = XmlityRootAttributeDeriveOpts::parse(&ast).expect("Wrong options");
43 let value_opts = XmlityRootValueDeriveOpts::parse(&ast).expect("Wrong options");
44
45 de::derive_deserialize_fn(ast, element_opts, attribute_opts, value_opts)
46 .expect("Wrong options")
47 .into()
48}
49
50#[proc_macro_derive(SerializationGroup, attributes(xelement, xattribute, xgroup))]
52pub fn derive_serialization_group_attribute_fn(
53 item: proc_macro::TokenStream,
54) -> proc_macro::TokenStream {
55 let ast = parse_macro_input!(item as DeriveInput);
56 let opts = XmlityRootGroupDeriveOpts::parse(&ast)
57 .expect("Wrong options")
58 .unwrap_or_default();
59
60 ser::derive_group_serialize_fn(ast, opts)
61 .expect("Wrong options")
62 .into()
63}
64
65#[proc_macro_derive(DeserializationGroup, attributes(xelement, xattribute, xgroup))]
67pub fn derive_deserialization_group_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
68 let ast = parse_macro_input!(item as DeriveInput);
69 let opts = XmlityRootGroupDeriveOpts::parse(&ast)
70 .expect("Wrong options")
71 .unwrap_or_default();
72
73 de::derive_deserialization_group_fn(ast, opts)
74 .expect("Wrong options")
75 .into()
76}
77
78fn simple_compile_error(text: &str) -> proc_macro2::TokenStream {
79 quote! {
80 compile_error!(#text);
81 }
82}
83
84#[derive(Clone)]
85enum XmlityFieldDeriveOpts {
86 Element(XmlityFieldElementDeriveOpts),
87 Attribute(XmlityFieldAttributeDeriveOpts),
88 Group(XmlityFieldGroupDeriveOpts),
89}
90
91#[derive(Clone)]
92enum XmlityFieldAttributeGroupDeriveOpts {
93 Attribute(XmlityFieldAttributeDeriveOpts),
94 Group(XmlityFieldGroupDeriveOpts),
95}
96
97#[derive(Clone)]
98enum XmlityFieldElementGroupDeriveOpts {
99 Element(XmlityFieldElementDeriveOpts),
100 Group(XmlityFieldGroupDeriveOpts),
101}
102
103impl XmlityFieldDeriveOpts {
104 fn from_field(field: &syn::Field) -> Result<Self, darling::Error> {
105 let element = XmlityFieldElementDeriveOpts::from_field(field)?;
106 let attribute = XmlityFieldAttributeDeriveOpts::from_field(field)?;
107 let group = XmlityFieldGroupDeriveOpts::from_field(field)?;
108 Ok(match (element, attribute, group) {
109 (Some(element), None, None) => Self::Element(element),
110 (None, Some(attribute), None) => Self::Attribute(attribute),
111 (None, None, Some(group)) => Self::Group(group),
112 (None, None, None) => Self::Element(XmlityFieldElementDeriveOpts::default()),
113 _ => {
114 return Err(darling::Error::custom(
115 "Cannot have multiple xmlity field attributes on the same field",
116 ))
117 }
118 })
119 }
120}
121
122#[derive(Clone)]
123enum FieldIdent {
124 Named(syn::Ident),
125 Indexed(syn::Index),
126}
127
128impl quote::ToTokens for FieldIdent {
129 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
130 match self {
131 FieldIdent::Named(ident) => ident.to_tokens(tokens),
132 FieldIdent::Indexed(index) => index.to_tokens(tokens),
133 }
134 }
135}
136
137#[derive(Clone)]
138struct DeserializeBuilderField<BuilderFieldIdent, OptionType> {
139 builder_field_ident: BuilderFieldIdent,
140 field_ident: FieldIdent,
142 field_type: syn::Type,
143 options: OptionType,
144}
145
146impl<A, T> DeserializeBuilderField<A, T> {
147 pub fn map_options<U, F: FnOnce(T) -> U>(self, f: F) -> DeserializeBuilderField<A, U> {
148 DeserializeBuilderField {
149 builder_field_ident: self.builder_field_ident,
150 field_ident: self.field_ident,
151 field_type: self.field_type,
152 options: f(self.options),
153 }
154 }
155
156 pub fn map_options_opt<U, F: FnOnce(T) -> Option<U>>(
157 self,
158 f: F,
159 ) -> Option<DeserializeBuilderField<A, U>> {
160 f(self.options).map(|options| DeserializeBuilderField {
161 builder_field_ident: self.builder_field_ident,
162 field_ident: self.field_ident,
163 field_type: self.field_type,
164 options,
165 })
166 }
167}
168
169#[derive(Clone)]
170struct SerializeField<OptionType> {
171 field_ident: FieldIdent,
173 field_type: syn::Type,
174 options: OptionType,
175}
176
177#[allow(dead_code)]
178impl<T> SerializeField<T> {
179 pub fn map_options<U, F: FnOnce(T) -> U>(self, f: F) -> SerializeField<U> {
180 SerializeField {
181 field_ident: self.field_ident,
182 field_type: self.field_type,
183 options: f(self.options),
184 }
185 }
186
187 pub fn map_options_opt<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<SerializeField<U>> {
188 f(self.options).map(|options| SerializeField {
189 field_ident: self.field_ident,
190 field_type: self.field_type,
191 options,
192 })
193 }
194}
195
196struct ExpandedName<'a> {
197 name: &'a str,
198 namespace: Option<&'a str>,
199}
200
201impl<'a> ExpandedName<'a> {
202 fn new(name: &'a str, namespace: Option<&'a str>) -> Self {
203 Self { name, namespace }
204 }
205
206 fn to_expression(Self { name, namespace }: &Self) -> proc_macro2::TokenStream {
207 let xml_namespace = match namespace {
208 Some(xml_namespace) => {
209 quote! { ::core::option::Option::Some(<::xmlity::XmlNamespace as ::core::str::FromStr>::from_str(#xml_namespace).expect("XML namespace in derive macro is invalid. This is a bug in xmlity. Please report it.")) }
210 }
211 None => quote! { ::core::option::Option::None },
212 };
213
214 quote! {
215 ::xmlity::ExpandedName::new(<::xmlity::LocalName as ::core::str::FromStr>::from_str(#name).expect("XML name in derive macro is invalid. This is a bug in xmlity. Please report it."), #xml_namespace)
216 }
217 }
218}
219
220impl quote::ToTokens for ExpandedName<'_> {
221 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
222 tokens.extend(Self::to_expression(self))
223 }
224}