1 use crate::fs::path::Path;
2 use crate::fs::SEPARATOR;
3 use crate::{CStr16, CString16, Char16};
4 use core::fmt::{Display, Formatter};
5 
6 /// A path buffer similar to the `PathBuf` of the standard library, but based on
7 /// [`CString16`] strings and [`SEPARATOR`] as separator.
8 ///
9 /// `/` is replaced by [`SEPARATOR`] on the fly.
10 #[derive(Clone, Debug, Default, Eq, PartialOrd, Ord)]
11 pub struct PathBuf(CString16);
12 
13 impl PathBuf {
14     /// Constructor.
15     #[must_use]
new() -> Self16     pub fn new() -> Self {
17         Self::default()
18     }
19 
20     /// Constructor that replaces all occurrences of `/` with `\`.
new_from_cstring16(mut string: CString16) -> Self21     fn new_from_cstring16(mut string: CString16) -> Self {
22         const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
23         string.replace_char(SEARCH, SEPARATOR);
24         Self(string)
25     }
26 
27     /// Extends self with path.
28     ///
29     /// UNIX separators (`/`) will be replaced by [`SEPARATOR`] on the fly.
push<P: AsRef<Path>>(&mut self, path: P)30     pub fn push<P: AsRef<Path>>(&mut self, path: P) {
31         const SEARCH: Char16 = unsafe { Char16::from_u16_unchecked('/' as u16) };
32 
33         // do nothing on empty path
34         if path.as_ref().is_empty() {
35             return;
36         }
37 
38         let empty = self.0.is_empty();
39         let needs_sep = *self
40             .0
41             .as_slice_with_nul()
42             .last()
43             .expect("Should have at least null character")
44             != SEPARATOR;
45         if !empty && needs_sep {
46             self.0.push(SEPARATOR)
47         }
48 
49         self.0.push_str(path.as_ref().to_cstr16());
50         self.0.replace_char(SEARCH, SEPARATOR);
51     }
52 }
53 
54 impl PartialEq for PathBuf {
eq(&self, other: &Self) -> bool55     fn eq(&self, other: &Self) -> bool {
56         let path1: &Path = self.as_ref();
57         let path2: &Path = other.as_ref();
58         path1 == path2
59     }
60 }
61 
62 impl Display for PathBuf {
fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result63     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
64         Display::fmt(self.to_cstr16(), f)
65     }
66 }
67 
68 mod convenience_impls {
69     use super::*;
70     use core::borrow::Borrow;
71     use core::ops::Deref;
72 
73     impl From<CString16> for PathBuf {
from(value: CString16) -> Self74         fn from(value: CString16) -> Self {
75             Self::new_from_cstring16(value)
76         }
77     }
78 
79     impl From<&CStr16> for PathBuf {
from(value: &CStr16) -> Self80         fn from(value: &CStr16) -> Self {
81             Self::new_from_cstring16(CString16::from(value))
82         }
83     }
84 
85     impl Deref for PathBuf {
86         type Target = Path;
87 
deref(&self) -> &Self::Target88         fn deref(&self) -> &Self::Target {
89             Path::new(&self.0)
90         }
91     }
92 
93     impl AsRef<Path> for PathBuf {
as_ref(&self) -> &Path94         fn as_ref(&self) -> &Path {
95             // falls back to deref impl
96             self
97         }
98     }
99 
100     impl Borrow<Path> for PathBuf {
borrow(&self) -> &Path101         fn borrow(&self) -> &Path {
102             // falls back to deref impl
103             self
104         }
105     }
106 
107     impl AsRef<CStr16> for PathBuf {
as_ref(&self) -> &CStr16108         fn as_ref(&self) -> &CStr16 {
109             &self.0
110         }
111     }
112 
113     impl Borrow<CStr16> for PathBuf {
borrow(&self) -> &CStr16114         fn borrow(&self) -> &CStr16 {
115             &self.0
116         }
117     }
118 }
119 
120 #[cfg(test)]
121 mod tests {
122     use super::*;
123     use crate::cstr16;
124     use alloc::string::ToString;
125 
126     #[test]
from_cstr16()127     fn from_cstr16() {
128         let source: &CStr16 = cstr16!("\\hello\\foo\\bar");
129         let _path: PathBuf = source.into();
130     }
131 
132     #[test]
from_cstring16()133     fn from_cstring16() {
134         let source = CString16::try_from("\\hello\\foo\\bar").unwrap();
135         let _path: PathBuf = source.as_ref().into();
136         let _path: PathBuf = source.clone().into();
137         let _path: PathBuf = PathBuf::new_from_cstring16(source);
138     }
139 
140     #[test]
from_std_string()141     fn from_std_string() {
142         let std_string = "\\hello\\foo\\bar".to_string();
143         let _path = PathBuf::new_from_cstring16(CString16::try_from(std_string.as_str()).unwrap());
144     }
145 
146     #[test]
push()147     fn push() {
148         let mut pathbuf = PathBuf::new();
149         pathbuf.push(cstr16!("first"));
150         pathbuf.push(cstr16!("second"));
151         pathbuf.push(cstr16!("third"));
152         assert_eq!(pathbuf.to_cstr16(), cstr16!("first\\second\\third"));
153 
154         let mut pathbuf = PathBuf::new();
155         pathbuf.push(cstr16!("\\first"));
156         pathbuf.push(cstr16!("second"));
157         assert_eq!(pathbuf.to_cstr16(), cstr16!("\\first\\second"));
158 
159         // empty pushes should be ignored and have no effect
160         let empty_cstring16 = CString16::try_from("").unwrap();
161         let mut pathbuf = PathBuf::new();
162         pathbuf.push(cstr16!("first"));
163         pathbuf.push(empty_cstring16.as_ref());
164         pathbuf.push(empty_cstring16.as_ref());
165         pathbuf.push(empty_cstring16.as_ref());
166         pathbuf.push(cstr16!("second"));
167         assert_eq!(pathbuf.to_cstr16(), cstr16!("first\\second"));
168     }
169 
170     #[test]
partial_eq()171     fn partial_eq() {
172         let mut pathbuf1 = PathBuf::new();
173         pathbuf1.push(cstr16!("first"));
174         pathbuf1.push(cstr16!("second"));
175         pathbuf1.push(cstr16!("third"));
176 
177         assert_eq!(pathbuf1, pathbuf1);
178 
179         let mut pathbuf2 = PathBuf::new();
180         pathbuf2.push(cstr16!("\\first"));
181         pathbuf2.push(cstr16!("second"));
182 
183         assert_eq!(pathbuf2, pathbuf2);
184         assert_ne!(pathbuf1, pathbuf2);
185     }
186 }
187