1use std::str::FromStr;
4
5use proc_macro2::{Ident, Span, TokenTree};
6use syn::{buffer::Cursor, Attribute, Error};
7
8use crate::Diagnostics;
9
10#[inline]
11fn parse_next_lit_str(next: Cursor) -> Option<(String, Span)> {
12 match next.token_tree() {
13 Some((tt, next)) => match tt {
14 TokenTree::Punct(punct) if punct.as_char() == '=' => parse_next_lit_str(next),
15 TokenTree::Literal(literal) => {
16 Some((literal.to_string().replace('\"', ""), literal.span()))
17 }
18 _ => None,
19 },
20 _ => None,
21 }
22}
23
24#[derive(Default)]
25#[cfg_attr(feature = "debug", derive(Debug))]
26#[cfg_attr(test, derive(PartialEq, Eq))]
27pub struct SerdeValue {
28 pub skip: bool,
29 pub rename: Option<String>,
30 pub default: bool,
31 pub flatten: bool,
32 pub skip_serializing_if: bool,
33 pub double_option: bool,
34}
35
36impl SerdeValue {
37 const SERDE_WITH_DOUBLE_OPTION: &'static str = "::serde_with::rust::double_option";
38}
39
40impl SerdeValue {
41 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
42 let mut value = Self::default();
43
44 input.step(|cursor| {
45 let mut rest = *cursor;
46 while let Some((tt, next)) = rest.token_tree() {
47 match tt {
48 TokenTree::Ident(ident)
49 if ident == "skip"
50 || ident == "skip_serializing"
51 || ident == "skip_deserializing" =>
52 {
53 value.skip = true
54 }
55 TokenTree::Ident(ident) if ident == "skip_serializing_if" => {
56 value.skip_serializing_if = true
57 }
58 TokenTree::Ident(ident) if ident == "with" => {
59 value.double_option = parse_next_lit_str(next)
60 .and_then(|(literal, _)| {
61 if literal == SerdeValue::SERDE_WITH_DOUBLE_OPTION {
62 Some(true)
63 } else {
64 None
65 }
66 })
67 .unwrap_or(false);
68 }
69 TokenTree::Ident(ident) if ident == "flatten" => value.flatten = true,
70 TokenTree::Ident(ident) if ident == "rename" => {
71 if let Some((literal, _)) = parse_next_lit_str(next) {
72 value.rename = Some(literal)
73 };
74 }
75 TokenTree::Ident(ident) if ident == "default" => value.default = true,
76 _ => (),
77 }
78
79 rest = next;
80 }
81 Ok(((), rest))
82 })?;
83
84 Ok(value)
85 }
86}
87
88#[derive(Clone, Default)]
91#[cfg_attr(feature = "debug", derive(Debug))]
92#[cfg_attr(test, derive(PartialEq, Eq))]
93pub enum SerdeEnumRepr {
94 #[default]
95 ExternallyTagged,
96 InternallyTagged {
97 tag: String,
98 },
99 AdjacentlyTagged {
100 tag: String,
101 content: String,
102 },
103 Untagged,
104 UnfinishedAdjacentlyTagged {
108 content: String,
109 },
110}
111
112#[derive(Default)]
114#[cfg_attr(feature = "debug", derive(Debug))]
115#[cfg_attr(test, derive(PartialEq, Eq))]
116pub struct SerdeContainer {
117 pub rename_all: Option<RenameRule>,
118 pub enum_repr: SerdeEnumRepr,
119 pub default: bool,
120 pub deny_unknown_fields: bool,
121}
122
123impl SerdeContainer {
124 fn parse_attribute(&mut self, ident: Ident, next: Cursor) -> syn::Result<()> {
132 match ident.to_string().as_str() {
133 "rename_all" => {
134 if let Some((literal, span)) = parse_next_lit_str(next) {
135 self.rename_all = Some(
136 literal
137 .parse::<RenameRule>()
138 .map_err(|error| Error::new(span, error.to_string()))?,
139 );
140 }
141 }
142 "tag" => {
143 if let Some((literal, span)) = parse_next_lit_str(next) {
144 self.enum_repr = match &self.enum_repr {
145 SerdeEnumRepr::ExternallyTagged => {
146 SerdeEnumRepr::InternallyTagged { tag: literal }
147 }
148 SerdeEnumRepr::UnfinishedAdjacentlyTagged { content } => {
149 SerdeEnumRepr::AdjacentlyTagged {
150 tag: literal,
151 content: content.clone(),
152 }
153 }
154 SerdeEnumRepr::InternallyTagged { .. }
155 | SerdeEnumRepr::AdjacentlyTagged { .. } => {
156 return Err(syn::Error::new(span, "Duplicate serde tag argument"));
157 }
158 SerdeEnumRepr::Untagged => {
159 return Err(syn::Error::new(span, "Untagged enum cannot have tag"))
160 }
161 };
162 }
163 }
164 "content" => {
165 if let Some((literal, span)) = parse_next_lit_str(next) {
166 self.enum_repr = match &self.enum_repr {
167 SerdeEnumRepr::InternallyTagged { tag } => {
168 SerdeEnumRepr::AdjacentlyTagged {
169 tag: tag.clone(),
170 content: literal,
171 }
172 }
173 SerdeEnumRepr::ExternallyTagged => {
174 SerdeEnumRepr::UnfinishedAdjacentlyTagged { content: literal }
175 }
176 SerdeEnumRepr::AdjacentlyTagged { .. }
177 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
178 return Err(syn::Error::new(span, "Duplicate serde content argument"))
179 }
180 SerdeEnumRepr::Untagged => {
181 return Err(syn::Error::new(span, "Untagged enum cannot have content"))
182 }
183 };
184 }
185 }
186 "untagged" => {
187 self.enum_repr = SerdeEnumRepr::Untagged;
188 }
189 "default" => {
190 self.default = true;
191 }
192 "deny_unknown_fields" => {
193 self.deny_unknown_fields = true;
194 }
195 _ => {}
196 }
197 Ok(())
198 }
199
200 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
202 let mut container = Self::default();
203
204 input.step(|cursor| {
205 let mut rest = *cursor;
206 while let Some((tt, next)) = rest.token_tree() {
207 if let TokenTree::Ident(ident) = tt {
208 container.parse_attribute(ident, next)?
209 }
210
211 rest = next;
212 }
213 Ok(((), rest))
214 })?;
215
216 Ok(container)
217 }
218}
219
220pub fn parse_value(attributes: &[Attribute]) -> Result<SerdeValue, Diagnostics> {
221 Ok(attributes
222 .iter()
223 .filter(|attribute| attribute.path().is_ident("serde"))
224 .map(|serde_attribute| {
225 serde_attribute
226 .parse_args_with(SerdeValue::parse)
227 .map_err(Diagnostics::from)
228 })
229 .collect::<Result<Vec<_>, Diagnostics>>()?
230 .into_iter()
231 .fold(SerdeValue::default(), |mut acc, value| {
232 if value.skip {
233 acc.skip = value.skip;
234 }
235 if value.skip_serializing_if {
236 acc.skip_serializing_if = value.skip_serializing_if;
237 }
238 if value.rename.is_some() {
239 acc.rename = value.rename;
240 }
241 if value.flatten {
242 acc.flatten = value.flatten;
243 }
244 if value.default {
245 acc.default = value.default;
246 }
247 if value.double_option {
248 acc.double_option = value.double_option;
249 }
250
251 acc
252 }))
253}
254
255pub fn parse_container(attributes: &[Attribute]) -> Result<SerdeContainer, Diagnostics> {
256 Ok(attributes
257 .iter()
258 .filter(|attribute| attribute.path().is_ident("serde"))
259 .map(|serde_attribute| {
260 serde_attribute
261 .parse_args_with(SerdeContainer::parse)
262 .map_err(Diagnostics::from)
263 })
264 .collect::<Result<Vec<_>, Diagnostics>>()?
265 .into_iter()
266 .fold(SerdeContainer::default(), |mut acc, value| {
267 if value.default {
268 acc.default = value.default;
269 }
270 if value.deny_unknown_fields {
271 acc.deny_unknown_fields = value.deny_unknown_fields;
272 }
273 match value.enum_repr {
274 SerdeEnumRepr::ExternallyTagged => {}
275 SerdeEnumRepr::Untagged
276 | SerdeEnumRepr::InternallyTagged { .. }
277 | SerdeEnumRepr::AdjacentlyTagged { .. }
278 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
279 acc.enum_repr = value.enum_repr;
280 }
281 }
282 if value.rename_all.is_some() {
283 acc.rename_all = value.rename_all;
284 }
285
286 acc
287 }))
288}
289
290#[derive(Clone)]
291#[cfg_attr(feature = "debug", derive(Debug))]
292#[cfg_attr(test, derive(PartialEq, Eq))]
293pub enum RenameRule {
294 Lower,
295 Upper,
296 Camel,
297 Snake,
298 ScreamingSnake,
299 Pascal,
300 Kebab,
301 ScreamingKebab,
302}
303
304impl RenameRule {
305 pub fn rename(&self, value: &str) -> String {
306 match self {
307 RenameRule::Lower => value.to_ascii_lowercase(),
308 RenameRule::Upper => value.to_ascii_uppercase(),
309 RenameRule::Camel => {
310 let mut camel_case = String::new();
311
312 let mut upper = false;
313 for letter in value.chars() {
314 if letter == '_' {
315 upper = true;
316 continue;
317 }
318
319 if upper {
320 camel_case.push(letter.to_ascii_uppercase());
321 upper = false;
322 } else {
323 camel_case.push(letter)
324 }
325 }
326
327 camel_case
328 }
329 RenameRule::Snake => value.to_string(),
330 RenameRule::ScreamingSnake => Self::Snake.rename(value).to_ascii_uppercase(),
331 RenameRule::Pascal => {
332 let mut pascal_case = String::from(&value[..1].to_ascii_uppercase());
333 pascal_case.push_str(&Self::Camel.rename(&value[1..]));
334
335 pascal_case
336 }
337 RenameRule::Kebab => Self::Snake.rename(value).replace('_', "-"),
338 RenameRule::ScreamingKebab => Self::Kebab.rename(value).to_ascii_uppercase(),
339 }
340 }
341
342 pub fn rename_variant(&self, variant: &str) -> String {
343 match self {
344 RenameRule::Lower => variant.to_ascii_lowercase(),
345 RenameRule::Upper => variant.to_ascii_uppercase(),
346 RenameRule::Camel => {
347 let mut snake_case = String::from(&variant[..1].to_ascii_lowercase());
348 snake_case.push_str(&variant[1..]);
349
350 snake_case
351 }
352 RenameRule::Snake => {
353 let mut snake_case = String::new();
354
355 for (index, letter) in variant.char_indices() {
356 if index > 0 && letter.is_uppercase() {
357 snake_case.push('_');
358 }
359 snake_case.push(letter);
360 }
361
362 snake_case.to_ascii_lowercase()
363 }
364 RenameRule::ScreamingSnake => Self::Snake.rename_variant(variant).to_ascii_uppercase(),
365 RenameRule::Pascal => variant.to_string(),
366 RenameRule::Kebab => Self::Snake.rename_variant(variant).replace('_', "-"),
367 RenameRule::ScreamingKebab => Self::Kebab.rename_variant(variant).to_ascii_uppercase(),
368 }
369 }
370}
371
372const RENAME_RULE_NAME_MAPPING: [(&str, RenameRule); 8] = [
373 ("lowercase", RenameRule::Lower),
374 ("UPPERCASE", RenameRule::Upper),
375 ("PascalCase", RenameRule::Pascal),
376 ("camelCase", RenameRule::Camel),
377 ("snake_case", RenameRule::Snake),
378 ("SCREAMING_SNAKE_CASE", RenameRule::ScreamingSnake),
379 ("kebab-case", RenameRule::Kebab),
380 ("SCREAMING-KEBAB-CASE", RenameRule::ScreamingKebab),
381];
382
383impl FromStr for RenameRule {
384 type Err = Error;
385
386 fn from_str(s: &str) -> Result<Self, Self::Err> {
387 let expected_one_of = RENAME_RULE_NAME_MAPPING
388 .into_iter()
389 .map(|(name, _)| format!(r#""{name}""#))
390 .collect::<Vec<_>>()
391 .join(", ");
392 RENAME_RULE_NAME_MAPPING
393 .into_iter()
394 .find_map(|(case, rule)| if case == s { Some(rule) } else { None })
395 .ok_or_else(|| {
396 Error::new(
397 Span::call_site(),
398 format!(r#"unexpected rename rule, expected one of: {expected_one_of}"#),
399 )
400 })
401 }
402}
403
404#[cfg(test)]
405mod tests {
406 use super::{parse_container, RenameRule, SerdeContainer, RENAME_RULE_NAME_MAPPING};
407 use syn::{parse_quote, Attribute};
408
409 macro_rules! test_rename_rule {
410 ( $($case:expr=> $value:literal = $expected:literal)* ) => {
411 #[test]
412 fn rename_all_rename_rules() {
413 $(
414 let value = $case.rename($value);
415 assert_eq!(value, $expected, "expected case: {} => {} != {}", stringify!($case), $value, $expected);
416 )*
417 }
418 };
419 }
420
421 macro_rules! test_rename_variant_rule {
422 ( $($case:expr=> $value:literal = $expected:literal)* ) => {
423 #[test]
424 fn rename_all_rename_variant_rules() {
425 $(
426 let value = $case.rename_variant($value);
427 assert_eq!(value, $expected, "expected case: {} => {} != {}", stringify!($case), $value, $expected);
428 )*
429 }
430 };
431 }
432
433 test_rename_rule! {
434 RenameRule::Lower=> "single" = "single"
435 RenameRule::Upper=> "single" = "SINGLE"
436 RenameRule::Pascal=> "single" = "Single"
437 RenameRule::Camel=> "single" = "single"
438 RenameRule::Snake=> "single" = "single"
439 RenameRule::ScreamingSnake=> "single" = "SINGLE"
440 RenameRule::Kebab=> "single" = "single"
441 RenameRule::ScreamingKebab=> "single" = "SINGLE"
442
443 RenameRule::Lower=> "multi_value" = "multi_value"
444 RenameRule::Upper=> "multi_value" = "MULTI_VALUE"
445 RenameRule::Pascal=> "multi_value" = "MultiValue"
446 RenameRule::Camel=> "multi_value" = "multiValue"
447 RenameRule::Snake=> "multi_value" = "multi_value"
448 RenameRule::ScreamingSnake=> "multi_value" = "MULTI_VALUE"
449 RenameRule::Kebab=> "multi_value" = "multi-value"
450 RenameRule::ScreamingKebab=> "multi_value" = "MULTI-VALUE"
451 }
452
453 test_rename_variant_rule! {
454 RenameRule::Lower=> "Single" = "single"
455 RenameRule::Upper=> "Single" = "SINGLE"
456 RenameRule::Pascal=> "Single" = "Single"
457 RenameRule::Camel=> "Single" = "single"
458 RenameRule::Snake=> "Single" = "single"
459 RenameRule::ScreamingSnake=> "Single" = "SINGLE"
460 RenameRule::Kebab=> "Single" = "single"
461 RenameRule::ScreamingKebab=> "Single" = "SINGLE"
462
463 RenameRule::Lower=> "MultiValue" = "multivalue"
464 RenameRule::Upper=> "MultiValue" = "MULTIVALUE"
465 RenameRule::Pascal=> "MultiValue" = "MultiValue"
466 RenameRule::Camel=> "MultiValue" = "multiValue"
467 RenameRule::Snake=> "MultiValue" = "multi_value"
468 RenameRule::ScreamingSnake=> "MultiValue" = "MULTI_VALUE"
469 RenameRule::Kebab=> "MultiValue" = "multi-value"
470 RenameRule::ScreamingKebab=> "MultiValue" = "MULTI-VALUE"
471 }
472
473 #[test]
474 fn test_serde_rename_rule_from_str() {
475 for (s, _) in RENAME_RULE_NAME_MAPPING {
476 s.parse::<RenameRule>().unwrap();
477 }
478 }
479
480 #[test]
481 fn test_serde_parse_container() {
482 let default_attribute_1: syn::Attribute = parse_quote! {
483 #[serde(default)]
484 };
485 let default_attribute_2: syn::Attribute = parse_quote! {
486 #[serde(default)]
487 };
488 let deny_unknown_fields_attribute: syn::Attribute = parse_quote! {
489 #[serde(deny_unknown_fields)]
490 };
491 let unsupported_attribute: syn::Attribute = parse_quote! {
492 #[serde(expecting = "...")]
493 };
494 let attributes: &[Attribute] = &[
495 default_attribute_1,
496 default_attribute_2,
497 deny_unknown_fields_attribute,
498 unsupported_attribute,
499 ];
500
501 let expected = SerdeContainer {
502 default: true,
503 deny_unknown_fields: true,
504 ..Default::default()
505 };
506
507 let result = parse_container(attributes).expect("parse success");
508 assert_eq!(expected, result);
509 }
510}