salvo_serde_util/
lib.rs

1//! Provides serde related features parsing serde attributes from types.
2//!
3//! Read more: <https://salvo.rs>
4#![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/// Value type of a `#[serde(...)]` attribute.
30#[derive(Default, Debug)]
31pub struct SerdeValue {
32    /// Skip field.
33    pub skip: bool,
34    /// Rename field.
35    pub rename: Option<String>,
36    /// Aliases of field.
37    pub aliases: Vec<String>,
38    /// Is default value.
39    pub is_default: bool,
40    /// Flatten field.
41    pub flatten: bool,
42    /// Skip serializing if.
43    pub skip_serializing_if: bool,
44    /// Double option.
45    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/// The [Serde Enum representation](https://serde.rs/enum-representations.html) being used
91/// The default case (when no serde attributes are present) is `ExternallyTagged`.
92#[derive(Default, Clone, Debug)]
93#[cfg_attr(test, derive(PartialEq, Eq))]
94pub enum SerdeEnumRepr {
95    /// ExternallyTagged.
96    #[default]
97    ExternallyTagged,
98    /// InternallyTagged.
99    InternallyTagged {
100        /// tag.
101        tag: String,
102    },
103    /// AdjacentlyTagged
104    AdjacentlyTagged {
105        /// tag.
106        tag: String,
107        /// content.
108        content: String,
109    },
110    /// Untagged
111    Untagged,
112    /// This is a variant that can never happen because `serde` will not accept it.
113    /// With the current implementation it is necessary to have it as an intermediate state when
114    /// parsing the attributes
115    UnfinishedAdjacentlyTagged {
116        /// content.
117        content: String,
118    },
119}
120
121/// Attributes defined within a `#[serde(...)]` container attribute.
122#[derive(Default, Debug)]
123#[cfg_attr(test, derive(PartialEq, Eq))]
124pub struct SerdeContainer {
125    /// Rename all fields.
126    pub rename_all: Option<RenameRule>,
127    /// Enum repr.
128    pub enum_repr: SerdeEnumRepr,
129    /// Is default.
130    pub is_default: bool,
131    /// Deny unknown fields.
132    pub deny_unknown_fields: bool,
133}
134
135impl SerdeContainer {
136    /// Parse a single serde attribute, currently supported attributes are:
137    ///     * `rename_all = ...`
138    ///     * `tag = ...`
139    ///     * `content = ...`
140    ///     * `untagged = ...`
141    ///     * `default = ...`
142    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    /// Parse the attributes inside a `#[serde(...)]` container attribute.
212    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/// Parse value.
232#[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/// Parse container.
267#[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}