minxp/
path.rs

1use alloc::borrow::{Cow, ToOwned};
2use alloc::boxed::Box;
3use crate::ffi::{OsStr, OsString};
4use crate::fs::*;
5use alloc::collections::TryReserveError;
6use alloc::rc::Rc;
7use alloc::string::String;
8use alloc::sync::Arc;
9use alloc::vec;
10use alloc::vec::Vec;
11use core::borrow::Borrow;
12use core::fmt::{Debug, Display, Formatter};
13use core::ops::Deref;
14use crate::env::current_dir;
15
16#[must_use]
17pub fn is_separator(c: char) -> bool {
18    c == MAIN_SEPARATOR || c == '/'
19}
20
21pub const MAIN_SEPARATOR: char = '\\';
22pub const MAIN_SEPARATOR_STR: &str = "\\";
23
24/// Base max path
25pub(crate) const MAX_PATH: usize = windows_sys::Win32::Foundation::MAX_PATH as usize;
26
27/// Extended max path
28pub(crate) const MAX_PATH_EXTENDED: usize = 32767 as usize;
29
30#[derive(Clone, PartialEq, Ord, PartialOrd, Eq)]
31#[repr(transparent)]
32pub struct PathBuf {
33    data: OsString
34}
35
36impl Debug for PathBuf {
37    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
38        Debug::fmt(&self.data, f)
39    }
40}
41
42impl Debug for Path {
43    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
44        Debug::fmt(&self.inner, f)
45    }
46}
47
48impl PathBuf {
49    pub fn new() -> PathBuf {
50        Self { data: OsString::new() }
51    }
52    pub fn with_capacity(capacity: usize) -> PathBuf {
53        Self { data: OsString::with_capacity(capacity) }
54    }
55    pub fn as_path(&self) -> &Path {
56        self
57    }
58    pub fn push<S: AsRef<Path>>(&mut self, part: S) {
59        let part = part.as_ref();
60
61        if part.is_absolute() || part.has_drive_letter() {
62            self.data.clear();
63            self.data.push(part.as_os_str());
64            return
65        }
66
67        if !self.data.as_str().chars().next_back().is_some_and(is_separator) {
68            self.data.push(MAIN_SEPARATOR_STR);
69        }
70        self.data.push(part.as_os_str());
71    }
72    pub fn pop(&mut self) -> bool {
73        match self.parent() {
74            Some(s) => {
75                self.data.truncate(s.as_os_str().len());
76                true
77            }
78            None => false
79        }
80    }
81    pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) {
82        self.truncate_extraneous_suffixes();
83
84        if self.file_name().is_some() {
85            self.pop();
86        }
87        self.push(file_name.as_ref());
88    }
89    pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
90        if !self.file_name().is_some() {
91            return false
92        }
93
94        self.truncate_extraneous_suffixes();
95
96        match self.extension() {
97            Some(e) => self.data.truncate(self.data.len() - e.len()),
98            None => self.data.push(".")
99        }
100        self.data.push(extension);
101
102        true
103    }
104    fn truncate_extraneous_suffixes(&mut self) {
105        self.data.truncate(self.remove_extraneous_suffixes().path_len())
106    }
107    pub fn as_mut_os_string(&mut self) -> &mut OsString {
108        &mut self.data
109    }
110    pub fn into_os_string(self) -> OsString {
111        self.data
112    }
113    pub fn into_boxed_path(self) -> Box<Path> {
114        let s = Box::into_raw(self.data.into_boxed_os_str());
115        unsafe { Box::from_raw(s as *mut Path) }
116    }
117    pub fn capacity(&self) -> usize {
118        self.data.capacity()
119    }
120    pub fn clear(&mut self) {
121        self.data.clear()
122    }
123    pub fn reserve(&mut self, additional: usize) {
124        self.data.reserve(additional)
125    }
126    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
127        self.data.try_reserve(additional)
128    }
129    pub fn reserve_exact(&mut self, additional: usize) {
130        self.data.reserve_exact(additional)
131    }
132    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
133        self.data.try_reserve_exact(additional)
134    }
135    pub fn shrink_to_fit(&mut self) {
136        self.data.shrink_to_fit()
137    }
138    pub fn shrink_to(&mut self, amount: usize) {
139        self.data.shrink_to(amount)
140    }
141    pub(crate) fn from_string(mut string: String) -> Self {
142        // TODO: Discombobulate forward slashes, too
143        if string.starts_with(r#"\\?\"#) {
144            string.remove(0);
145            string.remove(0);
146            string.remove(0);
147            string.remove(0);
148        }
149        Self { data: string.into() }
150    }
151}
152
153#[derive(PartialEq, Eq, PartialOrd, Ord)]
154#[repr(transparent)]
155pub struct Path {
156    inner: OsStr
157}
158
159impl Path {
160    pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path {
161        let mut s = s.as_ref().as_str();
162        if s.starts_with(r#"\\?\"#) {
163            s = &s[4..];
164        }
165        Self::from_os_str(s.as_ref())
166    }
167
168    pub fn as_os_str(&self) -> &OsStr {
169        &self.inner
170    }
171
172    pub fn as_mut_os_str(&mut self) -> &mut OsStr {
173        &mut self.inner
174    }
175
176    pub fn to_str(&self) -> Option<&str> {
177        self.inner.to_str()
178    }
179
180    pub fn to_string_lossy(&self) -> Cow<'_, str> {
181        self.inner.to_string_lossy()
182    }
183
184    pub fn to_path_buf(&self) -> PathBuf {
185        PathBuf { data: self.inner.to_os_string() }
186    }
187
188    pub fn is_absolute(&self) -> bool {
189        if self.has_drive_letter() && self.as_os_str().as_str().chars().skip(2).next().is_some_and(is_separator) {
190            return true
191        }
192
193        let mut p = self.iter();
194
195        // empty paths are not absolute
196        let Some(first) = p.next() else { return false };
197        let first = first.as_str();
198
199        // Starts with backslash
200        if !first.is_empty() {
201            return false
202        }
203
204        // Double backslash?
205        let Some(next) = p.next() else { return false };
206        if !next.is_empty() {
207            return false
208        }
209
210        // Triple backslash???
211        let Some(next) = p.next() else { return false };
212        if next.is_empty() {
213            return false
214        }
215
216        // Formatted as "\\Something\..."
217        p.next().is_some()
218    }
219
220    fn has_drive_letter(&self) -> bool {
221        let mut p = self.iter();
222
223        let Some(first) = p.next() else { return false };
224        let first = first.as_str();
225        let mut chars = first.chars();
226
227        // Stars with a character and a colon, and then either the end of the string or a backslash
228        let [Some(drive_letter), Some(':')] = [chars.next(), chars.next()] else {
229            return false
230        };
231
232        if !drive_letter.is_ascii_alphabetic() {
233            return false
234        }
235
236        drive_letter.is_ascii_alphabetic()
237    }
238
239    pub fn is_relative(&self) -> bool {
240        !self.is_absolute()
241    }
242
243    pub fn has_root(&self) -> bool {
244        self.is_absolute() || self.as_os_str().as_str().chars().next().is_some_and(is_separator)
245    }
246
247    pub fn parent(&self) -> Option<&Path> {
248        let mut i = self.as_relative_to_root().components();
249        let current = i.next_back()?;
250
251        unsafe {
252            match i.next_back() {
253                Some(n) => Some(self.trim_to_end_of(n.as_ref())),
254                None => Some(self.trim_to_start_of(current.as_ref()))
255            }
256        }
257    }
258
259    pub fn ancestors(&self) -> Ancestors {
260        Ancestors { path: self }
261    }
262
263    pub fn file_name(&self) -> Option<&OsStr> {
264        let final_component = self.as_relative_to_root().components().next_back()?;
265        (final_component.as_str() != "..").then_some(final_component)
266    }
267
268    pub fn strip_prefix<P: AsRef<Path>>(&self, base: P) -> Result<&Path, StripPrefixError> {
269        let mut a = self.components();
270        let mut b = base.as_ref().components();
271
272        loop {
273            let Some(n) = a.next() else { return Err(StripPrefixError {}) };
274            // SAFETY: part of the string
275            let Some(m) = b.next() else { return Ok(unsafe { self.rtrim_to_start_of(n.as_ref()) }) };
276            if n != m {
277                return Err(StripPrefixError {})
278            }
279        }
280    }
281
282    pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
283        let mut a = self.components();
284        let mut b = base.as_ref().components();
285
286        loop {
287            let Some(n) = a.next() else { return b.next().is_none() };
288            let Some(m) = b.next() else { return true };
289            if n != m {
290                return false
291            }
292        }
293    }
294
295    pub fn ends_with<P: AsRef<Path>>(&self, child: P) -> bool {
296        let mut a = self.components();
297        let mut b = child.as_ref().components();
298
299        loop {
300            let Some(n) = a.next_back() else { return b.next_back().is_none() };
301            let Some(m) = b.next_back() else { return true };
302            if n != m {
303                return false
304            }
305        }
306    }
307
308    pub fn file_stem(&self) -> Option<&OsStr> {
309        self.split_file_stem_extension().map(|i| i.0)
310    }
311
312    pub fn extension(&self) -> Option<&OsStr> {
313        self.split_file_stem_extension().map(|i| i.1).flatten()
314    }
315
316    fn split_file_stem_extension(&self) -> Option<(&OsStr, Option<&OsStr>)> {
317        let file_name = self.file_name()?.as_str();
318        let Some(dot) = file_name.rfind(".") else { return Some((file_name.as_ref(), None)) };
319        let (stem, extension) = file_name.split_at(dot);
320
321        if stem.is_empty() {
322            Some((file_name.as_ref(), None))
323        }
324        else {
325            let extension = &extension[1..]; // exclude dot
326            Some((stem.as_ref(), Some(extension.as_ref())))
327        }
328    }
329
330    pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
331        let mut new_path = self.to_owned();
332        new_path.push(path);
333        new_path
334    }
335
336    pub fn with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> PathBuf {
337        let mut p = self.to_owned();
338        p.set_file_name(file_name);
339        p
340    }
341
342    pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
343        let mut p = self.to_owned();
344        p.set_extension(extension);
345        p
346    }
347
348    pub fn components(&self) -> Components {
349        Components { iter: self.iter() }
350    }
351
352    pub fn iter(&self) -> Iter {
353        let string = self.inner.as_str();
354        Iter {
355            iter: string.split(is_separator),
356            original_string: string,
357            iterated_amount: 0
358        }
359    }
360
361    pub fn display(&self) -> impl Display {
362        self.inner.as_str()
363    }
364
365    pub fn metadata(&self) -> crate::io::Result<Metadata> {
366        metadata(self)
367    }
368
369    pub fn symlink_metadata(&self) -> crate::io::Result<Metadata> {
370        symlink_metadata(self)
371    }
372
373    pub fn canonicalize(&self) -> crate::io::Result<PathBuf> {
374        canonicalize(self)
375    }
376
377    // pub fn read_link(&self) -> crate::io::Result<PathBuf> {
378    //     read_link(self)
379    // }
380
381    pub fn read_dir(&self) -> crate::io::Result<ReadDir> {
382        read_dir(self)
383    }
384
385    pub fn exists(&self) -> bool {
386        exists_infallible(self)
387    }
388
389    pub fn try_exists(&self) -> crate::io::Result<bool> {
390        exists(self)
391    }
392
393    pub fn is_file(&self) -> bool {
394        self.metadata().is_ok_and(|m| m.is_file())
395    }
396
397    pub fn is_dir(&self) -> bool {
398        self.metadata().is_ok_and(|m| m.is_dir())
399    }
400
401    pub fn is_symlink(&self) -> bool {
402        self.symlink_metadata().is_ok()
403    }
404
405    pub fn into_path_buf(self: Box<Path>) -> PathBuf {
406        let b = Box::into_raw(self);
407        unsafe { PathBuf { data: Box::from_raw(b as *mut OsStr).into_os_string() } }
408    }
409    
410    fn from_os_str(os_str: &OsStr) -> &Path {
411        // SAFETY: This is just a OsStr anyway!
412        unsafe { &*(os_str as *const OsStr as *const Path) }
413    }
414
415    fn remove_extraneous_suffixes(&self) -> &Path {
416        let without_root = self.as_relative_to_root();
417        let mut i = without_root.components();
418
419        loop {
420            let Some(a) = i.next_back() else {
421                // SAFETY: Part of the string
422                return unsafe { self.trim_to_end_of(without_root.as_os_str().as_str()[0..].as_ref()) };
423            };
424
425            // SAFETY: Part of the string
426            return unsafe { self.trim_to_end_of(a.as_ref()) };
427        }
428    }
429
430    fn as_relative_to_root(&self) -> &Path {
431        let as_str = self.inner.as_str();
432        let mut as_str_chars = as_str.chars();
433
434        let Some(first_char) = as_str_chars.next() else {
435            // empty?
436            return self
437        };
438
439        if is_separator(first_char) {
440            if self.is_absolute() {
441                let a = &as_str[2..];
442                let (index, _) = a.char_indices().find(|i| is_separator(i.1)).unwrap();
443                a[index + 1..].as_ref()
444            }
445            else {
446                AsRef::<Path>::as_ref(&as_str[1..]).as_relative_to_root()
447            }
448        }
449
450        else if self.has_drive_letter() {
451            if self.is_absolute() {
452                // C:\...
453                as_str[3..].as_ref()
454            }
455            else {
456                // C:...
457                as_str[2..].as_ref()
458            }
459        }
460
461        else if self.is_relative() {
462            self
463        }
464
465        else {
466            unreachable!()
467        }
468    }
469
470    // SAFETY: `path` must be part of `self`
471    unsafe fn trim_to_end_of(&self, path: &Path) -> &Path {
472        let end = path.as_os_str().as_encoded_bytes().as_ptr_range().end;
473        let start = self.as_os_str().as_encoded_bytes().as_ptr_range().start;
474        let actual_length = (end as usize) - (start as usize);
475
476        // SAFETY: These are part of the same string
477        unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(start, actual_length)).as_ref() }
478    }
479
480    unsafe fn trim_to_start_of(&self, path: &Path) -> &Path {
481        let end = path.as_os_str().as_encoded_bytes().as_ptr_range().start;
482        let start = self.as_os_str().as_encoded_bytes().as_ptr_range().start;
483        let actual_length = (end as usize) - (start as usize);
484
485        // SAFETY: These are part of the same string
486        unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(start, actual_length)).as_ref() }
487    }
488
489    unsafe fn rtrim_to_start_of(&self, path: &Path) -> &Path {
490        let start = path.as_os_str().as_encoded_bytes().as_ptr_range().start;
491        let end = self.as_os_str().as_encoded_bytes().as_ptr_range().end;
492        let actual_length = (end as usize) - (start as usize);
493
494        // SAFETY: These are part of the same string
495        unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(start, actual_length)).as_ref() }
496    }
497
498    pub(crate) fn encode_for_win32(&self) -> Vec<u16> {
499        // Some Windows functions violently explode if the path is just "." or ".."
500        match self.as_os_str().as_str() {
501            "." => return current_dir().unwrap().encode_for_win32(),
502            ".." => return current_dir().unwrap().parent().and_then(|p| Some(p.encode_for_win32())).unwrap_or_else(|| vec!['.' as u16, '.' as u16, 0]),
503            _ => ()
504        }
505
506        let s = self.inner.as_str();
507        let mut c = Vec::with_capacity(self.inner.len() * 2 + 1);
508
509        for i in self.components() {
510            if !c.is_empty() {
511                c.extend_from_slice(MAIN_SEPARATOR.encode_utf16(&mut [0,0]));
512            }
513            c.extend(i.as_str().encode_utf16());
514        }
515        c.push(0);
516        c.shrink_to_fit();
517        c
518    }
519
520    pub(crate) fn path_len(&self) -> usize {
521        self.as_os_str().as_str().len()
522    }
523}
524
525impl From<String> for PathBuf {
526    fn from(value: String) -> Self {
527        PathBuf::from_string(value)
528    }
529}
530
531impl Deref for PathBuf {
532    type Target = Path;
533    fn deref(&self) -> &Self::Target {
534        Path::from_os_str(&self.data)
535    }
536}
537
538impl Borrow<Path> for PathBuf {
539    fn borrow(&self) -> &Path {
540        Path::from_os_str(&self.data)
541    }
542}
543
544impl ToOwned for Path {
545    type Owned = PathBuf;
546    fn to_owned(&self) -> Self::Owned {
547        PathBuf { data: self.inner.to_os_string() }
548    }
549}
550
551impl AsRef<Path> for Path {
552    fn as_ref(&self) -> &Path {
553        self
554    }
555}
556
557impl AsRef<Path> for OsStr {
558    fn as_ref(&self) -> &Path {
559        Path::from_os_str(self)
560    }
561}
562
563impl AsRef<Path> for OsString {
564    fn as_ref(&self) -> &Path {
565        Path::from_os_str(self)
566    }
567}
568
569impl AsRef<Path> for PathBuf {
570    fn as_ref(&self) -> &Path {
571        self.as_path()
572    }
573}
574
575impl AsRef<Path> for String {
576    fn as_ref(&self) -> &Path {
577        Path::from_os_str(self.as_ref())
578    }
579}
580
581impl AsRef<Path> for str {
582    fn as_ref(&self) -> &Path {
583        Path::from_os_str(self.as_ref())
584    }
585}
586
587#[derive(Clone, Debug)]
588pub struct StripPrefixError {
589
590}
591
592pub struct Iter<'a> {
593    iterated_amount: usize,
594    original_string: &'a str,
595    iter: core::str::Split<'a, fn(char) -> bool>
596}
597
598impl<'a> Iter<'a> {
599    pub fn as_path(&self) -> &Path {
600        self.original_string[self.iterated_amount..].as_ref()
601    }
602}
603
604impl<'a> Iterator for Iter<'a> {
605    type Item = &'a OsStr;
606    fn next(&mut self) -> Option<Self::Item> {
607        let string = self.iter.next().map(OsStr::from_str)?;
608        self.iterated_amount += string.len() + 1;
609        Some(string)
610    }
611}
612
613impl<'a> DoubleEndedIterator for Iter<'a> {
614    fn next_back(&mut self) -> Option<Self::Item> {
615        self.iter.next_back().map(OsStr::from_str)
616    }
617}
618
619impl<'a> IntoIterator for &'a Path {
620    type Item = &'a OsStr;
621    type IntoIter = Iter<'a>;
622    fn into_iter(self) -> Self::IntoIter {
623        self.iter()
624    }
625}
626
627impl<'a> IntoIterator for &'a PathBuf {
628    type Item = &'a OsStr;
629    type IntoIter = Iter<'a>;
630    fn into_iter(self) -> Self::IntoIter {
631        self.iter()
632    }
633}
634
635pub struct Components<'a> {
636    iter: Iter<'a>
637}
638
639impl<'a> Iterator for Components<'a> {
640    type Item = &'a OsStr;
641    fn next(&mut self) -> Option<Self::Item> {
642        loop {
643            let i = self.iter.next()?;
644            if i.as_str() == "" || i.as_str() == "." {
645                continue
646            }
647            return Some(i)
648        }
649    }
650}
651
652impl<'a> DoubleEndedIterator for Components<'a> {
653    fn next_back(&mut self) -> Option<Self::Item> {
654        loop {
655            let i = self.iter.next_back()?;
656            if i.as_str() == "" || i.as_str() == "." {
657                continue
658            }
659            return Some(i)
660        }
661    }
662}
663
664pub struct Ancestors<'a> {
665    path: &'a Path
666}
667
668impl<'a> Iterator for Ancestors<'a> {
669    type Item = &'a Path;
670    fn next(&mut self) -> Option<Self::Item> {
671        self.path = self.path.parent()?;
672        Some(self.path)
673    }
674}
675
676impl From<&Path> for Arc<Path> {
677    fn from(value: &Path) -> Self {
678        value.to_path_buf().into_boxed_path().into()
679    }
680}
681
682impl From<&Path> for Box<Path> {
683    fn from(value: &Path) -> Self {
684        value.to_path_buf().into_boxed_path()
685    }
686}
687
688impl From<&Path> for Rc<Path> {
689    fn from(value: &Path) -> Self {
690        value.to_path_buf().into_boxed_path().into()
691    }
692}
693
694impl<'a> From<&'a Path> for Cow<'a, Path> {
695    fn from(value: &'a Path) -> Self {
696        Cow::Borrowed(value)
697    }
698}
699
700impl From<&mut Path> for Arc<Path> {
701    fn from(value: &mut Path) -> Self {
702        Arc::from(&*value)
703    }
704}
705
706impl From<&mut Path> for Box<Path> {
707    fn from(value: &mut Path) -> Self {
708        Box::from(&*value)
709    }
710}
711
712impl From<&mut Path> for Rc<Path> {
713    fn from(value: &mut Path) -> Self {
714        Rc::from(&*value)
715    }
716}
717
718impl AsRef<OsStr> for Path {
719    fn as_ref(&self) -> &OsStr {
720        self.as_os_str()
721    }
722}
723
724impl From<Cow<'_, Path>> for Box<Path> {
725    fn from(value: Cow<'_, Path>) -> Self {
726        match value {
727            Cow::Borrowed(p) => p.to_path_buf().into_boxed_path(),
728            Cow::Owned(p) => p.into_boxed_path()
729        }
730    }
731}
732
733impl Clone for Box<Path> {
734    fn clone(&self) -> Self {
735        self.to_path_buf().into_boxed_path()
736    }
737}
738
739impl AsRef<Path> for Components<'_> {
740    fn as_ref(&self) -> &Path {
741        self.iter.as_path()
742    }
743}
744
745impl From<PathBuf> for Box<Path> {
746    fn from(value: PathBuf) -> Self {
747        value.into_boxed_path()
748    }
749}
750
751#[cfg(test)]
752mod test;