1mod 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, UnixEncoding, Utf8Encoding, Utf8Path, Utf8PathBuf};
12
13pub type Utf8UnixPath = Utf8Path<Utf8UnixEncoding>;
15
16pub type Utf8UnixPathBuf = Utf8PathBuf<Utf8UnixEncoding>;
18
19#[derive(Copy, Clone)]
21pub struct Utf8UnixEncoding;
22
23impl private::Sealed for Utf8UnixEncoding {}
24
25impl Utf8Encoding for Utf8UnixEncoding {
26 type Components<'a> = Utf8UnixComponents<'a>;
27
28 fn label() -> &'static str {
29 "unix"
30 }
31
32 fn components(path: &str) -> Self::Components<'_> {
33 Utf8UnixComponents::new(path)
34 }
35
36 fn hash<H: Hasher>(path: &str, h: &mut H) {
37 UnixEncoding::hash(path.as_bytes(), h);
38 }
39
40 fn push(current_path: &mut String, path: &str) {
41 unsafe {
42 UnixEncoding::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 { UnixEncoding::push_checked(current_path.as_mut_vec(), path.as_bytes()) }
48 }
49}
50
51impl fmt::Debug for Utf8UnixEncoding {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.debug_struct("Utf8UnixEncoding").finish()
54 }
55}
56
57impl fmt::Display for Utf8UnixEncoding {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "Utf8UnixEncoding")
60 }
61}
62
63impl<T> Utf8Path<T>
64where
65 T: Utf8Encoding,
66{
67 pub fn has_unix_encoding(&self) -> bool {
78 T::label() == Utf8UnixEncoding::label()
79 }
80
81 pub fn with_unix_encoding(&self) -> Utf8PathBuf<Utf8UnixEncoding> {
85 self.with_encoding()
86 }
87
88 pub fn with_unix_encoding_checked(
93 &self,
94 ) -> Result<Utf8PathBuf<Utf8UnixEncoding>, CheckedPathError> {
95 self.with_encoding_checked()
96 }
97}
98
99impl Utf8UnixPath {
100 pub fn to_typed_path(&self) -> Utf8TypedPath {
101 Utf8TypedPath::unix(self)
102 }
103
104 pub fn to_typed_path_buf(&self) -> Utf8TypedPathBuf {
105 Utf8TypedPathBuf::from_unix(self)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn push_should_replace_current_path_with_provided_path_if_provided_path_is_absolute() {
115 let mut current_path = String::new();
117 Utf8UnixEncoding::push(&mut current_path, "/abc");
118 assert_eq!(current_path, "/abc");
119
120 let mut current_path = String::from("some/path");
122 Utf8UnixEncoding::push(&mut current_path, "/abc");
123 assert_eq!(current_path, "/abc");
124
125 let mut current_path = String::from("/some/path/");
127 Utf8UnixEncoding::push(&mut current_path, "/abc");
128 assert_eq!(current_path, "/abc");
129 }
130
131 #[test]
132 fn push_should_append_path_to_current_path_with_a_separator_if_provided_path_is_relative() {
133 let mut current_path = String::new();
135 Utf8UnixEncoding::push(&mut current_path, "abc");
136 assert_eq!(current_path, "abc");
137
138 let mut current_path = String::from("some/path");
140 Utf8UnixEncoding::push(&mut current_path, "abc");
141 assert_eq!(current_path, "some/path/abc");
142
143 let mut current_path = String::from("some/path/");
145 Utf8UnixEncoding::push(&mut current_path, "abc");
146 assert_eq!(current_path, "some/path/abc");
147 }
148
149 #[test]
150 fn push_checked_should_fail_if_providing_an_absolute_path() {
151 let mut current_path = String::new();
153 assert_eq!(
154 Utf8UnixEncoding::push_checked(&mut current_path, "/abc"),
155 Err(CheckedPathError::UnexpectedRoot)
156 );
157 assert_eq!(current_path, "");
158
159 let mut current_path = String::from("some/path");
161 assert_eq!(
162 Utf8UnixEncoding::push_checked(&mut current_path, "/abc"),
163 Err(CheckedPathError::UnexpectedRoot)
164 );
165 assert_eq!(current_path, "some/path");
166
167 let mut current_path = String::from("/some/path/");
169 assert_eq!(
170 Utf8UnixEncoding::push_checked(&mut current_path, "/abc"),
171 Err(CheckedPathError::UnexpectedRoot)
172 );
173 assert_eq!(current_path, "/some/path/");
174 }
175
176 #[test]
177 fn push_checked_should_fail_if_providing_a_path_with_disallowed_filename_characters() {
178 let mut current_path = String::new();
180 assert_eq!(
181 Utf8UnixEncoding::push_checked(&mut current_path, "some/inva\0lid/path"),
182 Err(CheckedPathError::InvalidFilename)
183 );
184 assert_eq!(current_path, "");
185
186 let mut current_path = String::from("some/path");
189 assert_eq!(
190 Utf8UnixEncoding::push_checked(&mut current_path, "some/inva\0lid/path"),
191 Err(CheckedPathError::InvalidFilename)
192 );
193 assert_eq!(current_path, "some/path");
194
195 let mut current_path = String::from("/some/path/");
198 assert_eq!(
199 Utf8UnixEncoding::push_checked(&mut current_path, "some/inva\0lid/path"),
200 Err(CheckedPathError::InvalidFilename)
201 );
202 assert_eq!(current_path, "/some/path/");
203 }
204
205 #[test]
206 fn push_checked_should_fail_if_providing_a_path_that_would_escape_the_current_path() {
207 let mut current_path = String::new();
209 assert_eq!(
210 Utf8UnixEncoding::push_checked(&mut current_path, ".."),
211 Err(CheckedPathError::PathTraversalAttack)
212 );
213 assert_eq!(current_path, "");
214
215 let mut current_path = String::from("some/path");
217 assert_eq!(
218 Utf8UnixEncoding::push_checked(&mut current_path, ".."),
219 Err(CheckedPathError::PathTraversalAttack)
220 );
221 assert_eq!(current_path, "some/path");
222
223 let mut current_path = String::from("/some/path/");
225 assert_eq!(
226 Utf8UnixEncoding::push_checked(&mut current_path, ".."),
227 Err(CheckedPathError::PathTraversalAttack)
228 );
229 assert_eq!(current_path, "/some/path/");
230 }
231
232 #[test]
233 fn push_checked_should_append_path_to_current_path_with_a_separator_if_does_not_violate_rules()
234 {
235 let mut current_path = String::new();
238 assert_eq!(
239 Utf8UnixEncoding::push_checked(&mut current_path, "abc/../def/."),
240 Ok(()),
241 );
242 assert_eq!(current_path, "abc/../def/.");
243
244 let mut current_path = String::from("some/path");
245 assert_eq!(
246 Utf8UnixEncoding::push_checked(&mut current_path, "abc/../def/."),
247 Ok(()),
248 );
249 assert_eq!(current_path, "some/path/abc/../def/.");
250
251 let mut current_path = String::from("/some/path/");
252 assert_eq!(
253 Utf8UnixEncoding::push_checked(&mut current_path, "abc/../def/."),
254 Ok(()),
255 );
256 assert_eq!(current_path, "/some/path/abc/../def/.");
257 }
258}