1 | // Copyright (c) The camino Contributors |
2 | // SPDX-License-Identifier: MIT OR Apache-2.0 |
3 | |
4 | #![warn (missing_docs)] |
5 | #![cfg_attr (doc_cfg, feature(doc_cfg, doc_auto_cfg))] |
6 | |
7 | //! UTF-8 encoded paths. |
8 | //! |
9 | //! `camino` is an extension of the `std::path` module that adds new [`Utf8PathBuf`] and [`Utf8Path`] |
10 | //! types. These are like the standard library's [`PathBuf`] and [`Path`] types, except they are |
11 | //! guaranteed to only contain UTF-8 encoded data. Therefore, they expose the ability to get their |
12 | //! contents as strings, they implement `Display`, etc. |
13 | //! |
14 | //! The `std::path` types are not guaranteed to be valid UTF-8. This is the right decision for the standard library, |
15 | //! since it must be as general as possible. However, on all platforms, non-Unicode paths are vanishingly uncommon for a |
16 | //! number of reasons: |
17 | //! * Unicode won. There are still some legacy codebases that store paths in encodings like Shift-JIS, but most |
18 | //! have been converted to Unicode at this point. |
19 | //! * Unicode is the common subset of supported paths across Windows and Unix platforms. (On Windows, Rust stores paths |
20 | //! as [an extension to UTF-8](https://simonsapin.github.io/wtf-8/), and converts them to UTF-16 at Win32 |
21 | //! API boundaries.) |
22 | //! * There are already many systems, such as Cargo, that only support UTF-8 paths. If your own tool interacts with any such |
23 | //! system, you can assume that paths are valid UTF-8 without creating any additional burdens on consumers. |
24 | //! * The ["makefile problem"](https://www.mercurial-scm.org/wiki/EncodingStrategy#The_.22makefile_problem.22) |
25 | //! (which also applies to `Cargo.toml`, and any other metadata file that lists the names of other files) has *no general, |
26 | //! cross-platform solution* in systems that support non-UTF-8 paths. However, restricting paths to UTF-8 eliminates |
27 | //! this problem. |
28 | //! |
29 | //! Therefore, many programs that want to manipulate paths *do* assume they contain UTF-8 data, and convert them to `str`s |
30 | //! as necessary. However, because this invariant is not encoded in the `Path` type, conversions such as |
31 | //! `path.to_str().unwrap()` need to be repeated again and again, creating a frustrating experience. |
32 | //! |
33 | //! Instead, `camino` allows you to check that your paths are UTF-8 *once*, and then manipulate them |
34 | //! as valid UTF-8 from there on, avoiding repeated lossy and confusing conversions. |
35 | |
36 | use std::{ |
37 | borrow::{Borrow, Cow}, |
38 | cmp::Ordering, |
39 | convert::{Infallible, TryFrom, TryInto}, |
40 | error, |
41 | ffi::{OsStr, OsString}, |
42 | fmt, |
43 | fs::{self, Metadata}, |
44 | hash::{Hash, Hasher}, |
45 | io, |
46 | iter::FusedIterator, |
47 | ops::Deref, |
48 | path::*, |
49 | rc::Rc, |
50 | str::FromStr, |
51 | sync::Arc, |
52 | }; |
53 | |
54 | #[cfg (feature = "proptest1" )] |
55 | mod proptest_impls; |
56 | #[cfg (feature = "serde1" )] |
57 | mod serde_impls; |
58 | #[cfg (test)] |
59 | mod tests; |
60 | |
61 | /// An owned, mutable UTF-8 path (akin to [`String`]). |
62 | /// |
63 | /// This type provides methods like [`push`] and [`set_extension`] that mutate |
64 | /// the path in place. It also implements [`Deref`] to [`Utf8Path`], meaning that |
65 | /// all methods on [`Utf8Path`] slices are available on `Utf8PathBuf` values as well. |
66 | /// |
67 | /// [`push`]: Utf8PathBuf::push |
68 | /// [`set_extension`]: Utf8PathBuf::set_extension |
69 | /// |
70 | /// # Examples |
71 | /// |
72 | /// You can use [`push`] to build up a `Utf8PathBuf` from |
73 | /// components: |
74 | /// |
75 | /// ``` |
76 | /// use camino::Utf8PathBuf; |
77 | /// |
78 | /// let mut path = Utf8PathBuf::new(); |
79 | /// |
80 | /// path.push(r"C:\" ); |
81 | /// path.push("windows" ); |
82 | /// path.push("system32" ); |
83 | /// |
84 | /// path.set_extension("dll" ); |
85 | /// ``` |
86 | /// |
87 | /// However, [`push`] is best used for dynamic situations. This is a better way |
88 | /// to do this when you know all of the components ahead of time: |
89 | /// |
90 | /// ``` |
91 | /// use camino::Utf8PathBuf; |
92 | /// |
93 | /// let path: Utf8PathBuf = [r"C:\" , "windows" , "system32.dll" ].iter().collect(); |
94 | /// ``` |
95 | /// |
96 | /// We can still do better than this! Since these are all strings, we can use |
97 | /// `From::from`: |
98 | /// |
99 | /// ``` |
100 | /// use camino::Utf8PathBuf; |
101 | /// |
102 | /// let path = Utf8PathBuf::from(r"C:\windows\system32.dll" ); |
103 | /// ``` |
104 | /// |
105 | /// Which method works best depends on what kind of situation you're in. |
106 | // NB: Internal PathBuf must only contain utf8 data |
107 | #[derive (Clone, Default)] |
108 | #[cfg_attr (feature = "serde1" , derive(serde::Serialize, serde::Deserialize))] |
109 | #[cfg_attr (feature = "serde1" , serde(transparent))] |
110 | #[repr (transparent)] |
111 | pub struct Utf8PathBuf(PathBuf); |
112 | |
113 | impl Utf8PathBuf { |
114 | /// Allocates an empty `Utf8PathBuf`. |
115 | /// |
116 | /// # Examples |
117 | /// |
118 | /// ``` |
119 | /// use camino::Utf8PathBuf; |
120 | /// |
121 | /// let path = Utf8PathBuf::new(); |
122 | /// ``` |
123 | #[must_use ] |
124 | pub fn new() -> Utf8PathBuf { |
125 | Utf8PathBuf(PathBuf::new()) |
126 | } |
127 | |
128 | /// Creates a new `Utf8PathBuf` from a `PathBuf` containing valid UTF-8 characters. |
129 | /// |
130 | /// Errors with the original `PathBuf` if it is not valid UTF-8. |
131 | /// |
132 | /// For a version that returns a type that implements [`std::error::Error`], use the |
133 | /// `TryFrom<PathBuf>` impl. |
134 | /// |
135 | /// # Examples |
136 | /// |
137 | /// ``` |
138 | /// use camino::Utf8PathBuf; |
139 | /// use std::ffi::OsStr; |
140 | /// # #[cfg (unix)] |
141 | /// use std::os::unix::ffi::OsStrExt; |
142 | /// use std::path::PathBuf; |
143 | /// |
144 | /// let unicode_path = PathBuf::from("/valid/unicode" ); |
145 | /// Utf8PathBuf::from_path_buf(unicode_path).expect("valid Unicode path succeeded" ); |
146 | /// |
147 | /// // Paths on Unix can be non-UTF-8. |
148 | /// # #[cfg (unix)] |
149 | /// let non_unicode_str = OsStr::from_bytes(b" \xFF\xFF\xFF" ); |
150 | /// # #[cfg (unix)] |
151 | /// let non_unicode_path = PathBuf::from(non_unicode_str); |
152 | /// # #[cfg (unix)] |
153 | /// Utf8PathBuf::from_path_buf(non_unicode_path).expect_err("non-Unicode path failed" ); |
154 | /// ``` |
155 | pub fn from_path_buf(path: PathBuf) -> Result<Utf8PathBuf, PathBuf> { |
156 | match path.into_os_string().into_string() { |
157 | Ok(string) => Ok(Utf8PathBuf::from(string)), |
158 | Err(os_string) => Err(PathBuf::from(os_string)), |
159 | } |
160 | } |
161 | |
162 | /// Converts a `Utf8PathBuf` to a [`PathBuf`]. |
163 | /// |
164 | /// This is equivalent to the `From<Utf8PathBuf> for PathBuf` impl, but may aid in type |
165 | /// inference. |
166 | /// |
167 | /// # Examples |
168 | /// |
169 | /// ``` |
170 | /// use camino::Utf8PathBuf; |
171 | /// use std::path::PathBuf; |
172 | /// |
173 | /// let utf8_path_buf = Utf8PathBuf::from("foo.txt" ); |
174 | /// let std_path_buf = utf8_path_buf.into_std_path_buf(); |
175 | /// assert_eq!(std_path_buf.to_str(), Some("foo.txt" )); |
176 | /// |
177 | /// // Convert back to a Utf8PathBuf. |
178 | /// let new_utf8_path_buf = Utf8PathBuf::from_path_buf(std_path_buf).unwrap(); |
179 | /// assert_eq!(new_utf8_path_buf, "foo.txt" ); |
180 | /// ``` |
181 | #[must_use = "`self` will be dropped if the result is not used" ] |
182 | pub fn into_std_path_buf(self) -> PathBuf { |
183 | self.into() |
184 | } |
185 | |
186 | /// Creates a new `Utf8PathBuf` with a given capacity used to create the internal [`PathBuf`]. |
187 | /// See [`with_capacity`] defined on [`PathBuf`]. |
188 | /// |
189 | /// *Requires Rust 1.44 or newer.* |
190 | /// |
191 | /// # Examples |
192 | /// |
193 | /// ``` |
194 | /// use camino::Utf8PathBuf; |
195 | /// |
196 | /// let mut path = Utf8PathBuf::with_capacity(10); |
197 | /// let capacity = path.capacity(); |
198 | /// |
199 | /// // This push is done without reallocating |
200 | /// path.push(r"C:\" ); |
201 | /// |
202 | /// assert_eq!(capacity, path.capacity()); |
203 | /// ``` |
204 | /// |
205 | /// [`with_capacity`]: PathBuf::with_capacity |
206 | #[cfg (path_buf_capacity)] |
207 | #[must_use ] |
208 | pub fn with_capacity(capacity: usize) -> Utf8PathBuf { |
209 | Utf8PathBuf(PathBuf::with_capacity(capacity)) |
210 | } |
211 | |
212 | /// Coerces to a [`Utf8Path`] slice. |
213 | /// |
214 | /// # Examples |
215 | /// |
216 | /// ``` |
217 | /// use camino::{Utf8Path, Utf8PathBuf}; |
218 | /// |
219 | /// let p = Utf8PathBuf::from("/test" ); |
220 | /// assert_eq!(Utf8Path::new("/test" ), p.as_path()); |
221 | /// ``` |
222 | #[must_use ] |
223 | pub fn as_path(&self) -> &Utf8Path { |
224 | // SAFETY: every Utf8PathBuf constructor ensures that self is valid UTF-8 |
225 | unsafe { Utf8Path::assume_utf8(&self.0) } |
226 | } |
227 | |
228 | /// Extends `self` with `path`. |
229 | /// |
230 | /// If `path` is absolute, it replaces the current path. |
231 | /// |
232 | /// On Windows: |
233 | /// |
234 | /// * if `path` has a root but no prefix (e.g., `\windows`), it |
235 | /// replaces everything except for the prefix (if any) of `self`. |
236 | /// * if `path` has a prefix but no root, it replaces `self`. |
237 | /// |
238 | /// # Examples |
239 | /// |
240 | /// Pushing a relative path extends the existing path: |
241 | /// |
242 | /// ``` |
243 | /// use camino::Utf8PathBuf; |
244 | /// |
245 | /// let mut path = Utf8PathBuf::from("/tmp" ); |
246 | /// path.push("file.bk" ); |
247 | /// assert_eq!(path, Utf8PathBuf::from("/tmp/file.bk" )); |
248 | /// ``` |
249 | /// |
250 | /// Pushing an absolute path replaces the existing path: |
251 | /// |
252 | /// ``` |
253 | /// use camino::Utf8PathBuf; |
254 | /// |
255 | /// let mut path = Utf8PathBuf::from("/tmp" ); |
256 | /// path.push("/etc" ); |
257 | /// assert_eq!(path, Utf8PathBuf::from("/etc" )); |
258 | /// ``` |
259 | pub fn push(&mut self, path: impl AsRef<Utf8Path>) { |
260 | self.0.push(&path.as_ref().0) |
261 | } |
262 | |
263 | /// Truncates `self` to [`self.parent`]. |
264 | /// |
265 | /// Returns `false` and does nothing if [`self.parent`] is [`None`]. |
266 | /// Otherwise, returns `true`. |
267 | /// |
268 | /// [`self.parent`]: Utf8Path::parent |
269 | /// |
270 | /// # Examples |
271 | /// |
272 | /// ``` |
273 | /// use camino::{Utf8Path, Utf8PathBuf}; |
274 | /// |
275 | /// let mut p = Utf8PathBuf::from("/spirited/away.rs" ); |
276 | /// |
277 | /// p.pop(); |
278 | /// assert_eq!(Utf8Path::new("/spirited" ), p); |
279 | /// p.pop(); |
280 | /// assert_eq!(Utf8Path::new("/" ), p); |
281 | /// ``` |
282 | pub fn pop(&mut self) -> bool { |
283 | self.0.pop() |
284 | } |
285 | |
286 | /// Updates [`self.file_name`] to `file_name`. |
287 | /// |
288 | /// If [`self.file_name`] was [`None`], this is equivalent to pushing |
289 | /// `file_name`. |
290 | /// |
291 | /// Otherwise it is equivalent to calling [`pop`] and then pushing |
292 | /// `file_name`. The new path will be a sibling of the original path. |
293 | /// (That is, it will have the same parent.) |
294 | /// |
295 | /// [`self.file_name`]: Utf8Path::file_name |
296 | /// [`pop`]: Utf8PathBuf::pop |
297 | /// |
298 | /// # Examples |
299 | /// |
300 | /// ``` |
301 | /// use camino::Utf8PathBuf; |
302 | /// |
303 | /// let mut buf = Utf8PathBuf::from("/" ); |
304 | /// assert_eq!(buf.file_name(), None); |
305 | /// buf.set_file_name("bar" ); |
306 | /// assert_eq!(buf, Utf8PathBuf::from("/bar" )); |
307 | /// assert!(buf.file_name().is_some()); |
308 | /// buf.set_file_name("baz.txt" ); |
309 | /// assert_eq!(buf, Utf8PathBuf::from("/baz.txt" )); |
310 | /// ``` |
311 | pub fn set_file_name(&mut self, file_name: impl AsRef<str>) { |
312 | self.0.set_file_name(file_name.as_ref()) |
313 | } |
314 | |
315 | /// Updates [`self.extension`] to `extension`. |
316 | /// |
317 | /// Returns `false` and does nothing if [`self.file_name`] is [`None`], |
318 | /// returns `true` and updates the extension otherwise. |
319 | /// |
320 | /// If [`self.extension`] is [`None`], the extension is added; otherwise |
321 | /// it is replaced. |
322 | /// |
323 | /// [`self.file_name`]: Utf8Path::file_name |
324 | /// [`self.extension`]: Utf8Path::extension |
325 | /// |
326 | /// # Examples |
327 | /// |
328 | /// ``` |
329 | /// use camino::{Utf8Path, Utf8PathBuf}; |
330 | /// |
331 | /// let mut p = Utf8PathBuf::from("/feel/the" ); |
332 | /// |
333 | /// p.set_extension("force" ); |
334 | /// assert_eq!(Utf8Path::new("/feel/the.force" ), p.as_path()); |
335 | /// |
336 | /// p.set_extension("dark_side" ); |
337 | /// assert_eq!(Utf8Path::new("/feel/the.dark_side" ), p.as_path()); |
338 | /// ``` |
339 | pub fn set_extension(&mut self, extension: impl AsRef<str>) -> bool { |
340 | self.0.set_extension(extension.as_ref()) |
341 | } |
342 | |
343 | /// Consumes the `Utf8PathBuf`, yielding its internal [`String`] storage. |
344 | /// |
345 | /// # Examples |
346 | /// |
347 | /// ``` |
348 | /// use camino::Utf8PathBuf; |
349 | /// |
350 | /// let p = Utf8PathBuf::from("/the/head" ); |
351 | /// let s = p.into_string(); |
352 | /// assert_eq!(s, "/the/head" ); |
353 | /// ``` |
354 | #[must_use = "`self` will be dropped if the result is not used" ] |
355 | pub fn into_string(self) -> String { |
356 | self.into_os_string().into_string().unwrap() |
357 | } |
358 | |
359 | /// Consumes the `Utf8PathBuf`, yielding its internal [`OsString`] storage. |
360 | /// |
361 | /// # Examples |
362 | /// |
363 | /// ``` |
364 | /// use camino::Utf8PathBuf; |
365 | /// use std::ffi::OsStr; |
366 | /// |
367 | /// let p = Utf8PathBuf::from("/the/head" ); |
368 | /// let s = p.into_os_string(); |
369 | /// assert_eq!(s, OsStr::new("/the/head" )); |
370 | /// ``` |
371 | #[must_use = "`self` will be dropped if the result is not used" ] |
372 | pub fn into_os_string(self) -> OsString { |
373 | self.0.into_os_string() |
374 | } |
375 | |
376 | /// Converts this `Utf8PathBuf` into a [boxed](Box) [`Utf8Path`]. |
377 | #[must_use = "`self` will be dropped if the result is not used" ] |
378 | pub fn into_boxed_path(self) -> Box<Utf8Path> { |
379 | let ptr = Box::into_raw(self.0.into_boxed_path()) as *mut Utf8Path; |
380 | // SAFETY: |
381 | // * self is valid UTF-8 |
382 | // * ptr was constructed by consuming self so it represents an owned path |
383 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *mut Path to |
384 | // *mut Utf8Path is valid |
385 | unsafe { Box::from_raw(ptr) } |
386 | } |
387 | |
388 | /// Invokes [`capacity`] on the underlying instance of [`PathBuf`]. |
389 | /// |
390 | /// *Requires Rust 1.44 or newer.* |
391 | /// |
392 | /// [`capacity`]: PathBuf::capacity |
393 | #[cfg (path_buf_capacity)] |
394 | #[must_use ] |
395 | pub fn capacity(&self) -> usize { |
396 | self.0.capacity() |
397 | } |
398 | |
399 | /// Invokes [`clear`] on the underlying instance of [`PathBuf`]. |
400 | /// |
401 | /// *Requires Rust 1.44 or newer.* |
402 | /// |
403 | /// [`clear`]: PathBuf::clear |
404 | #[cfg (path_buf_capacity)] |
405 | pub fn clear(&mut self) { |
406 | self.0.clear() |
407 | } |
408 | |
409 | /// Invokes [`reserve`] on the underlying instance of [`PathBuf`]. |
410 | /// |
411 | /// *Requires Rust 1.44 or newer.* |
412 | /// |
413 | /// [`reserve`]: PathBuf::reserve |
414 | #[cfg (path_buf_capacity)] |
415 | pub fn reserve(&mut self, additional: usize) { |
416 | self.0.reserve(additional) |
417 | } |
418 | |
419 | /// Invokes [`try_reserve`] on the underlying instance of [`PathBuf`]. |
420 | /// |
421 | /// *Requires Rust 1.63 or newer.* |
422 | /// |
423 | /// [`try_reserve`]: PathBuf::try_reserve |
424 | #[cfg (try_reserve_2)] |
425 | #[inline ] |
426 | pub fn try_reserve( |
427 | &mut self, |
428 | additional: usize, |
429 | ) -> Result<(), std::collections::TryReserveError> { |
430 | self.0.try_reserve(additional) |
431 | } |
432 | |
433 | /// Invokes [`reserve_exact`] on the underlying instance of [`PathBuf`]. |
434 | /// |
435 | /// *Requires Rust 1.44 or newer.* |
436 | /// |
437 | /// [`reserve_exact`]: PathBuf::reserve_exact |
438 | #[cfg (path_buf_capacity)] |
439 | pub fn reserve_exact(&mut self, additional: usize) { |
440 | self.0.reserve_exact(additional) |
441 | } |
442 | |
443 | /// Invokes [`try_reserve_exact`] on the underlying instance of [`PathBuf`]. |
444 | /// |
445 | /// *Requires Rust 1.63 or newer.* |
446 | /// |
447 | /// [`try_reserve_exact`]: PathBuf::try_reserve_exact |
448 | #[cfg (try_reserve_2)] |
449 | #[inline ] |
450 | pub fn try_reserve_exact( |
451 | &mut self, |
452 | additional: usize, |
453 | ) -> Result<(), std::collections::TryReserveError> { |
454 | self.0.try_reserve_exact(additional) |
455 | } |
456 | |
457 | /// Invokes [`shrink_to_fit`] on the underlying instance of [`PathBuf`]. |
458 | /// |
459 | /// *Requires Rust 1.44 or newer.* |
460 | /// |
461 | /// [`shrink_to_fit`]: PathBuf::shrink_to_fit |
462 | #[cfg (path_buf_capacity)] |
463 | pub fn shrink_to_fit(&mut self) { |
464 | self.0.shrink_to_fit() |
465 | } |
466 | |
467 | /// Invokes [`shrink_to`] on the underlying instance of [`PathBuf`]. |
468 | /// |
469 | /// *Requires Rust 1.56 or newer.* |
470 | /// |
471 | /// [`shrink_to`]: PathBuf::shrink_to |
472 | #[cfg (shrink_to)] |
473 | #[inline ] |
474 | pub fn shrink_to(&mut self, min_capacity: usize) { |
475 | self.0.shrink_to(min_capacity) |
476 | } |
477 | } |
478 | |
479 | impl Deref for Utf8PathBuf { |
480 | type Target = Utf8Path; |
481 | |
482 | fn deref(&self) -> &Utf8Path { |
483 | self.as_path() |
484 | } |
485 | } |
486 | |
487 | /// *Requires Rust 1.68 or newer.* |
488 | #[cfg (path_buf_deref_mut)] |
489 | impl std::ops::DerefMut for Utf8PathBuf { |
490 | fn deref_mut(&mut self) -> &mut Self::Target { |
491 | unsafe { Utf8Path::assume_utf8_mut(&mut self.0) } |
492 | } |
493 | } |
494 | |
495 | impl fmt::Debug for Utf8PathBuf { |
496 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
497 | fmt::Debug::fmt(&**self, f) |
498 | } |
499 | } |
500 | |
501 | impl fmt::Display for Utf8PathBuf { |
502 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
503 | fmt::Display::fmt(self.as_str(), f) |
504 | } |
505 | } |
506 | |
507 | impl<P: AsRef<Utf8Path>> Extend<P> for Utf8PathBuf { |
508 | fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) { |
509 | for path: P in iter { |
510 | self.push(path); |
511 | } |
512 | } |
513 | } |
514 | |
515 | /// A slice of a UTF-8 path (akin to [`str`]). |
516 | /// |
517 | /// This type supports a number of operations for inspecting a path, including |
518 | /// breaking the path into its components (separated by `/` on Unix and by either |
519 | /// `/` or `\` on Windows), extracting the file name, determining whether the path |
520 | /// is absolute, and so on. |
521 | /// |
522 | /// This is an *unsized* type, meaning that it must always be used behind a |
523 | /// pointer like `&` or [`Box`]. For an owned version of this type, |
524 | /// see [`Utf8PathBuf`]. |
525 | /// |
526 | /// # Examples |
527 | /// |
528 | /// ``` |
529 | /// use camino::Utf8Path; |
530 | /// |
531 | /// // Note: this example does work on Windows |
532 | /// let path = Utf8Path::new("./foo/bar.txt" ); |
533 | /// |
534 | /// let parent = path.parent(); |
535 | /// assert_eq!(parent, Some(Utf8Path::new("./foo" ))); |
536 | /// |
537 | /// let file_stem = path.file_stem(); |
538 | /// assert_eq!(file_stem, Some("bar" )); |
539 | /// |
540 | /// let extension = path.extension(); |
541 | /// assert_eq!(extension, Some("txt" )); |
542 | /// ``` |
543 | // NB: Internal Path must only contain utf8 data |
544 | #[repr (transparent)] |
545 | pub struct Utf8Path(Path); |
546 | |
547 | impl Utf8Path { |
548 | /// Directly wraps a string slice as a `Utf8Path` slice. |
549 | /// |
550 | /// This is a cost-free conversion. |
551 | /// |
552 | /// # Examples |
553 | /// |
554 | /// ``` |
555 | /// use camino::Utf8Path; |
556 | /// |
557 | /// Utf8Path::new("foo.txt" ); |
558 | /// ``` |
559 | /// |
560 | /// You can create `Utf8Path`s from `String`s, or even other `Utf8Path`s: |
561 | /// |
562 | /// ``` |
563 | /// use camino::Utf8Path; |
564 | /// |
565 | /// let string = String::from("foo.txt" ); |
566 | /// let from_string = Utf8Path::new(&string); |
567 | /// let from_path = Utf8Path::new(&from_string); |
568 | /// assert_eq!(from_string, from_path); |
569 | /// ``` |
570 | pub fn new(s: &(impl AsRef<str> + ?Sized)) -> &Utf8Path { |
571 | let path = Path::new(s.as_ref()); |
572 | // SAFETY: s is a str which means it is always valid UTF-8 |
573 | unsafe { Utf8Path::assume_utf8(path) } |
574 | } |
575 | |
576 | /// Converts a [`Path`] to a `Utf8Path`. |
577 | /// |
578 | /// Returns `None` if the path is not valid UTF-8. |
579 | /// |
580 | /// For a version that returns a type that implements [`std::error::Error`], use the |
581 | /// `TryFrom<&Path>` impl. |
582 | /// |
583 | /// # Examples |
584 | /// |
585 | /// ``` |
586 | /// use camino::Utf8Path; |
587 | /// use std::ffi::OsStr; |
588 | /// # #[cfg (unix)] |
589 | /// use std::os::unix::ffi::OsStrExt; |
590 | /// use std::path::Path; |
591 | /// |
592 | /// let unicode_path = Path::new("/valid/unicode" ); |
593 | /// Utf8Path::from_path(unicode_path).expect("valid Unicode path succeeded" ); |
594 | /// |
595 | /// // Paths on Unix can be non-UTF-8. |
596 | /// # #[cfg (unix)] |
597 | /// let non_unicode_str = OsStr::from_bytes(b" \xFF\xFF\xFF" ); |
598 | /// # #[cfg (unix)] |
599 | /// let non_unicode_path = Path::new(non_unicode_str); |
600 | /// # #[cfg (unix)] |
601 | /// assert!(Utf8Path::from_path(non_unicode_path).is_none(), "non-Unicode path failed" ); |
602 | /// ``` |
603 | pub fn from_path(path: &Path) -> Option<&Utf8Path> { |
604 | path.as_os_str().to_str().map(Utf8Path::new) |
605 | } |
606 | |
607 | /// Converts a `Utf8Path` to a [`Path`]. |
608 | /// |
609 | /// This is equivalent to the `AsRef<&Path> for &Utf8Path` impl, but may aid in type inference. |
610 | /// |
611 | /// # Examples |
612 | /// |
613 | /// ``` |
614 | /// use camino::Utf8Path; |
615 | /// use std::path::Path; |
616 | /// |
617 | /// let utf8_path = Utf8Path::new("foo.txt" ); |
618 | /// let std_path: &Path = utf8_path.as_std_path(); |
619 | /// assert_eq!(std_path.to_str(), Some("foo.txt" )); |
620 | /// |
621 | /// // Convert back to a Utf8Path. |
622 | /// let new_utf8_path = Utf8Path::from_path(std_path).unwrap(); |
623 | /// assert_eq!(new_utf8_path, "foo.txt" ); |
624 | /// ``` |
625 | pub fn as_std_path(&self) -> &Path { |
626 | self.as_ref() |
627 | } |
628 | |
629 | /// Yields the underlying [`str`] slice. |
630 | /// |
631 | /// Unlike [`Path::to_str`], this always returns a slice because the contents of a `Utf8Path` |
632 | /// are guaranteed to be valid UTF-8. |
633 | /// |
634 | /// # Examples |
635 | /// |
636 | /// ``` |
637 | /// use camino::Utf8Path; |
638 | /// |
639 | /// let s = Utf8Path::new("foo.txt" ).as_str(); |
640 | /// assert_eq!(s, "foo.txt" ); |
641 | /// ``` |
642 | /// |
643 | /// [`str`]: str |
644 | #[must_use ] |
645 | pub fn as_str(&self) -> &str { |
646 | // SAFETY: every Utf8Path constructor ensures that self is valid UTF-8 |
647 | unsafe { assume_utf8(self.as_os_str()) } |
648 | } |
649 | |
650 | /// Yields the underlying [`OsStr`] slice. |
651 | /// |
652 | /// # Examples |
653 | /// |
654 | /// ``` |
655 | /// use camino::Utf8Path; |
656 | /// |
657 | /// let os_str = Utf8Path::new("foo.txt" ).as_os_str(); |
658 | /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt" )); |
659 | /// ``` |
660 | #[must_use ] |
661 | pub fn as_os_str(&self) -> &OsStr { |
662 | self.0.as_os_str() |
663 | } |
664 | |
665 | /// Converts a `Utf8Path` to an owned [`Utf8PathBuf`]. |
666 | /// |
667 | /// # Examples |
668 | /// |
669 | /// ``` |
670 | /// use camino::{Utf8Path, Utf8PathBuf}; |
671 | /// |
672 | /// let path_buf = Utf8Path::new("foo.txt" ).to_path_buf(); |
673 | /// assert_eq!(path_buf, Utf8PathBuf::from("foo.txt" )); |
674 | /// ``` |
675 | #[must_use = "this returns the result of the operation, \ |
676 | without modifying the original" ] |
677 | pub fn to_path_buf(&self) -> Utf8PathBuf { |
678 | Utf8PathBuf(self.0.to_path_buf()) |
679 | } |
680 | |
681 | /// Returns `true` if the `Utf8Path` is absolute, i.e., if it is independent of |
682 | /// the current directory. |
683 | /// |
684 | /// * On Unix, a path is absolute if it starts with the root, so |
685 | /// `is_absolute` and [`has_root`] are equivalent. |
686 | /// |
687 | /// * On Windows, a path is absolute if it has a prefix and starts with the |
688 | /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. |
689 | /// |
690 | /// # Examples |
691 | /// |
692 | /// ``` |
693 | /// use camino::Utf8Path; |
694 | /// |
695 | /// assert!(!Utf8Path::new("foo.txt" ).is_absolute()); |
696 | /// ``` |
697 | /// |
698 | /// [`has_root`]: Utf8Path::has_root |
699 | #[must_use ] |
700 | pub fn is_absolute(&self) -> bool { |
701 | self.0.is_absolute() |
702 | } |
703 | |
704 | /// Returns `true` if the `Utf8Path` is relative, i.e., not absolute. |
705 | /// |
706 | /// See [`is_absolute`]'s documentation for more details. |
707 | /// |
708 | /// # Examples |
709 | /// |
710 | /// ``` |
711 | /// use camino::Utf8Path; |
712 | /// |
713 | /// assert!(Utf8Path::new("foo.txt" ).is_relative()); |
714 | /// ``` |
715 | /// |
716 | /// [`is_absolute`]: Utf8Path::is_absolute |
717 | #[must_use ] |
718 | pub fn is_relative(&self) -> bool { |
719 | self.0.is_relative() |
720 | } |
721 | |
722 | /// Returns `true` if the `Utf8Path` has a root. |
723 | /// |
724 | /// * On Unix, a path has a root if it begins with `/`. |
725 | /// |
726 | /// * On Windows, a path has a root if it: |
727 | /// * has no prefix and begins with a separator, e.g., `\windows` |
728 | /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` |
729 | /// * has any non-disk prefix, e.g., `\\server\share` |
730 | /// |
731 | /// # Examples |
732 | /// |
733 | /// ``` |
734 | /// use camino::Utf8Path; |
735 | /// |
736 | /// assert!(Utf8Path::new("/etc/passwd" ).has_root()); |
737 | /// ``` |
738 | #[must_use ] |
739 | pub fn has_root(&self) -> bool { |
740 | self.0.has_root() |
741 | } |
742 | |
743 | /// Returns the `Path` without its final component, if there is one. |
744 | /// |
745 | /// Returns [`None`] if the path terminates in a root or prefix. |
746 | /// |
747 | /// # Examples |
748 | /// |
749 | /// ``` |
750 | /// use camino::Utf8Path; |
751 | /// |
752 | /// let path = Utf8Path::new("/foo/bar" ); |
753 | /// let parent = path.parent().unwrap(); |
754 | /// assert_eq!(parent, Utf8Path::new("/foo" )); |
755 | /// |
756 | /// let grand_parent = parent.parent().unwrap(); |
757 | /// assert_eq!(grand_parent, Utf8Path::new("/" )); |
758 | /// assert_eq!(grand_parent.parent(), None); |
759 | /// ``` |
760 | #[must_use ] |
761 | pub fn parent(&self) -> Option<&Utf8Path> { |
762 | self.0.parent().map(|path| { |
763 | // SAFETY: self is valid UTF-8, so parent is valid UTF-8 as well |
764 | unsafe { Utf8Path::assume_utf8(path) } |
765 | }) |
766 | } |
767 | |
768 | /// Produces an iterator over `Utf8Path` and its ancestors. |
769 | /// |
770 | /// The iterator will yield the `Utf8Path` that is returned if the [`parent`] method is used zero |
771 | /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`, |
772 | /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns |
773 | /// [`None`], the iterator will do likewise. The iterator will always yield at least one value, |
774 | /// namely `&self`. |
775 | /// |
776 | /// # Examples |
777 | /// |
778 | /// ``` |
779 | /// use camino::Utf8Path; |
780 | /// |
781 | /// let mut ancestors = Utf8Path::new("/foo/bar" ).ancestors(); |
782 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo/bar" ))); |
783 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/foo" ))); |
784 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("/" ))); |
785 | /// assert_eq!(ancestors.next(), None); |
786 | /// |
787 | /// let mut ancestors = Utf8Path::new("../foo/bar" ).ancestors(); |
788 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo/bar" ))); |
789 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("../foo" ))); |
790 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new(".." ))); |
791 | /// assert_eq!(ancestors.next(), Some(Utf8Path::new("" ))); |
792 | /// assert_eq!(ancestors.next(), None); |
793 | /// ``` |
794 | /// |
795 | /// [`parent`]: Utf8Path::parent |
796 | pub fn ancestors(&self) -> Utf8Ancestors<'_> { |
797 | Utf8Ancestors(self.0.ancestors()) |
798 | } |
799 | |
800 | /// Returns the final component of the `Utf8Path`, if there is one. |
801 | /// |
802 | /// If the path is a normal file, this is the file name. If it's the path of a directory, this |
803 | /// is the directory name. |
804 | /// |
805 | /// Returns [`None`] if the path terminates in `..`. |
806 | /// |
807 | /// # Examples |
808 | /// |
809 | /// ``` |
810 | /// use camino::Utf8Path; |
811 | /// |
812 | /// assert_eq!(Some("bin" ), Utf8Path::new("/usr/bin/" ).file_name()); |
813 | /// assert_eq!(Some("foo.txt" ), Utf8Path::new("tmp/foo.txt" ).file_name()); |
814 | /// assert_eq!(Some("foo.txt" ), Utf8Path::new("foo.txt/." ).file_name()); |
815 | /// assert_eq!(Some("foo.txt" ), Utf8Path::new("foo.txt/.//" ).file_name()); |
816 | /// assert_eq!(None, Utf8Path::new("foo.txt/.." ).file_name()); |
817 | /// assert_eq!(None, Utf8Path::new("/" ).file_name()); |
818 | /// ``` |
819 | #[must_use ] |
820 | pub fn file_name(&self) -> Option<&str> { |
821 | self.0.file_name().map(|s| { |
822 | // SAFETY: self is valid UTF-8, so file_name is valid UTF-8 as well |
823 | unsafe { assume_utf8(s) } |
824 | }) |
825 | } |
826 | |
827 | /// Returns a path that, when joined onto `base`, yields `self`. |
828 | /// |
829 | /// # Errors |
830 | /// |
831 | /// If `base` is not a prefix of `self` (i.e., [`starts_with`] |
832 | /// returns `false`), returns [`Err`]. |
833 | /// |
834 | /// [`starts_with`]: Utf8Path::starts_with |
835 | /// |
836 | /// # Examples |
837 | /// |
838 | /// ``` |
839 | /// use camino::{Utf8Path, Utf8PathBuf}; |
840 | /// |
841 | /// let path = Utf8Path::new("/test/haha/foo.txt" ); |
842 | /// |
843 | /// assert_eq!(path.strip_prefix("/" ), Ok(Utf8Path::new("test/haha/foo.txt" ))); |
844 | /// assert_eq!(path.strip_prefix("/test" ), Ok(Utf8Path::new("haha/foo.txt" ))); |
845 | /// assert_eq!(path.strip_prefix("/test/" ), Ok(Utf8Path::new("haha/foo.txt" ))); |
846 | /// assert_eq!(path.strip_prefix("/test/haha/foo.txt" ), Ok(Utf8Path::new("" ))); |
847 | /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/" ), Ok(Utf8Path::new("" ))); |
848 | /// |
849 | /// assert!(path.strip_prefix("test" ).is_err()); |
850 | /// assert!(path.strip_prefix("/haha" ).is_err()); |
851 | /// |
852 | /// let prefix = Utf8PathBuf::from("/test/" ); |
853 | /// assert_eq!(path.strip_prefix(prefix), Ok(Utf8Path::new("haha/foo.txt" ))); |
854 | /// ``` |
855 | pub fn strip_prefix(&self, base: impl AsRef<Path>) -> Result<&Utf8Path, StripPrefixError> { |
856 | self.0.strip_prefix(base).map(|path| { |
857 | // SAFETY: self is valid UTF-8, and strip_prefix returns a part of self (or an empty |
858 | // string), so it is valid UTF-8 as well. |
859 | unsafe { Utf8Path::assume_utf8(path) } |
860 | }) |
861 | } |
862 | |
863 | /// Determines whether `base` is a prefix of `self`. |
864 | /// |
865 | /// Only considers whole path components to match. |
866 | /// |
867 | /// # Examples |
868 | /// |
869 | /// ``` |
870 | /// use camino::Utf8Path; |
871 | /// |
872 | /// let path = Utf8Path::new("/etc/passwd" ); |
873 | /// |
874 | /// assert!(path.starts_with("/etc" )); |
875 | /// assert!(path.starts_with("/etc/" )); |
876 | /// assert!(path.starts_with("/etc/passwd" )); |
877 | /// assert!(path.starts_with("/etc/passwd/" )); // extra slash is okay |
878 | /// assert!(path.starts_with("/etc/passwd///" )); // multiple extra slashes are okay |
879 | /// |
880 | /// assert!(!path.starts_with("/e" )); |
881 | /// assert!(!path.starts_with("/etc/passwd.txt" )); |
882 | /// |
883 | /// assert!(!Utf8Path::new("/etc/foo.rs" ).starts_with("/etc/foo" )); |
884 | /// ``` |
885 | #[must_use ] |
886 | pub fn starts_with(&self, base: impl AsRef<Path>) -> bool { |
887 | self.0.starts_with(base) |
888 | } |
889 | |
890 | /// Determines whether `child` is a suffix of `self`. |
891 | /// |
892 | /// Only considers whole path components to match. |
893 | /// |
894 | /// # Examples |
895 | /// |
896 | /// ``` |
897 | /// use camino::Utf8Path; |
898 | /// |
899 | /// let path = Utf8Path::new("/etc/resolv.conf" ); |
900 | /// |
901 | /// assert!(path.ends_with("resolv.conf" )); |
902 | /// assert!(path.ends_with("etc/resolv.conf" )); |
903 | /// assert!(path.ends_with("/etc/resolv.conf" )); |
904 | /// |
905 | /// assert!(!path.ends_with("/resolv.conf" )); |
906 | /// assert!(!path.ends_with("conf" )); // use .extension() instead |
907 | /// ``` |
908 | #[must_use ] |
909 | pub fn ends_with(&self, base: impl AsRef<Path>) -> bool { |
910 | self.0.ends_with(base) |
911 | } |
912 | |
913 | /// Extracts the stem (non-extension) portion of [`self.file_name`]. |
914 | /// |
915 | /// [`self.file_name`]: Utf8Path::file_name |
916 | /// |
917 | /// The stem is: |
918 | /// |
919 | /// * [`None`], if there is no file name; |
920 | /// * The entire file name if there is no embedded `.`; |
921 | /// * The entire file name if the file name begins with `.` and has no other `.`s within; |
922 | /// * Otherwise, the portion of the file name before the final `.` |
923 | /// |
924 | /// # Examples |
925 | /// |
926 | /// ``` |
927 | /// use camino::Utf8Path; |
928 | /// |
929 | /// assert_eq!("foo" , Utf8Path::new("foo.rs" ).file_stem().unwrap()); |
930 | /// assert_eq!("foo.tar" , Utf8Path::new("foo.tar.gz" ).file_stem().unwrap()); |
931 | /// ``` |
932 | #[must_use ] |
933 | pub fn file_stem(&self) -> Option<&str> { |
934 | self.0.file_stem().map(|s| { |
935 | // SAFETY: self is valid UTF-8, so file_stem is valid UTF-8 as well |
936 | unsafe { assume_utf8(s) } |
937 | }) |
938 | } |
939 | |
940 | /// Extracts the extension of [`self.file_name`], if possible. |
941 | /// |
942 | /// The extension is: |
943 | /// |
944 | /// * [`None`], if there is no file name; |
945 | /// * [`None`], if there is no embedded `.`; |
946 | /// * [`None`], if the file name begins with `.` and has no other `.`s within; |
947 | /// * Otherwise, the portion of the file name after the final `.` |
948 | /// |
949 | /// [`self.file_name`]: Utf8Path::file_name |
950 | /// |
951 | /// # Examples |
952 | /// |
953 | /// ``` |
954 | /// use camino::Utf8Path; |
955 | /// |
956 | /// assert_eq!("rs" , Utf8Path::new("foo.rs" ).extension().unwrap()); |
957 | /// assert_eq!("gz" , Utf8Path::new("foo.tar.gz" ).extension().unwrap()); |
958 | /// ``` |
959 | #[must_use ] |
960 | pub fn extension(&self) -> Option<&str> { |
961 | self.0.extension().map(|s| { |
962 | // SAFETY: self is valid UTF-8, so extension is valid UTF-8 as well |
963 | unsafe { assume_utf8(s) } |
964 | }) |
965 | } |
966 | |
967 | /// Creates an owned [`Utf8PathBuf`] with `path` adjoined to `self`. |
968 | /// |
969 | /// See [`Utf8PathBuf::push`] for more details on what it means to adjoin a path. |
970 | /// |
971 | /// # Examples |
972 | /// |
973 | /// ``` |
974 | /// use camino::{Utf8Path, Utf8PathBuf}; |
975 | /// |
976 | /// assert_eq!(Utf8Path::new("/etc" ).join("passwd" ), Utf8PathBuf::from("/etc/passwd" )); |
977 | /// ``` |
978 | #[must_use ] |
979 | pub fn join(&self, path: impl AsRef<Utf8Path>) -> Utf8PathBuf { |
980 | Utf8PathBuf(self.0.join(&path.as_ref().0)) |
981 | } |
982 | |
983 | /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. |
984 | /// |
985 | /// See [`PathBuf::push`] for more details on what it means to adjoin a path. |
986 | /// |
987 | /// # Examples |
988 | /// |
989 | /// ``` |
990 | /// use camino::Utf8Path; |
991 | /// use std::path::PathBuf; |
992 | /// |
993 | /// assert_eq!(Utf8Path::new("/etc" ).join_os("passwd" ), PathBuf::from("/etc/passwd" )); |
994 | /// ``` |
995 | #[must_use ] |
996 | pub fn join_os(&self, path: impl AsRef<Path>) -> PathBuf { |
997 | self.0.join(path) |
998 | } |
999 | |
1000 | /// Creates an owned [`Utf8PathBuf`] like `self` but with the given file name. |
1001 | /// |
1002 | /// See [`Utf8PathBuf::set_file_name`] for more details. |
1003 | /// |
1004 | /// # Examples |
1005 | /// |
1006 | /// ``` |
1007 | /// use camino::{Utf8Path, Utf8PathBuf}; |
1008 | /// |
1009 | /// let path = Utf8Path::new("/tmp/foo.txt" ); |
1010 | /// assert_eq!(path.with_file_name("bar.txt" ), Utf8PathBuf::from("/tmp/bar.txt" )); |
1011 | /// |
1012 | /// let path = Utf8Path::new("/tmp" ); |
1013 | /// assert_eq!(path.with_file_name("var" ), Utf8PathBuf::from("/var" )); |
1014 | /// ``` |
1015 | #[must_use ] |
1016 | pub fn with_file_name(&self, file_name: impl AsRef<str>) -> Utf8PathBuf { |
1017 | Utf8PathBuf(self.0.with_file_name(file_name.as_ref())) |
1018 | } |
1019 | |
1020 | /// Creates an owned [`Utf8PathBuf`] like `self` but with the given extension. |
1021 | /// |
1022 | /// See [`Utf8PathBuf::set_extension`] for more details. |
1023 | /// |
1024 | /// # Examples |
1025 | /// |
1026 | /// ``` |
1027 | /// use camino::{Utf8Path, Utf8PathBuf}; |
1028 | /// |
1029 | /// let path = Utf8Path::new("foo.rs" ); |
1030 | /// assert_eq!(path.with_extension("txt" ), Utf8PathBuf::from("foo.txt" )); |
1031 | /// |
1032 | /// let path = Utf8Path::new("foo.tar.gz" ); |
1033 | /// assert_eq!(path.with_extension("" ), Utf8PathBuf::from("foo.tar" )); |
1034 | /// assert_eq!(path.with_extension("xz" ), Utf8PathBuf::from("foo.tar.xz" )); |
1035 | /// assert_eq!(path.with_extension("" ).with_extension("txt" ), Utf8PathBuf::from("foo.txt" )); |
1036 | /// ``` |
1037 | pub fn with_extension(&self, extension: impl AsRef<str>) -> Utf8PathBuf { |
1038 | Utf8PathBuf(self.0.with_extension(extension.as_ref())) |
1039 | } |
1040 | |
1041 | /// Produces an iterator over the [`Utf8Component`]s of the path. |
1042 | /// |
1043 | /// When parsing the path, there is a small amount of normalization: |
1044 | /// |
1045 | /// * Repeated separators are ignored, so `a/b` and `a//b` both have |
1046 | /// `a` and `b` as components. |
1047 | /// |
1048 | /// * Occurrences of `.` are normalized away, except if they are at the |
1049 | /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and |
1050 | /// `a/b` all have `a` and `b` as components, but `./a/b` starts with |
1051 | /// an additional [`CurDir`] component. |
1052 | /// |
1053 | /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent. |
1054 | /// |
1055 | /// Note that no other normalization takes place; in particular, `a/c` |
1056 | /// and `a/b/../c` are distinct, to account for the possibility that `b` |
1057 | /// is a symbolic link (so its parent isn't `a`). |
1058 | /// |
1059 | /// # Examples |
1060 | /// |
1061 | /// ``` |
1062 | /// use camino::{Utf8Component, Utf8Path}; |
1063 | /// |
1064 | /// let mut components = Utf8Path::new("/tmp/foo.txt" ).components(); |
1065 | /// |
1066 | /// assert_eq!(components.next(), Some(Utf8Component::RootDir)); |
1067 | /// assert_eq!(components.next(), Some(Utf8Component::Normal("tmp" ))); |
1068 | /// assert_eq!(components.next(), Some(Utf8Component::Normal("foo.txt" ))); |
1069 | /// assert_eq!(components.next(), None) |
1070 | /// ``` |
1071 | /// |
1072 | /// [`CurDir`]: Utf8Component::CurDir |
1073 | pub fn components(&self) -> Utf8Components { |
1074 | Utf8Components(self.0.components()) |
1075 | } |
1076 | |
1077 | /// Produces an iterator over the path's components viewed as [`str`] |
1078 | /// slices. |
1079 | /// |
1080 | /// For more information about the particulars of how the path is separated |
1081 | /// into components, see [`components`]. |
1082 | /// |
1083 | /// [`components`]: Utf8Path::components |
1084 | /// |
1085 | /// # Examples |
1086 | /// |
1087 | /// ``` |
1088 | /// use camino::Utf8Path; |
1089 | /// |
1090 | /// let mut it = Utf8Path::new("/tmp/foo.txt" ).iter(); |
1091 | /// assert_eq!(it.next(), Some(std::path::MAIN_SEPARATOR.to_string().as_str())); |
1092 | /// assert_eq!(it.next(), Some("tmp" )); |
1093 | /// assert_eq!(it.next(), Some("foo.txt" )); |
1094 | /// assert_eq!(it.next(), None) |
1095 | /// ``` |
1096 | pub fn iter(&self) -> Iter<'_> { |
1097 | Iter { |
1098 | inner: self.components(), |
1099 | } |
1100 | } |
1101 | |
1102 | /// Queries the file system to get information about a file, directory, etc. |
1103 | /// |
1104 | /// This function will traverse symbolic links to query information about the |
1105 | /// destination file. |
1106 | /// |
1107 | /// This is an alias to [`fs::metadata`]. |
1108 | /// |
1109 | /// # Examples |
1110 | /// |
1111 | /// ```no_run |
1112 | /// use camino::Utf8Path; |
1113 | /// |
1114 | /// let path = Utf8Path::new("/Minas/tirith" ); |
1115 | /// let metadata = path.metadata().expect("metadata call failed" ); |
1116 | /// println!("{:?}" , metadata.file_type()); |
1117 | /// ``` |
1118 | pub fn metadata(&self) -> io::Result<fs::Metadata> { |
1119 | self.0.metadata() |
1120 | } |
1121 | |
1122 | /// Queries the metadata about a file without following symlinks. |
1123 | /// |
1124 | /// This is an alias to [`fs::symlink_metadata`]. |
1125 | /// |
1126 | /// # Examples |
1127 | /// |
1128 | /// ```no_run |
1129 | /// use camino::Utf8Path; |
1130 | /// |
1131 | /// let path = Utf8Path::new("/Minas/tirith" ); |
1132 | /// let metadata = path.symlink_metadata().expect("symlink_metadata call failed" ); |
1133 | /// println!("{:?}" , metadata.file_type()); |
1134 | /// ``` |
1135 | pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> { |
1136 | self.0.symlink_metadata() |
1137 | } |
1138 | |
1139 | /// Returns the canonical, absolute form of the path with all intermediate |
1140 | /// components normalized and symbolic links resolved. |
1141 | /// |
1142 | /// This returns a [`PathBuf`] because even if a symlink is valid Unicode, its target may not |
1143 | /// be. For a version that returns a [`Utf8PathBuf`], see |
1144 | /// [`canonicalize_utf8`](Self::canonicalize_utf8). |
1145 | /// |
1146 | /// This is an alias to [`fs::canonicalize`]. |
1147 | /// |
1148 | /// # Examples |
1149 | /// |
1150 | /// ```no_run |
1151 | /// use camino::Utf8Path; |
1152 | /// use std::path::PathBuf; |
1153 | /// |
1154 | /// let path = Utf8Path::new("/foo/test/../test/bar.rs" ); |
1155 | /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs" )); |
1156 | /// ``` |
1157 | pub fn canonicalize(&self) -> io::Result<PathBuf> { |
1158 | self.0.canonicalize() |
1159 | } |
1160 | |
1161 | /// Returns the canonical, absolute form of the path with all intermediate |
1162 | /// components normalized and symbolic links resolved. |
1163 | /// |
1164 | /// This method attempts to convert the resulting [`PathBuf`] into a [`Utf8PathBuf`]. For a |
1165 | /// version that does not attempt to do this conversion, see |
1166 | /// [`canonicalize`](Self::canonicalize). |
1167 | /// |
1168 | /// # Errors |
1169 | /// |
1170 | /// The I/O operation may return an error: see the [`fs::canonicalize`] |
1171 | /// documentation for more. |
1172 | /// |
1173 | /// If the resulting path is not UTF-8, an [`io::Error`] is returned with the |
1174 | /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a |
1175 | /// [`FromPathBufError`]. |
1176 | /// |
1177 | /// # Examples |
1178 | /// |
1179 | /// ```no_run |
1180 | /// use camino::{Utf8Path, Utf8PathBuf}; |
1181 | /// |
1182 | /// let path = Utf8Path::new("/foo/test/../test/bar.rs" ); |
1183 | /// assert_eq!(path.canonicalize_utf8().unwrap(), Utf8PathBuf::from("/foo/test/bar.rs" )); |
1184 | /// ``` |
1185 | pub fn canonicalize_utf8(&self) -> io::Result<Utf8PathBuf> { |
1186 | self.canonicalize() |
1187 | .and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error)) |
1188 | } |
1189 | |
1190 | /// Reads a symbolic link, returning the file that the link points to. |
1191 | /// |
1192 | /// This returns a [`PathBuf`] because even if a symlink is valid Unicode, its target may not |
1193 | /// be. For a version that returns a [`Utf8PathBuf`], see |
1194 | /// [`read_link_utf8`](Self::read_link_utf8). |
1195 | /// |
1196 | /// This is an alias to [`fs::read_link`]. |
1197 | /// |
1198 | /// # Examples |
1199 | /// |
1200 | /// ```no_run |
1201 | /// use camino::Utf8Path; |
1202 | /// |
1203 | /// let path = Utf8Path::new("/laputa/sky_castle.rs" ); |
1204 | /// let path_link = path.read_link().expect("read_link call failed" ); |
1205 | /// ``` |
1206 | pub fn read_link(&self) -> io::Result<PathBuf> { |
1207 | self.0.read_link() |
1208 | } |
1209 | |
1210 | /// Reads a symbolic link, returning the file that the link points to. |
1211 | /// |
1212 | /// This method attempts to convert the resulting [`PathBuf`] into a [`Utf8PathBuf`]. For a |
1213 | /// version that does not attempt to do this conversion, see [`read_link`](Self::read_link). |
1214 | /// |
1215 | /// # Errors |
1216 | /// |
1217 | /// The I/O operation may return an error: see the [`fs::read_link`] |
1218 | /// documentation for more. |
1219 | /// |
1220 | /// If the resulting path is not UTF-8, an [`io::Error`] is returned with the |
1221 | /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a |
1222 | /// [`FromPathBufError`]. |
1223 | /// |
1224 | /// # Examples |
1225 | /// |
1226 | /// ```no_run |
1227 | /// use camino::Utf8Path; |
1228 | /// |
1229 | /// let path = Utf8Path::new("/laputa/sky_castle.rs" ); |
1230 | /// let path_link = path.read_link_utf8().expect("read_link call failed" ); |
1231 | /// ``` |
1232 | pub fn read_link_utf8(&self) -> io::Result<Utf8PathBuf> { |
1233 | self.read_link() |
1234 | .and_then(|path| path.try_into().map_err(FromPathBufError::into_io_error)) |
1235 | } |
1236 | |
1237 | /// Returns an iterator over the entries within a directory. |
1238 | /// |
1239 | /// The iterator will yield instances of [`io::Result`]`<`[`fs::DirEntry`]`>`. New |
1240 | /// errors may be encountered after an iterator is initially constructed. |
1241 | /// |
1242 | /// This is an alias to [`fs::read_dir`]. |
1243 | /// |
1244 | /// # Examples |
1245 | /// |
1246 | /// ```no_run |
1247 | /// use camino::Utf8Path; |
1248 | /// |
1249 | /// let path = Utf8Path::new("/laputa" ); |
1250 | /// for entry in path.read_dir().expect("read_dir call failed" ) { |
1251 | /// if let Ok(entry) = entry { |
1252 | /// println!("{:?}" , entry.path()); |
1253 | /// } |
1254 | /// } |
1255 | /// ``` |
1256 | pub fn read_dir(&self) -> io::Result<fs::ReadDir> { |
1257 | self.0.read_dir() |
1258 | } |
1259 | |
1260 | /// Returns an iterator over the entries within a directory. |
1261 | /// |
1262 | /// The iterator will yield instances of [`io::Result`]`<`[`Utf8DirEntry`]`>`. New |
1263 | /// errors may be encountered after an iterator is initially constructed. |
1264 | /// |
1265 | /// # Errors |
1266 | /// |
1267 | /// The I/O operation may return an error: see the [`fs::read_dir`] |
1268 | /// documentation for more. |
1269 | /// |
1270 | /// If a directory entry is not UTF-8, an [`io::Error`] is returned with the |
1271 | /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a |
1272 | /// [`FromPathBufError`]. |
1273 | /// |
1274 | /// # Examples |
1275 | /// |
1276 | /// ```no_run |
1277 | /// use camino::Utf8Path; |
1278 | /// |
1279 | /// let path = Utf8Path::new("/laputa" ); |
1280 | /// for entry in path.read_dir_utf8().expect("read_dir call failed" ) { |
1281 | /// if let Ok(entry) = entry { |
1282 | /// println!("{}" , entry.path()); |
1283 | /// } |
1284 | /// } |
1285 | /// ``` |
1286 | #[inline ] |
1287 | pub fn read_dir_utf8(&self) -> io::Result<ReadDirUtf8> { |
1288 | self.0.read_dir().map(|inner| ReadDirUtf8 { inner }) |
1289 | } |
1290 | |
1291 | /// Returns `true` if the path points at an existing entity. |
1292 | /// |
1293 | /// Warning: this method may be error-prone, consider using [`try_exists()`] instead! |
1294 | /// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs. |
1295 | /// |
1296 | /// This function will traverse symbolic links to query information about the |
1297 | /// destination file. In case of broken symbolic links this will return `false`. |
1298 | /// |
1299 | /// If you cannot access the directory containing the file, e.g., because of a |
1300 | /// permission error, this will return `false`. |
1301 | /// |
1302 | /// # Examples |
1303 | /// |
1304 | /// ```no_run |
1305 | /// use camino::Utf8Path; |
1306 | /// assert!(!Utf8Path::new("does_not_exist.txt" ).exists()); |
1307 | /// ``` |
1308 | /// |
1309 | /// # See Also |
1310 | /// |
1311 | /// This is a convenience function that coerces errors to false. If you want to |
1312 | /// check errors, call [`fs::metadata`]. |
1313 | /// |
1314 | /// [`try_exists()`]: Self::try_exists |
1315 | #[must_use ] |
1316 | pub fn exists(&self) -> bool { |
1317 | self.0.exists() |
1318 | } |
1319 | |
1320 | /// Returns `Ok(true)` if the path points at an existing entity. |
1321 | /// |
1322 | /// This function will traverse symbolic links to query information about the |
1323 | /// destination file. In case of broken symbolic links this will return `Ok(false)`. |
1324 | /// |
1325 | /// As opposed to the [`exists()`] method, this one doesn't silently ignore errors |
1326 | /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission |
1327 | /// denied on some of the parent directories.) |
1328 | /// |
1329 | /// Note that while this avoids some pitfalls of the `exists()` method, it still can not |
1330 | /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios |
1331 | /// where those bugs are not an issue. |
1332 | /// |
1333 | /// # Examples |
1334 | /// |
1335 | /// ```no_run |
1336 | /// use camino::Utf8Path; |
1337 | /// assert!(!Utf8Path::new("does_not_exist.txt" ).try_exists().expect("Can't check existence of file does_not_exist.txt" )); |
1338 | /// assert!(Utf8Path::new("/root/secret_file.txt" ).try_exists().is_err()); |
1339 | /// ``` |
1340 | /// |
1341 | /// [`exists()`]: Self::exists |
1342 | #[inline ] |
1343 | pub fn try_exists(&self) -> io::Result<bool> { |
1344 | // Note: this block is written this way rather than with a pattern guard to appease Rust |
1345 | // 1.34. |
1346 | match fs::metadata(self) { |
1347 | Ok(_) => Ok(true), |
1348 | Err(error) => { |
1349 | if error.kind() == io::ErrorKind::NotFound { |
1350 | Ok(false) |
1351 | } else { |
1352 | Err(error) |
1353 | } |
1354 | } |
1355 | } |
1356 | } |
1357 | |
1358 | /// Returns `true` if the path exists on disk and is pointing at a regular file. |
1359 | /// |
1360 | /// This function will traverse symbolic links to query information about the |
1361 | /// destination file. In case of broken symbolic links this will return `false`. |
1362 | /// |
1363 | /// If you cannot access the directory containing the file, e.g., because of a |
1364 | /// permission error, this will return `false`. |
1365 | /// |
1366 | /// # Examples |
1367 | /// |
1368 | /// ```no_run |
1369 | /// use camino::Utf8Path; |
1370 | /// assert_eq!(Utf8Path::new("./is_a_directory/" ).is_file(), false); |
1371 | /// assert_eq!(Utf8Path::new("a_file.txt" ).is_file(), true); |
1372 | /// ``` |
1373 | /// |
1374 | /// # See Also |
1375 | /// |
1376 | /// This is a convenience function that coerces errors to false. If you want to |
1377 | /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call |
1378 | /// [`fs::Metadata::is_file`] if it was [`Ok`]. |
1379 | /// |
1380 | /// When the goal is simply to read from (or write to) the source, the most |
1381 | /// reliable way to test the source can be read (or written to) is to open |
1382 | /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on |
1383 | /// a Unix-like system for example. See [`fs::File::open`] or |
1384 | /// [`fs::OpenOptions::open`] for more information. |
1385 | #[must_use ] |
1386 | pub fn is_file(&self) -> bool { |
1387 | self.0.is_file() |
1388 | } |
1389 | |
1390 | /// Returns `true` if the path exists on disk and is pointing at a directory. |
1391 | /// |
1392 | /// This function will traverse symbolic links to query information about the |
1393 | /// destination file. In case of broken symbolic links this will return `false`. |
1394 | /// |
1395 | /// If you cannot access the directory containing the file, e.g., because of a |
1396 | /// permission error, this will return `false`. |
1397 | /// |
1398 | /// # Examples |
1399 | /// |
1400 | /// ```no_run |
1401 | /// use camino::Utf8Path; |
1402 | /// assert_eq!(Utf8Path::new("./is_a_directory/" ).is_dir(), true); |
1403 | /// assert_eq!(Utf8Path::new("a_file.txt" ).is_dir(), false); |
1404 | /// ``` |
1405 | /// |
1406 | /// # See Also |
1407 | /// |
1408 | /// This is a convenience function that coerces errors to false. If you want to |
1409 | /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call |
1410 | /// [`fs::Metadata::is_dir`] if it was [`Ok`]. |
1411 | #[must_use ] |
1412 | pub fn is_dir(&self) -> bool { |
1413 | self.0.is_dir() |
1414 | } |
1415 | |
1416 | /// Returns `true` if the path exists on disk and is pointing at a symbolic link. |
1417 | /// |
1418 | /// This function will not traverse symbolic links. |
1419 | /// In case of a broken symbolic link this will also return true. |
1420 | /// |
1421 | /// If you cannot access the directory containing the file, e.g., because of a |
1422 | /// permission error, this will return false. |
1423 | /// |
1424 | /// # Examples |
1425 | /// |
1426 | #[cfg_attr (unix, doc = "```no_run" )] |
1427 | #[cfg_attr (not(unix), doc = "```ignore" )] |
1428 | /// use camino::Utf8Path; |
1429 | /// use std::os::unix::fs::symlink; |
1430 | /// |
1431 | /// let link_path = Utf8Path::new("link" ); |
1432 | /// symlink("/origin_does_not_exist/" , link_path).unwrap(); |
1433 | /// assert_eq!(link_path.is_symlink(), true); |
1434 | /// assert_eq!(link_path.exists(), false); |
1435 | /// ``` |
1436 | /// |
1437 | /// # See Also |
1438 | /// |
1439 | /// This is a convenience function that coerces errors to false. If you want to |
1440 | /// check errors, call [`Utf8Path::symlink_metadata`] and handle its [`Result`]. Then call |
1441 | /// [`fs::Metadata::is_symlink`] if it was [`Ok`]. |
1442 | #[must_use ] |
1443 | pub fn is_symlink(&self) -> bool { |
1444 | self.symlink_metadata() |
1445 | .map(|m| m.file_type().is_symlink()) |
1446 | .unwrap_or(false) |
1447 | } |
1448 | |
1449 | /// Converts a `Box<Utf8Path>` into a [`Utf8PathBuf`] without copying or allocating. |
1450 | #[must_use = "`self` will be dropped if the result is not used" ] |
1451 | pub fn into_path_buf(self: Box<Utf8Path>) -> Utf8PathBuf { |
1452 | let ptr = Box::into_raw(self) as *mut Path; |
1453 | // SAFETY: |
1454 | // * self is valid UTF-8 |
1455 | // * ptr was constructed by consuming self so it represents an owned path. |
1456 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from a *mut Utf8Path to a |
1457 | // *mut Path is valid. |
1458 | let boxed_path = unsafe { Box::from_raw(ptr) }; |
1459 | Utf8PathBuf(boxed_path.into_path_buf()) |
1460 | } |
1461 | |
1462 | // invariant: Path must be guaranteed to be utf-8 data |
1463 | unsafe fn assume_utf8(path: &Path) -> &Utf8Path { |
1464 | // SAFETY: Utf8Path is marked as #[repr(transparent)] so the conversion from a |
1465 | // *const Path to a *const Utf8Path is valid. |
1466 | &*(path as *const Path as *const Utf8Path) |
1467 | } |
1468 | |
1469 | #[cfg (path_buf_deref_mut)] |
1470 | unsafe fn assume_utf8_mut(path: &mut Path) -> &mut Utf8Path { |
1471 | &mut *(path as *mut Path as *mut Utf8Path) |
1472 | } |
1473 | } |
1474 | |
1475 | impl Clone for Box<Utf8Path> { |
1476 | fn clone(&self) -> Self { |
1477 | let boxed: Box<Path> = self.0.into(); |
1478 | let ptr: *mut Utf8Path = Box::into_raw(boxed) as *mut Utf8Path; |
1479 | // SAFETY: |
1480 | // * self is valid UTF-8 |
1481 | // * ptr was created by consuming a Box<Path> so it represents an rced pointer |
1482 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *mut Path to |
1483 | // *mut Utf8Path is valid |
1484 | unsafe { Box::from_raw(ptr) } |
1485 | } |
1486 | } |
1487 | |
1488 | impl fmt::Display for Utf8Path { |
1489 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1490 | fmt::Display::fmt(self.as_str(), f) |
1491 | } |
1492 | } |
1493 | |
1494 | impl fmt::Debug for Utf8Path { |
1495 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1496 | fmt::Debug::fmt(self.as_str(), f) |
1497 | } |
1498 | } |
1499 | |
1500 | /// An iterator over [`Utf8Path`] and its ancestors. |
1501 | /// |
1502 | /// This `struct` is created by the [`ancestors`] method on [`Utf8Path`]. |
1503 | /// See its documentation for more. |
1504 | /// |
1505 | /// # Examples |
1506 | /// |
1507 | /// ``` |
1508 | /// use camino::Utf8Path; |
1509 | /// |
1510 | /// let path = Utf8Path::new("/foo/bar" ); |
1511 | /// |
1512 | /// for ancestor in path.ancestors() { |
1513 | /// println!("{}" , ancestor); |
1514 | /// } |
1515 | /// ``` |
1516 | /// |
1517 | /// [`ancestors`]: Utf8Path::ancestors |
1518 | #[derive (Copy, Clone)] |
1519 | #[must_use = "iterators are lazy and do nothing unless consumed" ] |
1520 | #[repr (transparent)] |
1521 | pub struct Utf8Ancestors<'a>(Ancestors<'a>); |
1522 | |
1523 | impl<'a> fmt::Debug for Utf8Ancestors<'a> { |
1524 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1525 | fmt::Debug::fmt(&self.0, f) |
1526 | } |
1527 | } |
1528 | |
1529 | impl<'a> Iterator for Utf8Ancestors<'a> { |
1530 | type Item = &'a Utf8Path; |
1531 | |
1532 | fn next(&mut self) -> Option<Self::Item> { |
1533 | self.0.next().map(|path: &Path| { |
1534 | // SAFETY: Utf8Ancestors was constructed from a Utf8Path, so it is guaranteed to |
1535 | // be valid UTF-8 |
1536 | unsafe { Utf8Path::assume_utf8(path) } |
1537 | }) |
1538 | } |
1539 | } |
1540 | |
1541 | impl<'a> FusedIterator for Utf8Ancestors<'a> {} |
1542 | |
1543 | /// An iterator over the [`Utf8Component`]s of a [`Utf8Path`]. |
1544 | /// |
1545 | /// This `struct` is created by the [`components`] method on [`Utf8Path`]. |
1546 | /// See its documentation for more. |
1547 | /// |
1548 | /// # Examples |
1549 | /// |
1550 | /// ``` |
1551 | /// use camino::Utf8Path; |
1552 | /// |
1553 | /// let path = Utf8Path::new("/tmp/foo/bar.txt" ); |
1554 | /// |
1555 | /// for component in path.components() { |
1556 | /// println!("{:?}" , component); |
1557 | /// } |
1558 | /// ``` |
1559 | /// |
1560 | /// [`components`]: Utf8Path::components |
1561 | #[derive (Clone, Eq, Ord, PartialEq, PartialOrd)] |
1562 | #[must_use = "iterators are lazy and do nothing unless consumed" ] |
1563 | pub struct Utf8Components<'a>(Components<'a>); |
1564 | |
1565 | impl<'a> Utf8Components<'a> { |
1566 | /// Extracts a slice corresponding to the portion of the path remaining for iteration. |
1567 | /// |
1568 | /// # Examples |
1569 | /// |
1570 | /// ``` |
1571 | /// use camino::Utf8Path; |
1572 | /// |
1573 | /// let mut components = Utf8Path::new("/tmp/foo/bar.txt" ).components(); |
1574 | /// components.next(); |
1575 | /// components.next(); |
1576 | /// |
1577 | /// assert_eq!(Utf8Path::new("foo/bar.txt" ), components.as_path()); |
1578 | /// ``` |
1579 | #[must_use ] |
1580 | pub fn as_path(&self) -> &'a Utf8Path { |
1581 | // SAFETY: Utf8Components was constructed from a Utf8Path, so it is guaranteed to be valid |
1582 | // UTF-8 |
1583 | unsafe { Utf8Path::assume_utf8(self.0.as_path()) } |
1584 | } |
1585 | } |
1586 | |
1587 | impl<'a> Iterator for Utf8Components<'a> { |
1588 | type Item = Utf8Component<'a>; |
1589 | |
1590 | fn next(&mut self) -> Option<Self::Item> { |
1591 | self.0.next().map(|component: Component<'_>| { |
1592 | // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be |
1593 | // valid UTF-8 |
1594 | unsafe { Utf8Component::new(component) } |
1595 | }) |
1596 | } |
1597 | } |
1598 | |
1599 | impl<'a> FusedIterator for Utf8Components<'a> {} |
1600 | |
1601 | impl<'a> DoubleEndedIterator for Utf8Components<'a> { |
1602 | fn next_back(&mut self) -> Option<Self::Item> { |
1603 | self.0.next_back().map(|component: Component<'_>| { |
1604 | // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be |
1605 | // valid UTF-8 |
1606 | unsafe { Utf8Component::new(component) } |
1607 | }) |
1608 | } |
1609 | } |
1610 | |
1611 | impl<'a> fmt::Debug for Utf8Components<'a> { |
1612 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1613 | fmt::Debug::fmt(&self.0, f) |
1614 | } |
1615 | } |
1616 | |
1617 | impl AsRef<Utf8Path> for Utf8Components<'_> { |
1618 | fn as_ref(&self) -> &Utf8Path { |
1619 | self.as_path() |
1620 | } |
1621 | } |
1622 | |
1623 | impl AsRef<Path> for Utf8Components<'_> { |
1624 | fn as_ref(&self) -> &Path { |
1625 | self.as_path().as_ref() |
1626 | } |
1627 | } |
1628 | |
1629 | impl AsRef<str> for Utf8Components<'_> { |
1630 | fn as_ref(&self) -> &str { |
1631 | self.as_path().as_ref() |
1632 | } |
1633 | } |
1634 | |
1635 | impl AsRef<OsStr> for Utf8Components<'_> { |
1636 | fn as_ref(&self) -> &OsStr { |
1637 | self.as_path().as_os_str() |
1638 | } |
1639 | } |
1640 | |
1641 | /// An iterator over the [`Utf8Component`]s of a [`Utf8Path`], as [`str`] slices. |
1642 | /// |
1643 | /// This `struct` is created by the [`iter`] method on [`Utf8Path`]. |
1644 | /// See its documentation for more. |
1645 | /// |
1646 | /// [`iter`]: Utf8Path::iter |
1647 | #[derive (Clone)] |
1648 | #[must_use = "iterators are lazy and do nothing unless consumed" ] |
1649 | pub struct Iter<'a> { |
1650 | inner: Utf8Components<'a>, |
1651 | } |
1652 | |
1653 | impl fmt::Debug for Iter<'_> { |
1654 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1655 | struct DebugHelper<'a>(&'a Utf8Path); |
1656 | |
1657 | impl fmt::Debug for DebugHelper<'_> { |
1658 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1659 | f.debug_list().entries(self.0.iter()).finish() |
1660 | } |
1661 | } |
1662 | |
1663 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"Iter" ) |
1664 | .field(&DebugHelper(self.as_path())) |
1665 | .finish() |
1666 | } |
1667 | } |
1668 | |
1669 | impl<'a> Iter<'a> { |
1670 | /// Extracts a slice corresponding to the portion of the path remaining for iteration. |
1671 | /// |
1672 | /// # Examples |
1673 | /// |
1674 | /// ``` |
1675 | /// use camino::Utf8Path; |
1676 | /// |
1677 | /// let mut iter = Utf8Path::new("/tmp/foo/bar.txt" ).iter(); |
1678 | /// iter.next(); |
1679 | /// iter.next(); |
1680 | /// |
1681 | /// assert_eq!(Utf8Path::new("foo/bar.txt" ), iter.as_path()); |
1682 | /// ``` |
1683 | #[must_use ] |
1684 | pub fn as_path(&self) -> &'a Utf8Path { |
1685 | self.inner.as_path() |
1686 | } |
1687 | } |
1688 | |
1689 | impl AsRef<Utf8Path> for Iter<'_> { |
1690 | fn as_ref(&self) -> &Utf8Path { |
1691 | self.as_path() |
1692 | } |
1693 | } |
1694 | |
1695 | impl AsRef<Path> for Iter<'_> { |
1696 | fn as_ref(&self) -> &Path { |
1697 | self.as_path().as_ref() |
1698 | } |
1699 | } |
1700 | |
1701 | impl AsRef<str> for Iter<'_> { |
1702 | fn as_ref(&self) -> &str { |
1703 | self.as_path().as_ref() |
1704 | } |
1705 | } |
1706 | |
1707 | impl AsRef<OsStr> for Iter<'_> { |
1708 | fn as_ref(&self) -> &OsStr { |
1709 | self.as_path().as_os_str() |
1710 | } |
1711 | } |
1712 | |
1713 | impl<'a> Iterator for Iter<'a> { |
1714 | type Item = &'a str; |
1715 | |
1716 | fn next(&mut self) -> Option<&'a str> { |
1717 | self.inner.next().map(|component: Utf8Component<'_>| component.as_str()) |
1718 | } |
1719 | } |
1720 | |
1721 | impl<'a> DoubleEndedIterator for Iter<'a> { |
1722 | fn next_back(&mut self) -> Option<&'a str> { |
1723 | self.inner.next_back().map(|component: Utf8Component<'_>| component.as_str()) |
1724 | } |
1725 | } |
1726 | |
1727 | impl FusedIterator for Iter<'_> {} |
1728 | |
1729 | /// A single component of a path. |
1730 | /// |
1731 | /// A `Utf8Component` roughly corresponds to a substring between path separators |
1732 | /// (`/` or `\`). |
1733 | /// |
1734 | /// This `enum` is created by iterating over [`Utf8Components`], which in turn is |
1735 | /// created by the [`components`](Utf8Path::components) method on [`Utf8Path`]. |
1736 | /// |
1737 | /// # Examples |
1738 | /// |
1739 | /// ```rust |
1740 | /// use camino::{Utf8Component, Utf8Path}; |
1741 | /// |
1742 | /// let path = Utf8Path::new("/tmp/foo/bar.txt" ); |
1743 | /// let components = path.components().collect::<Vec<_>>(); |
1744 | /// assert_eq!(&components, &[ |
1745 | /// Utf8Component::RootDir, |
1746 | /// Utf8Component::Normal("tmp" ), |
1747 | /// Utf8Component::Normal("foo" ), |
1748 | /// Utf8Component::Normal("bar.txt" ), |
1749 | /// ]); |
1750 | /// ``` |
1751 | #[derive (Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] |
1752 | pub enum Utf8Component<'a> { |
1753 | /// A Windows path prefix, e.g., `C:` or `\\server\share`. |
1754 | /// |
1755 | /// There is a large variety of prefix types, see [`Utf8Prefix`]'s documentation |
1756 | /// for more. |
1757 | /// |
1758 | /// Does not occur on Unix. |
1759 | Prefix(Utf8PrefixComponent<'a>), |
1760 | |
1761 | /// The root directory component, appears after any prefix and before anything else. |
1762 | /// |
1763 | /// It represents a separator that designates that a path starts from root. |
1764 | RootDir, |
1765 | |
1766 | /// A reference to the current directory, i.e., `.`. |
1767 | CurDir, |
1768 | |
1769 | /// A reference to the parent directory, i.e., `..`. |
1770 | ParentDir, |
1771 | |
1772 | /// A normal component, e.g., `a` and `b` in `a/b`. |
1773 | /// |
1774 | /// This variant is the most common one, it represents references to files |
1775 | /// or directories. |
1776 | Normal(&'a str), |
1777 | } |
1778 | |
1779 | impl<'a> Utf8Component<'a> { |
1780 | unsafe fn new(component: Component<'a>) -> Utf8Component<'a> { |
1781 | match component { |
1782 | Component::Prefix(prefix) => Utf8Component::Prefix(Utf8PrefixComponent(prefix)), |
1783 | Component::RootDir => Utf8Component::RootDir, |
1784 | Component::CurDir => Utf8Component::CurDir, |
1785 | Component::ParentDir => Utf8Component::ParentDir, |
1786 | Component::Normal(s) => Utf8Component::Normal(assume_utf8(s)), |
1787 | } |
1788 | } |
1789 | |
1790 | /// Extracts the underlying [`str`] slice. |
1791 | /// |
1792 | /// # Examples |
1793 | /// |
1794 | /// ``` |
1795 | /// use camino::Utf8Path; |
1796 | /// |
1797 | /// let path = Utf8Path::new("./tmp/foo/bar.txt" ); |
1798 | /// let components: Vec<_> = path.components().map(|comp| comp.as_str()).collect(); |
1799 | /// assert_eq!(&components, &["." , "tmp" , "foo" , "bar.txt" ]); |
1800 | /// ``` |
1801 | #[must_use ] |
1802 | pub fn as_str(&self) -> &'a str { |
1803 | // SAFETY: Utf8Component was constructed from a Utf8Path, so it is guaranteed to be |
1804 | // valid UTF-8 |
1805 | unsafe { assume_utf8(self.as_os_str()) } |
1806 | } |
1807 | |
1808 | /// Extracts the underlying [`OsStr`] slice. |
1809 | /// |
1810 | /// # Examples |
1811 | /// |
1812 | /// ``` |
1813 | /// use camino::Utf8Path; |
1814 | /// |
1815 | /// let path = Utf8Path::new("./tmp/foo/bar.txt" ); |
1816 | /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); |
1817 | /// assert_eq!(&components, &["." , "tmp" , "foo" , "bar.txt" ]); |
1818 | /// ``` |
1819 | #[must_use ] |
1820 | pub fn as_os_str(&self) -> &'a OsStr { |
1821 | match *self { |
1822 | Utf8Component::Prefix(prefix) => prefix.as_os_str(), |
1823 | Utf8Component::RootDir => Component::RootDir.as_os_str(), |
1824 | Utf8Component::CurDir => Component::CurDir.as_os_str(), |
1825 | Utf8Component::ParentDir => Component::ParentDir.as_os_str(), |
1826 | Utf8Component::Normal(s) => OsStr::new(s), |
1827 | } |
1828 | } |
1829 | } |
1830 | |
1831 | impl<'a> fmt::Debug for Utf8Component<'a> { |
1832 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1833 | fmt::Debug::fmt(self.as_os_str(), f) |
1834 | } |
1835 | } |
1836 | |
1837 | impl<'a> fmt::Display for Utf8Component<'a> { |
1838 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1839 | fmt::Display::fmt(self.as_str(), f) |
1840 | } |
1841 | } |
1842 | |
1843 | impl AsRef<Utf8Path> for Utf8Component<'_> { |
1844 | fn as_ref(&self) -> &Utf8Path { |
1845 | self.as_str().as_ref() |
1846 | } |
1847 | } |
1848 | |
1849 | impl AsRef<Path> for Utf8Component<'_> { |
1850 | fn as_ref(&self) -> &Path { |
1851 | self.as_os_str().as_ref() |
1852 | } |
1853 | } |
1854 | |
1855 | impl AsRef<str> for Utf8Component<'_> { |
1856 | fn as_ref(&self) -> &str { |
1857 | self.as_str() |
1858 | } |
1859 | } |
1860 | |
1861 | impl AsRef<OsStr> for Utf8Component<'_> { |
1862 | fn as_ref(&self) -> &OsStr { |
1863 | self.as_os_str() |
1864 | } |
1865 | } |
1866 | |
1867 | /// Windows path prefixes, e.g., `C:` or `\\server\share`. |
1868 | /// |
1869 | /// Windows uses a variety of path prefix styles, including references to drive |
1870 | /// volumes (like `C:`), network shared folders (like `\\server\share`), and |
1871 | /// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with |
1872 | /// `\\?\`), in which case `/` is *not* treated as a separator and essentially |
1873 | /// no normalization is performed. |
1874 | /// |
1875 | /// # Examples |
1876 | /// |
1877 | /// ``` |
1878 | /// use camino::{Utf8Component, Utf8Path, Utf8Prefix}; |
1879 | /// use camino::Utf8Prefix::*; |
1880 | /// |
1881 | /// fn get_path_prefix(s: &str) -> Utf8Prefix { |
1882 | /// let path = Utf8Path::new(s); |
1883 | /// match path.components().next().unwrap() { |
1884 | /// Utf8Component::Prefix(prefix_component) => prefix_component.kind(), |
1885 | /// _ => panic!(), |
1886 | /// } |
1887 | /// } |
1888 | /// |
1889 | /// # if cfg!(windows) { |
1890 | /// assert_eq!(Verbatim("pictures" ), get_path_prefix(r"\\?\pictures\kittens" )); |
1891 | /// assert_eq!(VerbatimUNC("server" , "share" ), get_path_prefix(r"\\?\UNC\server\share" )); |
1892 | /// assert_eq!(VerbatimDisk(b'C' ), get_path_prefix(r"\\?\c:\" )); |
1893 | /// assert_eq!(DeviceNS("BrainInterface" ), get_path_prefix(r"\\.\BrainInterface" )); |
1894 | /// assert_eq!(UNC("server" , "share" ), get_path_prefix(r"\\server\share" )); |
1895 | /// assert_eq!(Disk(b'C' ), get_path_prefix(r"C:\Users\Rust\Pictures\Ferris" )); |
1896 | /// # } |
1897 | /// ``` |
1898 | #[derive (Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] |
1899 | pub enum Utf8Prefix<'a> { |
1900 | /// Verbatim prefix, e.g., `\\?\cat_pics`. |
1901 | /// |
1902 | /// Verbatim prefixes consist of `\\?\` immediately followed by the given |
1903 | /// component. |
1904 | Verbatim(&'a str), |
1905 | |
1906 | /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_, |
1907 | /// e.g., `\\?\UNC\server\share`. |
1908 | /// |
1909 | /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the |
1910 | /// server's hostname and a share name. |
1911 | VerbatimUNC(&'a str, &'a str), |
1912 | |
1913 | /// Verbatim disk prefix, e.g., `\\?\C:`. |
1914 | /// |
1915 | /// Verbatim disk prefixes consist of `\\?\` immediately followed by the |
1916 | /// drive letter and `:`. |
1917 | VerbatimDisk(u8), |
1918 | |
1919 | /// Device namespace prefix, e.g., `\\.\COM42`. |
1920 | /// |
1921 | /// Device namespace prefixes consist of `\\.\` immediately followed by the |
1922 | /// device name. |
1923 | DeviceNS(&'a str), |
1924 | |
1925 | /// Prefix using Windows' _**U**niform **N**aming **C**onvention_, e.g. |
1926 | /// `\\server\share`. |
1927 | /// |
1928 | /// UNC prefixes consist of the server's hostname and a share name. |
1929 | UNC(&'a str, &'a str), |
1930 | |
1931 | /// Prefix `C:` for the given disk drive. |
1932 | Disk(u8), |
1933 | } |
1934 | |
1935 | impl<'a> Utf8Prefix<'a> { |
1936 | /// Determines if the prefix is verbatim, i.e., begins with `\\?\`. |
1937 | /// |
1938 | /// # Examples |
1939 | /// |
1940 | /// ``` |
1941 | /// use camino::Utf8Prefix::*; |
1942 | /// |
1943 | /// assert!(Verbatim("pictures" ).is_verbatim()); |
1944 | /// assert!(VerbatimUNC("server" , "share" ).is_verbatim()); |
1945 | /// assert!(VerbatimDisk(b'C' ).is_verbatim()); |
1946 | /// assert!(!DeviceNS("BrainInterface" ).is_verbatim()); |
1947 | /// assert!(!UNC("server" , "share" ).is_verbatim()); |
1948 | /// assert!(!Disk(b'C' ).is_verbatim()); |
1949 | /// ``` |
1950 | #[must_use ] |
1951 | pub fn is_verbatim(&self) -> bool { |
1952 | use Utf8Prefix::*; |
1953 | match self { |
1954 | Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(..) => true, |
1955 | _ => false, |
1956 | } |
1957 | } |
1958 | } |
1959 | |
1960 | /// A structure wrapping a Windows path prefix as well as its unparsed string |
1961 | /// representation. |
1962 | /// |
1963 | /// In addition to the parsed [`Utf8Prefix`] information returned by [`kind`], |
1964 | /// `Utf8PrefixComponent` also holds the raw and unparsed [`str`] slice, |
1965 | /// returned by [`as_str`]. |
1966 | /// |
1967 | /// Instances of this `struct` can be obtained by matching against the |
1968 | /// [`Prefix` variant] on [`Utf8Component`]. |
1969 | /// |
1970 | /// Does not occur on Unix. |
1971 | /// |
1972 | /// # Examples |
1973 | /// |
1974 | /// ``` |
1975 | /// # if cfg!(windows) { |
1976 | /// use camino::{Utf8Component, Utf8Path, Utf8Prefix}; |
1977 | /// use std::ffi::OsStr; |
1978 | /// |
1979 | /// let path = Utf8Path::new(r"c:\you\later\" ); |
1980 | /// match path.components().next().unwrap() { |
1981 | /// Utf8Component::Prefix(prefix_component) => { |
1982 | /// assert_eq!(Utf8Prefix::Disk(b'C' ), prefix_component.kind()); |
1983 | /// assert_eq!("c:" , prefix_component.as_str()); |
1984 | /// } |
1985 | /// _ => unreachable!(), |
1986 | /// } |
1987 | /// # } |
1988 | /// ``` |
1989 | /// |
1990 | /// [`as_str`]: Utf8PrefixComponent::as_str |
1991 | /// [`kind`]: Utf8PrefixComponent::kind |
1992 | /// [`Prefix` variant]: Utf8Component::Prefix |
1993 | #[repr (transparent)] |
1994 | #[derive (Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] |
1995 | pub struct Utf8PrefixComponent<'a>(PrefixComponent<'a>); |
1996 | |
1997 | impl<'a> Utf8PrefixComponent<'a> { |
1998 | /// Returns the parsed prefix data. |
1999 | /// |
2000 | /// See [`Utf8Prefix`]'s documentation for more information on the different |
2001 | /// kinds of prefixes. |
2002 | #[must_use ] |
2003 | pub fn kind(&self) -> Utf8Prefix<'a> { |
2004 | // SAFETY for all the below unsafe blocks: the path self was originally constructed from was |
2005 | // UTF-8 so any parts of it are valid UTF-8 |
2006 | match self.0.kind() { |
2007 | Prefix::Verbatim(prefix) => Utf8Prefix::Verbatim(unsafe { assume_utf8(prefix) }), |
2008 | Prefix::VerbatimUNC(server, share) => { |
2009 | let server = unsafe { assume_utf8(server) }; |
2010 | let share = unsafe { assume_utf8(share) }; |
2011 | Utf8Prefix::VerbatimUNC(server, share) |
2012 | } |
2013 | Prefix::VerbatimDisk(drive) => Utf8Prefix::VerbatimDisk(drive), |
2014 | Prefix::DeviceNS(prefix) => Utf8Prefix::DeviceNS(unsafe { assume_utf8(prefix) }), |
2015 | Prefix::UNC(server, share) => { |
2016 | let server = unsafe { assume_utf8(server) }; |
2017 | let share = unsafe { assume_utf8(share) }; |
2018 | Utf8Prefix::UNC(server, share) |
2019 | } |
2020 | Prefix::Disk(drive) => Utf8Prefix::Disk(drive), |
2021 | } |
2022 | } |
2023 | |
2024 | /// Returns the [`str`] slice for this prefix. |
2025 | #[must_use ] |
2026 | pub fn as_str(&self) -> &'a str { |
2027 | // SAFETY: Utf8PrefixComponent was constructed from a Utf8Path, so it is guaranteed to be |
2028 | // valid UTF-8 |
2029 | unsafe { assume_utf8(self.as_os_str()) } |
2030 | } |
2031 | |
2032 | /// Returns the raw [`OsStr`] slice for this prefix. |
2033 | #[must_use ] |
2034 | pub fn as_os_str(&self) -> &'a OsStr { |
2035 | self.0.as_os_str() |
2036 | } |
2037 | } |
2038 | |
2039 | impl<'a> fmt::Debug for Utf8PrefixComponent<'a> { |
2040 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
2041 | fmt::Debug::fmt(&self.0, f) |
2042 | } |
2043 | } |
2044 | |
2045 | impl<'a> fmt::Display for Utf8PrefixComponent<'a> { |
2046 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
2047 | fmt::Display::fmt(self.as_str(), f) |
2048 | } |
2049 | } |
2050 | |
2051 | // --- |
2052 | // read_dir_utf8 |
2053 | // --- |
2054 | |
2055 | /// Iterator over the entries in a directory. |
2056 | /// |
2057 | /// This iterator is returned from [`Utf8Path::read_dir_utf8`] and will yield instances of |
2058 | /// <code>[io::Result]<[Utf8DirEntry]></code>. Through a [`Utf8 DirEntry`] information like the entry's path |
2059 | /// and possibly other metadata can be learned. |
2060 | /// |
2061 | /// The order in which this iterator returns entries is platform and filesystem |
2062 | /// dependent. |
2063 | /// |
2064 | /// # Errors |
2065 | /// |
2066 | /// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent |
2067 | /// IO error during iteration. |
2068 | /// |
2069 | /// If a directory entry is not UTF-8, an [`io::Error`] is returned with the |
2070 | /// [`ErrorKind`](io::ErrorKind) set to `InvalidData` and the payload set to a [`FromPathBufError`]. |
2071 | #[derive (Debug)] |
2072 | pub struct ReadDirUtf8 { |
2073 | inner: fs::ReadDir, |
2074 | } |
2075 | |
2076 | impl Iterator for ReadDirUtf8 { |
2077 | type Item = io::Result<Utf8DirEntry>; |
2078 | |
2079 | fn next(&mut self) -> Option<io::Result<Utf8DirEntry>> { |
2080 | self.inner |
2081 | .next() |
2082 | .map(|entry: Result| entry.and_then(op:Utf8DirEntry::new)) |
2083 | } |
2084 | } |
2085 | |
2086 | /// Entries returned by the [`ReadDirUtf8`] iterator. |
2087 | /// |
2088 | /// An instance of `Utf8DirEntry` represents an entry inside of a directory on the filesystem. Each |
2089 | /// entry can be inspected via methods to learn about the full path or possibly other metadata. |
2090 | #[derive (Debug)] |
2091 | pub struct Utf8DirEntry { |
2092 | inner: fs::DirEntry, |
2093 | path: Utf8PathBuf, |
2094 | } |
2095 | |
2096 | impl Utf8DirEntry { |
2097 | fn new(inner: fs::DirEntry) -> io::Result<Self> { |
2098 | let path = inner |
2099 | .path() |
2100 | .try_into() |
2101 | .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; |
2102 | Ok(Self { inner, path }) |
2103 | } |
2104 | |
2105 | /// Returns the full path to the file that this entry represents. |
2106 | /// |
2107 | /// The full path is created by joining the original path to `read_dir` |
2108 | /// with the filename of this entry. |
2109 | /// |
2110 | /// # Examples |
2111 | /// |
2112 | /// ```no_run |
2113 | /// use camino::Utf8Path; |
2114 | /// |
2115 | /// fn main() -> std::io::Result<()> { |
2116 | /// for entry in Utf8Path::new("." ).read_dir_utf8()? { |
2117 | /// let dir = entry?; |
2118 | /// println!("{}" , dir.path()); |
2119 | /// } |
2120 | /// Ok(()) |
2121 | /// } |
2122 | /// ``` |
2123 | /// |
2124 | /// This prints output like: |
2125 | /// |
2126 | /// ```text |
2127 | /// ./whatever.txt |
2128 | /// ./foo.html |
2129 | /// ./hello_world.rs |
2130 | /// ``` |
2131 | /// |
2132 | /// The exact text, of course, depends on what files you have in `.`. |
2133 | #[inline ] |
2134 | pub fn path(&self) -> &Utf8Path { |
2135 | &self.path |
2136 | } |
2137 | |
2138 | /// Returns the metadata for the file that this entry points at. |
2139 | /// |
2140 | /// This function will not traverse symlinks if this entry points at a symlink. To traverse |
2141 | /// symlinks use [`Utf8Path::metadata`] or [`fs::File::metadata`]. |
2142 | /// |
2143 | /// # Platform-specific behavior |
2144 | /// |
2145 | /// On Windows this function is cheap to call (no extra system calls |
2146 | /// needed), but on Unix platforms this function is the equivalent of |
2147 | /// calling `symlink_metadata` on the path. |
2148 | /// |
2149 | /// # Examples |
2150 | /// |
2151 | /// ``` |
2152 | /// use camino::Utf8Path; |
2153 | /// |
2154 | /// if let Ok(entries) = Utf8Path::new("." ).read_dir_utf8() { |
2155 | /// for entry in entries { |
2156 | /// if let Ok(entry) = entry { |
2157 | /// // Here, `entry` is a `Utf8DirEntry`. |
2158 | /// if let Ok(metadata) = entry.metadata() { |
2159 | /// // Now let's show our entry's permissions! |
2160 | /// println!("{}: {:?}" , entry.path(), metadata.permissions()); |
2161 | /// } else { |
2162 | /// println!("Couldn't get metadata for {}" , entry.path()); |
2163 | /// } |
2164 | /// } |
2165 | /// } |
2166 | /// } |
2167 | /// ``` |
2168 | #[inline ] |
2169 | pub fn metadata(&self) -> io::Result<Metadata> { |
2170 | self.inner.metadata() |
2171 | } |
2172 | |
2173 | /// Returns the file type for the file that this entry points at. |
2174 | /// |
2175 | /// This function will not traverse symlinks if this entry points at a |
2176 | /// symlink. |
2177 | /// |
2178 | /// # Platform-specific behavior |
2179 | /// |
2180 | /// On Windows and most Unix platforms this function is free (no extra |
2181 | /// system calls needed), but some Unix platforms may require the equivalent |
2182 | /// call to `symlink_metadata` to learn about the target file type. |
2183 | /// |
2184 | /// # Examples |
2185 | /// |
2186 | /// ``` |
2187 | /// use camino::Utf8Path; |
2188 | /// |
2189 | /// if let Ok(entries) = Utf8Path::new("." ).read_dir_utf8() { |
2190 | /// for entry in entries { |
2191 | /// if let Ok(entry) = entry { |
2192 | /// // Here, `entry` is a `DirEntry`. |
2193 | /// if let Ok(file_type) = entry.file_type() { |
2194 | /// // Now let's show our entry's file type! |
2195 | /// println!("{}: {:?}" , entry.path(), file_type); |
2196 | /// } else { |
2197 | /// println!("Couldn't get file type for {}" , entry.path()); |
2198 | /// } |
2199 | /// } |
2200 | /// } |
2201 | /// } |
2202 | /// ``` |
2203 | #[inline ] |
2204 | pub fn file_type(&self) -> io::Result<fs::FileType> { |
2205 | self.inner.file_type() |
2206 | } |
2207 | |
2208 | /// Returns the bare file name of this directory entry without any other |
2209 | /// leading path component. |
2210 | /// |
2211 | /// # Examples |
2212 | /// |
2213 | /// ``` |
2214 | /// use camino::Utf8Path; |
2215 | /// |
2216 | /// if let Ok(entries) = Utf8Path::new("." ).read_dir_utf8() { |
2217 | /// for entry in entries { |
2218 | /// if let Ok(entry) = entry { |
2219 | /// // Here, `entry` is a `DirEntry`. |
2220 | /// println!("{}" , entry.file_name()); |
2221 | /// } |
2222 | /// } |
2223 | /// } |
2224 | /// ``` |
2225 | pub fn file_name(&self) -> &str { |
2226 | self.path |
2227 | .file_name() |
2228 | .expect("path created through DirEntry must have a filename" ) |
2229 | } |
2230 | |
2231 | /// Returns the original [`fs::DirEntry`] within this [`Utf8DirEntry`]. |
2232 | #[inline ] |
2233 | pub fn into_inner(self) -> fs::DirEntry { |
2234 | self.inner |
2235 | } |
2236 | |
2237 | /// Returns the full path to the file that this entry represents. |
2238 | /// |
2239 | /// This is analogous to [`path`], but moves ownership of the path. |
2240 | /// |
2241 | /// [`path`]: struct.Utf8DirEntry.html#method.path |
2242 | #[inline ] |
2243 | #[must_use = "`self` will be dropped if the result is not used" ] |
2244 | pub fn into_path(self) -> Utf8PathBuf { |
2245 | self.path |
2246 | } |
2247 | } |
2248 | |
2249 | impl From<String> for Utf8PathBuf { |
2250 | fn from(string: String) -> Utf8PathBuf { |
2251 | Utf8PathBuf(string.into()) |
2252 | } |
2253 | } |
2254 | |
2255 | impl FromStr for Utf8PathBuf { |
2256 | type Err = Infallible; |
2257 | |
2258 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
2259 | Ok(Utf8PathBuf(s.into())) |
2260 | } |
2261 | } |
2262 | |
2263 | // --- |
2264 | // From impls: borrowed -> borrowed |
2265 | // --- |
2266 | |
2267 | impl<'a> From<&'a str> for &'a Utf8Path { |
2268 | fn from(s: &'a str) -> &'a Utf8Path { |
2269 | Utf8Path::new(s) |
2270 | } |
2271 | } |
2272 | |
2273 | // --- |
2274 | // From impls: borrowed -> owned |
2275 | // --- |
2276 | |
2277 | impl<T: ?Sized + AsRef<str>> From<&T> for Utf8PathBuf { |
2278 | fn from(s: &T) -> Utf8PathBuf { |
2279 | Utf8PathBuf::from(s.as_ref().to_owned()) |
2280 | } |
2281 | } |
2282 | |
2283 | impl<T: ?Sized + AsRef<str>> From<&T> for Box<Utf8Path> { |
2284 | fn from(s: &T) -> Box<Utf8Path> { |
2285 | Utf8PathBuf::from(s).into_boxed_path() |
2286 | } |
2287 | } |
2288 | |
2289 | impl From<&'_ Utf8Path> for Arc<Utf8Path> { |
2290 | fn from(path: &Utf8Path) -> Arc<Utf8Path> { |
2291 | let arc: Arc<Path> = Arc::from(AsRef::<Path>::as_ref(self:path)); |
2292 | let ptr: *const Utf8Path = Arc::into_raw(this:arc) as *const Utf8Path; |
2293 | // SAFETY: |
2294 | // * path is valid UTF-8 |
2295 | // * ptr was created by consuming an Arc<Path> so it represents an arced pointer |
2296 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to |
2297 | // *const Utf8Path is valid |
2298 | unsafe { Arc::from_raw(ptr) } |
2299 | } |
2300 | } |
2301 | |
2302 | impl From<&'_ Utf8Path> for Rc<Utf8Path> { |
2303 | fn from(path: &Utf8Path) -> Rc<Utf8Path> { |
2304 | let rc: Rc<Path> = Rc::from(AsRef::<Path>::as_ref(self:path)); |
2305 | let ptr: *const Utf8Path = Rc::into_raw(this:rc) as *const Utf8Path; |
2306 | // SAFETY: |
2307 | // * path is valid UTF-8 |
2308 | // * ptr was created by consuming an Rc<Path> so it represents an rced pointer |
2309 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to |
2310 | // *const Utf8Path is valid |
2311 | unsafe { Rc::from_raw(ptr) } |
2312 | } |
2313 | } |
2314 | |
2315 | impl<'a> From<&'a Utf8Path> for Cow<'a, Utf8Path> { |
2316 | fn from(path: &'a Utf8Path) -> Cow<'a, Utf8Path> { |
2317 | Cow::Borrowed(path) |
2318 | } |
2319 | } |
2320 | |
2321 | impl From<&'_ Utf8Path> for Box<Path> { |
2322 | fn from(path: &Utf8Path) -> Box<Path> { |
2323 | AsRef::<Path>::as_ref(self:path).into() |
2324 | } |
2325 | } |
2326 | |
2327 | impl From<&'_ Utf8Path> for Arc<Path> { |
2328 | fn from(path: &Utf8Path) -> Arc<Path> { |
2329 | AsRef::<Path>::as_ref(self:path).into() |
2330 | } |
2331 | } |
2332 | |
2333 | impl From<&'_ Utf8Path> for Rc<Path> { |
2334 | fn from(path: &Utf8Path) -> Rc<Path> { |
2335 | AsRef::<Path>::as_ref(self:path).into() |
2336 | } |
2337 | } |
2338 | |
2339 | impl<'a> From<&'a Utf8Path> for Cow<'a, Path> { |
2340 | fn from(path: &'a Utf8Path) -> Cow<'a, Path> { |
2341 | Cow::Borrowed(path.as_ref()) |
2342 | } |
2343 | } |
2344 | |
2345 | // --- |
2346 | // From impls: owned -> owned |
2347 | // --- |
2348 | |
2349 | impl From<Box<Utf8Path>> for Utf8PathBuf { |
2350 | fn from(path: Box<Utf8Path>) -> Utf8PathBuf { |
2351 | path.into_path_buf() |
2352 | } |
2353 | } |
2354 | |
2355 | impl From<Utf8PathBuf> for Box<Utf8Path> { |
2356 | fn from(path: Utf8PathBuf) -> Box<Utf8Path> { |
2357 | path.into_boxed_path() |
2358 | } |
2359 | } |
2360 | |
2361 | impl<'a> From<Cow<'a, Utf8Path>> for Utf8PathBuf { |
2362 | fn from(path: Cow<'a, Utf8Path>) -> Utf8PathBuf { |
2363 | path.into_owned() |
2364 | } |
2365 | } |
2366 | |
2367 | impl From<Utf8PathBuf> for String { |
2368 | fn from(path: Utf8PathBuf) -> String { |
2369 | path.into_string() |
2370 | } |
2371 | } |
2372 | |
2373 | impl From<Utf8PathBuf> for OsString { |
2374 | fn from(path: Utf8PathBuf) -> OsString { |
2375 | path.into_os_string() |
2376 | } |
2377 | } |
2378 | |
2379 | impl<'a> From<Utf8PathBuf> for Cow<'a, Utf8Path> { |
2380 | fn from(path: Utf8PathBuf) -> Cow<'a, Utf8Path> { |
2381 | Cow::Owned(path) |
2382 | } |
2383 | } |
2384 | |
2385 | impl From<Utf8PathBuf> for Arc<Utf8Path> { |
2386 | fn from(path: Utf8PathBuf) -> Arc<Utf8Path> { |
2387 | let arc: Arc<Path> = Arc::from(path.0); |
2388 | let ptr: *const Utf8Path = Arc::into_raw(this:arc) as *const Utf8Path; |
2389 | // SAFETY: |
2390 | // * path is valid UTF-8 |
2391 | // * ptr was created by consuming an Arc<Path> so it represents an arced pointer |
2392 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to |
2393 | // *const Utf8Path is valid |
2394 | unsafe { Arc::from_raw(ptr) } |
2395 | } |
2396 | } |
2397 | |
2398 | impl From<Utf8PathBuf> for Rc<Utf8Path> { |
2399 | fn from(path: Utf8PathBuf) -> Rc<Utf8Path> { |
2400 | let rc: Rc<Path> = Rc::from(path.0); |
2401 | let ptr: *const Utf8Path = Rc::into_raw(this:rc) as *const Utf8Path; |
2402 | // SAFETY: |
2403 | // * path is valid UTF-8 |
2404 | // * ptr was created by consuming an Rc<Path> so it represents an rced pointer |
2405 | // * Utf8Path is marked as #[repr(transparent)] so the conversion from *const Path to |
2406 | // *const Utf8Path is valid |
2407 | unsafe { Rc::from_raw(ptr) } |
2408 | } |
2409 | } |
2410 | |
2411 | impl From<Utf8PathBuf> for PathBuf { |
2412 | fn from(path: Utf8PathBuf) -> PathBuf { |
2413 | path.0 |
2414 | } |
2415 | } |
2416 | |
2417 | impl From<Utf8PathBuf> for Box<Path> { |
2418 | fn from(path: Utf8PathBuf) -> Box<Path> { |
2419 | PathBuf::from(path).into_boxed_path() |
2420 | } |
2421 | } |
2422 | |
2423 | impl From<Utf8PathBuf> for Arc<Path> { |
2424 | fn from(path: Utf8PathBuf) -> Arc<Path> { |
2425 | PathBuf::from(path).into() |
2426 | } |
2427 | } |
2428 | |
2429 | impl From<Utf8PathBuf> for Rc<Path> { |
2430 | fn from(path: Utf8PathBuf) -> Rc<Path> { |
2431 | PathBuf::from(path).into() |
2432 | } |
2433 | } |
2434 | |
2435 | impl<'a> From<Utf8PathBuf> for Cow<'a, Path> { |
2436 | fn from(path: Utf8PathBuf) -> Cow<'a, Path> { |
2437 | PathBuf::from(path).into() |
2438 | } |
2439 | } |
2440 | |
2441 | // --- |
2442 | // TryFrom impls |
2443 | // --- |
2444 | |
2445 | impl TryFrom<PathBuf> for Utf8PathBuf { |
2446 | type Error = FromPathBufError; |
2447 | |
2448 | fn try_from(path: PathBuf) -> Result<Utf8PathBuf, Self::Error> { |
2449 | Utf8PathBuf::from_path_buf(path).map_err(|path: PathBuf| FromPathBufError { |
2450 | path, |
2451 | error: FromPathError(()), |
2452 | }) |
2453 | } |
2454 | } |
2455 | |
2456 | impl<'a> TryFrom<&'a Path> for &'a Utf8Path { |
2457 | type Error = FromPathError; |
2458 | |
2459 | fn try_from(path: &'a Path) -> Result<&'a Utf8Path, Self::Error> { |
2460 | Utf8Path::from_path(path).ok_or(err:FromPathError(())) |
2461 | } |
2462 | } |
2463 | |
2464 | /// A possible error value while converting a [`PathBuf`] to a [`Utf8PathBuf`]. |
2465 | /// |
2466 | /// Produced by the `TryFrom<PathBuf>` implementation for [`Utf8PathBuf`]. |
2467 | /// |
2468 | /// # Examples |
2469 | /// |
2470 | /// ``` |
2471 | /// use camino::{Utf8PathBuf, FromPathBufError}; |
2472 | /// use std::convert::{TryFrom, TryInto}; |
2473 | /// use std::ffi::OsStr; |
2474 | /// # #[cfg (unix)] |
2475 | /// use std::os::unix::ffi::OsStrExt; |
2476 | /// use std::path::PathBuf; |
2477 | /// |
2478 | /// let unicode_path = PathBuf::from("/valid/unicode" ); |
2479 | /// let utf8_path_buf: Utf8PathBuf = unicode_path.try_into().expect("valid Unicode path succeeded" ); |
2480 | /// |
2481 | /// // Paths on Unix can be non-UTF-8. |
2482 | /// # #[cfg (unix)] |
2483 | /// let non_unicode_str = OsStr::from_bytes(b" \xFF\xFF\xFF" ); |
2484 | /// # #[cfg (unix)] |
2485 | /// let non_unicode_path = PathBuf::from(non_unicode_str); |
2486 | /// # #[cfg (unix)] |
2487 | /// let err: FromPathBufError = Utf8PathBuf::try_from(non_unicode_path.clone()) |
2488 | /// .expect_err("non-Unicode path failed" ); |
2489 | /// # #[cfg (unix)] |
2490 | /// assert_eq!(err.as_path(), &non_unicode_path); |
2491 | /// # #[cfg (unix)] |
2492 | /// assert_eq!(err.into_path_buf(), non_unicode_path); |
2493 | /// ``` |
2494 | #[derive (Clone, Debug, Eq, PartialEq)] |
2495 | pub struct FromPathBufError { |
2496 | path: PathBuf, |
2497 | error: FromPathError, |
2498 | } |
2499 | |
2500 | impl FromPathBufError { |
2501 | /// Returns the [`Path`] slice that was attempted to be converted to [`Utf8PathBuf`]. |
2502 | pub fn as_path(&self) -> &Path { |
2503 | &self.path |
2504 | } |
2505 | |
2506 | /// Returns the [`PathBuf`] that was attempted to be converted to [`Utf8PathBuf`]. |
2507 | pub fn into_path_buf(self) -> PathBuf { |
2508 | self.path |
2509 | } |
2510 | |
2511 | /// Fetches a [`FromPathError`] for more about the conversion failure. |
2512 | /// |
2513 | /// At the moment this struct does not contain any additional information, but is provided for |
2514 | /// completeness. |
2515 | pub fn from_path_error(&self) -> FromPathError { |
2516 | self.error |
2517 | } |
2518 | |
2519 | /// Converts self into a [`std::io::Error`] with kind |
2520 | /// [`InvalidData`](io::ErrorKind::InvalidData). |
2521 | /// |
2522 | /// Many users of `FromPathBufError` will want to convert it into an `io::Error`. This is a |
2523 | /// convenience method to do that. |
2524 | pub fn into_io_error(self) -> io::Error { |
2525 | // NOTE: we don't currently implement `From<FromPathBufError> for io::Error` because we want |
2526 | // to ensure the user actually desires that conversion. |
2527 | io::Error::new(io::ErrorKind::InvalidData, self) |
2528 | } |
2529 | } |
2530 | |
2531 | impl fmt::Display for FromPathBufError { |
2532 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
2533 | write!(f, "PathBuf contains invalid UTF-8: {}" , self.path.display()) |
2534 | } |
2535 | } |
2536 | |
2537 | impl error::Error for FromPathBufError { |
2538 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
2539 | Some(&self.error) |
2540 | } |
2541 | } |
2542 | |
2543 | /// A possible error value while converting a [`Path`] to a [`Utf8Path`]. |
2544 | /// |
2545 | /// Produced by the `TryFrom<&Path>` implementation for [`&Utf8Path`](Utf8Path). |
2546 | /// |
2547 | /// |
2548 | /// # Examples |
2549 | /// |
2550 | /// ``` |
2551 | /// use camino::{Utf8Path, FromPathError}; |
2552 | /// use std::convert::{TryFrom, TryInto}; |
2553 | /// use std::ffi::OsStr; |
2554 | /// # #[cfg (unix)] |
2555 | /// use std::os::unix::ffi::OsStrExt; |
2556 | /// use std::path::Path; |
2557 | /// |
2558 | /// let unicode_path = Path::new("/valid/unicode" ); |
2559 | /// let utf8_path: &Utf8Path = unicode_path.try_into().expect("valid Unicode path succeeded" ); |
2560 | /// |
2561 | /// // Paths on Unix can be non-UTF-8. |
2562 | /// # #[cfg (unix)] |
2563 | /// let non_unicode_str = OsStr::from_bytes(b" \xFF\xFF\xFF" ); |
2564 | /// # #[cfg (unix)] |
2565 | /// let non_unicode_path = Path::new(non_unicode_str); |
2566 | /// # #[cfg (unix)] |
2567 | /// let err: FromPathError = <&Utf8Path>::try_from(non_unicode_path) |
2568 | /// .expect_err("non-Unicode path failed" ); |
2569 | /// ``` |
2570 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
2571 | pub struct FromPathError(()); |
2572 | |
2573 | impl FromPathError { |
2574 | /// Converts self into a [`std::io::Error`] with kind |
2575 | /// [`InvalidData`](io::ErrorKind::InvalidData). |
2576 | /// |
2577 | /// Many users of `FromPathError` will want to convert it into an `io::Error`. This is a |
2578 | /// convenience method to do that. |
2579 | pub fn into_io_error(self) -> io::Error { |
2580 | // NOTE: we don't currently implement `From<FromPathBufError> for io::Error` because we want |
2581 | // to ensure the user actually desires that conversion. |
2582 | io::Error::new(kind:io::ErrorKind::InvalidData, self) |
2583 | } |
2584 | } |
2585 | |
2586 | impl fmt::Display for FromPathError { |
2587 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
2588 | write!(f, "Path contains invalid UTF-8" ) |
2589 | } |
2590 | } |
2591 | |
2592 | impl error::Error for FromPathError { |
2593 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
2594 | None |
2595 | } |
2596 | } |
2597 | |
2598 | // --- |
2599 | // AsRef impls |
2600 | // --- |
2601 | |
2602 | impl AsRef<Utf8Path> for Utf8Path { |
2603 | fn as_ref(&self) -> &Utf8Path { |
2604 | self |
2605 | } |
2606 | } |
2607 | |
2608 | impl AsRef<Utf8Path> for Utf8PathBuf { |
2609 | fn as_ref(&self) -> &Utf8Path { |
2610 | self.as_path() |
2611 | } |
2612 | } |
2613 | |
2614 | impl AsRef<Utf8Path> for str { |
2615 | fn as_ref(&self) -> &Utf8Path { |
2616 | Utf8Path::new(self) |
2617 | } |
2618 | } |
2619 | |
2620 | impl AsRef<Utf8Path> for String { |
2621 | fn as_ref(&self) -> &Utf8Path { |
2622 | Utf8Path::new(self) |
2623 | } |
2624 | } |
2625 | |
2626 | impl AsRef<Path> for Utf8Path { |
2627 | fn as_ref(&self) -> &Path { |
2628 | &self.0 |
2629 | } |
2630 | } |
2631 | |
2632 | impl AsRef<Path> for Utf8PathBuf { |
2633 | fn as_ref(&self) -> &Path { |
2634 | &self.0 |
2635 | } |
2636 | } |
2637 | |
2638 | impl AsRef<str> for Utf8Path { |
2639 | fn as_ref(&self) -> &str { |
2640 | self.as_str() |
2641 | } |
2642 | } |
2643 | |
2644 | impl AsRef<str> for Utf8PathBuf { |
2645 | fn as_ref(&self) -> &str { |
2646 | self.as_str() |
2647 | } |
2648 | } |
2649 | |
2650 | impl AsRef<OsStr> for Utf8Path { |
2651 | fn as_ref(&self) -> &OsStr { |
2652 | self.as_os_str() |
2653 | } |
2654 | } |
2655 | |
2656 | impl AsRef<OsStr> for Utf8PathBuf { |
2657 | fn as_ref(&self) -> &OsStr { |
2658 | self.as_os_str() |
2659 | } |
2660 | } |
2661 | |
2662 | // --- |
2663 | // Borrow and ToOwned |
2664 | // --- |
2665 | |
2666 | impl Borrow<Utf8Path> for Utf8PathBuf { |
2667 | fn borrow(&self) -> &Utf8Path { |
2668 | self.as_path() |
2669 | } |
2670 | } |
2671 | |
2672 | impl ToOwned for Utf8Path { |
2673 | type Owned = Utf8PathBuf; |
2674 | |
2675 | fn to_owned(&self) -> Utf8PathBuf { |
2676 | self.to_path_buf() |
2677 | } |
2678 | } |
2679 | |
2680 | impl<P: AsRef<Utf8Path>> std::iter::FromIterator<P> for Utf8PathBuf { |
2681 | fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Utf8PathBuf { |
2682 | let mut buf: Utf8PathBuf = Utf8PathBuf::new(); |
2683 | buf.extend(iter); |
2684 | buf |
2685 | } |
2686 | } |
2687 | |
2688 | // --- |
2689 | // [Partial]Eq, [Partial]Ord, Hash |
2690 | // --- |
2691 | |
2692 | impl PartialEq for Utf8PathBuf { |
2693 | fn eq(&self, other: &Utf8PathBuf) -> bool { |
2694 | self.components() == other.components() |
2695 | } |
2696 | } |
2697 | |
2698 | impl Eq for Utf8PathBuf {} |
2699 | |
2700 | impl Hash for Utf8PathBuf { |
2701 | fn hash<H: Hasher>(&self, state: &mut H) { |
2702 | self.as_path().hash(state) |
2703 | } |
2704 | } |
2705 | |
2706 | impl PartialOrd for Utf8PathBuf { |
2707 | fn partial_cmp(&self, other: &Utf8PathBuf) -> Option<Ordering> { |
2708 | self.components().partial_cmp(other.components()) |
2709 | } |
2710 | } |
2711 | |
2712 | impl Ord for Utf8PathBuf { |
2713 | fn cmp(&self, other: &Utf8PathBuf) -> Ordering { |
2714 | self.components().cmp(other.components()) |
2715 | } |
2716 | } |
2717 | |
2718 | impl PartialEq for Utf8Path { |
2719 | fn eq(&self, other: &Utf8Path) -> bool { |
2720 | self.components().eq(other.components()) |
2721 | } |
2722 | } |
2723 | |
2724 | impl Eq for Utf8Path {} |
2725 | |
2726 | impl Hash for Utf8Path { |
2727 | fn hash<H: Hasher>(&self, state: &mut H) { |
2728 | for component: Utf8Component<'_> in self.components() { |
2729 | component.hash(state) |
2730 | } |
2731 | } |
2732 | } |
2733 | |
2734 | impl PartialOrd for Utf8Path { |
2735 | fn partial_cmp(&self, other: &Utf8Path) -> Option<Ordering> { |
2736 | self.components().partial_cmp(other.components()) |
2737 | } |
2738 | } |
2739 | |
2740 | impl Ord for Utf8Path { |
2741 | fn cmp(&self, other: &Utf8Path) -> Ordering { |
2742 | self.components().cmp(other.components()) |
2743 | } |
2744 | } |
2745 | |
2746 | impl<'a> IntoIterator for &'a Utf8PathBuf { |
2747 | type Item = &'a str; |
2748 | type IntoIter = Iter<'a>; |
2749 | fn into_iter(self) -> Iter<'a> { |
2750 | self.iter() |
2751 | } |
2752 | } |
2753 | |
2754 | impl<'a> IntoIterator for &'a Utf8Path { |
2755 | type Item = &'a str; |
2756 | type IntoIter = Iter<'a>; |
2757 | fn into_iter(self) -> Iter<'a> { |
2758 | self.iter() |
2759 | } |
2760 | } |
2761 | |
2762 | macro_rules! impl_cmp { |
2763 | ($lhs:ty, $rhs: ty) => { |
2764 | #[allow(clippy::extra_unused_lifetimes)] |
2765 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
2766 | #[inline] |
2767 | fn eq(&self, other: &$rhs) -> bool { |
2768 | <Utf8Path as PartialEq>::eq(self, other) |
2769 | } |
2770 | } |
2771 | |
2772 | #[allow(clippy::extra_unused_lifetimes)] |
2773 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
2774 | #[inline] |
2775 | fn eq(&self, other: &$lhs) -> bool { |
2776 | <Utf8Path as PartialEq>::eq(self, other) |
2777 | } |
2778 | } |
2779 | |
2780 | #[allow(clippy::extra_unused_lifetimes)] |
2781 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
2782 | #[inline] |
2783 | fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { |
2784 | <Utf8Path as PartialOrd>::partial_cmp(self, other) |
2785 | } |
2786 | } |
2787 | |
2788 | #[allow(clippy::extra_unused_lifetimes)] |
2789 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
2790 | #[inline] |
2791 | fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { |
2792 | <Utf8Path as PartialOrd>::partial_cmp(self, other) |
2793 | } |
2794 | } |
2795 | }; |
2796 | } |
2797 | |
2798 | impl_cmp!(Utf8PathBuf, Utf8Path); |
2799 | impl_cmp!(Utf8PathBuf, &'a Utf8Path); |
2800 | impl_cmp!(Cow<'a, Utf8Path>, Utf8Path); |
2801 | impl_cmp!(Cow<'a, Utf8Path>, &'b Utf8Path); |
2802 | impl_cmp!(Cow<'a, Utf8Path>, Utf8PathBuf); |
2803 | |
2804 | macro_rules! impl_cmp_std_path { |
2805 | ($lhs:ty, $rhs: ty) => { |
2806 | #[allow(clippy::extra_unused_lifetimes)] |
2807 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
2808 | #[inline] |
2809 | fn eq(&self, other: &$rhs) -> bool { |
2810 | <Path as PartialEq>::eq(self.as_ref(), other) |
2811 | } |
2812 | } |
2813 | |
2814 | #[allow(clippy::extra_unused_lifetimes)] |
2815 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
2816 | #[inline] |
2817 | fn eq(&self, other: &$lhs) -> bool { |
2818 | <Path as PartialEq>::eq(self, other.as_ref()) |
2819 | } |
2820 | } |
2821 | |
2822 | #[allow(clippy::extra_unused_lifetimes)] |
2823 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
2824 | #[inline] |
2825 | fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> { |
2826 | <Path as PartialOrd>::partial_cmp(self.as_ref(), other) |
2827 | } |
2828 | } |
2829 | |
2830 | #[allow(clippy::extra_unused_lifetimes)] |
2831 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
2832 | #[inline] |
2833 | fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> { |
2834 | <Path as PartialOrd>::partial_cmp(self, other.as_ref()) |
2835 | } |
2836 | } |
2837 | }; |
2838 | } |
2839 | |
2840 | impl_cmp_std_path!(Utf8PathBuf, Path); |
2841 | impl_cmp_std_path!(Utf8PathBuf, &'a Path); |
2842 | impl_cmp_std_path!(Utf8PathBuf, Cow<'a, Path>); |
2843 | impl_cmp_std_path!(Utf8PathBuf, PathBuf); |
2844 | impl_cmp_std_path!(Utf8Path, Path); |
2845 | impl_cmp_std_path!(Utf8Path, &'a Path); |
2846 | impl_cmp_std_path!(Utf8Path, Cow<'a, Path>); |
2847 | impl_cmp_std_path!(Utf8Path, PathBuf); |
2848 | impl_cmp_std_path!(&'a Utf8Path, Path); |
2849 | impl_cmp_std_path!(&'a Utf8Path, Cow<'b, Path>); |
2850 | impl_cmp_std_path!(&'a Utf8Path, PathBuf); |
2851 | // NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117) |
2852 | |
2853 | macro_rules! impl_cmp_str { |
2854 | ($lhs:ty, $rhs: ty) => { |
2855 | #[allow(clippy::extra_unused_lifetimes)] |
2856 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
2857 | #[inline] |
2858 | fn eq(&self, other: &$rhs) -> bool { |
2859 | <Utf8Path as PartialEq>::eq(self, Utf8Path::new(other)) |
2860 | } |
2861 | } |
2862 | |
2863 | #[allow(clippy::extra_unused_lifetimes)] |
2864 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
2865 | #[inline] |
2866 | fn eq(&self, other: &$lhs) -> bool { |
2867 | <Utf8Path as PartialEq>::eq(Utf8Path::new(self), other) |
2868 | } |
2869 | } |
2870 | |
2871 | #[allow(clippy::extra_unused_lifetimes)] |
2872 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
2873 | #[inline] |
2874 | fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> { |
2875 | <Utf8Path as PartialOrd>::partial_cmp(self, Utf8Path::new(other)) |
2876 | } |
2877 | } |
2878 | |
2879 | #[allow(clippy::extra_unused_lifetimes)] |
2880 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
2881 | #[inline] |
2882 | fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> { |
2883 | <Utf8Path as PartialOrd>::partial_cmp(Utf8Path::new(self), other) |
2884 | } |
2885 | } |
2886 | }; |
2887 | } |
2888 | |
2889 | impl_cmp_str!(Utf8PathBuf, str); |
2890 | impl_cmp_str!(Utf8PathBuf, &'a str); |
2891 | impl_cmp_str!(Utf8PathBuf, Cow<'a, str>); |
2892 | impl_cmp_str!(Utf8PathBuf, String); |
2893 | impl_cmp_str!(Utf8Path, str); |
2894 | impl_cmp_str!(Utf8Path, &'a str); |
2895 | impl_cmp_str!(Utf8Path, Cow<'a, str>); |
2896 | impl_cmp_str!(Utf8Path, String); |
2897 | impl_cmp_str!(&'a Utf8Path, str); |
2898 | impl_cmp_str!(&'a Utf8Path, Cow<'b, str>); |
2899 | impl_cmp_str!(&'a Utf8Path, String); |
2900 | // NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117) |
2901 | |
2902 | macro_rules! impl_cmp_os_str { |
2903 | ($lhs:ty, $rhs: ty) => { |
2904 | #[allow(clippy::extra_unused_lifetimes)] |
2905 | impl<'a, 'b> PartialEq<$rhs> for $lhs { |
2906 | #[inline] |
2907 | fn eq(&self, other: &$rhs) -> bool { |
2908 | <Path as PartialEq>::eq(self.as_ref(), other.as_ref()) |
2909 | } |
2910 | } |
2911 | |
2912 | #[allow(clippy::extra_unused_lifetimes)] |
2913 | impl<'a, 'b> PartialEq<$lhs> for $rhs { |
2914 | #[inline] |
2915 | fn eq(&self, other: &$lhs) -> bool { |
2916 | <Path as PartialEq>::eq(self.as_ref(), other.as_ref()) |
2917 | } |
2918 | } |
2919 | |
2920 | #[allow(clippy::extra_unused_lifetimes)] |
2921 | impl<'a, 'b> PartialOrd<$rhs> for $lhs { |
2922 | #[inline] |
2923 | fn partial_cmp(&self, other: &$rhs) -> Option<std::cmp::Ordering> { |
2924 | <Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref()) |
2925 | } |
2926 | } |
2927 | |
2928 | #[allow(clippy::extra_unused_lifetimes)] |
2929 | impl<'a, 'b> PartialOrd<$lhs> for $rhs { |
2930 | #[inline] |
2931 | fn partial_cmp(&self, other: &$lhs) -> Option<std::cmp::Ordering> { |
2932 | <Path as PartialOrd>::partial_cmp(self.as_ref(), other.as_ref()) |
2933 | } |
2934 | } |
2935 | }; |
2936 | } |
2937 | |
2938 | impl_cmp_os_str!(Utf8PathBuf, OsStr); |
2939 | impl_cmp_os_str!(Utf8PathBuf, &'a OsStr); |
2940 | impl_cmp_os_str!(Utf8PathBuf, Cow<'a, OsStr>); |
2941 | impl_cmp_os_str!(Utf8PathBuf, OsString); |
2942 | impl_cmp_os_str!(Utf8Path, OsStr); |
2943 | impl_cmp_os_str!(Utf8Path, &'a OsStr); |
2944 | impl_cmp_os_str!(Utf8Path, Cow<'a, OsStr>); |
2945 | impl_cmp_os_str!(Utf8Path, OsString); |
2946 | impl_cmp_os_str!(&'a Utf8Path, OsStr); |
2947 | impl_cmp_os_str!(&'a Utf8Path, Cow<'b, OsStr>); |
2948 | impl_cmp_os_str!(&'a Utf8Path, OsString); |
2949 | // NOTE: impls for Cow<'a, Utf8Path> cannot be defined because of the orphan rule (E0117) |
2950 | |
2951 | // invariant: OsStr must be guaranteed to be utf8 data |
2952 | unsafe fn assume_utf8(string: &OsStr) -> &str { |
2953 | &*(string as *const OsStr as *const str) |
2954 | } |
2955 | |