1 // Copyright (c) The camino Contributors
2 // SPDX-License-Identifier: MIT OR Apache-2.0
3 
4 //! Serde implementations for `Utf8Path`.
5 //!
6 //! The Serde implementations for `Utf8PathBuf` are derived, but `Utf8Path` is an unsized type which
7 //! the derive impls can't handle. Implement these by hand.
8 
9 use crate::{Utf8Path, Utf8PathBuf};
10 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
11 use std::fmt;
12 
13 struct Utf8PathVisitor;
14 
15 impl<'a> de::Visitor<'a> for Utf8PathVisitor {
16     type Value = &'a Utf8Path;
17 
expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result18     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
19         formatter.write_str("a borrowed path")
20     }
21 
visit_borrowed_str<E>(self, v: &'a str) -> Result<Self::Value, E> where E: de::Error,22     fn visit_borrowed_str<E>(self, v: &'a str) -> Result<Self::Value, E>
23     where
24         E: de::Error,
25     {
26         Ok(v.as_ref())
27     }
28 
visit_borrowed_bytes<E>(self, v: &'a [u8]) -> Result<Self::Value, E> where E: de::Error,29     fn visit_borrowed_bytes<E>(self, v: &'a [u8]) -> Result<Self::Value, E>
30     where
31         E: de::Error,
32     {
33         std::str::from_utf8(v)
34             .map(AsRef::as_ref)
35             .map_err(|_| de::Error::invalid_value(de::Unexpected::Bytes(v), &self))
36     }
37 }
38 
39 impl<'de: 'a, 'a> Deserialize<'de> for &'a Utf8Path {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,40     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41     where
42         D: Deserializer<'de>,
43     {
44         deserializer.deserialize_str(Utf8PathVisitor)
45     }
46 }
47 
48 impl Serialize for Utf8Path {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,49     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50     where
51         S: Serializer,
52     {
53         self.as_str().serialize(serializer)
54     }
55 }
56 
57 impl<'de> Deserialize<'de> for Box<Utf8Path> {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,58     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59     where
60         D: Deserializer<'de>,
61     {
62         Ok(Utf8PathBuf::deserialize(deserializer)?.into())
63     }
64 }
65 
66 // impl Serialize for Box<Utf8Path> comes from impl Serialize for Utf8Path.
67 
68 // Can't provide impls for Arc/Rc due to orphan rule issues, but we could provide
69 // `with` impls in the future as requested.
70 
71 #[cfg(test)]
72 mod tests {
73     use super::*;
74     use crate::Utf8PathBuf;
75     use serde_bytes::ByteBuf;
76 
77     #[test]
valid_utf8()78     fn valid_utf8() {
79         let valid_utf8 = &["", "bar", "��"];
80         for input in valid_utf8 {
81             let encode = Encode {
82                 path: ByteBuf::from(*input),
83             };
84             let encoded = bincode::serialize(&encode).expect("encoded correctly");
85 
86             assert_valid_utf8::<DecodeOwned>(input, &encoded);
87             assert_valid_utf8::<DecodeBorrowed>(input, &encoded);
88             assert_valid_utf8::<DecodeBoxed>(input, &encoded);
89         }
90     }
91 
assert_valid_utf8<'de, T: TestTrait<'de>>(input: &str, encoded: &'de [u8])92     fn assert_valid_utf8<'de, T: TestTrait<'de>>(input: &str, encoded: &'de [u8]) {
93         let output = bincode::deserialize::<T>(encoded).expect("valid UTF-8 should be fine");
94         assert_eq!(
95             output.path(),
96             input,
97             "for input, with {}, paths should match",
98             T::description()
99         );
100         let roundtrip = bincode::serialize(&output).expect("message should roundtrip");
101         assert_eq!(roundtrip, encoded, "encoded path matches");
102     }
103 
104     #[test]
invalid_utf8()105     fn invalid_utf8() {
106         let invalid_utf8: &[(&[u8], _, _)] = &[
107             (b"\xff", 0, 1),
108             (b"foo\xfe", 3, 1),
109             (b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9", 4, 1),
110         ];
111 
112         for (input, valid_up_to, error_len) in invalid_utf8 {
113             let encode = Encode {
114                 path: ByteBuf::from(*input),
115             };
116             let encoded = bincode::serialize(&encode).expect("encoded correctly");
117 
118             assert_invalid_utf8::<DecodeOwned>(input, &encoded, *valid_up_to, *error_len);
119             assert_invalid_utf8::<DecodeBorrowed>(input, &encoded, *valid_up_to, *error_len);
120             assert_invalid_utf8::<DecodeBoxed>(input, &encoded, *valid_up_to, *error_len);
121         }
122     }
123 
assert_invalid_utf8<'de, T: TestTrait<'de>>( input: &[u8], encoded: &'de [u8], valid_up_to: usize, error_len: usize, )124     fn assert_invalid_utf8<'de, T: TestTrait<'de>>(
125         input: &[u8],
126         encoded: &'de [u8],
127         valid_up_to: usize,
128         error_len: usize,
129     ) {
130         let error = bincode::deserialize::<T>(encoded).expect_err("invalid UTF-8 should error out");
131         let utf8_error = match *error {
132             bincode::ErrorKind::InvalidUtf8Encoding(utf8_error) => utf8_error,
133             other => panic!(
134                 "for input {:?}, with {}, expected ErrorKind::InvalidUtf8Encoding, found: {}",
135                 input,
136                 T::description(),
137                 other
138             ),
139         };
140         assert_eq!(
141             utf8_error.valid_up_to(),
142             valid_up_to,
143             "for input {:?}, with {}, valid_up_to didn't match",
144             input,
145             T::description(),
146         );
147         assert_eq!(
148             utf8_error.error_len(),
149             Some(error_len),
150             "for input {:?}, with {}, error_len didn't match",
151             input,
152             T::description(),
153         );
154     }
155 
156     #[derive(Serialize, Debug)]
157     struct Encode {
158         path: ByteBuf,
159     }
160 
161     trait TestTrait<'de>: Serialize + Deserialize<'de> + fmt::Debug {
description() -> &'static str162         fn description() -> &'static str;
path(&self) -> &Utf8Path163         fn path(&self) -> &Utf8Path;
164     }
165 
166     #[derive(Serialize, Deserialize, Debug)]
167     #[allow(unused)]
168     struct DecodeOwned {
169         path: Utf8PathBuf,
170     }
171 
172     impl<'de> TestTrait<'de> for DecodeOwned {
description() -> &'static str173         fn description() -> &'static str {
174             "DecodeOwned"
175         }
176 
path(&self) -> &Utf8Path177         fn path(&self) -> &Utf8Path {
178             &self.path
179         }
180     }
181 
182     #[derive(Serialize, Deserialize, Debug)]
183     #[allow(unused)]
184     struct DecodeBorrowed<'a> {
185         #[serde(borrow)]
186         path: &'a Utf8Path,
187     }
188 
189     impl<'de> TestTrait<'de> for DecodeBorrowed<'de> {
description() -> &'static str190         fn description() -> &'static str {
191             "DecodeBorrowed"
192         }
193 
path(&self) -> &Utf8Path194         fn path(&self) -> &Utf8Path {
195             self.path
196         }
197     }
198 
199     #[derive(Serialize, Deserialize, Debug)]
200     #[allow(unused)]
201     struct DecodeBoxed {
202         path: Box<Utf8Path>,
203     }
204 
205     impl<'de> TestTrait<'de> for DecodeBoxed {
description() -> &'static str206         fn description() -> &'static str {
207             "DecodeBoxed"
208         }
209 
path(&self) -> &Utf8Path210         fn path(&self) -> &Utf8Path {
211             &self.path
212         }
213     }
214 }
215