1use std::str::FromStr;
4
5use proc_macro2::{Ident, Span, TokenTree};
6use proc_macro_error::abort;
7use syn::{buffer::Cursor, Attribute, Error};
8
9use crate::ResultExt;
10
11#[inline]
12fn parse_next_lit_str(next: Cursor) -> Option<(String, Span)> {
13 match next.token_tree() {
14 Some((tt, next)) => match tt {
15 TokenTree::Punct(punct) if punct.as_char() == '=' => parse_next_lit_str(next),
16 TokenTree::Literal(literal) => {
17 Some((literal.to_string().replace('\"', ""), literal.span()))
18 }
19 _ => None,
20 },
21 _ => None,
22 }
23}
24
25#[derive(Default)]
26#[cfg_attr(feature = "debug", derive(Debug))]
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, Debug)]
91pub enum SerdeEnumRepr {
92 ExternallyTagged,
93 InternallyTagged {
94 tag: String,
95 },
96 AdjacentlyTagged {
97 tag: String,
98 content: String,
99 },
100 Untagged,
101 UnfinishedAdjacentlyTagged {
105 content: String,
106 },
107}
108
109impl Default for SerdeEnumRepr {
110 fn default() -> SerdeEnumRepr {
111 SerdeEnumRepr::ExternallyTagged
112 }
113}
114
115#[derive(Default)]
117#[cfg_attr(feature = "debug", derive(Debug))]
118pub struct SerdeContainer {
119 pub rename_all: Option<RenameRule>,
120 pub enum_repr: SerdeEnumRepr,
121 pub default: bool,
122}
123
124impl SerdeContainer {
125 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 abort!(span, "Duplicate serde tag argument")
157 }
158 SerdeEnumRepr::Untagged => abort!(span, "Untagged enum cannot have tag"),
159 };
160 }
161 }
162 "content" => {
163 if let Some((literal, span)) = parse_next_lit_str(next) {
164 self.enum_repr = match &self.enum_repr {
165 SerdeEnumRepr::InternallyTagged { tag } => {
166 SerdeEnumRepr::AdjacentlyTagged {
167 tag: tag.clone(),
168 content: literal,
169 }
170 }
171 SerdeEnumRepr::ExternallyTagged => {
172 SerdeEnumRepr::UnfinishedAdjacentlyTagged { content: literal }
173 }
174 SerdeEnumRepr::AdjacentlyTagged { .. }
175 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
176 abort!(span, "Duplicate serde content argument")
177 }
178 SerdeEnumRepr::Untagged => {
179 abort!(span, "Untagged enum cannot have content")
180 }
181 };
182 }
183 }
184 "untagged" => {
185 self.enum_repr = SerdeEnumRepr::Untagged;
186 }
187 "default" => {
188 self.default = true;
189 }
190 _ => {}
191 }
192 Ok(())
193 }
194
195 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
197 let mut container = Self::default();
198
199 input.step(|cursor| {
200 let mut rest = *cursor;
201 while let Some((tt, next)) = rest.token_tree() {
202 if let TokenTree::Ident(ident) = tt {
203 container.parse_attribute(ident, next)?
204 }
205
206 rest = next;
207 }
208 Ok(((), rest))
209 })?;
210
211 Ok(container)
212 }
213}
214
215pub fn parse_value(attributes: &[Attribute]) -> Option<SerdeValue> {
216 attributes
217 .iter()
218 .filter(|attribute| attribute.path().is_ident("serde"))
219 .map(|serde_attribute| {
220 serde_attribute
221 .parse_args_with(SerdeValue::parse)
222 .unwrap_or_abort()
223 })
224 .fold(Some(SerdeValue::default()), |acc, value| {
225 acc.map(|mut acc| {
226 if value.skip {
227 acc.skip = value.skip;
228 }
229 if value.skip_serializing_if {
230 acc.skip_serializing_if = value.skip_serializing_if;
231 }
232 if value.rename.is_some() {
233 acc.rename = value.rename;
234 }
235 if value.flatten {
236 acc.flatten = value.flatten;
237 }
238 if value.default {
239 acc.default = value.default;
240 }
241 if value.double_option {
242 acc.double_option = value.double_option;
243 }
244
245 acc
246 })
247 })
248}
249
250pub fn parse_container(attributes: &[Attribute]) -> Option<SerdeContainer> {
251 attributes
252 .iter()
253 .filter(|attribute| attribute.path().is_ident("serde"))
254 .map(|serde_attribute| {
255 serde_attribute
256 .parse_args_with(SerdeContainer::parse)
257 .unwrap_or_abort()
258 })
259 .fold(Some(SerdeContainer::default()), |acc, value| {
260 acc.map(|mut acc| {
261 if value.default {
262 acc.default = value.default;
263 }
264 match value.enum_repr {
265 SerdeEnumRepr::ExternallyTagged => {}
266 SerdeEnumRepr::Untagged
267 | SerdeEnumRepr::InternallyTagged { .. }
268 | SerdeEnumRepr::AdjacentlyTagged { .. }
269 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
270 acc.enum_repr = value.enum_repr;
271 }
272 }
273 if value.rename_all.is_some() {
274 acc.rename_all = value.rename_all;
275 }
276
277 acc
278 })
279 })
280}
281
282#[derive(Clone)]
283#[cfg_attr(feature = "debug", derive(Debug))]
284pub enum RenameRule {
285 Lower,
286 Upper,
287 Camel,
288 Snake,
289 ScreamingSnake,
290 Pascal,
291 Kebab,
292 ScreamingKebab,
293}
294
295impl RenameRule {
296 pub fn rename(&self, value: &str) -> String {
297 match self {
298 RenameRule::Lower => value.to_ascii_lowercase(),
299 RenameRule::Upper => value.to_ascii_uppercase(),
300 RenameRule::Camel => {
301 let mut camel_case = String::new();
302
303 let mut upper = false;
304 for letter in value.chars() {
305 if letter == '_' {
306 upper = true;
307 continue;
308 }
309
310 if upper {
311 camel_case.push(letter.to_ascii_uppercase());
312 upper = false;
313 } else {
314 camel_case.push(letter)
315 }
316 }
317
318 camel_case
319 }
320 RenameRule::Snake => value.to_string(),
321 RenameRule::ScreamingSnake => Self::Snake.rename(value).to_ascii_uppercase(),
322 RenameRule::Pascal => {
323 let mut pascal_case = String::from(&value[..1].to_ascii_uppercase());
324 pascal_case.push_str(&Self::Camel.rename(&value[1..]));
325
326 pascal_case
327 }
328 RenameRule::Kebab => Self::Snake.rename(value).replace('_', "-"),
329 RenameRule::ScreamingKebab => Self::Kebab.rename(value).to_ascii_uppercase(),
330 }
331 }
332
333 pub fn rename_variant(&self, variant: &str) -> String {
334 match self {
335 RenameRule::Lower => variant.to_ascii_lowercase(),
336 RenameRule::Upper => variant.to_ascii_uppercase(),
337 RenameRule::Camel => {
338 let mut snake_case = String::from(&variant[..1].to_ascii_lowercase());
339 snake_case.push_str(&variant[1..]);
340
341 snake_case
342 }
343 RenameRule::Snake => {
344 let mut snake_case = String::new();
345
346 for (index, letter) in variant.char_indices() {
347 if index > 0 && letter.is_uppercase() {
348 snake_case.push('_');
349 }
350 snake_case.push(letter);
351 }
352
353 snake_case.to_ascii_lowercase()
354 }
355 RenameRule::ScreamingSnake => Self::Snake.rename_variant(variant).to_ascii_uppercase(),
356 RenameRule::Pascal => variant.to_string(),
357 RenameRule::Kebab => Self::Snake.rename_variant(variant).replace('_', "-"),
358 RenameRule::ScreamingKebab => Self::Kebab.rename_variant(variant).to_ascii_uppercase(),
359 }
360 }
361}
362
363const RENAME_RULE_NAME_MAPPING: [(&str, RenameRule); 8] = [
364 ("lowercase", RenameRule::Lower),
365 ("UPPERCASE", RenameRule::Upper),
366 ("PascalCase", RenameRule::Pascal),
367 ("camelCase", RenameRule::Camel),
368 ("snake_case", RenameRule::Snake),
369 ("SCREAMING_SNAKE_CASE", RenameRule::ScreamingSnake),
370 ("kebab-case", RenameRule::Kebab),
371 ("SCREAMING-KEBAB-CASE", RenameRule::ScreamingKebab),
372];
373
374impl FromStr for RenameRule {
375 type Err = Error;
376
377 fn from_str(s: &str) -> Result<Self, Self::Err> {
378 let expected_one_of = RENAME_RULE_NAME_MAPPING
379 .into_iter()
380 .map(|(name, _)| format!(r#""{name}""#))
381 .collect::<Vec<_>>()
382 .join(", ");
383 RENAME_RULE_NAME_MAPPING
384 .into_iter()
385 .find_map(|(case, rule)| if case == s { Some(rule) } else { None })
386 .ok_or_else(|| {
387 Error::new(
388 Span::call_site(),
389 format!(r#"unexpected rename rule, expected one of: {expected_one_of}"#),
390 )
391 })
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::{RenameRule, RENAME_RULE_NAME_MAPPING};
398
399 macro_rules! test_rename_rule {
400 ( $($case:expr=> $value:literal = $expected:literal)* ) => {
401 #[test]
402 fn rename_all_rename_rules() {
403 $(
404 let value = $case.rename($value);
405 assert_eq!(value, $expected, "expected case: {} => {} != {}", stringify!($case), $value, $expected);
406 )*
407 }
408 };
409 }
410
411 macro_rules! test_rename_variant_rule {
412 ( $($case:expr=> $value:literal = $expected:literal)* ) => {
413 #[test]
414 fn rename_all_rename_variant_rules() {
415 $(
416 let value = $case.rename_variant($value);
417 assert_eq!(value, $expected, "expected case: {} => {} != {}", stringify!($case), $value, $expected);
418 )*
419 }
420 };
421 }
422
423 test_rename_rule! {
424 RenameRule::Lower=> "single" = "single"
425 RenameRule::Upper=> "single" = "SINGLE"
426 RenameRule::Pascal=> "single" = "Single"
427 RenameRule::Camel=> "single" = "single"
428 RenameRule::Snake=> "single" = "single"
429 RenameRule::ScreamingSnake=> "single" = "SINGLE"
430 RenameRule::Kebab=> "single" = "single"
431 RenameRule::ScreamingKebab=> "single" = "SINGLE"
432
433 RenameRule::Lower=> "multi_value" = "multi_value"
434 RenameRule::Upper=> "multi_value" = "MULTI_VALUE"
435 RenameRule::Pascal=> "multi_value" = "MultiValue"
436 RenameRule::Camel=> "multi_value" = "multiValue"
437 RenameRule::Snake=> "multi_value" = "multi_value"
438 RenameRule::ScreamingSnake=> "multi_value" = "MULTI_VALUE"
439 RenameRule::Kebab=> "multi_value" = "multi-value"
440 RenameRule::ScreamingKebab=> "multi_value" = "MULTI-VALUE"
441 }
442
443 test_rename_variant_rule! {
444 RenameRule::Lower=> "Single" = "single"
445 RenameRule::Upper=> "Single" = "SINGLE"
446 RenameRule::Pascal=> "Single" = "Single"
447 RenameRule::Camel=> "Single" = "single"
448 RenameRule::Snake=> "Single" = "single"
449 RenameRule::ScreamingSnake=> "Single" = "SINGLE"
450 RenameRule::Kebab=> "Single" = "single"
451 RenameRule::ScreamingKebab=> "Single" = "SINGLE"
452
453 RenameRule::Lower=> "MultiValue" = "multivalue"
454 RenameRule::Upper=> "MultiValue" = "MULTIVALUE"
455 RenameRule::Pascal=> "MultiValue" = "MultiValue"
456 RenameRule::Camel=> "MultiValue" = "multiValue"
457 RenameRule::Snake=> "MultiValue" = "multi_value"
458 RenameRule::ScreamingSnake=> "MultiValue" = "MULTI_VALUE"
459 RenameRule::Kebab=> "MultiValue" = "multi-value"
460 RenameRule::ScreamingKebab=> "MultiValue" = "MULTI-VALUE"
461 }
462
463 #[test]
464 fn test_serde_rename_rule_from_str() {
465 for (s, _) in RENAME_RULE_NAME_MAPPING {
466 s.parse::<RenameRule>().unwrap();
467 }
468 }
469}