typed_path/common/utf8/path.rs
1use alloc::borrow::{Cow, ToOwned};
2use alloc::rc::Rc;
3use alloc::sync::Arc;
4use core::hash::{Hash, Hasher};
5use core::marker::PhantomData;
6use core::str::Utf8Error;
7use core::{cmp, fmt};
8
9use crate::no_std_compat::*;
10use crate::{
11 CheckedPathError, Encoding, Path, StripPrefixError, Utf8Ancestors, Utf8Component,
12 Utf8Components, Utf8Encoding, Utf8Iter, Utf8PathBuf,
13};
14
15/// A slice of a path (akin to [`str`]).
16///
17/// This type supports a number of operations for inspecting a path, including
18/// breaking the path into its components (separated by `/` on Unix and by either
19/// `/` or `\` on Windows), extracting the file name, determining whether the path
20/// is absolute, and so on.
21///
22/// This is an *unsized* type, meaning that it must always be used behind a
23/// pointer like `&` or [`Box`]. For an owned version of this type,
24/// see [`Utf8PathBuf`].
25///
26/// # Examples
27///
28/// ```
29/// use typed_path::{Utf8Path, Utf8UnixEncoding};
30///
31/// // NOTE: A path cannot be created on its own without a defined encoding,
32/// // but all encodings work on all operating systems, providing the
33/// // ability to parse and operate on paths independently of the
34/// // compiled platform
35/// let path = Utf8Path::<Utf8UnixEncoding>::new("./foo/bar.txt");
36///
37/// let parent = path.parent();
38/// assert_eq!(parent, Some(Utf8Path::new("./foo")));
39///
40/// let file_stem = path.file_stem();
41/// assert_eq!(file_stem, Some("bar"));
42///
43/// let extension = path.extension();
44/// assert_eq!(extension, Some("txt"));
45/// ```
46///
47/// In addition to explicitly using [`Utf8Encoding`]s, you can also
48/// leverage aliases available from the crate to work with paths:
49///
50/// ```
51/// use typed_path::{Utf8UnixPath, Utf8WindowsPath};
52///
53/// // Same as Utf8Path<Utf8UnixEncoding>
54/// let path = Utf8UnixPath::new("/foo/bar.txt");
55///
56/// // Same as Utf8Path<Utf8WindowsEncoding>
57/// let path = Utf8WindowsPath::new(r"C:\foo\bar.txt");
58/// ```
59///
60/// To mirror the design of Rust's standard library, you can access
61/// the path associated with the compiled rust platform using [`Utf8NativePath`],
62/// which itself is an alias to one of the other choices:
63///
64/// ```
65/// use typed_path::Utf8NativePath;
66///
67/// // On Unix, this would be Utf8UnixPath aka Utf8Path<Utf8UnixEncoding>
68/// // On Windows, this would be Utf8WindowsPath aka Utf8Path<Utf8WindowsEncoding>
69/// let path = Utf8NativePath::new("/foo/bar.txt");
70/// ```
71///
72/// [`Utf8NativePath`]: crate::Utf8NativePath
73#[repr(transparent)]
74pub struct Utf8Path<T>
75where
76 T: Utf8Encoding,
77{
78 /// Encoding associated with path buf
79 _encoding: PhantomData<T>,
80
81 /// Path as an unparsed str slice
82 pub(crate) inner: str,
83}
84
85impl<T> Utf8Path<T>
86where
87 T: Utf8Encoding,
88{
89 /// Directly wraps a str slice as a `Utf8Path` slice.
90 ///
91 /// This is a cost-free conversion.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
97 ///
98 /// // NOTE: A path cannot be created on its own without a defined encoding
99 /// Utf8Path::<Utf8UnixEncoding>::new("foo.txt");
100 /// ```
101 ///
102 /// You can create `Utf8Path`s from `String`s, or even other `Utf8Path`s:
103 ///
104 /// ```
105 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
106 ///
107 /// // NOTE: A path cannot be created on its own without a defined encoding
108 /// let string = String::from("foo.txt");
109 /// let from_string = Utf8Path::<Utf8UnixEncoding>::new(&string);
110 /// let from_path = Utf8Path::new(&from_string);
111 /// assert_eq!(from_string, from_path);
112 /// ```
113 ///
114 /// There are also handy aliases to the `Utf8Path` with [`Utf8Encoding`]:
115 ///
116 /// ```
117 /// use typed_path::Utf8UnixPath;
118 ///
119 /// let string = String::from("foo.txt");
120 /// let from_string = Utf8UnixPath::new(&string);
121 /// let from_path = Utf8UnixPath::new(&from_string);
122 /// assert_eq!(from_string, from_path);
123 /// ```
124 #[inline]
125 pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Self {
126 unsafe { &*(s.as_ref() as *const str as *const Self) }
127 }
128
129 /// Yields the underlying [`str`] slice.
130 ///
131 /// # Examples
132 ///
133 /// ```
134 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
135 ///
136 /// // NOTE: A path cannot be created on its own without a defined encoding
137 /// let s = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").as_str();
138 /// assert_eq!(s, "foo.txt");
139 /// ```
140 pub fn as_str(&self) -> &str {
141 &self.inner
142 }
143
144 /// Converts a `Utf8Path` to an owned [`Utf8PathBuf`].
145 ///
146 /// # Examples
147 ///
148 /// ```
149 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
150 ///
151 /// // NOTE: A path cannot be created on its own without a defined encoding
152 /// let path_buf = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").to_path_buf();
153 /// assert_eq!(path_buf, Utf8PathBuf::from("foo.txt"));
154 /// ```
155 pub fn to_path_buf(&self) -> Utf8PathBuf<T> {
156 Utf8PathBuf {
157 _encoding: PhantomData,
158 inner: self.inner.to_owned(),
159 }
160 }
161
162 /// Returns `true` if the `Utf8Path` is absolute, i.e., if it is independent of
163 /// the current directory.
164 ///
165 /// * On Unix ([`Utf8UnixPath`]]), a path is absolute if it starts with the root, so
166 /// `is_absolute` and [`has_root`] are equivalent.
167 ///
168 /// * On Windows ([`Utf8WindowsPath`]), a path is absolute if it has a prefix and starts with
169 /// the root: `c:\windows` is absolute, while `c:temp` and `\temp` are not.
170 ///
171 /// [`Utf8UnixPath`]: crate::Utf8UnixPath
172 /// [`Utf8WindowsPath`]: crate::Utf8WindowsPath
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
178 ///
179 /// // NOTE: A path cannot be created on its own without a defined encoding
180 /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_absolute());
181 /// ```
182 ///
183 /// [`has_root`]: Utf8Path::has_root
184 pub fn is_absolute(&self) -> bool {
185 self.components().is_absolute()
186 }
187
188 /// Returns `true` if the `Utf8Path` is relative, i.e., not absolute.
189 ///
190 /// See [`is_absolute`]'s documentation for more details.
191 ///
192 /// # Examples
193 ///
194 /// ```
195 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
196 ///
197 /// // NOTE: A path cannot be created on its own without a defined encoding
198 /// assert!(Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_relative());
199 /// ```
200 ///
201 /// [`is_absolute`]: Utf8Path::is_absolute
202 #[inline]
203 pub fn is_relative(&self) -> bool {
204 !self.is_absolute()
205 }
206
207 /// Returns `true` if the path is valid, meaning that all of its components are valid.
208 ///
209 /// See [`Utf8Component::is_valid`]'s documentation for more details.
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
215 ///
216 /// // NOTE: A path cannot be created on its own without a defined encoding
217 /// assert!(Utf8Path::<Utf8UnixEncoding>::new("foo.txt").is_valid());
218 /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("foo\0.txt").is_valid());
219 /// ```
220 ///
221 /// [`Utf8Component::is_valid`]: crate::Utf8Component::is_valid
222 pub fn is_valid(&self) -> bool {
223 self.components().all(|c| c.is_valid())
224 }
225
226 /// Returns `true` if the `Utf8Path` has a root.
227 ///
228 /// * On Unix ([`Utf8UnixPath`]), a path has a root if it begins with `/`.
229 ///
230 /// * On Windows ([`Utf8WindowsPath`]), a path has a root if it:
231 /// * has no prefix and begins with a separator, e.g., `\windows`
232 /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows`
233 /// * has any non-disk prefix, e.g., `\\server\share`
234 ///
235 /// [`Utf8UnixPath`]: crate::Utf8UnixPath
236 /// [`Utf8WindowsPath`]: crate::Utf8WindowsPath
237 ///
238 /// # Examples
239 ///
240 /// ```
241 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
242 ///
243 /// // NOTE: A path cannot be created on its own without a defined encoding
244 /// assert!(Utf8Path::<Utf8UnixEncoding>::new("/etc/passwd").has_root());
245 /// ```
246 #[inline]
247 pub fn has_root(&self) -> bool {
248 self.components().has_root()
249 }
250
251 /// Returns the `Utf8Path` without its final component, if there is one.
252 ///
253 /// Returns [`None`] if the path terminates in a root or prefix.
254 ///
255 /// # Examples
256 ///
257 /// ```
258 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
259 ///
260 /// // NOTE: A path cannot be created on its own without a defined encoding
261 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/foo/bar");
262 /// let parent = path.parent().unwrap();
263 /// assert_eq!(parent, Utf8Path::new("/foo"));
264 ///
265 /// let grand_parent = parent.parent().unwrap();
266 /// assert_eq!(grand_parent, Utf8Path::new("/"));
267 /// assert_eq!(grand_parent.parent(), None);
268 /// ```
269 pub fn parent(&self) -> Option<&Self> {
270 let mut comps = self.components();
271 let comp = comps.next_back();
272 comp.and_then(|p| {
273 if !p.is_root() {
274 Some(Self::new(comps.as_str()))
275 } else {
276 None
277 }
278 })
279 }
280
281 /// Produces an iterator over `Utf8Path` and its ancestors.
282 ///
283 /// The iterator will yield the `Utf8Path` that is returned if the [`parent`] method is used zero
284 /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`,
285 /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns
286 /// [`None`], the iterator will do likewise. The iterator will always yield at least one value,
287 /// namely `&self`.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
293 ///
294 /// // NOTE: A path cannot be created on its own without a defined encoding
295 /// let mut ancestors = Utf8Path::<Utf8UnixEncoding>::new("/foo/bar").ancestors();
296 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo/bar")));
297 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo")));
298 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/")));
299 /// assert_eq!(ancestors.next(), None);
300 ///
301 /// // NOTE: A path cannot be created on its own without a defined encoding
302 /// let mut ancestors = Utf8Path::<Utf8UnixEncoding>::new("../foo/bar").ancestors();
303 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo/bar")));
304 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo")));
305 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("..")));
306 /// assert_eq!(ancestors.next(), Some(Utf8Path::new("")));
307 /// assert_eq!(ancestors.next(), None);
308 /// ```
309 ///
310 /// [`parent`]: Utf8Path::parent
311 #[inline]
312 pub fn ancestors(&self) -> Utf8Ancestors<T> {
313 Utf8Ancestors { next: Some(self) }
314 }
315
316 /// Returns the final component of the `Utf8Path`, if there is one.
317 ///
318 /// If the path is a normal file, this is the file name. If it's the path of a directory, this
319 /// is the directory name.
320 ///
321 /// Returns [`None`] if the path terminates in `..`.
322 ///
323 /// # Examples
324 ///
325 /// ```
326 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
327 ///
328 /// // NOTE: A path cannot be created on its own without a defined encoding
329 /// assert_eq!(Some("bin"), Utf8Path::<Utf8UnixEncoding>::new("/usr/bin/").file_name());
330 /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("tmp/foo.txt").file_name());
331 /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("foo.txt/.").file_name());
332 /// assert_eq!(Some("foo.txt"), Utf8Path::<Utf8UnixEncoding>::new("foo.txt/.//").file_name());
333 /// assert_eq!(None, Utf8Path::<Utf8UnixEncoding>::new("foo.txt/..").file_name());
334 /// assert_eq!(None, Utf8Path::<Utf8UnixEncoding>::new("/").file_name());
335 /// ```
336 pub fn file_name(&self) -> Option<&str> {
337 match self.components().next_back() {
338 Some(p) => {
339 if p.is_normal() {
340 Some(p.as_str())
341 } else {
342 None
343 }
344 }
345 None => None,
346 }
347 }
348
349 /// Returns a path that, when joined onto `base`, yields `self`.
350 ///
351 /// # Errors
352 ///
353 /// If `base` is not a prefix of `self` (i.e., [`starts_with`]
354 /// returns `false`), returns [`Err`].
355 ///
356 /// [`starts_with`]: Utf8Path::starts_with
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
362 ///
363 /// // NOTE: A path cannot be created on its own without a defined encoding
364 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/test/haha/foo.txt");
365 ///
366 /// assert_eq!(path.strip_prefix("/"), Ok(Utf8Path::new("test/haha/foo.txt")));
367 /// assert_eq!(path.strip_prefix("/test"), Ok(Utf8Path::new("haha/foo.txt")));
368 /// assert_eq!(path.strip_prefix("/test/"), Ok(Utf8Path::new("haha/foo.txt")));
369 /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Utf8Path::new("")));
370 /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Utf8Path::new("")));
371 ///
372 /// assert!(path.strip_prefix("test").is_err());
373 /// assert!(path.strip_prefix("/haha").is_err());
374 ///
375 /// let prefix = Utf8PathBuf::<Utf8UnixEncoding>::from("/test/");
376 /// assert_eq!(path.strip_prefix(prefix), Ok(Utf8Path::new("haha/foo.txt")));
377 /// ```
378 pub fn strip_prefix<P>(&self, base: P) -> Result<&Utf8Path<T>, StripPrefixError>
379 where
380 P: AsRef<Utf8Path<T>>,
381 {
382 self._strip_prefix(base.as_ref())
383 }
384
385 fn _strip_prefix(&self, base: &Utf8Path<T>) -> Result<&Utf8Path<T>, StripPrefixError> {
386 match helpers::iter_after(self.components(), base.components()) {
387 Some(c) => Ok(Utf8Path::new(c.as_str())),
388 None => Err(StripPrefixError(())),
389 }
390 }
391
392 /// Determines whether `base` is a prefix of `self`.
393 ///
394 /// Only considers whole path components to match.
395 ///
396 /// # Examples
397 ///
398 /// ```
399 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
400 ///
401 /// // NOTE: A path cannot be created on its own without a defined encoding
402 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc/passwd");
403 ///
404 /// assert!(path.starts_with("/etc"));
405 /// assert!(path.starts_with("/etc/"));
406 /// assert!(path.starts_with("/etc/passwd"));
407 /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay
408 /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay
409 ///
410 /// assert!(!path.starts_with("/e"));
411 /// assert!(!path.starts_with("/etc/passwd.txt"));
412 ///
413 /// assert!(!Utf8Path::<Utf8UnixEncoding>::new("/etc/foo.rs").starts_with("/etc/foo"));
414 /// ```
415 pub fn starts_with<P>(&self, base: P) -> bool
416 where
417 P: AsRef<Utf8Path<T>>,
418 {
419 self._starts_with(base.as_ref())
420 }
421
422 fn _starts_with(&self, base: &Utf8Path<T>) -> bool {
423 helpers::iter_after(self.components(), base.components()).is_some()
424 }
425
426 /// Determines whether `child` is a suffix of `self`.
427 ///
428 /// Only considers whole path components to match.
429 ///
430 /// # Examples
431 ///
432 /// ```
433 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
434 ///
435 /// // NOTE: A path cannot be created on its own without a defined encoding
436 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc/resolv.conf");
437 ///
438 /// assert!(path.ends_with("resolv.conf"));
439 /// assert!(path.ends_with("etc/resolv.conf"));
440 /// assert!(path.ends_with("/etc/resolv.conf"));
441 ///
442 /// assert!(!path.ends_with("/resolv.conf"));
443 /// assert!(!path.ends_with("conf")); // use .extension() instead
444 /// ```
445 pub fn ends_with<P>(&self, child: P) -> bool
446 where
447 P: AsRef<Utf8Path<T>>,
448 {
449 self._ends_with(child.as_ref())
450 }
451
452 fn _ends_with(&self, child: &Utf8Path<T>) -> bool {
453 helpers::iter_after(self.components().rev(), child.components().rev()).is_some()
454 }
455
456 /// Extracts the stem (non-extension) portion of [`self.file_name`].
457 ///
458 /// [`self.file_name`]: Utf8Path::file_name
459 ///
460 /// The stem is:
461 ///
462 /// * [`None`], if there is no file name;
463 /// * The entire file name if there is no embedded `.`;
464 /// * The entire file name if the file name begins with `.` and has no other `.`s within;
465 /// * Otherwise, the portion of the file name before the final `.`
466 ///
467 /// # Examples
468 ///
469 /// ```
470 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
471 ///
472 /// // NOTE: A path cannot be created on its own without a defined encoding
473 /// assert_eq!("foo", Utf8Path::<Utf8UnixEncoding>::new("foo.rs").file_stem().unwrap());
474 /// assert_eq!("foo.tar", Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz").file_stem().unwrap());
475 /// ```
476 ///
477 pub fn file_stem(&self) -> Option<&str> {
478 self.file_name()
479 .map(helpers::rsplit_file_at_dot)
480 .and_then(|(before, after)| before.or(after))
481 }
482
483 /// Extracts the extension of [`self.file_name`], if possible.
484 ///
485 /// The extension is:
486 ///
487 /// * [`None`], if there is no file name;
488 /// * [`None`], if there is no embedded `.`;
489 /// * [`None`], if the file name begins with `.` and has no other `.`s within;
490 /// * Otherwise, the portion of the file name after the final `.`
491 ///
492 /// [`self.file_name`]: Utf8Path::file_name
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
498 ///
499 /// // NOTE: A path cannot be created on its own without a defined encoding
500 /// assert_eq!("rs", Utf8Path::<Utf8UnixEncoding>::new("foo.rs").extension().unwrap());
501 /// assert_eq!("gz", Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz").extension().unwrap());
502 /// ```
503 pub fn extension(&self) -> Option<&str> {
504 self.file_name()
505 .map(helpers::rsplit_file_at_dot)
506 .and_then(|(before, after)| before.and(after))
507 }
508
509 /// Returns an owned [`Utf8PathBuf`] by resolving `..` and `.` segments.
510 ///
511 /// When multiple, sequential path segment separation characters are found (e.g. `/` for Unix
512 /// and either `\` or `/` on Windows), they are replaced by a single instance of the
513 /// platform-specific path segment separator (`/` on Unix and `\` on Windows).
514 ///
515 /// # Examples
516 ///
517 /// ```
518 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
519 ///
520 /// // NOTE: A path cannot be created on its own without a defined encoding
521 /// assert_eq!(
522 /// Utf8Path::<Utf8UnixEncoding>::new("foo/bar//baz/./asdf/quux/..").normalize(),
523 /// Utf8PathBuf::from("foo/bar/baz/asdf"),
524 /// );
525 /// ```
526 ///
527 /// When starting with a root directory, any `..` segment whose parent is the root directory
528 /// will be filtered out:
529 ///
530 /// ```
531 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
532 ///
533 /// // NOTE: A path cannot be created on its own without a defined encoding
534 /// assert_eq!(
535 /// Utf8Path::<Utf8UnixEncoding>::new("/../foo").normalize(),
536 /// Utf8PathBuf::from("/foo"),
537 /// );
538 /// ```
539 ///
540 /// If any `..` is left unresolved as the path is relative and no parent is found, it is
541 /// discarded:
542 ///
543 /// ```
544 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding, Utf8WindowsEncoding};
545 ///
546 /// assert_eq!(
547 /// Utf8Path::<Utf8UnixEncoding>::new("../foo/..").normalize(),
548 /// Utf8PathBuf::from(""),
549 /// );
550 ///
551 /// //Windows prefixes also count this way, but the prefix remains
552 /// assert_eq!(
553 /// Utf8Path::<Utf8WindowsEncoding>::new(r"C:..\foo\..").normalize(),
554 /// Utf8PathBuf::from(r"C:"),
555 /// );
556 /// ```
557 pub fn normalize(&self) -> Utf8PathBuf<T> {
558 let mut components = Vec::new();
559 for component in self.components() {
560 if !component.is_current() && !component.is_parent() {
561 components.push(component);
562 } else if component.is_parent() {
563 if let Some(last) = components.last() {
564 if last.is_normal() {
565 components.pop();
566 }
567 }
568 }
569 }
570
571 let mut path = Utf8PathBuf::<T>::new();
572
573 for component in components {
574 path.push(component.as_str());
575 }
576
577 path
578 }
579
580 /// Converts a path to an absolute form by [`normalizing`] the path, returning a
581 /// [`Utf8PathBuf`].
582 ///
583 /// In the case that the path is relative, the current working directory is prepended prior to
584 /// normalizing.
585 ///
586 /// [`normalizing`]: Utf8Path::normalize
587 ///
588 /// # Examples
589 ///
590 /// ```
591 /// use typed_path::{utils, Utf8Path, Utf8UnixEncoding};
592 ///
593 /// // With an absolute path, it is just normalized
594 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/a/b/../c/./d");
595 /// assert_eq!(path.absolutize().unwrap(), Utf8Path::new("/a/c/d"));
596 ///
597 /// // With a relative path, it is first joined with the current working directory
598 /// // and then normalized
599 /// let cwd = utils::utf8_current_dir().unwrap().with_encoding::<Utf8UnixEncoding>();
600 /// let path = cwd.join(Utf8Path::new("a/b/../c/./d"));
601 /// assert_eq!(path.absolutize().unwrap(), cwd.join(Utf8Path::new("a/c/d")));
602 /// ```
603 #[cfg(all(feature = "std", not(target_family = "wasm")))]
604 pub fn absolutize(&self) -> std::io::Result<Utf8PathBuf<T>> {
605 if self.is_absolute() {
606 Ok(self.normalize())
607 } else {
608 // Get the cwd as a native path and convert to this path's encoding
609 let cwd = crate::utils::utf8_current_dir()?.with_encoding();
610
611 Ok(cwd.join(self).normalize())
612 }
613 }
614
615 /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`.
616 ///
617 /// See [`Utf8PathBuf::push`] for more details on what it means to adjoin a path.
618 ///
619 /// # Examples
620 ///
621 /// ```
622 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
623 ///
624 /// // NOTE: A path cannot be created on its own without a defined encoding
625 /// assert_eq!(
626 /// Utf8Path::<Utf8UnixEncoding>::new("/etc").join("passwd"),
627 /// Utf8PathBuf::from("/etc/passwd"),
628 /// );
629 /// ```
630 pub fn join<P: AsRef<Utf8Path<T>>>(&self, path: P) -> Utf8PathBuf<T> {
631 self._join(path.as_ref())
632 }
633
634 fn _join(&self, path: &Utf8Path<T>) -> Utf8PathBuf<T> {
635 let mut buf = self.to_path_buf();
636 buf.push(path);
637 buf
638 }
639
640 /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`, checking the `path` to
641 /// ensure it is safe to join. _When dealing with user-provided paths, this is the preferred
642 /// method._
643 ///
644 /// See [`Utf8PathBuf::push_checked`] for more details on what it means to adjoin a path
645 /// safely.
646 ///
647 /// # Examples
648 ///
649 /// ```
650 /// use typed_path::{CheckedPathError, Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
651 ///
652 /// // NOTE: A path cannot be created on its own without a defined encoding
653 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/etc");
654 ///
655 /// // A valid path can be joined onto the existing one
656 /// assert_eq!(path.join_checked("passwd"), Ok(Utf8PathBuf::from("/etc/passwd")));
657 ///
658 /// // An invalid path will result in an error
659 /// assert_eq!(path.join_checked("/sneaky/replacement"), Err(CheckedPathError::UnexpectedRoot));
660 /// ```
661 pub fn join_checked<P: AsRef<Utf8Path<T>>>(
662 &self,
663 path: P,
664 ) -> Result<Utf8PathBuf<T>, CheckedPathError> {
665 self._join_checked(path.as_ref())
666 }
667
668 fn _join_checked(&self, path: &Utf8Path<T>) -> Result<Utf8PathBuf<T>, CheckedPathError> {
669 let mut buf = self.to_path_buf();
670 buf.push_checked(path)?;
671 Ok(buf)
672 }
673
674 /// Creates an owned [`Utf8PathBuf`] like `self` but with the given file name.
675 ///
676 /// See [`Utf8PathBuf::set_file_name`] for more details.
677 ///
678 /// # Examples
679 ///
680 /// ```
681 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
682 ///
683 /// // NOTE: A path cannot be created on its own without a defined encoding
684 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
685 /// assert_eq!(path.with_file_name("bar.txt"), Utf8PathBuf::from("/tmp/bar.txt"));
686 ///
687 /// // NOTE: A path cannot be created on its own without a defined encoding
688 /// let path = Utf8Path::<Utf8UnixEncoding>::new("/tmp");
689 /// assert_eq!(path.with_file_name("var"), Utf8PathBuf::from("/var"));
690 /// ```
691 pub fn with_file_name<S: AsRef<str>>(&self, file_name: S) -> Utf8PathBuf<T> {
692 self._with_file_name(file_name.as_ref())
693 }
694
695 fn _with_file_name(&self, file_name: &str) -> Utf8PathBuf<T> {
696 let mut buf = self.to_path_buf();
697 buf.set_file_name(file_name);
698 buf
699 }
700
701 /// Creates an owned [`Utf8PathBuf`] like `self` but with the given extension.
702 ///
703 /// See [`Utf8PathBuf::set_extension`] for more details.
704 ///
705 /// # Examples
706 ///
707 /// ```
708 /// use typed_path::{Utf8Path, Utf8PathBuf, Utf8UnixEncoding};
709 ///
710 /// // NOTE: A path cannot be created on its own without a defined encoding
711 /// let path = Utf8Path::<Utf8UnixEncoding>::new("foo.rs");
712 /// assert_eq!(path.with_extension("txt"), Utf8PathBuf::from("foo.txt"));
713 ///
714 /// // NOTE: A path cannot be created on its own without a defined encoding
715 /// let path = Utf8Path::<Utf8UnixEncoding>::new("foo.tar.gz");
716 /// assert_eq!(path.with_extension(""), Utf8PathBuf::from("foo.tar"));
717 /// assert_eq!(path.with_extension("xz"), Utf8PathBuf::from("foo.tar.xz"));
718 /// assert_eq!(path.with_extension("").with_extension("txt"), Utf8PathBuf::from("foo.txt"));
719 /// ```
720 pub fn with_extension<S: AsRef<str>>(&self, extension: S) -> Utf8PathBuf<T> {
721 self._with_extension(extension.as_ref())
722 }
723
724 fn _with_extension(&self, extension: &str) -> Utf8PathBuf<T> {
725 let mut buf = self.to_path_buf();
726 buf.set_extension(extension);
727 buf
728 }
729
730 /// Produces an iterator over the [`Utf8Component`]s of the path.
731 ///
732 /// When parsing the path, there is a small amount of normalization:
733 ///
734 /// * Repeated separators are ignored, so `a/b` and `a//b` both have
735 /// `a` and `b` as components.
736 ///
737 /// * Occurrences of `.` are normalized away, except if they are at the
738 /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and
739 /// `a/b` all have `a` and `b` as components, but `./a/b` starts with
740 /// an additional [`CurDir`] component.
741 ///
742 /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent.
743 ///
744 /// Note that no other normalization takes place; in particular, `a/c`
745 /// and `a/b/../c` are distinct, to account for the possibility that `b`
746 /// is a symbolic link (so its parent isn't `a`).
747 ///
748 /// # Examples
749 ///
750 /// ```
751 /// use typed_path::{Utf8Path, Utf8UnixComponent, Utf8UnixEncoding};
752 ///
753 /// // NOTE: A path cannot be created on its own without a defined encoding
754 /// let mut components = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt").components();
755 ///
756 /// assert_eq!(components.next(), Some(Utf8UnixComponent::RootDir));
757 /// assert_eq!(components.next(), Some(Utf8UnixComponent::Normal("tmp")));
758 /// assert_eq!(components.next(), Some(Utf8UnixComponent::Normal("foo.txt")));
759 /// assert_eq!(components.next(), None)
760 /// ```
761 ///
762 /// [`CurDir`]: crate::unix::UnixComponent::CurDir
763 pub fn components(&self) -> <T as Utf8Encoding>::Components<'_> {
764 T::components(&self.inner)
765 }
766
767 /// Produces an iterator over the path's components viewed as [`str`] slices.
768 ///
769 /// For more information about the particulars of how the path is separated
770 /// into components, see [`components`].
771 ///
772 /// [`components`]: Utf8Path::components
773 ///
774 /// # Examples
775 ///
776 /// ```
777 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
778 ///
779 /// // NOTE: A path cannot be created on its own without a defined encoding
780 /// let mut it = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt").iter();
781 ///
782 /// assert_eq!(it.next(), Some(typed_path::constants::unix::SEPARATOR_STR));
783 /// assert_eq!(it.next(), Some("tmp"));
784 /// assert_eq!(it.next(), Some("foo.txt"));
785 /// assert_eq!(it.next(), None)
786 /// ```
787 #[inline]
788 pub fn iter(&self) -> Utf8Iter<T> {
789 Utf8Iter::new(self.components())
790 }
791
792 /// Creates an owned [`Utf8PathBuf`] like `self` but with a different encoding.
793 ///
794 /// # Note
795 ///
796 /// As part of the process of converting between encodings, the path will need to be rebuilt.
797 /// This involves [`pushing`] each component, which may result in differences in the resulting
798 /// path such as resolving `.` and `..` early or other unexpected side effects.
799 ///
800 /// [`pushing`]: Utf8PathBuf::push
801 ///
802 /// # Examples
803 ///
804 /// ```
805 /// use typed_path::{Utf8Path, Utf8UnixEncoding, Utf8WindowsEncoding};
806 ///
807 /// // Convert from Unix to Windows
808 /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
809 /// let windows_path = unix_path.with_encoding::<Utf8WindowsEncoding>();
810 /// assert_eq!(windows_path, Utf8Path::<Utf8WindowsEncoding>::new(r"\tmp\foo.txt"));
811 ///
812 /// // Converting from Windows to Unix will drop any prefix
813 /// let windows_path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
814 /// let unix_path = windows_path.with_encoding::<Utf8UnixEncoding>();
815 /// assert_eq!(unix_path, Utf8Path::<Utf8UnixEncoding>::new(r"/tmp/foo.txt"));
816 ///
817 /// // Converting to itself should retain everything
818 /// let path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
819 /// assert_eq!(
820 /// path.with_encoding::<Utf8WindowsEncoding>(),
821 /// Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt"),
822 /// );
823 /// ```
824 pub fn with_encoding<U>(&self) -> Utf8PathBuf<U>
825 where
826 U: Utf8Encoding,
827 {
828 // If we're the same, just return the path buf, which
829 // we do with a fancy trick to convert it
830 if T::label() == U::label() {
831 Utf8Path::new(self.as_str()).to_path_buf()
832 } else {
833 // Otherwise, we have to rebuild the path from the components
834 let mut path = Utf8PathBuf::new();
835
836 // For root, current, and parent we specially handle to convert
837 // to the appropriate type, otherwise we pass along as-is
838 for component in self.components() {
839 if component.is_root() {
840 path.push(<
841 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
842 as Utf8Component
843 >::root().as_str());
844 } else if component.is_current() {
845 path.push(<
846 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
847 as Utf8Component
848 >::current().as_str());
849 } else if component.is_parent() {
850 path.push(<
851 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
852 as Utf8Component
853 >::parent().as_str());
854 } else {
855 path.push(component.as_str());
856 }
857 }
858
859 path
860 }
861 }
862
863 /// Like [`with_encoding`], creates an owned [`Utf8PathBuf`] like `self` but with a different
864 /// encoding. Additionally, checks to ensure that the produced path will be valid.
865 ///
866 /// # Note
867 ///
868 /// As part of the process of converting between encodings, the path will need to be rebuilt.
869 /// This involves [`pushing and checking`] each component, which may result in differences in
870 /// the resulting path such as resolving `.` and `..` early or other unexpected side effects.
871 ///
872 /// [`pushing and checking`]: Utf8PathBuf::push_checked
873 /// [`with_encoding`]: Utf8Path::with_encoding
874 ///
875 /// # Examples
876 ///
877 /// ```
878 /// use typed_path::{CheckedPathError, Utf8Path, Utf8UnixEncoding, Utf8WindowsEncoding};
879 ///
880 /// // Convert from Unix to Windows
881 /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/tmp/foo.txt");
882 /// let windows_path = unix_path.with_encoding_checked::<Utf8WindowsEncoding>().unwrap();
883 /// assert_eq!(windows_path, Utf8Path::<Utf8WindowsEncoding>::new(r"\tmp\foo.txt"));
884 ///
885 /// // Converting from Windows to Unix will drop any prefix
886 /// let windows_path = Utf8Path::<Utf8WindowsEncoding>::new(r"C:\tmp\foo.txt");
887 /// let unix_path = windows_path.with_encoding_checked::<Utf8UnixEncoding>().unwrap();
888 /// assert_eq!(unix_path, Utf8Path::<Utf8UnixEncoding>::new(r"/tmp/foo.txt"));
889 ///
890 /// // Converting from Unix to Windows with invalid filename characters like `|` should fail
891 /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/|invalid|/foo.txt");
892 /// assert_eq!(
893 /// unix_path.with_encoding_checked::<Utf8WindowsEncoding>(),
894 /// Err(CheckedPathError::InvalidFilename),
895 /// );
896 ///
897 /// // Converting from Unix to Windows with unexpected prefix embedded in path should fail
898 /// let unix_path = Utf8Path::<Utf8UnixEncoding>::new("/path/c:/foo.txt");
899 /// assert_eq!(
900 /// unix_path.with_encoding_checked::<Utf8WindowsEncoding>(),
901 /// Err(CheckedPathError::UnexpectedPrefix),
902 /// );
903 /// ```
904 pub fn with_encoding_checked<U>(&self) -> Result<Utf8PathBuf<U>, CheckedPathError>
905 where
906 U: Utf8Encoding,
907 {
908 let mut path = Utf8PathBuf::new();
909
910 // For root, current, and parent we specially handle to convert to the appropriate type,
911 // otherwise we attempt to push using the checked variant, which will ensure that the
912 // destination encoding is respected
913 for component in self.components() {
914 if component.is_root() {
915 path.push(<
916 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
917 as Utf8Component
918 >::root().as_str());
919 } else if component.is_current() {
920 path.push(<
921 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
922 as Utf8Component
923 >::current().as_str());
924 } else if component.is_parent() {
925 path.push(<
926 <<U as Utf8Encoding>::Components<'_> as Utf8Components>::Component
927 as Utf8Component
928 >::parent().as_str());
929 } else {
930 path.push_checked(component.as_str())?;
931 }
932 }
933
934 Ok(path)
935 }
936
937 /// Converts a [`Box<Utf8Path>`](Box) into a
938 /// [`Utf8PathBuf`] without copying or allocating.
939 pub fn into_path_buf(self: Box<Utf8Path<T>>) -> Utf8PathBuf<T> {
940 let rw = Box::into_raw(self) as *mut str;
941 let inner = unsafe { Box::from_raw(rw) };
942 Utf8PathBuf {
943 _encoding: PhantomData,
944 inner: inner.into_string(),
945 }
946 }
947
948 /// Converts a non-UTF-8 [`Path`] to a UTF-8 [`Utf8PathBuf`] by checking that the path contains
949 /// valid UTF-8.
950 ///
951 /// # Errors
952 ///
953 /// Returns `Err` if the path is not UTF-8 with a description as to why the
954 /// provided component is not UTF-8.
955 ///
956 /// # Examples
957 ///
958 /// ```
959 /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
960 ///
961 /// let path = Path::<UnixEncoding>::new(&[0xf0, 0x9f, 0x92, 0x96]);
962 /// let utf8_path = Utf8Path::<Utf8UnixEncoding>::from_bytes_path(&path).unwrap();
963 /// assert_eq!(utf8_path.as_str(), "💖");
964 /// ```
965 pub fn from_bytes_path<U>(path: &Path<U>) -> Result<&Self, Utf8Error>
966 where
967 U: Encoding,
968 {
969 Ok(Self::new(core::str::from_utf8(path.as_bytes())?))
970 }
971
972 /// Converts a non-UTF-8 [`Path`] to a UTF-8 [`Utf8Path`] without checking that the path
973 /// contains valid UTF-8.
974 ///
975 /// See the safe version, [`from_bytes_path`], for more information.
976 ///
977 /// [`from_bytes_path`]: Utf8Path::from_bytes_path
978 ///
979 /// # Safety
980 ///
981 /// The path passed in must be valid UTF-8.
982 ///
983 /// # Examples
984 ///
985 /// ```
986 /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
987 ///
988 /// let path = Path::<UnixEncoding>::new(&[0xf0, 0x9f, 0x92, 0x96]);
989 /// let utf8_path = unsafe {
990 /// Utf8Path::<Utf8UnixEncoding>::from_bytes_path_unchecked(&path)
991 /// };
992 /// assert_eq!(utf8_path.as_str(), "💖");
993 /// ```
994 pub unsafe fn from_bytes_path_unchecked<U>(path: &Path<U>) -> &Self
995 where
996 U: Encoding,
997 {
998 Self::new(core::str::from_utf8_unchecked(path.as_bytes()))
999 }
1000
1001 /// Converts a UTF-8 [`Utf8Path`] to a non-UTF-8 [`Path`].
1002 ///
1003 /// # Examples
1004 ///
1005 /// ```
1006 /// use typed_path::{Path, Utf8Path, UnixEncoding, Utf8UnixEncoding};
1007 ///
1008 /// let utf8_path = Utf8Path::<Utf8UnixEncoding>::new("💖");
1009 /// let path = utf8_path.as_bytes_path::<UnixEncoding>();
1010 /// assert_eq!(path.as_bytes(), &[0xf0, 0x9f, 0x92, 0x96]);
1011 /// ```
1012 pub fn as_bytes_path<U>(&self) -> &Path<U>
1013 where
1014 U: Encoding,
1015 {
1016 Path::new(self.as_str())
1017 }
1018}
1019
1020impl<T> Clone for Box<Utf8Path<T>>
1021where
1022 T: Utf8Encoding,
1023{
1024 fn clone(&self) -> Self {
1025 self.to_path_buf().into_boxed_path()
1026 }
1027}
1028
1029impl<T> AsRef<[u8]> for Utf8Path<T>
1030where
1031 T: Utf8Encoding,
1032{
1033 #[inline]
1034 fn as_ref(&self) -> &[u8] {
1035 self.inner.as_bytes()
1036 }
1037}
1038
1039impl<T> AsRef<str> for Utf8Path<T>
1040where
1041 T: Utf8Encoding,
1042{
1043 #[inline]
1044 fn as_ref(&self) -> &str {
1045 &self.inner
1046 }
1047}
1048
1049impl<T> AsRef<Utf8Path<T>> for Utf8Path<T>
1050where
1051 T: Utf8Encoding,
1052{
1053 #[inline]
1054 fn as_ref(&self) -> &Utf8Path<T> {
1055 self
1056 }
1057}
1058
1059impl<T> AsRef<Utf8Path<T>> for str
1060where
1061 T: Utf8Encoding,
1062{
1063 #[inline]
1064 fn as_ref(&self) -> &Utf8Path<T> {
1065 Utf8Path::new(self)
1066 }
1067}
1068
1069impl<T> AsRef<Utf8Path<T>> for Cow<'_, str>
1070where
1071 T: Utf8Encoding,
1072{
1073 #[inline]
1074 fn as_ref(&self) -> &Utf8Path<T> {
1075 Utf8Path::new(self)
1076 }
1077}
1078
1079impl<T> AsRef<Utf8Path<T>> for String
1080where
1081 T: Utf8Encoding,
1082{
1083 #[inline]
1084 fn as_ref(&self) -> &Utf8Path<T> {
1085 Utf8Path::new(self)
1086 }
1087}
1088
1089impl<T> fmt::Debug for Utf8Path<T>
1090where
1091 T: Utf8Encoding,
1092{
1093 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1094 f.debug_struct("Utf8Path")
1095 .field("_encoding", &T::label())
1096 .field("inner", &&self.inner)
1097 .finish()
1098 }
1099}
1100
1101impl<T> fmt::Display for Utf8Path<T>
1102where
1103 T: Utf8Encoding,
1104{
1105 /// Format path into a [`String`] using the underlying [`str`] representation.
1106 ///
1107 /// # Examples
1108 ///
1109 /// ```
1110 /// use typed_path::{Utf8Path, Utf8UnixEncoding};
1111 ///
1112 /// // NOTE: A path cannot be created on its own without a defined encoding
1113 /// let s = Utf8Path::<Utf8UnixEncoding>::new("foo.txt").to_string();
1114 /// assert_eq!(s, "foo.txt");
1115 /// ```
1116 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1117 fmt::Display::fmt(&self.inner, formatter)
1118 }
1119}
1120
1121impl<T> cmp::PartialEq for Utf8Path<T>
1122where
1123 T: Utf8Encoding,
1124{
1125 #[inline]
1126 fn eq(&self, other: &Utf8Path<T>) -> bool {
1127 self.components() == other.components()
1128 }
1129}
1130
1131impl<T> cmp::Eq for Utf8Path<T> where T: Utf8Encoding {}
1132
1133impl<T> Hash for Utf8Path<T>
1134where
1135 T: Utf8Encoding,
1136{
1137 fn hash<H: Hasher>(&self, h: &mut H) {
1138 T::hash(self.as_str(), h)
1139 }
1140}
1141
1142impl<T> cmp::PartialOrd for Utf8Path<T>
1143where
1144 T: Utf8Encoding,
1145{
1146 #[inline]
1147 fn partial_cmp(&self, other: &Utf8Path<T>) -> Option<cmp::Ordering> {
1148 Some(self.cmp(other))
1149 }
1150}
1151
1152impl<T> cmp::Ord for Utf8Path<T>
1153where
1154 T: Utf8Encoding,
1155{
1156 #[inline]
1157 fn cmp(&self, other: &Utf8Path<T>) -> cmp::Ordering {
1158 self.components().cmp(other.components())
1159 }
1160}
1161
1162impl<T> From<&Utf8Path<T>> for Box<Utf8Path<T>>
1163where
1164 T: Utf8Encoding,
1165{
1166 /// Creates a boxed [`Utf8Path`] from a reference.
1167 ///
1168 /// This will allocate and clone `path` to it.
1169 fn from(path: &Utf8Path<T>) -> Self {
1170 let boxed: Box<str> = path.inner.into();
1171 let rw = Box::into_raw(boxed) as *mut Utf8Path<T>;
1172 unsafe { Box::from_raw(rw) }
1173 }
1174}
1175
1176impl<T> From<Cow<'_, Utf8Path<T>>> for Box<Utf8Path<T>>
1177where
1178 T: Utf8Encoding,
1179{
1180 /// Creates a boxed [`Utf8Path`] from a clone-on-write pointer.
1181 ///
1182 /// Converting from a `Cow::Owned` does not clone or allocate.
1183 #[inline]
1184 fn from(cow: Cow<'_, Utf8Path<T>>) -> Box<Utf8Path<T>> {
1185 match cow {
1186 Cow::Borrowed(path) => Box::from(path),
1187 Cow::Owned(path) => Box::from(path),
1188 }
1189 }
1190}
1191
1192impl<T> From<Utf8PathBuf<T>> for Box<Utf8Path<T>>
1193where
1194 T: Utf8Encoding,
1195{
1196 /// Converts a [`Utf8PathBuf`] into a <code>[Box]<[Utf8Path]></code>.
1197 ///
1198 /// This conversion currently should not allocate memory,
1199 /// but this behavior is not guaranteed on all platforms or in all future versions.
1200 #[inline]
1201 fn from(p: Utf8PathBuf<T>) -> Box<Utf8Path<T>> {
1202 p.into_boxed_path()
1203 }
1204}
1205
1206impl<'a, T> From<&'a Utf8Path<T>> for Cow<'a, Utf8Path<T>>
1207where
1208 T: Utf8Encoding,
1209{
1210 /// Creates a clone-on-write pointer from a reference to
1211 /// [`Utf8Path`].
1212 ///
1213 /// This conversion does not clone or allocate.
1214 #[inline]
1215 fn from(s: &'a Utf8Path<T>) -> Self {
1216 Cow::Borrowed(s)
1217 }
1218}
1219
1220impl<T> From<Utf8PathBuf<T>> for Cow<'_, Utf8Path<T>>
1221where
1222 T: Utf8Encoding,
1223{
1224 /// Creates a clone-on-write pointer from an owned
1225 /// instance of [`Utf8PathBuf`].
1226 ///
1227 /// This conversion does not clone or allocate.
1228 #[inline]
1229 fn from(s: Utf8PathBuf<T>) -> Self {
1230 Cow::Owned(s)
1231 }
1232}
1233
1234impl<'a, T> From<&'a Utf8PathBuf<T>> for Cow<'a, Utf8Path<T>>
1235where
1236 T: Utf8Encoding,
1237{
1238 /// Creates a clone-on-write pointer from a reference to
1239 /// [`Utf8PathBuf`].
1240 ///
1241 /// This conversion does not clone or allocate.
1242 #[inline]
1243 fn from(p: &'a Utf8PathBuf<T>) -> Self {
1244 Cow::Borrowed(p.as_path())
1245 }
1246}
1247
1248impl<T> From<Utf8PathBuf<T>> for Arc<Utf8Path<T>>
1249where
1250 T: Utf8Encoding,
1251{
1252 /// Converts a [`Utf8PathBuf`] into an <code>[Arc]<[Utf8Path]></code> by moving the [`Utf8PathBuf`] data
1253 /// into a new [`Arc`] buffer.
1254 #[inline]
1255 fn from(path_buf: Utf8PathBuf<T>) -> Self {
1256 let arc: Arc<str> = Arc::from(path_buf.into_string());
1257 unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Utf8Path<T>) }
1258 }
1259}
1260
1261impl<T> From<&Utf8Path<T>> for Arc<Utf8Path<T>>
1262where
1263 T: Utf8Encoding,
1264{
1265 /// Converts a [`Utf8Path`] into an [`Arc`] by copying the [`Utf8Path`] data into a new [`Arc`] buffer.
1266 #[inline]
1267 fn from(path: &Utf8Path<T>) -> Self {
1268 let arc: Arc<str> = Arc::from(path.as_str().to_string());
1269 unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Utf8Path<T>) }
1270 }
1271}
1272
1273impl<T> From<Utf8PathBuf<T>> for Rc<Utf8Path<T>>
1274where
1275 T: Utf8Encoding,
1276{
1277 /// Converts a [`Utf8PathBuf`] into an <code>[Rc]<[Utf8Path]></code> by moving the [`Utf8PathBuf`] data into
1278 /// a new [`Rc`] buffer.
1279 #[inline]
1280 fn from(path_buf: Utf8PathBuf<T>) -> Self {
1281 let rc: Rc<str> = Rc::from(path_buf.into_string());
1282 unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Utf8Path<T>) }
1283 }
1284}
1285
1286impl<T> From<&Utf8Path<T>> for Rc<Utf8Path<T>>
1287where
1288 T: Utf8Encoding,
1289{
1290 /// Converts a [`Utf8Path`] into an [`Rc`] by copying the [`Utf8Path`] data into a new [`Rc`] buffer.
1291 #[inline]
1292 fn from(path: &Utf8Path<T>) -> Self {
1293 let rc: Rc<str> = Rc::from(path.as_str());
1294 unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Utf8Path<T>) }
1295 }
1296}
1297
1298impl<'a, T> IntoIterator for &'a Utf8Path<T>
1299where
1300 T: Utf8Encoding,
1301{
1302 type IntoIter = Utf8Iter<'a, T>;
1303 type Item = &'a str;
1304
1305 #[inline]
1306 fn into_iter(self) -> Self::IntoIter {
1307 self.iter()
1308 }
1309}
1310
1311impl<T> ToOwned for Utf8Path<T>
1312where
1313 T: Utf8Encoding,
1314{
1315 type Owned = Utf8PathBuf<T>;
1316
1317 #[inline]
1318 fn to_owned(&self) -> Self::Owned {
1319 self.to_path_buf()
1320 }
1321}
1322
1323macro_rules! impl_cmp {
1324 ($($lt:lifetime),* ; $lhs:ty, $rhs: ty) => {
1325 impl<$($lt,)* T> PartialEq<$rhs> for $lhs
1326 where
1327 T: Utf8Encoding,
1328 {
1329 #[inline]
1330 fn eq(&self, other: &$rhs) -> bool {
1331 <Utf8Path<T> as PartialEq>::eq(self, other)
1332 }
1333 }
1334
1335 impl<$($lt,)* T> PartialEq<$lhs> for $rhs
1336 where
1337 T: Utf8Encoding,
1338 {
1339 #[inline]
1340 fn eq(&self, other: &$lhs) -> bool {
1341 <Utf8Path<T> as PartialEq>::eq(self, other)
1342 }
1343 }
1344
1345 impl<$($lt,)* T> PartialOrd<$rhs> for $lhs
1346 where
1347 T: Utf8Encoding,
1348 {
1349 #[inline]
1350 fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
1351 <Utf8Path<T> as PartialOrd>::partial_cmp(self, other)
1352 }
1353 }
1354
1355 impl<$($lt,)* T> PartialOrd<$lhs> for $rhs
1356 where
1357 T: Utf8Encoding,
1358 {
1359 #[inline]
1360 fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
1361 <Utf8Path<T> as PartialOrd>::partial_cmp(self, other)
1362 }
1363 }
1364 };
1365}
1366
1367impl_cmp!(; Utf8PathBuf<T>, Utf8Path<T>);
1368impl_cmp!('a; Utf8PathBuf<T>, &'a Utf8Path<T>);
1369impl_cmp!('a; Cow<'a, Utf8Path<T>>, Utf8Path<T>);
1370impl_cmp!('a, 'b; Cow<'a, Utf8Path<T>>, &'b Utf8Path<T>);
1371impl_cmp!('a; Cow<'a, Utf8Path<T>>, Utf8PathBuf<T>);
1372
1373macro_rules! impl_cmp_bytes {
1374 ($($lt:lifetime),* ; $lhs:ty, $rhs: ty) => {
1375 impl<$($lt,)* T> PartialEq<$rhs> for $lhs
1376 where
1377 T: Utf8Encoding,
1378 {
1379 #[inline]
1380 fn eq(&self, other: &$rhs) -> bool {
1381 <Utf8Path<T> as PartialEq>::eq(self, other.as_ref())
1382 }
1383 }
1384
1385 impl<$($lt,)* T> PartialEq<$lhs> for $rhs
1386 where
1387 T: Utf8Encoding,
1388 {
1389 #[inline]
1390 fn eq(&self, other: &$lhs) -> bool {
1391 <Utf8Path<T> as PartialEq>::eq(self.as_ref(), other)
1392 }
1393 }
1394
1395 impl<$($lt,)* T> PartialOrd<$rhs> for $lhs
1396 where
1397 T: Utf8Encoding,
1398 {
1399 #[inline]
1400 fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
1401 <Utf8Path<T> as PartialOrd>::partial_cmp(self, other.as_ref())
1402 }
1403 }
1404
1405 impl<$($lt,)* T> PartialOrd<$lhs> for $rhs
1406 where
1407 T: Utf8Encoding,
1408 {
1409 #[inline]
1410 fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
1411 <Utf8Path<T> as PartialOrd>::partial_cmp(self.as_ref(), other)
1412 }
1413 }
1414 };
1415}
1416
1417impl_cmp_bytes!(; Utf8PathBuf<T>, str);
1418impl_cmp_bytes!('a; Utf8PathBuf<T>, &'a str);
1419impl_cmp_bytes!('a; Utf8PathBuf<T>, Cow<'a, str>);
1420impl_cmp_bytes!(; Utf8PathBuf<T>, String);
1421impl_cmp_bytes!(; Utf8Path<T>, str);
1422impl_cmp_bytes!('a; Utf8Path<T>, &'a str);
1423impl_cmp_bytes!('a; Utf8Path<T>, Cow<'a, str>);
1424impl_cmp_bytes!(; Utf8Path<T>, String);
1425impl_cmp_bytes!('a; &'a Utf8Path<T>, str);
1426impl_cmp_bytes!('a, 'b; &'a Utf8Path<T>, Cow<'b, str>);
1427impl_cmp_bytes!('a; &'a Utf8Path<T>, String);
1428
1429mod helpers {
1430 use super::*;
1431
1432 pub fn rsplit_file_at_dot(file: &str) -> (Option<&str>, Option<&str>) {
1433 if file == ".." {
1434 return (Some(file), None);
1435 }
1436
1437 let mut iter = file.rsplitn(2, '.');
1438 let after = iter.next();
1439 let before = iter.next();
1440 if before == Some("") {
1441 (Some(file), None)
1442 } else {
1443 (before, after)
1444 }
1445 }
1446
1447 // Iterate through `iter` while it matches `prefix`; return `None` if `prefix`
1448 // is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving
1449 // `iter` after having exhausted `prefix`.
1450 pub fn iter_after<'a, 'b, T, U, I, J>(mut iter: I, mut prefix: J) -> Option<I>
1451 where
1452 T: Utf8Component<'a>,
1453 U: Utf8Component<'b>,
1454 I: Iterator<Item = T> + Clone,
1455 J: Iterator<Item = U>,
1456 {
1457 loop {
1458 let mut iter_next = iter.clone();
1459 match (iter_next.next(), prefix.next()) {
1460 // TODO: Because there is not a `Component` struct, there is no direct comparison
1461 // between T and U since they aren't the same type due to different
1462 // lifetimes. We get around this with an equality check by converting these
1463 // components to bytes, which should work for the Unix and Windows component
1464 // implementations, but is error-prone as any new implementation could
1465 // deviate in a way that breaks this subtlely. Instead, need to figure out
1466 // either how to bring back equality of x == y WITHOUT needing to have
1467 // T: PartialEq<U> because that causes lifetime problems for `strip_prefix`
1468 (Some(ref x), Some(ref y)) if x.as_str() == y.as_str() => (),
1469 (Some(_), Some(_)) => return None,
1470 (Some(_), None) => return Some(iter),
1471 (None, None) => return Some(iter),
1472 (None, Some(_)) => return None,
1473 }
1474 iter = iter_next;
1475 }
1476 }
1477}
1478
1479#[cfg(any(
1480 unix,
1481 all(target_vendor = "fortanix", target_env = "sgx"),
1482 target_os = "solid_asp3",
1483 target_os = "hermit",
1484 target_os = "wasi"
1485))]
1486#[cfg(feature = "std")]
1487mod std_conversions {
1488 use std::ffi::{OsStr, OsString};
1489 #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
1490 use std::os::fortanix_sgx as os;
1491 #[cfg(target_os = "solid_asp3")]
1492 use std::os::solid as os;
1493 #[cfg(any(target_os = "hermit", unix))]
1494 use std::os::unix as os;
1495 #[cfg(target_os = "wasi")]
1496 use std::os::wasi as os;
1497
1498 use os::ffi::OsStrExt;
1499
1500 use super::*;
1501 use crate::common::TryAsRef;
1502
1503 impl<T> TryAsRef<Utf8Path<T>> for OsStr
1504 where
1505 T: Utf8Encoding,
1506 {
1507 #[inline]
1508 fn try_as_ref(&self) -> Option<&Utf8Path<T>> {
1509 std::str::from_utf8(self.as_bytes()).ok().map(Utf8Path::new)
1510 }
1511 }
1512
1513 impl<T> TryAsRef<Utf8Path<T>> for OsString
1514 where
1515 T: Utf8Encoding,
1516 {
1517 #[inline]
1518 fn try_as_ref(&self) -> Option<&Utf8Path<T>> {
1519 std::str::from_utf8(self.as_bytes()).ok().map(Utf8Path::new)
1520 }
1521 }
1522
1523 impl<T> AsRef<OsStr> for Utf8Path<T>
1524 where
1525 T: Utf8Encoding,
1526 {
1527 #[inline]
1528 fn as_ref(&self) -> &OsStr {
1529 OsStrExt::from_bytes(self.as_str().as_bytes())
1530 }
1531 }
1532}