1use crate::{Utf8Path, Utf8PathBuf};
11use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
12use std::fmt;
13
14struct Utf8PathBufVisitor;
15
16impl<'a> de::Visitor<'a> for Utf8PathBufVisitor {
17 type Value = Utf8PathBuf;
18
19 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
20 formatter.write_str("a UTF-8 path string")
21 }
22
23 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
24 where
25 E: de::Error,
26 {
27 Ok(v.into())
28 }
29
30 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
31 where
32 E: de::Error,
33 {
34 Ok(v.into())
35 }
36
37 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
38 where
39 E: de::Error,
40 {
41 std::str::from_utf8(v)
42 .map(Into::into)
43 .map_err(|_| de::Error::invalid_value(de::Unexpected::Bytes(v), &self))
44 }
45
46 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
47 where
48 E: de::Error,
49 {
50 String::from_utf8(v)
51 .map(Into::into)
52 .map_err(|e| de::Error::invalid_value(de::Unexpected::Bytes(&e.into_bytes()), &self))
53 }
54}
55
56impl<'de> Deserialize<'de> for Utf8PathBuf {
57 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58 where
59 D: Deserializer<'de>,
60 {
61 deserializer.deserialize_string(Utf8PathBufVisitor)
62 }
63}
64
65impl Serialize for Utf8PathBuf {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: Serializer,
69 {
70 self.as_str().serialize(serializer)
71 }
72}
73
74struct Utf8PathVisitor;
75
76impl<'a> de::Visitor<'a> for Utf8PathVisitor {
77 type Value = &'a Utf8Path;
78
79 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
80 formatter.write_str("a borrowed UTF-8 path")
81 }
82
83 fn visit_borrowed_str<E>(self, v: &'a str) -> Result<Self::Value, E>
84 where
85 E: de::Error,
86 {
87 Ok(v.as_ref())
88 }
89
90 fn visit_borrowed_bytes<E>(self, v: &'a [u8]) -> Result<Self::Value, E>
91 where
92 E: de::Error,
93 {
94 std::str::from_utf8(v)
95 .map(AsRef::as_ref)
96 .map_err(|_| de::Error::invalid_value(de::Unexpected::Bytes(v), &self))
97 }
98}
99
100impl<'de: 'a, 'a> Deserialize<'de> for &'a Utf8Path {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: Deserializer<'de>,
104 {
105 deserializer.deserialize_str(Utf8PathVisitor)
106 }
107}
108
109impl Serialize for Utf8Path {
110 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111 where
112 S: Serializer,
113 {
114 self.as_str().serialize(serializer)
115 }
116}
117
118impl<'de> Deserialize<'de> for Box<Utf8Path> {
119 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120 where
121 D: Deserializer<'de>,
122 {
123 Ok(Utf8PathBuf::deserialize(deserializer)?.into())
124 }
125}
126
127#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::Utf8PathBuf;
136 use serde_bytes::ByteBuf;
137 use serde_derive::{Deserialize, Serialize};
138
139 #[test]
140 fn valid_utf8() {
141 let valid_utf8 = &["", "bar", "💩"];
142 for input in valid_utf8 {
143 let encode = Encode {
144 path: ByteBuf::from(*input),
145 };
146 let encoded = bincode::serialize(&encode).expect("encoded correctly");
147
148 assert_valid_utf8::<DecodeOwned>(input, &encoded);
149 assert_valid_utf8::<DecodeBorrowed>(input, &encoded);
150 assert_valid_utf8::<DecodeBoxed>(input, &encoded);
151 }
152 }
153
154 fn assert_valid_utf8<'de, T: TestTrait<'de>>(input: &str, encoded: &'de [u8]) {
155 let output = bincode::deserialize::<T>(encoded).expect("valid UTF-8 should be fine");
156 assert_eq!(
157 output.path(),
158 input,
159 "for input, with {}, paths should match",
160 T::description()
161 );
162 let roundtrip = bincode::serialize(&output).expect("message should roundtrip");
163 assert_eq!(roundtrip, encoded, "encoded path matches");
164 }
165
166 #[test]
167 fn invalid_utf8() {
168 let invalid_utf8: &[(&[u8], _, _)] = &[
169 (b"\xff", 0, 1),
170 (b"foo\xfe", 3, 1),
171 (b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9", 4, 1),
172 ];
173
174 for (input, valid_up_to, error_len) in invalid_utf8 {
175 let encode = Encode {
176 path: ByteBuf::from(*input),
177 };
178 let encoded = bincode::serialize(&encode).expect("encoded correctly");
179
180 assert_invalid_utf8::<DecodeOwned>(input, &encoded, *valid_up_to, *error_len);
181 assert_invalid_utf8::<DecodeBorrowed>(input, &encoded, *valid_up_to, *error_len);
182 assert_invalid_utf8::<DecodeBoxed>(input, &encoded, *valid_up_to, *error_len);
183 }
184 }
185
186 fn assert_invalid_utf8<'de, T: TestTrait<'de>>(
187 input: &[u8],
188 encoded: &'de [u8],
189 valid_up_to: usize,
190 error_len: usize,
191 ) {
192 let error = bincode::deserialize::<T>(encoded).expect_err("invalid UTF-8 should error out");
193 let utf8_error = match *error {
194 bincode::ErrorKind::InvalidUtf8Encoding(utf8_error) => utf8_error,
195 other => panic!(
196 "for input {:?}, with {}, expected ErrorKind::InvalidUtf8Encoding, found: {}",
197 input,
198 T::description(),
199 other
200 ),
201 };
202 assert_eq!(
203 utf8_error.valid_up_to(),
204 valid_up_to,
205 "for input {:?}, with {}, valid_up_to didn't match",
206 input,
207 T::description(),
208 );
209 assert_eq!(
210 utf8_error.error_len(),
211 Some(error_len),
212 "for input {:?}, with {}, error_len didn't match",
213 input,
214 T::description(),
215 );
216 }
217
218 #[derive(Serialize, Debug)]
219 struct Encode {
220 path: ByteBuf,
221 }
222
223 trait TestTrait<'de>: Serialize + Deserialize<'de> + fmt::Debug {
224 fn description() -> &'static str;
225 fn path(&self) -> &Utf8Path;
226 }
227
228 #[derive(Serialize, Deserialize, Debug)]
229 #[allow(unused)]
230 struct DecodeOwned {
231 path: Utf8PathBuf,
232 }
233
234 impl TestTrait<'_> for DecodeOwned {
235 fn description() -> &'static str {
236 "DecodeOwned"
237 }
238
239 fn path(&self) -> &Utf8Path {
240 &self.path
241 }
242 }
243
244 #[derive(Serialize, Deserialize, Debug)]
245 #[allow(unused)]
246 struct DecodeBorrowed<'a> {
247 #[serde(borrow)]
248 path: &'a Utf8Path,
249 }
250
251 impl<'de> TestTrait<'de> for DecodeBorrowed<'de> {
252 fn description() -> &'static str {
253 "DecodeBorrowed"
254 }
255
256 fn path(&self) -> &Utf8Path {
257 self.path
258 }
259 }
260
261 #[derive(Serialize, Deserialize, Debug)]
262 #[allow(unused)]
263 struct DecodeBoxed {
264 path: Box<Utf8Path>,
265 }
266
267 impl TestTrait<'_> for DecodeBoxed {
268 fn description() -> &'static str {
269 "DecodeBoxed"
270 }
271
272 fn path(&self) -> &Utf8Path {
273 &self.path
274 }
275 }
276}