serde_aux/
container_attributes.rs

1use serde::de::{DeserializeOwned, Error};
2use serde::{Deserialize, Deserializer};
3
4/// Deserializes a struct without checking for the fields case sensititivity.
5///
6/// # **Notes**
7///
8/// - The following deserializer is incompatible with serde's one. If you wish
9///   to use `serde(rename)`, there is a high risk it won't work. Please see
10///   <https://github.com/vityafx/serde-aux/issues/8> for further information.
11///
12/// # Example:
13///
14/// ```rust
15/// use serde_aux::prelude::*;
16///
17/// #[derive(serde::Deserialize, Debug)]
18/// struct AnotherStruct {
19///     aaa: String,
20/// }
21/// #[derive(serde::Deserialize, Debug)]
22/// struct MyStruct {
23///     #[serde(deserialize_with = "deserialize_struct_case_insensitive")]
24///     another_struct: AnotherStruct,
25/// }
26///
27/// let s = r#"{ "another_struct": { "AaA": "Test example" } }"#;
28/// let a: MyStruct = serde_json::from_str(s).unwrap();
29/// assert_eq!(a.another_struct.aaa, "Test example");
30/// ```
31pub fn deserialize_struct_case_insensitive<'de, T, D>(deserializer: D) -> Result<T, D::Error>
32where
33    T: DeserializeOwned,
34    D: Deserializer<'de>,
35{
36    use serde_json::Value;
37
38    use std::collections::BTreeMap as Map;
39
40    let map = Map::<String, Value>::deserialize(deserializer)?;
41    let lower = map
42        .into_iter()
43        .map(|(k, v)| (k.to_lowercase(), v))
44        .collect();
45    T::deserialize(Value::Object(lower)).map_err(Error::custom)
46}
47
48/// This contains both serialization and ser/deserialization of a enum into and from numbers.
49/// The [reference implementation](https://serde.rs/enum-number.html) does not work if your
50/// enum has negative values. This `enum_number` handles this also.
51///
52/// # Example
53///
54/// ```rust
55/// serde_aux::enum_number_declare!(pub TestEnum {
56///     Up = 1,
57///     None = 0,
58///     Down = -1,
59/// });
60///
61/// let s = r#"1"#;
62/// let a: TestEnum = serde_json::from_str(s).unwrap();
63/// assert_eq!(a, TestEnum::Up);
64///
65/// let s = r#"0"#;
66/// let a: TestEnum = serde_json::from_str(s).unwrap();
67/// assert_eq!(a, TestEnum::None);
68///
69/// let s = r#"-1"#;
70/// let a: TestEnum = serde_json::from_str(s).unwrap();
71/// assert_eq!(a, TestEnum::Down);
72///
73/// let s = r#"5"#;
74/// assert!(serde_json::from_str::<TestEnum>(s).is_err());
75/// ```
76#[macro_export]
77macro_rules! enum_number_declare {
78    ($visibility:vis $name:ident { $($variant:ident = $value:expr, )* }) => {
79        #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
80        $visibility enum $name {
81            $($variant = $value,)*
82        }
83
84        impl<'de> serde::Deserialize<'de> for $name {
85            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86                where D: serde::Deserializer<'de>
87            {
88                use std::fmt;
89                struct Visitor;
90
91                impl<'de> serde::de::Visitor<'de> for Visitor {
92                    type Value = $name;
93
94                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95                        formatter.write_str("integer")
96                    }
97
98                    fn visit_i64<E>(self, value: i64) -> Result<$name, E>
99                        where E: serde::de::Error
100                    {
101                        // Rust does not come with a simple way of converting a
102                        // number to an enum, so use a big `match`.
103                        match value {
104                            $( $value => Ok($name::$variant), )*
105                            _ => Err(E::custom(
106                                format!("unknown {} value: {}",
107                                stringify!($name), value))),
108                        }
109                    }
110
111                    fn visit_u64<E>(self, value: u64) -> Result<$name, E>
112                        where E: serde::de::Error
113                    {
114                        self.visit_i64(value as i64)
115                    }
116                }
117
118                // Deserialize the enum from a i64.
119                deserializer.deserialize_i64(Visitor)
120            }
121        }
122    }
123}