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