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