compact_str/features/
serde.rs1use 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 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 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 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 assert_eq!(std_de_compact, std);
165 assert_eq!(compact_de_std, compact);
166 }
167}