1#![doc(html_favicon_url = "https://salvo.rs/favicon-32x32.png")]
5#![doc(html_logo_url = "https://salvo.rs/images/logo.svg")]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7
8use proc_macro2::{Ident, Span, TokenTree};
9use syn::buffer::Cursor;
10use syn::{Attribute, Error};
11
12pub(crate) mod case;
13pub use case::RenameRule;
14
15#[inline]
16fn parse_next_lit_str(next: Cursor) -> Option<(String, Span)> {
17 match next.token_tree() {
18 Some((tt, next)) => match tt {
19 TokenTree::Punct(punct) if punct.as_char() == '=' => parse_next_lit_str(next),
20 TokenTree::Literal(literal) => {
21 Some((literal.to_string().replace('\"', ""), literal.span()))
22 }
23 _ => None,
24 },
25 _ => None,
26 }
27}
28
29#[derive(Default, Debug)]
31pub struct SerdeValue {
32 pub skip: bool,
34 pub rename: Option<String>,
36 pub aliases: Vec<String>,
38 pub is_default: bool,
40 pub flatten: bool,
42 pub skip_serializing_if: bool,
44 pub double_option: bool,
46}
47
48impl SerdeValue {
49 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
50 let mut value = Self::default();
51
52 input.step(|cursor| {
53 let mut rest = *cursor;
54 while let Some((tt, next)) = rest.token_tree() {
55 match tt {
56 TokenTree::Ident(ident)
57 if ident == "skip"
58 || ident == "skip_serializing"
59 || ident == "skip_deserializing" =>
60 {
61 value.skip = true
62 }
63 TokenTree::Ident(ident) if ident == "skip_serializing_if" => {
64 value.skip_serializing_if = true
65 }
66 TokenTree::Ident(ident) if ident == "flatten" => value.flatten = true,
67 TokenTree::Ident(ident) if ident == "rename" => {
68 if let Some((literal, _)) = parse_next_lit_str(next) {
69 value.rename = Some(literal)
70 };
71 }
72 TokenTree::Ident(ident) if ident == "alias" => {
73 if let Some((literal, _)) = parse_next_lit_str(next) {
74 value.aliases.push(literal)
75 };
76 }
77 TokenTree::Ident(ident) if ident == "default" => value.is_default = true,
78 _ => (),
79 }
80
81 rest = next;
82 }
83 Ok(((), rest))
84 })?;
85
86 Ok(value)
87 }
88}
89
90#[derive(Default, Clone, Debug)]
93#[cfg_attr(test, derive(PartialEq, Eq))]
94pub enum SerdeEnumRepr {
95 #[default]
97 ExternallyTagged,
98 InternallyTagged {
100 tag: String,
102 },
103 AdjacentlyTagged {
105 tag: String,
107 content: String,
109 },
110 Untagged,
112 UnfinishedAdjacentlyTagged {
116 content: String,
118 },
119}
120
121#[derive(Default, Debug)]
123#[cfg_attr(test, derive(PartialEq, Eq))]
124pub struct SerdeContainer {
125 pub rename_all: Option<RenameRule>,
127 pub enum_repr: SerdeEnumRepr,
129 pub is_default: bool,
131 pub deny_unknown_fields: bool,
133}
134
135impl SerdeContainer {
136 fn parse_attribute(&mut self, ident: &Ident, next: Cursor) -> syn::Result<()> {
143 match ident.to_string().as_str() {
144 "rename_all" => {
145 if let Some((literal, span)) = parse_next_lit_str(next) {
146 self.rename_all = Some(
147 literal
148 .parse::<RenameRule>()
149 .map_err(|error| Error::new(span, error.to_string()))?,
150 );
151 }
152 }
153 "tag" => {
154 if let Some((literal, span)) = parse_next_lit_str(next) {
155 self.enum_repr = match &self.enum_repr {
156 SerdeEnumRepr::ExternallyTagged => {
157 SerdeEnumRepr::InternallyTagged { tag: literal }
158 }
159 SerdeEnumRepr::UnfinishedAdjacentlyTagged { content } => {
160 SerdeEnumRepr::AdjacentlyTagged {
161 tag: literal,
162 content: content.clone(),
163 }
164 }
165 SerdeEnumRepr::InternallyTagged { .. }
166 | SerdeEnumRepr::AdjacentlyTagged { .. } => {
167 return Err(Error::new(span, "Duplicate serde tag argument"));
168 }
169 SerdeEnumRepr::Untagged => {
170 return Err(Error::new(span, "Untagged enum cannot have tag"));
171 }
172 };
173 }
174 }
175 "content" => {
176 if let Some((literal, span)) = parse_next_lit_str(next) {
177 self.enum_repr = match &self.enum_repr {
178 SerdeEnumRepr::InternallyTagged { tag } => {
179 SerdeEnumRepr::AdjacentlyTagged {
180 tag: tag.clone(),
181 content: literal,
182 }
183 }
184 SerdeEnumRepr::ExternallyTagged => {
185 SerdeEnumRepr::UnfinishedAdjacentlyTagged { content: literal }
186 }
187 SerdeEnumRepr::AdjacentlyTagged { .. }
188 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
189 return Err(Error::new(span, "Duplicate serde content argument"));
190 }
191 SerdeEnumRepr::Untagged => {
192 return Err(Error::new(span, "Untagged enum cannot have content"));
193 }
194 };
195 }
196 }
197 "untagged" => {
198 self.enum_repr = SerdeEnumRepr::Untagged;
199 }
200 "default" => {
201 self.is_default = true;
202 }
203 "deny_unknown_fields" => {
204 self.deny_unknown_fields = true;
205 }
206 _ => {}
207 }
208 Ok(())
209 }
210
211 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
213 let mut container = Self::default();
214
215 input.step(|cursor| {
216 let mut rest = *cursor;
217 while let Some((tt, next)) = rest.token_tree() {
218 if let TokenTree::Ident(ident) = tt {
219 container.parse_attribute(&ident, next)?
220 }
221
222 rest = next;
223 }
224 Ok(((), rest))
225 })?;
226
227 Ok(container)
228 }
229}
230
231#[must_use]
233pub fn parse_value(attributes: &[Attribute]) -> Option<SerdeValue> {
234 attributes
235 .iter()
236 .filter(|attribute| attribute.path().is_ident("serde"))
237 .map(|serde_attribute| serde_attribute.parse_args_with(SerdeValue::parse))
238 .try_fold(SerdeValue::default(), |mut acc, value| {
239 let Ok(value) = value else {
240 return Some(acc);
241 };
242 if value.skip {
243 acc.skip = value.skip;
244 }
245 if value.skip_serializing_if {
246 acc.skip_serializing_if = value.skip_serializing_if;
247 }
248 if value.rename.is_some() {
249 acc.rename = value.rename;
250 }
251 acc.aliases.extend(value.aliases);
252 if value.flatten {
253 acc.flatten = value.flatten;
254 }
255 if value.is_default {
256 acc.is_default = value.is_default;
257 }
258 if value.double_option {
259 acc.double_option = value.double_option;
260 }
261
262 Some(acc)
263 })
264}
265
266#[must_use]
268pub fn parse_container(attributes: &[Attribute]) -> Option<SerdeContainer> {
269 attributes
270 .iter()
271 .filter(|attribute| attribute.path().is_ident("serde"))
272 .map(|serde_attribute| serde_attribute.parse_args_with(SerdeContainer::parse))
273 .try_fold(SerdeContainer::default(), |mut acc, value| {
274 let Ok(value) = value else {
275 return Some(acc);
276 };
277 if value.is_default {
278 acc.is_default = value.is_default;
279 }
280 if value.deny_unknown_fields {
281 acc.deny_unknown_fields = value.deny_unknown_fields;
282 }
283 match value.enum_repr {
284 SerdeEnumRepr::ExternallyTagged => {}
285 SerdeEnumRepr::Untagged
286 | SerdeEnumRepr::InternallyTagged { .. }
287 | SerdeEnumRepr::AdjacentlyTagged { .. }
288 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
289 acc.enum_repr = value.enum_repr;
290 }
291 }
292 if value.rename_all.is_some() {
293 acc.rename_all = value.rename_all;
294 }
295
296 Some(acc)
297 })
298}
299
300#[cfg(test)]
301mod tests {
302 use syn::{Attribute, parse_quote};
303
304 use super::case::RENAME_RULES;
305 use super::{RenameRule, SerdeContainer, parse_container, parse_value};
306
307 #[test]
308 fn test_serde_parse_value() {
309 let skip_attribute: syn::Attribute = parse_quote! {
310 #[serde(skip)]
311 };
312 let rename_attribute: syn::Attribute = parse_quote! {
313 #[serde(rename = "new_name")]
314 };
315 let default_attribute: syn::Attribute = parse_quote! {
316 #[serde(default)]
317 };
318 let flatten_attribute: syn::Attribute = parse_quote! {
319 #[serde(flatten)]
320 };
321 let skip_serializing_if_attribute: syn::Attribute = parse_quote! {
322 #[serde(skip_serializing_if = "Option::is_none")]
323 };
324 let attributes: &[Attribute] = &[
325 skip_attribute,
326 rename_attribute,
327 default_attribute,
328 flatten_attribute,
329 skip_serializing_if_attribute,
330 ];
331
332 let result = parse_value(attributes).unwrap();
333 assert!(result.skip);
334 assert_eq!(result.rename.unwrap(), "new_name");
335 assert!(result.is_default);
336 assert!(result.flatten);
337 assert!(result.skip_serializing_if);
338 }
339
340 #[test]
341 fn test_serde_parse_container() {
342 let default_attribute_1: syn::Attribute = parse_quote! {
343 #[serde(default)]
344 };
345 let default_attribute_2: syn::Attribute = parse_quote! {
346 #[serde(default)]
347 };
348 let deny_unknown_fields_attribute: syn::Attribute = parse_quote! {
349 #[serde(deny_unknown_fields)]
350 };
351 let unsupported_attribute: syn::Attribute = parse_quote! {
352 #[serde(expecting = "...")]
353 };
354 let attributes: &[Attribute] = &[
355 default_attribute_1,
356 default_attribute_2,
357 deny_unknown_fields_attribute,
358 unsupported_attribute,
359 ];
360
361 let expected = SerdeContainer {
362 is_default: true,
363 deny_unknown_fields: true,
364 ..Default::default()
365 };
366
367 let result = parse_container(attributes).unwrap();
368 assert_eq!(expected.is_default, result.is_default);
369 assert_eq!(expected.deny_unknown_fields, result.deny_unknown_fields);
370 }
371
372 #[test]
373 fn test_serde_rename_rule_from_str() {
374 for (s, _) in RENAME_RULES {
375 s.parse::<RenameRule>().unwrap();
376 }
377 }
378
379 #[test]
380 fn test_serde_parse_container_rename_all() {
381 let rename_all_attribute: syn::Attribute = parse_quote! {
382 #[serde(rename_all = "camelCase")]
383 };
384 let attributes: &[Attribute] = &[rename_all_attribute];
385
386 let result = parse_container(attributes).unwrap();
387 assert_eq!(result.rename_all, Some(RenameRule::CamelCase));
388 }
389
390 #[test]
391 fn test_serde_parse_container_untagged() {
392 let untagged_attribute: syn::Attribute = parse_quote! {
393 #[serde(untagged)]
394 };
395 let attributes: &[Attribute] = &[untagged_attribute];
396
397 let result = parse_container(attributes).unwrap();
398 assert_eq!(result.enum_repr, super::SerdeEnumRepr::Untagged);
399 }
400
401 #[test]
402 fn test_serde_parse_container_internally_tagged() {
403 let tag_attribute: syn::Attribute = parse_quote! {
404 #[serde(tag = "t")]
405 };
406 let attributes: &[Attribute] = &[tag_attribute];
407
408 let result = parse_container(attributes).unwrap();
409 assert_eq!(
410 result.enum_repr,
411 super::SerdeEnumRepr::InternallyTagged {
412 tag: "t".to_string()
413 }
414 );
415 }
416
417 #[test]
418 fn test_serde_parse_container_adjacently_tagged() {
419 let tag_attribute: syn::Attribute = parse_quote! {
420 #[serde(tag = "t", content = "c")]
421 };
422 let attributes: &[Attribute] = &[tag_attribute];
423
424 let result = parse_container(attributes).unwrap();
425 assert_eq!(
426 result.enum_repr,
427 super::SerdeEnumRepr::AdjacentlyTagged {
428 tag: "t".to_string(),
429 content: "c".to_string()
430 }
431 );
432 }
433}