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
9use crate::Utf8Path;
10use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
11use std::fmt;
12
13struct Utf8PathVisitor;
14
15impl<'a> de::Visitor<'a> for Utf8PathVisitor {
16 type Value = &'a Utf8Path;
17
18 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
19 formatter.write_str(data:"a borrowed path")
20 }
21
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
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(unexp:de::Unexpected::Bytes(v), &self))
36 }
37}
38
39#[cfg(feature = "serde1")]
40impl<'de: 'a, 'a> Deserialize<'de> for &'a Utf8Path {
41 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42 where
43 D: Deserializer<'de>,
44 {
45 deserializer.deserialize_str(visitor:Utf8PathVisitor)
46 }
47}
48
49#[cfg(feature = "serde1")]
50impl Serialize for Utf8Path {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: Serializer,
54 {
55 self.as_str().serialize(serializer)
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::Utf8PathBuf;
63 use serde_bytes::ByteBuf;
64
65 #[test]
66 fn valid_utf8() {
67 let valid_utf8 = &["", "bar", "💩"];
68 for input in valid_utf8 {
69 let encode = Encode {
70 path: ByteBuf::from(*input),
71 };
72 let encoded = bincode::serialize(&encode).expect("encoded correctly");
73
74 assert_valid_utf8::<DecodeOwned>(input, &encoded);
75 assert_valid_utf8::<DecodeBorrowed>(input, &encoded);
76 }
77 }
78
79 fn assert_valid_utf8<'de, T: TestTrait<'de>>(input: &str, encoded: &'de [u8]) {
80 let output = bincode::deserialize::<T>(encoded).expect("valid UTF-8 should be fine");
81 assert_eq!(
82 output.path(),
83 input,
84 "for input, with {}, paths should match",
85 T::description()
86 );
87 }
88
89 #[test]
90 fn invalid_utf8() {
91 let invalid_utf8: &[(&[u8], _, _)] = &[
92 (b"\xff", 0, 1),
93 (b"foo\xfe", 3, 1),
94 (b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9", 4, 1),
95 ];
96
97 for (input, valid_up_to, error_len) in invalid_utf8 {
98 let encode = Encode {
99 path: ByteBuf::from(*input),
100 };
101 let encoded = bincode::serialize(&encode).expect("encoded correctly");
102
103 assert_invalid_utf8::<DecodeOwned>(input, &encoded, *valid_up_to, *error_len);
104 assert_invalid_utf8::<DecodeBorrowed>(input, &encoded, *valid_up_to, *error_len)
105 }
106 }
107
108 fn assert_invalid_utf8<'de, T: TestTrait<'de>>(
109 input: &[u8],
110 encoded: &'de [u8],
111 valid_up_to: usize,
112 error_len: usize,
113 ) {
114 let error = bincode::deserialize::<T>(encoded).expect_err("invalid UTF-8 should error out");
115 let utf8_error = match *error {
116 bincode::ErrorKind::InvalidUtf8Encoding(utf8_error) => utf8_error,
117 other => panic!(
118 "for input {:?}, with {}, expected ErrorKind::InvalidUtf8Encoding, found: {}",
119 input,
120 T::description(),
121 other
122 ),
123 };
124 assert_eq!(
125 utf8_error.valid_up_to(),
126 valid_up_to,
127 "for input {:?}, with {}, valid_up_to didn't match",
128 input,
129 T::description(),
130 );
131 assert_eq!(
132 utf8_error.error_len(),
133 Some(error_len),
134 "for input {:?}, with {}, error_len didn't match",
135 input,
136 T::description(),
137 );
138 }
139
140 #[derive(Serialize, Debug)]
141 struct Encode {
142 path: ByteBuf,
143 }
144
145 trait TestTrait<'de>: Deserialize<'de> + fmt::Debug {
146 fn description() -> &'static str;
147 fn path(&self) -> &Utf8Path;
148 }
149
150 #[derive(Deserialize, Debug)]
151 #[allow(unused)]
152 struct DecodeOwned {
153 path: Utf8PathBuf,
154 }
155
156 impl<'de> TestTrait<'de> for DecodeOwned {
157 fn description() -> &'static str {
158 "DecodeOwned"
159 }
160
161 fn path(&self) -> &Utf8Path {
162 &self.path
163 }
164 }
165
166 #[derive(Deserialize, Debug)]
167 #[allow(unused)]
168 struct DecodeBorrowed<'a> {
169 #[serde(borrow)]
170 path: &'a Utf8Path,
171 }
172
173 impl<'de> TestTrait<'de> for DecodeBorrowed<'de> {
174 fn description() -> &'static str {
175 "DecodeBorrowed"
176 }
177
178 fn path(&self) -> &Utf8Path {
179 self.path
180 }
181 }
182}
183