compact_str/features/
serde.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3
4use serde::de::{
5    Deserializer,
6    Error,
7    Unexpected,
8    Visitor,
9};
10
11use crate::CompactString;
12
13fn compact_string<'de: 'a, 'a, D: Deserializer<'de>>(
14    deserializer: D,
15) -> Result<CompactString, D::Error> {
16    struct CompactStringVisitor;
17
18    impl<'a> Visitor<'a> for CompactStringVisitor {
19        type Value = CompactString;
20
21        fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
22            formatter.write_str("a string")
23        }
24
25        fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
26            Ok(CompactString::from(v))
27        }
28
29        fn visit_borrowed_str<E: Error>(self, v: &'a str) -> Result<Self::Value, E> {
30            Ok(CompactString::from(v))
31        }
32
33        fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
34            Ok(CompactString::from(v))
35        }
36
37        fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
38            match core::str::from_utf8(v) {
39                Ok(s) => Ok(CompactString::from(s)),
40                Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
41            }
42        }
43
44        fn visit_borrowed_bytes<E: Error>(self, v: &'a [u8]) -> Result<Self::Value, E> {
45            match core::str::from_utf8(v) {
46                Ok(s) => Ok(CompactString::from(s)),
47                Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
48            }
49        }
50
51        fn visit_byte_buf<E: Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
52            match String::from_utf8(v) {
53                Ok(s) => Ok(CompactString::from(s)),
54                Err(e) => Err(Error::invalid_value(
55                    Unexpected::Bytes(&e.into_bytes()),
56                    &self,
57                )),
58            }
59        }
60    }
61
62    deserializer.deserialize_str(CompactStringVisitor)
63}
64
65#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
66impl serde::Serialize for CompactString {
67    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
68        self.as_str().serialize(serializer)
69    }
70}
71
72#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
73impl<'de> serde::Deserialize<'de> for CompactString {
74    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
75        compact_string(deserializer)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use alloc::string::{
82        String,
83        ToString,
84    };
85    use alloc::vec::Vec;
86
87    use serde::{
88        Deserialize,
89        Serialize,
90    };
91    use test_strategy::proptest;
92
93    use crate::CompactString;
94
95    #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
96    struct PersonString {
97        name: String,
98        phones: Vec<String>,
99        address: Option<String>,
100    }
101
102    #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
103    struct PersonCompactString {
104        name: CompactString,
105        phones: Vec<CompactString>,
106        address: Option<CompactString>,
107    }
108
109    #[test]
110    fn test_roundtrip() {
111        let name = "Ferris the Crab";
112        let phones = vec!["1-800-111-1111", "2-222-222-2222"];
113        let address = Some("123 Sesame Street");
114
115        let std = PersonString {
116            name: name.to_string(),
117            phones: phones.iter().map(|s| s.to_string()).collect(),
118            address: address.as_ref().map(|s| s.to_string()),
119        };
120        let compact = PersonCompactString {
121            name: name.into(),
122            phones: phones.iter().map(|s| CompactString::from(*s)).collect(),
123            address: address.as_ref().map(|s| CompactString::from(*s)),
124        };
125
126        let std_json = serde_json::to_string(&std).unwrap();
127        let compact_json = serde_json::to_string(&compact).unwrap();
128
129        // the serialized forms should be the same
130        assert_eq!(std_json, compact_json);
131
132        let std_de_compact: PersonString = serde_json::from_str(&compact_json).unwrap();
133        let compact_de_std: PersonCompactString = serde_json::from_str(&std_json).unwrap();
134
135        // we should be able to deserailze from the opposite, serialized, source
136        assert_eq!(std_de_compact, std);
137        assert_eq!(compact_de_std, compact);
138    }
139
140    #[cfg_attr(miri, ignore)]
141    #[proptest]
142    fn proptest_roundtrip(name: String, phones: Vec<String>, address: Option<String>) {
143        let std = PersonString {
144            name: name.clone(),
145            phones: phones.iter().map(|s| s.clone()).collect(),
146            address: address.clone(),
147        };
148        let compact = PersonCompactString {
149            name: name.into(),
150            phones: phones.iter().map(|s| CompactString::from(s)).collect(),
151            address: address.map(|s| CompactString::from(s)),
152        };
153
154        let std_json = serde_json::to_string(&std).unwrap();
155        let compact_json = serde_json::to_string(&compact).unwrap();
156
157        // the serialized forms should be the same
158        assert_eq!(std_json, compact_json);
159
160        let std_de_compact: PersonString = serde_json::from_str(&compact_json).unwrap();
161        let compact_de_std: PersonCompactString = serde_json::from_str(&std_json).unwrap();
162
163        // we should be able to deserailze from the opposite, serialized, source
164        assert_eq!(std_de_compact, std);
165        assert_eq!(compact_de_std, compact);
166    }
167}