standard_lib/fs/path/
path.rs

1use std::borrow::{Borrow, Cow};
2use std::cmp::Ordering;
3use std::ffi::{CString, OsStr, OsString};
4use std::fmt::{self, Debug, Formatter};
5use std::marker::PhantomData;
6use std::mem;
7use std::num::NonZero;
8use std::ops::Deref;
9use std::os::unix::ffi::{OsStrExt, OsStringExt};
10
11use super::{DisplayPath, Rel};
12use crate::collections::contiguous::Vector;
13use crate::fs::path::{Ancestors, Components, validity};
14use crate::util::{self, sealed::Sealed};
15
16pub trait PathState: Sealed + Debug {}
17
18pub struct OwnedPath<State: PathState> {
19    pub(crate) _state: PhantomData<fn() -> State>,
20    pub(crate) inner: OsString,
21}
22
23#[repr(transparent)]
24pub struct Path<State: PathState> {
25    pub(crate) _state: PhantomData<fn() -> State>,
26    pub(crate) inner: OsStr,
27}
28
29impl<T: AsRef<OsStr>, S: PathState> From<T> for OwnedPath<S> {
30    fn from(value: T) -> Self {
31        Self {
32            _state: PhantomData,
33            inner: validity::sanitize(value.as_ref()),
34        }
35    }
36}
37
38impl<S: PathState> OwnedPath<S> {
39    pub unsafe fn from_unchecked<O: Into<OsString>>(inner: O) -> Self {
40        Self {
41            _state: PhantomData,
42            inner: inner.into(),
43        }
44    }
45
46    pub fn as_path(&self) -> &Path<S> {
47        self
48    }
49
50    pub fn push<P: AsRef<Path<Rel>>>(&mut self, other: P) {
51        let other_path = other.as_ref();
52        let mut vec: Vector<u8> = mem::take(&mut self.inner).into_vec().into();
53        vec.reserve(other_path.len().get());
54        vec.extend(other_path.inner.as_bytes().iter().cloned());
55        let _ = mem::replace(
56            &mut self.inner,
57            OsString::from_vec(vec.into())
58        );
59    }
60
61    pub fn pop(&mut self) -> Option<OwnedPath<Rel>> {
62        // OsString doesn't make guarantees about its layout as a Vec, so we have to take and
63        // convert it. Using mem::take replaces the value with an empty Vec, which avoids an
64        // allocation. However, "" is not a valid path, so this function isn't currently unwind
65        // safe. It would be dumb to perform an allocation so that OsString's internal Vec can be
66        // taken and returned shortly after.
67        // TODO: Make this unwind safe.
68        let mut bytes = mem::take(&mut self.inner).into_vec();
69
70        let mut index = bytes.len().checked_sub(1)?;
71
72        while let Some(ch) = bytes.get(index) && *ch != b'/' {
73            index = index.checked_sub(1)?;
74        }
75
76        let split = bytes.split_off(index);
77
78        drop(mem::replace(&mut self.inner, OsString::from_vec(bytes)));
79
80        Some(OwnedPath::<Rel> {
81            _state: PhantomData,
82            inner: OsString::from_vec(split),
83        })
84    }
85}
86
87impl<S: PathState> Path<S> {
88    pub fn new<'a, O: AsRef<OsStr> + ?Sized>(value: &'a O) -> Cow<'a, Path<S>> {
89        match validity::validate(value.as_ref()) {
90            Some(_) => Cow::Borrowed(unsafe { Path::from_unchecked(value) }),
91            None    => Cow::Owned(OwnedPath::from(value)),
92        }
93    }
94
95    pub fn from_checked<O: AsRef<OsStr> + ?Sized>(value: &O) -> Option<&Path<S>> {
96        validity::validate(value.as_ref())?;
97        Some(unsafe { Path::from_unchecked(value) })
98    }
99
100    pub unsafe fn from_unchecked<O: AsRef<OsStr> + ?Sized>(value: &O) -> &Self {
101        // SAFETY: Path<S> is `repr(transparent)`, so to it has the same layout as OsStr.
102        unsafe { &*(value.as_ref() as *const OsStr as *const Self) }
103    }
104
105    pub unsafe fn from_unchecked_mut<O: AsMut<OsStr> + ?Sized>(value: &mut O) -> &mut Self {
106        // SAFETY: Path<S> is `repr(transparent)`, so to it has the same layout as OsStr.
107        unsafe { &mut *(value.as_mut() as *mut OsStr as *mut Self) }
108    }
109
110    pub const fn display<'a>(&'a self) -> DisplayPath<'a, S> {
111        DisplayPath::<S> {
112            _phantom: PhantomData,
113            inner: self,
114        }
115    }
116
117    pub fn len(&self) -> NonZero<usize> {
118        unsafe { NonZero::new(self.inner.len()).unwrap_unchecked() }
119    }
120
121    pub const fn as_os_str(&self) -> &OsStr {
122        &self.inner
123    }
124
125    pub fn as_os_str_no_lead(&self) -> &OsStr {
126        OsStr::from_bytes(&self.as_bytes()[1..])
127    }
128
129    pub fn as_bytes(&self) -> &[u8] {
130        self.inner.as_bytes()
131    }
132
133    // TODO: no_lead methods
134
135    /// Returns the basename of this path (the OsStr following the last `/` in the path). This OsStr
136    /// won't contain any instances of `/`.
137    /// 
138    /// See [`parent()`](Path::parent) for more info.
139    pub fn basename(&self) -> &OsStr {
140        let bytes = self.as_bytes();
141
142        let mut index = bytes.len() - 1;
143
144        while let Some(ch) = bytes.get(index) && *ch != b'/' {
145            index -= 1;
146        }
147
148        OsStr::from_bytes(&bytes[(index + 1)..])
149    }
150
151    /// Returns the parent directory of this path (lexically speaking). The result is a Path with
152    /// basename and the preceding slash removed, such that the following holds for any `path`.
153    /// 
154    /// ```
155    /// # use standard_lib::fs::path::Path;
156    /// let owned = OwnedPath::<Abs>::from("/my/path");
157    /// let path: &Path<Abs> = &owned;
158    /// let new_path = path.parent().join(Path::new(path.basename()));
159    /// assert_eq!(path, new_path);
160    /// ```
161    /// 
162    /// Because this method is the counterpart of [`basename`](Path::basename) and `basename` won't
163    /// contain any `/`, the behavior when calling these methods on `"/"` is as follows:
164    /// 
165    /// ```
166    /// # use standard_lib::fs::path::Path;
167    /// assert_eq!(Path::root().basename(), "");
168    /// assert_eq!(Path::root().parent(), Path::root());
169    /// ```
170    ///
171    /// This behavior is also consistent with Unix defaults: the `..` entry in the root directory
172    /// refers to the root itself.
173    pub fn parent(&self) -> &Self {
174        let bytes = self.as_bytes();
175
176        let mut index = bytes.len() - 1;
177
178        while let Some(ch) = bytes.get(index) && *ch != b'/' {
179            index -= 1;
180        }
181        
182        // If we would return an empty string, instead include the first slash representing the
183        // absolute or relative root.
184        if index == 0 {
185            index = 1;
186        }
187
188        unsafe { Path::from_unchecked(OsStr::from_bytes(&bytes[..index])) }
189    }
190
191    pub fn join<P: AsRef<Path<Rel>>>(&self, other: P) -> OwnedPath<S> {
192        unsafe {
193            OwnedPath::<S>::from_unchecked(
194                [self.as_os_str(), other.as_ref().as_os_str()].into_iter().collect::<OsString>()
195            )
196        }
197    }
198
199    pub fn relative_to(&self, other: &Self) -> Option<&Path<Rel>> {
200        // As a general note for path interpretation: paths on Linux have no encoding, with the only
201        // constant being that they are delimited by b'/'. Because of this, we don't have to
202        // consider encoding, and splitting by b"/" is always entirely valid because thats what
203        // Linux does, even if b'/' is a later part of a variable-length character.
204        match self.inner.as_bytes().strip_prefix(other.inner.as_bytes()) {
205            None => None,
206            // If there is no leading slash, strip_prefix matched only part of a component so
207            // treat it as a fail.
208            Some(replaced) if !replaced.starts_with(b"/") => None,
209            // SAFETY: If the relative path starts with a b"/", then it is still a valid Path.
210            Some(replaced) => unsafe {
211                Some(Path::<Rel>::from_unchecked(OsStr::from_bytes(replaced)))
212            },
213        }
214    }
215
216    /// Creates an [`Iterator`] over the components of a `Path`. This iterator produces `Path<Rel>`s
217    /// representing each `/`-separated string in the Path, from left to right.
218    pub fn components<'a>(&'a self) -> Components<'a, S> {
219        Components {
220            _state: PhantomData,
221            path: self.as_bytes(),
222            head: 0,
223        }
224    }
225
226    /// Creates an [`Iterator`] over the ancestors of a `Path`. This iterator produces `Path<S>`s
227    /// representing each directory in the Path ordered with descending depth and ending with the
228    /// Path itself.
229    pub fn ancestors<'a>(&'a self) -> Ancestors<'a, S> {
230        Ancestors {
231            _state: PhantomData,
232            path: self.as_bytes(),
233            index: 0,
234        }
235    }
236}
237
238impl<S: PathState> From<OwnedPath<S>> for CString {
239    fn from(value: OwnedPath<S>) -> Self {
240        // SAFETY: OsString already guarantees that the internal string contains no '\0'.
241        unsafe { CString::from_vec_unchecked(value.inner.into_vec()) }
242    }
243}
244
245impl<S: PathState> Deref for OwnedPath<S> {
246    type Target = Path<S>;
247
248    fn deref(&self) -> &Self::Target {
249        // SAFETY: OwnedPath upholds the same invariants as Path.
250        unsafe { Path::<S>::from_unchecked(&self.inner) }
251    }
252}
253
254impl<S: PathState> From<&Path<S>> for OwnedPath<S> {
255    fn from(value: &Path<S>) -> Self {
256        value.to_owned()
257    }
258}
259
260impl<S: PathState> AsRef<Path<S>> for OwnedPath<S> {
261    fn as_ref(&self) -> &Path<S> {
262        self.deref()
263    }
264}
265
266// Apparently there isn't a blanket impl for this?
267impl<S: PathState> AsRef<Path<S>> for Path<S> {
268    fn as_ref(&self) -> &Path<S> {
269        self
270    }
271}
272
273impl<S: PathState> Borrow<Path<S>> for OwnedPath<S> {
274    fn borrow(&self) -> &Path<S> {
275        self.as_ref()
276    }
277}
278
279// AsRef<OsStr> causes conflicting implementations and makes it slightly too easy to interpret a
280// Path as an OsStr. The same functionality has been moved to Path::as_os_str(), which requires
281// explicit usage. Otherwise, users can accidentally convert between Path<Abs> and Path<Rel>.
282// impl<S: PathState> AsRef<OsStr> for Path<S> {
283//     fn as_ref(&self) -> &OsStr {
284//         &self.inner
285//     }
286// }
287
288impl<S: PathState> ToOwned for Path<S> {
289    type Owned = OwnedPath<S>;
290
291    fn to_owned(&self) -> Self::Owned {
292        OwnedPath::<S> {
293            _state: PhantomData,
294            inner: self.as_os_str().to_owned(),
295        }
296    }
297}
298
299impl<S: PathState> Clone for OwnedPath<S> {
300    fn clone(&self) -> Self {
301        Self {
302            _state: PhantomData,
303            inner: self.inner.clone()
304        }
305    }
306}
307
308impl<S: PathState> PartialEq for OwnedPath<S> {
309    fn eq(&self, other: &Self) -> bool {
310        self.as_ref().inner == other.as_ref().inner
311    }
312}
313
314impl<S: PathState> PartialEq for Path<S> {
315    fn eq(&self, other: &Self) -> bool {
316        self.inner == other.inner
317    }
318}
319
320impl<S: PathState> PartialEq<Path<S>> for OwnedPath<S> {
321    fn eq(&self, other: &Path<S>) -> bool {
322        self.as_ref().inner == other.inner
323    }
324}
325
326impl<S: PathState> PartialEq<OwnedPath<S>> for Path<S> {
327    fn eq(&self, other: &OwnedPath<S>) -> bool {
328        self.inner == other.as_ref().inner
329    }
330}
331
332impl<S: PathState> Eq for OwnedPath<S> {}
333
334impl<S: PathState> Eq for Path<S> {}
335
336impl<S: PathState> PartialOrd for OwnedPath<S> {
337    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
338        Some(self.cmp(other))
339    }
340}
341
342impl<S: PathState> Ord for OwnedPath<S> {
343    fn cmp(&self, other: &Self) -> Ordering {
344        self.as_ref().inner.cmp(&other.as_ref().inner)
345    }
346}
347
348impl<S: PathState> PartialOrd for Path<S> {
349    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
350        Some(self.cmp(other))
351    }
352}
353
354impl<S: PathState> Ord for Path<S> {
355    fn cmp(&self, other: &Self) -> Ordering {
356        self.inner.cmp(&other.inner)
357    }
358}
359
360impl<S: PathState> PartialOrd<Path<S>> for OwnedPath<S> {
361    fn partial_cmp(&self, other: &Path<S>) -> Option<Ordering> {
362        Some(self.as_ref().cmp(other))
363    }
364}
365
366impl<S: PathState> PartialOrd<OwnedPath<S>> for Path<S> {
367    fn partial_cmp(&self, other: &OwnedPath<S>) -> Option<Ordering> {
368        Some(self.cmp(other.as_ref()))
369    }
370}
371
372impl<S: PathState> Debug for OwnedPath<S> {
373    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
374        f.debug_struct("OwnedPath")
375            .field("<state>", &util::fmt::raw_type_name::<S>())
376            .field("value", &self.inner)
377            .finish()
378    }
379}
380
381impl<S: PathState> Debug for Path<S> {
382    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
383        f.debug_struct("Path")
384            .field("<state>", &util::fmt::raw_type_name::<S>())
385            .field("value", &&self.inner)
386            .finish()
387    }
388}