1 // allow "path.rs" in "path"
2 #![allow(clippy::module_inception)]
3 
4 use crate::fs::path::{PathBuf, SEPARATOR};
5 use crate::{CStr16, CString16};
6 use core::fmt::{Display, Formatter};
7 
8 /// A path similar to the `Path` of the standard library, but based on
9 /// [`CStr16`] strings and [`SEPARATOR`] as separator.
10 ///
11 /// [`SEPARATOR`]: super::SEPARATOR
12 #[derive(Debug, Eq, PartialOrd, Ord)]
13 pub struct Path(CStr16);
14 
15 impl Path {
16     /// Constructor.
17     #[must_use]
new<S: AsRef<CStr16> + ?Sized>(s: &S) -> &Self18     pub fn new<S: AsRef<CStr16> + ?Sized>(s: &S) -> &Self {
19         unsafe { &*(s.as_ref() as *const CStr16 as *const Self) }
20     }
21 
22     /// Returns the underlying string.
23     #[must_use]
to_cstr16(&self) -> &CStr1624     pub const fn to_cstr16(&self) -> &CStr16 {
25         &self.0
26     }
27 
28     /// Returns a path buf from that type.
29     #[must_use]
to_path_buf(&self) -> PathBuf30     pub fn to_path_buf(&self) -> PathBuf {
31         let cstring = CString16::from(&self.0);
32         PathBuf::from(cstring)
33     }
34 
35     /// Iterator over the components of a path.
36     #[must_use]
components(&self) -> Components37     pub fn components(&self) -> Components {
38         Components {
39             path: self.as_ref(),
40             i: 0,
41         }
42     }
43 
44     /// Returns the parent directory as [`PathBuf`].
45     ///
46     /// If the path is a top-level component, this returns None.
47     #[must_use]
parent(&self) -> Option<PathBuf>48     pub fn parent(&self) -> Option<PathBuf> {
49         let components_count = self.components().count();
50         if components_count == 0 {
51             return None;
52         }
53 
54         // Return None, as we do not treat "\\" as dedicated component.
55         let sep_count = self
56             .0
57             .as_slice()
58             .iter()
59             .filter(|char| **char == SEPARATOR)
60             .count();
61         if sep_count == 0 {
62             return None;
63         }
64 
65         let path =
66             self.components()
67                 .take(components_count - 1)
68                 .fold(CString16::new(), |mut acc, next| {
69                     // Add separator, as needed.
70                     if !acc.is_empty() && *acc.as_slice().last().unwrap() != SEPARATOR {
71                         acc.push(SEPARATOR);
72                     }
73                     acc.push_str(next.as_ref());
74                     acc
75                 });
76         let path = PathBuf::from(path);
77         Some(path)
78     }
79 
80     /// Returns of the path is empty.
81     #[must_use]
is_empty(&self) -> bool82     pub const fn is_empty(&self) -> bool {
83         self.to_cstr16().is_empty()
84     }
85 }
86 
87 impl Display for Path {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result88     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
89         Display::fmt(self.to_cstr16(), f)
90     }
91 }
92 
93 impl PartialEq for Path {
eq(&self, other: &Self) -> bool94     fn eq(&self, other: &Self) -> bool {
95         self.components().count() == other.components().count()
96             && !self
97                 .components()
98                 .zip(other.components())
99                 .any(|(c1, c2)| c1 != c2)
100     }
101 }
102 
103 /// Iterator over the components of a path. For example, the path `\\a\\b\\c`
104 /// has the components `[a, b, c]`. This is a more basic approach than the
105 /// components type of the standard library.
106 #[derive(Debug)]
107 pub struct Components<'a> {
108     path: &'a CStr16,
109     i: usize,
110 }
111 
112 impl<'a> Iterator for Components<'a> {
113     // Attention. We can't iterate over &'Ctr16, as we would break any guarantee
114     // made for the terminating null character.
115     type Item = CString16;
116 
next(&mut self) -> Option<Self::Item>117     fn next(&mut self) -> Option<Self::Item> {
118         if self.path.is_empty() {
119             return None;
120         }
121         if self.path.num_chars() == 1 && self.path.as_slice()[0] == SEPARATOR {
122             // The current implementation does not handle the root dir as
123             // dedicated component so far. We just return nothing.
124             return None;
125         }
126 
127         // If the path is not empty and starts with a separator, skip it.
128         if self.i == 0 && *self.path.as_slice().first().unwrap() == SEPARATOR {
129             self.i = 1;
130         }
131 
132         // Count how many characters are there until the next separator is
133         // found.
134         let len = self
135             .path
136             .iter()
137             .skip(self.i)
138             .take_while(|c| **c != SEPARATOR)
139             .count();
140 
141         let progress = self.i + len;
142         if progress > self.path.num_chars() {
143             None
144         } else {
145             // select the next component and build an owned string
146             let part = &self.path.as_slice()[self.i..self.i + len];
147             let mut string = CString16::new();
148             part.iter().for_each(|c| string.push(*c));
149 
150             // +1: skip the separator
151             self.i = progress + 1;
152             Some(string)
153         }
154     }
155 }
156 
157 mod convenience_impls {
158     use super::*;
159     use core::borrow::Borrow;
160 
161     impl AsRef<Path> for &Path {
as_ref(&self) -> &Path162         fn as_ref(&self) -> &Path {
163             self
164         }
165     }
166 
167     impl<'a> From<&'a CStr16> for &'a Path {
from(value: &'a CStr16) -> Self168         fn from(value: &'a CStr16) -> Self {
169             Path::new(value)
170         }
171     }
172 
173     impl AsRef<CStr16> for Path {
as_ref(&self) -> &CStr16174         fn as_ref(&self) -> &CStr16 {
175             &self.0
176         }
177     }
178 
179     impl Borrow<CStr16> for Path {
borrow(&self) -> &CStr16180         fn borrow(&self) -> &CStr16 {
181             &self.0
182         }
183     }
184 
185     impl AsRef<Path> for CStr16 {
as_ref(&self) -> &Path186         fn as_ref(&self) -> &Path {
187             Path::new(self)
188         }
189     }
190 
191     impl Borrow<Path> for CStr16 {
borrow(&self) -> &Path192         fn borrow(&self) -> &Path {
193             Path::new(self)
194         }
195     }
196 }
197 
198 #[cfg(test)]
199 mod tests {
200     use super::*;
201     use crate::cstr16;
202     use alloc::vec::Vec;
203 
204     #[test]
from_cstr16()205     fn from_cstr16() {
206         let source: &CStr16 = cstr16!("\\hello\\foo\\bar");
207         let _path: &Path = source.into();
208         let _path: &Path = Path::new(source);
209     }
210 
211     #[test]
from_cstring16()212     fn from_cstring16() {
213         let source = CString16::try_from("\\hello\\foo\\bar").unwrap();
214         let _path: &Path = source.as_ref().into();
215         let _path: &Path = Path::new(source.as_ref());
216     }
217 
218     #[test]
components_iter()219     fn components_iter() {
220         let path = Path::new(cstr16!("foo\\bar\\hello"));
221         let components = path.components().collect::<Vec<_>>();
222         let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
223         let expected: &[&CStr16] = &[cstr16!("foo"), cstr16!("bar"), cstr16!("hello")];
224         assert_eq!(components.as_slice(), expected);
225 
226         // In case there is a leading slash, it should be ignored.
227         let path = Path::new(cstr16!("\\foo\\bar\\hello"));
228         let components = path.components().collect::<Vec<_>>();
229         let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
230         let expected: &[&CStr16] = &[cstr16!("foo"), cstr16!("bar"), cstr16!("hello")];
231         assert_eq!(components.as_slice(), expected);
232 
233         // empty path iteration should be just fine
234         let empty_cstring16 = CString16::try_from("").unwrap();
235         let path = Path::new(empty_cstring16.as_ref());
236         let components = path.components().collect::<Vec<_>>();
237         let expected: &[CString16] = &[];
238         assert_eq!(components.as_slice(), expected);
239 
240         // test empty path
241         let _path = Path::new(cstr16!());
242         let path = Path::new(cstr16!(""));
243         let components = path.components().collect::<Vec<_>>();
244         let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
245         let expected: &[&CStr16] = &[];
246         assert_eq!(components.as_slice(), expected);
247 
248         // test path that has only root component. Treated as empty path by
249         // the components iterator.
250         let path = Path::new(cstr16!("\\"));
251         let components = path.components().collect::<Vec<_>>();
252         let components: Vec<&CStr16> = components.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
253         let expected: &[&CStr16] = &[];
254         assert_eq!(components.as_slice(), expected);
255     }
256 
257     #[test]
258     fn test_parent() {
259         assert_eq!(None, Path::new(cstr16!("")).parent());
260         assert_eq!(None, Path::new(cstr16!("\\")).parent());
261         assert_eq!(
262             Path::new(cstr16!("a\\b")).parent(),
263             Some(PathBuf::from(cstr16!("a"))),
264         );
265         assert_eq!(
266             Path::new(cstr16!("\\a\\b")).parent(),
267             Some(PathBuf::from(cstr16!("a"))),
268         );
269         assert_eq!(
270             Path::new(cstr16!("a\\b\\c\\d")).parent(),
271             Some(PathBuf::from(cstr16!("a\\b\\c"))),
272         );
273         assert_eq!(Path::new(cstr16!("abc")).parent(), None,);
274     }
275 
276     #[test]
277     fn partial_eq() {
278         let path1 = Path::new(cstr16!(r"a\b"));
279         let path2 = Path::new(cstr16!(r"\a\b"));
280         let path3 = Path::new(cstr16!(r"a\b\c"));
281 
282         assert_eq!(path1, path1);
283         assert_eq!(path2, path2);
284         assert_eq!(path3, path3);
285 
286         // Equal as currently, we only support absolute paths, so the leading
287         // separator is obligatory.
288         assert_eq!(path1, path2);
289         assert_eq!(path2, path1);
290 
291         assert_ne!(path1, path3);
292         assert_ne!(path3, path1);
293     }
294 }
295