Skip to main content

teaql_core/
serde_utils.rs

1use serde::{Deserialize, Deserializer, Serializer};
2
3pub mod trimmed_string {
4    use super::*;
5
6    pub fn serialize<S>(value: &str, serializer: S) -> Result<S::Ok, S::Error>
7    where
8        S: Serializer,
9    {
10        serializer.serialize_str(value.trim())
11    }
12
13    pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
14    where
15        D: Deserializer<'de>,
16    {
17        let s = String::deserialize(deserializer)?;
18        Ok(s.trim().to_owned())
19    }
20}
21
22pub mod trimmed_opt_string {
23    use super::*;
24
25    pub fn serialize<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
26    where
27        S: Serializer,
28    {
29        match value {
30            Some(v) => serializer.serialize_str(v.trim()),
31            None => serializer.serialize_none(),
32        }
33    }
34
35    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
36    where
37        D: Deserializer<'de>,
38    {
39        let opt = Option::<String>::deserialize(deserializer)?;
40        Ok(opt.map(|s| s.trim().to_owned()))
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use serde::{Deserialize, Serialize};
47
48    use super::{trimmed_opt_string, trimmed_string};
49
50    #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
51    struct TrimmedFields {
52        #[serde(with = "trimmed_string")]
53        required: String,
54        #[serde(default, with = "trimmed_opt_string")]
55        optional: Option<String>,
56    }
57
58    #[test]
59    fn trimmed_string_helpers_trim_during_serialization() {
60        let fields = TrimmedFields {
61            required: "  required value\n".to_owned(),
62            optional: Some("\toptional value  ".to_owned()),
63        };
64
65        assert_eq!(
66            serde_json::to_value(fields).expect("trimmed fields should serialize"),
67            serde_json::json!({
68                "required": "required value",
69                "optional": "optional value"
70            })
71        );
72    }
73
74    #[test]
75    fn trimmed_optional_string_preserves_none_during_serialization() {
76        let fields = TrimmedFields {
77            required: " value ".to_owned(),
78            optional: None,
79        };
80
81        assert_eq!(
82            serde_json::to_value(fields).expect("trimmed fields should serialize"),
83            serde_json::json!({
84                "required": "value",
85                "optional": null
86            })
87        );
88    }
89
90    #[test]
91    fn trimmed_string_helpers_trim_during_deserialization() {
92        let fields: TrimmedFields = serde_json::from_value(serde_json::json!({
93            "required": "  required value\n",
94            "optional": "\toptional value  "
95        }))
96        .expect("trimmed fields should deserialize");
97
98        assert_eq!(
99            fields,
100            TrimmedFields {
101                required: "required value".to_owned(),
102                optional: Some("optional value".to_owned()),
103            }
104        );
105    }
106
107    #[test]
108    fn trimmed_optional_string_keeps_whitespace_only_input_as_some_empty() {
109        let fields: TrimmedFields = serde_json::from_value(serde_json::json!({
110            "required": " value ",
111            "optional": " \t\n "
112        }))
113        .expect("trimmed fields should deserialize");
114
115        assert_eq!(fields.required, "value");
116        assert_eq!(fields.optional, Some(String::new()));
117    }
118}