typed_path/common/utf8/
pathbuf.rs

1use alloc::borrow::Cow;
2use alloc::collections::TryReserveError;
3use alloc::string::FromUtf8Error;
4use core::borrow::Borrow;
5use core::hash::{Hash, Hasher};
6use core::iter::{Extend, FromIterator};
7use core::marker::PhantomData;
8use core::ops::Deref;
9use core::str::FromStr;
10use core::{cmp, fmt};
11
12use crate::no_std_compat::*;
13use crate::{CheckedPathError, Encoding, PathBuf, Utf8Encoding, Utf8Iter, Utf8Path};
14
15/// An owned, mutable path that mirrors [`std::path::PathBuf`], but operatings using a
16/// [`Utf8Encoding`] to determine how to parse the underlying str.
17///
18/// This type provides methods like [`push`] and [`set_extension`] that mutate
19/// the path in place. It also implements [`Deref`] to [`Utf8Path`], meaning that
20/// all methods on [`Utf8Path`] slices are available on `Utf8PathBuf` values as well.
21///
22/// [`push`]: Utf8PathBuf::push
23/// [`set_extension`]: Utf8PathBuf::set_extension
24///
25/// # Examples
26///
27/// You can use [`push`] to build up a `Utf8PathBuf` from
28/// components:
29///
30/// ```
31/// use typed_path::{Utf8PathBuf, Utf8WindowsEncoding};
32///
33/// // NOTE: A pathbuf cannot be created on its own without a defined encoding
34/// let mut path = Utf8PathBuf::<Utf8WindowsEncoding>::new();
35///
36/// path.push(r"C:\");
37/// path.push("windows");
38/// path.push("system32");
39///
40/// path.set_extension("dll");
41/// ```
42///
43/// However, [`push`] is best used for dynamic situations. This is a better way
44/// to do this when you know all of the components ahead of time:
45///
46/// ```
47/// use typed_path::{Utf8PathBuf, Utf8WindowsEncoding};
48///
49/// let path: Utf8PathBuf<Utf8WindowsEncoding> = [
50///     r"C:\",
51///     "windows",
52///     "system32.dll",
53/// ].iter().collect();
54/// ```
55///
56/// We can still do better than this! Since these are all strings, we can use
57/// `From::from`:
58///
59/// ```
60/// use typed_path::{Utf8PathBuf, Utf8WindowsEncoding};
61///
62/// let path = Utf8PathBuf::<Utf8WindowsEncoding>::from(r"C:\windows\system32.dll");
63/// ```
64///
65/// Which method works best depends on what kind of situation you're in.
66pub struct Utf8PathBuf<T>
67where
68    T: Utf8Encoding,
69{
70    /// Encoding associated with path buf
71    pub(crate) _encoding: PhantomData<T>,
72
73    /// Path as an unparsed string
74    pub(crate) inner: String,
75}
76
77impl<T> Utf8PathBuf<T>
78where
79    T: Utf8Encoding,
80{
81    /// Allocates an empty `Utf8PathBuf`.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
87    ///
88    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
89    /// let path = Utf8PathBuf::<Utf8UnixEncoding>::new();
90    /// ```
91    pub fn new() -> Self {
92        Utf8PathBuf {
93            _encoding: PhantomData,
94            inner: String::new(),
95        }
96    }
97
98    /// Creates a new `PathBuf` with a given capacity used to create the
99    /// internal [`String`]. See [`with_capacity`] defined on [`String`].
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
105    ///
106    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
107    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::with_capacity(10);
108    /// let capacity = path.capacity();
109    ///
110    /// // This push is done without reallocating
111    /// path.push(r"C:\");
112    ///
113    /// assert_eq!(capacity, path.capacity());
114    /// ```
115    ///
116    /// [`with_capacity`]: String::with_capacity
117    #[inline]
118    pub fn with_capacity(capacity: usize) -> Self {
119        Utf8PathBuf {
120            _encoding: PhantomData,
121            inner: String::with_capacity(capacity),
122        }
123    }
124
125    /// Coerces to a [`Utf8Path`] slice.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
131    ///
132    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
133    /// let p = Utf8PathBuf::<Utf8UnixEncoding>::from("/test");
134    /// assert_eq!(Utf8Path::new("/test"), p.as_path());
135    /// ```
136    #[inline]
137    pub fn as_path(&self) -> &Utf8Path<T> {
138        self
139    }
140
141    /// Extends `self` with `path`.
142    ///
143    /// If `path` is absolute, it replaces the current path.
144    ///
145    /// With [`Utf8WindowsPathBuf`]:
146    ///
147    /// * if `path` has a root but no prefix (e.g., `\windows`), it
148    ///   replaces everything except for the prefix (if any) of `self`.
149    /// * if `path` has a prefix but no root, it replaces `self`.
150    /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`)
151    ///   and `path` is not empty, the new path is normalized: all references
152    ///   to `.` and `..` are removed.
153    ///
154    /// [`Utf8WindowsPathBuf`]: crate::Utf8WindowsPathBuf
155    ///
156    /// # Examples
157    ///
158    /// Pushing a relative path extends the existing path:
159    ///
160    /// ```
161    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
162    ///
163    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
164    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::from("/tmp");
165    /// path.push("file.bk");
166    /// assert_eq!(path, Utf8PathBuf::from("/tmp/file.bk"));
167    /// ```
168    ///
169    /// Pushing an absolute path replaces the existing path:
170    ///
171    /// ```
172    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
173    ///
174    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
175    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::from("/tmp");
176    /// path.push("/etc");
177    /// assert_eq!(path, Utf8PathBuf::from("/etc"));
178    /// ```
179    pub fn push<P: AsRef<Utf8Path<T>>>(&mut self, path: P) {
180        T::push(&mut self.inner, path.as_ref().as_str());
181    }
182
183    /// Like [`Utf8PathBuf::push`], extends `self` with `path`, but also checks to ensure that
184    /// `path` abides by a set of rules.
185    ///
186    /// # Rules
187    ///
188    /// 1. `path` cannot contain a prefix component.
189    /// 2. `path` cannot contain a root component.
190    /// 3. `path` cannot contain invalid filename bytes.
191    /// 4. `path` cannot contain parent components such that the current path would be escaped.
192    ///
193    /// # Examples
194    ///
195    /// Pushing a relative path extends the existing path:
196    ///
197    /// ```
198    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
199    ///
200    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
201    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::from("/tmp");
202    ///
203    /// // Pushing a relative path works like normal
204    /// assert!(path.push_checked("file.bk").is_ok());
205    /// assert_eq!(path, Utf8PathBuf::from("/tmp/file.bk"));
206    /// ```
207    ///
208    /// Pushing a relative path that contains unresolved parent directory references fails
209    /// with an error:
210    ///
211    /// ```
212    /// use typed_path::{CheckedPathError, Utf8PathBuf, Utf8UnixEncoding};
213    ///
214    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
215    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::from("/tmp");
216    ///
217    /// // Pushing a relative path that contains parent directory references that cannot be
218    /// // resolved within the path is considered an error as this is considered a path
219    /// // traversal attack!
220    /// assert_eq!(path.push_checked(".."), Err(CheckedPathError::PathTraversalAttack));
221    /// assert_eq!(path, Utf8PathBuf::from("/tmp"));
222    /// ```
223    ///
224    /// Pushing an absolute path fails with an error:
225    ///
226    /// ```
227    /// use typed_path::{CheckedPathError, Utf8PathBuf, Utf8UnixEncoding};
228    ///
229    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
230    /// let mut path = Utf8PathBuf::<Utf8UnixEncoding>::from("/tmp");
231    ///
232    /// // Pushing an absolute path will fail with an error
233    /// assert_eq!(path.push_checked("/etc"), Err(CheckedPathError::UnexpectedRoot));
234    /// assert_eq!(path, Utf8PathBuf::from("/tmp"));
235    /// ```
236    pub fn push_checked<P: AsRef<Utf8Path<T>>>(&mut self, path: P) -> Result<(), CheckedPathError> {
237        T::push_checked(&mut self.inner, path.as_ref().as_str())
238    }
239
240    /// Truncates `self` to [`self.parent`].
241    ///
242    /// Returns `false` and does nothing if [`self.parent`] is [`None`].
243    /// Otherwise, returns `true`.
244    ///
245    /// [`self.parent`]: Utf8Path::parent
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
251    ///
252    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
253    /// let mut p = Utf8PathBuf::<Utf8UnixEncoding>::from("/spirited/away.rs");
254    ///
255    /// p.pop();
256    /// assert_eq!(Utf8Path::new("/spirited"), p);
257    /// p.pop();
258    /// assert_eq!(Utf8Path::new("/"), p);
259    /// ```
260    pub fn pop(&mut self) -> bool {
261        match self.parent().map(|p| p.as_str().len()) {
262            Some(len) => {
263                self.inner.truncate(len);
264                true
265            }
266            None => false,
267        }
268    }
269
270    /// Updates [`self.file_name`] to `file_name`.
271    ///
272    /// If [`self.file_name`] was [`None`], this is equivalent to pushing
273    /// `file_name`.
274    ///
275    /// Otherwise it is equivalent to calling [`pop`] and then pushing
276    /// `file_name`. The new path will be a sibling of the original path.
277    /// (That is, it will have the same parent.)
278    ///
279    /// [`self.file_name`]: Utf8Path::file_name
280    /// [`pop`]: Utf8PathBuf::pop
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
286    ///
287    /// // NOTE: A pathbuf cannot be created on its own without a defined encoding
288    /// let mut buf = Utf8PathBuf::<Utf8UnixEncoding>::from("/");
289    /// assert!(buf.file_name() == None);
290    /// buf.set_file_name("bar");
291    /// assert!(buf == Utf8PathBuf::from("/bar"));
292    /// assert!(buf.file_name().is_some());
293    /// buf.set_file_name("baz.txt");
294    /// assert!(buf == Utf8PathBuf::from("/baz.txt"));
295    /// ```
296    pub fn set_file_name<S: AsRef<str>>(&mut self, file_name: S) {
297        self._set_file_name(file_name.as_ref())
298    }
299
300    fn _set_file_name(&mut self, file_name: &str) {
301        if self.file_name().is_some() {
302            let popped = self.pop();
303            debug_assert!(popped);
304        }
305        self.push(file_name);
306    }
307
308    /// Updates [`self.extension`] to `extension`.
309    ///
310    /// Returns `false` and does nothing if [`self.file_name`] is [`None`],
311    /// returns `true` and updates the extension otherwise.
312    ///
313    /// If [`self.extension`] is [`None`], the extension is added; otherwise
314    /// it is replaced.
315    ///
316    /// [`self.file_name`]: Utf8Path::file_name
317    /// [`self.extension`]: Utf8Path::extension
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
323    ///
324    /// let mut p = Utf8PathBuf::<Utf8UnixEncoding>::from("/feel/the");
325    ///
326    /// p.set_extension("force");
327    /// assert_eq!(Utf8Path::new("/feel/the.force"), p.as_path());
328    ///
329    /// p.set_extension("dark_side");
330    /// assert_eq!(Utf8Path::new("/feel/the.dark_side"), p.as_path());
331    /// ```
332    pub fn set_extension<S: AsRef<str>>(&mut self, extension: S) -> bool {
333        self._set_extension(extension.as_ref())
334    }
335
336    fn _set_extension(&mut self, extension: &str) -> bool {
337        if self.file_stem().is_none() {
338            return false;
339        }
340
341        let old_ext_len = self.extension().map(|ext| ext.len()).unwrap_or(0);
342
343        // Truncate to remove the extension
344        if old_ext_len > 0 {
345            self.inner.truncate(self.inner.len() - old_ext_len);
346
347            // If we end with a '.' now from the previous extension, remove that too
348            if self.inner.ends_with('.') {
349                self.inner.pop();
350            }
351        }
352
353        // Add the new extension if it exists
354        if !extension.is_empty() {
355            // Add a '.' at the end prior to adding the extension
356            if !self.inner.ends_with('.') {
357                self.inner.push('.');
358            }
359
360            self.inner.push_str(extension);
361        }
362
363        true
364    }
365
366    /// Consumes the `PathBuf`, yielding its internal [`String`] storage.
367    ///
368    /// # Examples
369    ///
370    /// ```
371    /// use typed_path::{Utf8PathBuf, Utf8UnixEncoding};
372    ///
373    /// let p = Utf8PathBuf::<Utf8UnixEncoding>::from("/the/head");
374    /// let s = p.into_string();
375    /// ```
376    #[inline]
377    pub fn into_string(self) -> String {
378        self.inner
379    }
380
381    /// Converts this [`Utf8PathBuf`] into a [boxed](Box) [`Utf8Path`].
382    #[inline]
383    pub fn into_boxed_path(self) -> Box<Utf8Path<T>> {
384        let rw = Box::into_raw(self.inner.into_boxed_str()) as *mut Utf8Path<T>;
385        unsafe { Box::from_raw(rw) }
386    }
387
388    /// Invokes [`capacity`] on the underlying instance of [`String`].
389    ///
390    /// [`capacity`]: String::capacity
391    #[inline]
392    pub fn capacity(&self) -> usize {
393        self.inner.capacity()
394    }
395
396    /// Invokes [`clear`] on the underlying instance of [`String`].
397    ///
398    /// [`clear`]: String::clear
399    #[inline]
400    pub fn clear(&mut self) {
401        self.inner.clear()
402    }
403
404    /// Invokes [`reserve`] on the underlying instance of [`String`].
405    ///
406    /// [`reserve`]: String::reserve
407    #[inline]
408    pub fn reserve(&mut self, additional: usize) {
409        self.inner.reserve(additional)
410    }
411
412    /// Invokes [`try_reserve`] on the underlying instance of [`String`].
413    ///
414    /// [`try_reserve`]: String::try_reserve
415    #[inline]
416    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
417        self.inner.try_reserve(additional)
418    }
419
420    /// Invokes [`reserve_exact`] on the underlying instance of [`String`].
421    ///
422    /// [`reserve_exact`]: String::reserve_exact
423    #[inline]
424    pub fn reserve_exact(&mut self, additional: usize) {
425        self.inner.reserve_exact(additional)
426    }
427
428    /// Invokes [`try_reserve_exact`] on the underlying instance of [`String`].
429    ///
430    /// [`try_reserve_exact`]: String::try_reserve_exact
431    #[inline]
432    pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
433        self.inner.try_reserve_exact(additional)
434    }
435
436    /// Invokes [`shrink_to_fit`] on the underlying instance of [`String`].
437    ///
438    /// [`shrink_to_fit`]: String::shrink_to_fit
439    #[inline]
440    pub fn shrink_to_fit(&mut self) {
441        self.inner.shrink_to_fit()
442    }
443
444    /// Invokes [`shrink_to`] on the underlying instance of [`String`].
445    ///
446    /// [`shrink_to`]: String::shrink_to
447    #[inline]
448    pub fn shrink_to(&mut self, min_capacity: usize) {
449        self.inner.shrink_to(min_capacity)
450    }
451
452    /// Consumes [`PathBuf`] and returns a new [`Utf8PathBuf`] by checking that the path contains
453    /// valid UTF-8.
454    ///
455    /// # Errors
456    ///
457    /// Returns `Err` if the path is not UTF-8 with a description as to why the
458    /// provided component is not UTF-8.
459    ///
460    /// # Examples
461    ///
462    /// ```
463    /// use typed_path::{PathBuf, Utf8PathBuf, UnixEncoding, Utf8UnixEncoding};
464    ///
465    /// let path_buf = PathBuf::<UnixEncoding>::from(&[0xf0, 0x9f, 0x92, 0x96]);
466    /// let utf8_path_buf = Utf8PathBuf::<Utf8UnixEncoding>::from_bytes_path_buf(path_buf).unwrap();
467    /// assert_eq!(utf8_path_buf.as_str(), "💖");
468    /// ```
469    pub fn from_bytes_path_buf<U>(path_buf: PathBuf<U>) -> Result<Self, FromUtf8Error>
470    where
471        U: Encoding,
472    {
473        Ok(Self {
474            _encoding: PhantomData,
475            inner: String::from_utf8(path_buf.inner)?,
476        })
477    }
478
479    /// Consumes [`PathBuf`] and returns a new [`Utf8PathBuf`] by checking that the path contains
480    /// valid UTF-8.
481    ///
482    /// # Errors
483    ///
484    /// Returns `Err` if the path is not UTF-8 with a description as to why the
485    /// provided component is not UTF-8.
486    ///
487    /// # Safety
488    ///
489    /// The path passed in must be valid UTF-8.
490    ///
491    /// # Examples
492    ///
493    /// ```
494    /// use typed_path::{PathBuf, Utf8PathBuf, UnixEncoding, Utf8UnixEncoding};
495    ///
496    /// let path_buf = PathBuf::<UnixEncoding>::from(&[0xf0, 0x9f, 0x92, 0x96]);
497    /// let utf8_path_buf = unsafe {
498    ///     Utf8PathBuf::<Utf8UnixEncoding>::from_bytes_path_buf_unchecked(path_buf)
499    /// };
500    /// assert_eq!(utf8_path_buf.as_str(), "💖");
501    /// ```
502    pub unsafe fn from_bytes_path_buf_unchecked<U>(path_buf: PathBuf<U>) -> Self
503    where
504        U: Encoding,
505    {
506        Self {
507            _encoding: PhantomData,
508            inner: String::from_utf8_unchecked(path_buf.inner),
509        }
510    }
511
512    /// Consumes [`Utf8PathBuf`] and returns a new [`PathBuf`]
513    ///
514    /// # Examples
515    ///
516    /// ```
517    /// use typed_path::{PathBuf, Utf8PathBuf, UnixEncoding, Utf8UnixEncoding};
518    ///
519    /// let utf8_path_buf = Utf8PathBuf::<Utf8UnixEncoding>::from("💖");
520    /// let path_buf = utf8_path_buf.into_bytes_path_buf::<UnixEncoding>();
521    /// assert_eq!(path_buf.as_bytes(), &[0xf0, 0x9f, 0x92, 0x96]);
522    /// ```
523    pub fn into_bytes_path_buf<U>(self) -> PathBuf<U>
524    where
525        U: Encoding,
526    {
527        PathBuf {
528            _encoding: PhantomData,
529            inner: self.inner.into_bytes(),
530        }
531    }
532}
533
534impl<T> Clone for Utf8PathBuf<T>
535where
536    T: Utf8Encoding,
537{
538    #[inline]
539    fn clone(&self) -> Self {
540        Self {
541            _encoding: self._encoding,
542            inner: self.inner.clone(),
543        }
544    }
545}
546
547impl<T> fmt::Debug for Utf8PathBuf<T>
548where
549    T: Utf8Encoding,
550{
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552        f.debug_struct("Utf8PathBuf")
553            .field("_encoding", &T::label())
554            .field("inner", &self.inner)
555            .finish()
556    }
557}
558
559impl<T> AsRef<[u8]> for Utf8PathBuf<T>
560where
561    T: Utf8Encoding,
562{
563    #[inline]
564    fn as_ref(&self) -> &[u8] {
565        self.as_str().as_bytes()
566    }
567}
568
569impl<T> AsRef<str> for Utf8PathBuf<T>
570where
571    T: Utf8Encoding,
572{
573    #[inline]
574    fn as_ref(&self) -> &str {
575        self.as_str()
576    }
577}
578
579impl<T> AsRef<Utf8Path<T>> for Utf8PathBuf<T>
580where
581    T: Utf8Encoding,
582{
583    #[inline]
584    fn as_ref(&self) -> &Utf8Path<T> {
585        self
586    }
587}
588
589impl<T> Borrow<Utf8Path<T>> for Utf8PathBuf<T>
590where
591    T: Utf8Encoding,
592{
593    #[inline]
594    fn borrow(&self) -> &Utf8Path<T> {
595        self.deref()
596    }
597}
598
599impl<T> Default for Utf8PathBuf<T>
600where
601    T: Utf8Encoding,
602{
603    #[inline]
604    fn default() -> Utf8PathBuf<T> {
605        Utf8PathBuf::new()
606    }
607}
608
609impl<T> fmt::Display for Utf8PathBuf<T>
610where
611    T: Utf8Encoding,
612{
613    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
614        fmt::Display::fmt(&self.inner, f)
615    }
616}
617
618impl<T> Deref for Utf8PathBuf<T>
619where
620    T: Utf8Encoding,
621{
622    type Target = Utf8Path<T>;
623
624    #[inline]
625    fn deref(&self) -> &Utf8Path<T> {
626        Utf8Path::new(&self.inner)
627    }
628}
629
630impl<T> Eq for Utf8PathBuf<T> where T: Utf8Encoding {}
631
632impl<T> PartialEq for Utf8PathBuf<T>
633where
634    T: Utf8Encoding,
635{
636    fn eq(&self, other: &Self) -> bool {
637        self.components() == other.components()
638    }
639}
640
641impl<T, P> Extend<P> for Utf8PathBuf<T>
642where
643    T: Utf8Encoding,
644    P: AsRef<Utf8Path<T>>,
645{
646    fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
647        iter.into_iter().for_each(move |p| self.push(p.as_ref()));
648    }
649}
650
651impl<T> From<Box<Utf8Path<T>>> for Utf8PathBuf<T>
652where
653    T: Utf8Encoding,
654{
655    fn from(boxed: Box<Utf8Path<T>>) -> Self {
656        boxed.into_path_buf()
657    }
658}
659
660impl<T, V> From<&V> for Utf8PathBuf<T>
661where
662    T: Utf8Encoding,
663    V: ?Sized + AsRef<str>,
664{
665    /// Converts a borrowed [`str`] to a [`Utf8PathBuf`].
666    ///
667    /// Allocates a [`Utf8PathBuf`] and copies the data into it.
668    #[inline]
669    fn from(s: &V) -> Self {
670        Utf8PathBuf::from(s.as_ref().to_string())
671    }
672}
673
674impl<T> From<String> for Utf8PathBuf<T>
675where
676    T: Utf8Encoding,
677{
678    /// Converts a [`String`] into a [`Utf8PathBuf`]
679    ///
680    /// This conversion does not allocate or copy memory.
681    #[inline]
682    fn from(inner: String) -> Self {
683        Utf8PathBuf {
684            _encoding: PhantomData,
685            inner,
686        }
687    }
688}
689
690impl<T> From<Utf8PathBuf<T>> for String
691where
692    T: Utf8Encoding,
693{
694    /// Converts a [`Utf8PathBuf`] into a [`String`]
695    ///
696    /// This conversion does not allocate or copy memory.
697    #[inline]
698    fn from(path_buf: Utf8PathBuf<T>) -> Self {
699        path_buf.inner
700    }
701}
702
703impl<T> FromStr for Utf8PathBuf<T>
704where
705    T: Utf8Encoding,
706{
707    type Err = core::convert::Infallible;
708
709    #[inline]
710    fn from_str(s: &str) -> Result<Self, Self::Err> {
711        Ok(Utf8PathBuf::from(s))
712    }
713}
714
715impl<'a, T> From<Cow<'a, Utf8Path<T>>> for Utf8PathBuf<T>
716where
717    T: Utf8Encoding,
718{
719    /// Converts a clone-on-write pointer to an owned path.
720    ///
721    /// Converting from a `Cow::Owned` does not clone or allocate.
722    #[inline]
723    fn from(p: Cow<'a, Utf8Path<T>>) -> Self {
724        p.into_owned()
725    }
726}
727
728impl<T, P> FromIterator<P> for Utf8PathBuf<T>
729where
730    T: Utf8Encoding,
731    P: AsRef<Utf8Path<T>>,
732{
733    fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
734        let mut buf = Utf8PathBuf::new();
735        buf.extend(iter);
736        buf
737    }
738}
739
740impl<T> Hash for Utf8PathBuf<T>
741where
742    T: Utf8Encoding,
743{
744    fn hash<H: Hasher>(&self, h: &mut H) {
745        self.as_path().hash(h)
746    }
747}
748
749impl<'a, T> IntoIterator for &'a Utf8PathBuf<T>
750where
751    T: Utf8Encoding,
752{
753    type IntoIter = Utf8Iter<'a, T>;
754    type Item = &'a str;
755
756    #[inline]
757    fn into_iter(self) -> Self::IntoIter {
758        self.iter()
759    }
760}
761
762impl<T> cmp::PartialOrd for Utf8PathBuf<T>
763where
764    T: Utf8Encoding,
765{
766    #[inline]
767    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
768        Some(self.cmp(other))
769    }
770}
771
772impl<T> cmp::Ord for Utf8PathBuf<T>
773where
774    T: Utf8Encoding,
775{
776    #[inline]
777    fn cmp(&self, other: &Self) -> cmp::Ordering {
778        self.components().cmp(other.components())
779    }
780}
781
782#[cfg(any(
783    unix,
784    all(target_vendor = "fortanix", target_env = "sgx"),
785    target_os = "solid_asp3",
786    target_os = "hermit",
787    target_os = "wasi"
788))]
789#[cfg(feature = "std")]
790mod std_conversions {
791    use std::ffi::{OsStr, OsString};
792    #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
793    use std::os::fortanix_sgx as os;
794    #[cfg(target_os = "solid_asp3")]
795    use std::os::solid as os;
796    #[cfg(any(target_os = "hermit", unix))]
797    use std::os::unix as os;
798    #[cfg(target_os = "wasi")]
799    use std::os::wasi as os;
800
801    use os::ffi::{OsStrExt, OsStringExt};
802
803    use super::*;
804
805    impl<T> From<Utf8PathBuf<T>> for OsString
806    where
807        T: Utf8Encoding,
808    {
809        #[inline]
810        fn from(path_buf: Utf8PathBuf<T>) -> Self {
811            OsStringExt::from_vec(path_buf.into_string().into_bytes())
812        }
813    }
814
815    impl<T> AsRef<OsStr> for Utf8PathBuf<T>
816    where
817        T: Utf8Encoding,
818    {
819        #[inline]
820        fn as_ref(&self) -> &OsStr {
821            OsStrExt::from_bytes(self.as_str().as_bytes())
822        }
823    }
824}