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