typed_path/windows/
utf8.rs1mod components;
2
3use core::fmt;
4use core::hash::Hasher;
5
6pub use components::*;
7
8use crate::common::CheckedPathError;
9use crate::no_std_compat::*;
10use crate::typed::{Utf8TypedPath, Utf8TypedPathBuf};
11use crate::{private, Encoding, Utf8Encoding, Utf8Path, Utf8PathBuf, WindowsEncoding};
12
13pub type Utf8WindowsPath = Utf8Path<Utf8WindowsEncoding>;
15
16pub type Utf8WindowsPathBuf = Utf8PathBuf<Utf8WindowsEncoding>;
18
19#[derive(Copy, Clone)]
21pub struct Utf8WindowsEncoding;
22
23impl private::Sealed for Utf8WindowsEncoding {}
24
25impl Utf8Encoding for Utf8WindowsEncoding {
26 type Components<'a> = Utf8WindowsComponents<'a>;
27
28 fn label() -> &'static str {
29 "windows"
30 }
31
32 fn components(path: &str) -> Self::Components<'_> {
33 Utf8WindowsComponents::new(path)
34 }
35
36 fn hash<H: Hasher>(path: &str, h: &mut H) {
37 WindowsEncoding::hash(path.as_bytes(), h);
38 }
39
40 fn push(current_path: &mut String, path: &str) {
41 unsafe {
42 WindowsEncoding::push(current_path.as_mut_vec(), path.as_bytes());
43 }
44 }
45
46 fn push_checked(current_path: &mut String, path: &str) -> Result<(), CheckedPathError> {
47 unsafe { WindowsEncoding::push_checked(current_path.as_mut_vec(), path.as_bytes()) }
48 }
49}
50
51impl fmt::Debug for Utf8WindowsEncoding {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.debug_struct("Utf8WindowsEncoding").finish()
54 }
55}
56
57impl fmt::Display for Utf8WindowsEncoding {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "Utf8WindowsEncoding")
60 }
61}
62
63impl<T> Utf8Path<T>
64where
65 T: Utf8Encoding,
66{
67 pub fn has_windows_encoding(&self) -> bool {
78 T::label() == Utf8WindowsEncoding::label()
79 }
80
81 pub fn with_windows_encoding(&self) -> Utf8PathBuf<Utf8WindowsEncoding> {
85 self.with_encoding()
86 }
87
88 pub fn with_windows_encoding_checked(
93 &self,
94 ) -> Result<Utf8PathBuf<Utf8WindowsEncoding>, CheckedPathError> {
95 self.with_encoding_checked()
96 }
97}
98
99impl Utf8WindowsPath {
100 pub fn to_typed_path(&self) -> Utf8TypedPath<'_> {
101 Utf8TypedPath::windows(self)
102 }
103
104 pub fn to_typed_path_buf(&self) -> Utf8TypedPathBuf {
105 Utf8TypedPathBuf::from_windows(self)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn push_checked_should_fail_if_providing_an_absolute_path() {
115 let mut current_path = String::new();
117 assert_eq!(
118 Utf8WindowsEncoding::push_checked(&mut current_path, r"\abc"),
119 Err(CheckedPathError::UnexpectedRoot)
120 );
121 assert_eq!(current_path, "");
122
123 let mut current_path = String::from(r"some\path");
125 assert_eq!(
126 Utf8WindowsEncoding::push_checked(&mut current_path, r"\abc"),
127 Err(CheckedPathError::UnexpectedRoot)
128 );
129 assert_eq!(current_path, r"some\path");
130
131 let mut current_path = String::from(r"\some\path\");
133 assert_eq!(
134 Utf8WindowsEncoding::push_checked(&mut current_path, r"\abc"),
135 Err(CheckedPathError::UnexpectedRoot)
136 );
137 assert_eq!(current_path, r"\some\path\");
138 }
139
140 #[test]
141 fn push_checked_should_fail_if_providing_a_path_with_an_embedded_prefix() {
142 let mut current_path = String::new();
144 assert_eq!(
145 Utf8WindowsEncoding::push_checked(&mut current_path, r"C:abc"),
146 Err(CheckedPathError::UnexpectedPrefix)
147 );
148 assert_eq!(current_path, "");
149
150 let mut current_path = String::from(r"some\path");
152 assert_eq!(
153 Utf8WindowsEncoding::push_checked(&mut current_path, r"C:abc"),
154 Err(CheckedPathError::UnexpectedPrefix)
155 );
156 assert_eq!(current_path, r"some\path");
157
158 let mut current_path = String::from(r"\some\path\");
160 assert_eq!(
161 Utf8WindowsEncoding::push_checked(&mut current_path, r"C:abc"),
162 Err(CheckedPathError::UnexpectedPrefix)
163 );
164 assert_eq!(current_path, r"\some\path\");
165 }
166
167 #[test]
168 fn push_checked_should_fail_if_providing_a_path_with_disallowed_filename_bytes() {
169 let mut current_path = String::new();
171 assert_eq!(
172 Utf8WindowsEncoding::push_checked(&mut current_path, r"some\inva|lid\path"),
173 Err(CheckedPathError::InvalidFilename)
174 );
175 assert_eq!(current_path, "");
176
177 let mut current_path = String::from(r"some\path");
180 assert_eq!(
181 Utf8WindowsEncoding::push_checked(&mut current_path, r"some\inva|lid\path"),
182 Err(CheckedPathError::InvalidFilename)
183 );
184 assert_eq!(current_path, r"some\path");
185
186 let mut current_path = String::from(r"\some\path\");
189 assert_eq!(
190 Utf8WindowsEncoding::push_checked(&mut current_path, r"some\inva|lid\path"),
191 Err(CheckedPathError::InvalidFilename)
192 );
193 assert_eq!(current_path, r"\some\path\");
194 }
195
196 #[test]
197 fn push_checked_should_fail_if_providing_a_path_that_would_escape_the_current_path() {
198 let mut current_path = String::new();
200 assert_eq!(
201 Utf8WindowsEncoding::push_checked(&mut current_path, ".."),
202 Err(CheckedPathError::PathTraversalAttack)
203 );
204 assert_eq!(current_path, "");
205
206 let mut current_path = String::from(r"some\path");
208 assert_eq!(
209 Utf8WindowsEncoding::push_checked(&mut current_path, ".."),
210 Err(CheckedPathError::PathTraversalAttack)
211 );
212 assert_eq!(current_path, r"some\path");
213
214 let mut current_path = String::from(r"\some\path\");
216 assert_eq!(
217 Utf8WindowsEncoding::push_checked(&mut current_path, ".."),
218 Err(CheckedPathError::PathTraversalAttack)
219 );
220 assert_eq!(current_path, r"\some\path\");
221 }
222
223 #[test]
224 fn push_checked_should_append_path_to_current_path_with_a_separator_if_does_not_violate_rules()
225 {
226 let mut current_path = String::new();
229 assert_eq!(
230 Utf8WindowsEncoding::push_checked(&mut current_path, r"abc\..\def\."),
231 Ok(()),
232 );
233 assert_eq!(current_path, r"abc\..\def\.");
234
235 let mut current_path = String::from(r"some\path");
236 assert_eq!(
237 Utf8WindowsEncoding::push_checked(&mut current_path, r"abc\..\def\."),
238 Ok(()),
239 );
240 assert_eq!(current_path, r"some\path\abc\..\def\.");
241
242 let mut current_path = String::from(r"\some\path\");
243 assert_eq!(
244 Utf8WindowsEncoding::push_checked(&mut current_path, r"abc\..\def\."),
245 Ok(()),
246 );
247 assert_eq!(current_path, r"\some\path\abc\..\def\.");
248 }
249}