Skip to main content

vfat_rs/formats/
path.rs

1use alloc::string::String;
2use core::iter;
3
4/// A simple implementation of PathBuf. Vfat uses utf8/utf16 for encoding: https://wiki.gentoo.org/wiki/FAT/en#UTF-8.2FUTF-16_character_hardware_bugs
5/// therefore it's ok to use a String as a baking data structure.
6#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
7pub struct PathBuf(pub String);
8
9impl PathBuf {
10    /// Create a new `PathBuf` from a string-like value.
11    pub fn new<S: AsRef<str>>(path: S) -> Self {
12        Self(String::from(path.as_ref()))
13    }
14    /// Returns an iterator over the path components.
15    pub fn iter(&self) -> impl Iterator<Item = &str> {
16        iter::once("/").chain(self.0[1..].split_terminator('/'))
17    }
18    /// Returns the path as a string slice.
19    pub fn to_str(&self) -> &str {
20        self.0.as_str()
21    }
22    /// Returns a displayable string slice.
23    pub fn display(&self) -> &str {
24        self.to_str()
25    }
26    /// Returns `true` if the path starts with `/`.
27    pub fn is_absolute(&self) -> bool {
28        self.0.starts_with('/')
29    }
30    /// Returns the parent directory path, or None if this is the root.
31    pub fn parent(&self) -> Option<PathBuf> {
32        if self.0 == "/" {
33            return None;
34        }
35        let trimmed = self.0.trim_end_matches('/');
36        match trimmed.rfind('/') {
37            Some(0) => Some(PathBuf::from("/")),
38            Some(pos) => Some(PathBuf::from(&trimmed[..pos])),
39            None => None,
40        }
41    }
42    /// Returns the final component of the path.
43    pub fn file_name(&self) -> Option<&str> {
44        let trimmed = self.0.trim_end_matches('/');
45        if trimmed.is_empty() {
46            return None;
47        }
48        match trimmed.rfind('/') {
49            Some(pos) => {
50                let name = &trimmed[pos + 1..];
51                if name.is_empty() { None } else { Some(name) }
52            }
53            None => Some(trimmed),
54        }
55    }
56    /// Returns true if `self` starts with the given base path.
57    pub fn starts_with(&self, base: &PathBuf) -> bool {
58        let self_trimmed = self.0.trim_end_matches('/');
59        let base_trimmed = base.0.trim_end_matches('/');
60        if base_trimmed == "/" {
61            return self_trimmed.starts_with('/');
62        }
63        self_trimmed == base_trimmed
64            || self_trimmed.starts_with(&alloc::format!("{}/", base_trimmed))
65    }
66}
67impl core::fmt::Display for PathBuf {
68    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        write!(f, "{}", self.0)
70    }
71}
72impl PartialEq<String> for &PathBuf {
73    fn eq(&self, other: &String) -> bool {
74        other.as_str() == self.0.as_str()
75    }
76}
77impl PartialEq<&str> for &PathBuf {
78    fn eq(&self, other: &&str) -> bool {
79        *other == self.0.as_str()
80    }
81}
82impl PartialEq<&str> for PathBuf {
83    fn eq(&self, other: &&str) -> bool {
84        *other == self.0.as_str()
85    }
86}
87
88impl From<&str> for PathBuf {
89    fn from(s: &str) -> Self {
90        Self::new(s)
91    }
92}
93impl From<String> for PathBuf {
94    fn from(s: String) -> Self {
95        Self::new(s)
96    }
97}