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