1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 Cross-platform path support
15 This module implements support for two flavors of paths. `PosixPath` represents
16 a path on any unix-like system, whereas `WindowsPath` represents a path on
17 Windows. This module also exposes a typedef `Path` which is equal to the
18 appropriate platform-specific path variant.
20 Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which
21 contains the set of methods that behave the same for both paths. They each also
22 implement some methods that could not be expressed in `GenericPath`, yet behave
23 identically for both path flavors, such as `.components()`.
25 The three main design goals of this module are 1) to avoid unnecessary
26 allocation, 2) to behave the same regardless of which flavor of path is being
27 used, and 3) to support paths that cannot be represented in UTF-8 (as Linux has
28 no restriction on paths beyond disallowing NUL).
32 Usage of this module is fairly straightforward. Unless writing platform-specific
33 code, `Path` should be used to refer to the platform-native path.
35 Creation of a path is typically done with either `Path::new(some_str)` or
36 `Path::new(some_vec)`. This path can be modified with `.push()` and
37 `.pop()` (and other setters). The resulting Path can either be passed to another
38 API that expects a path, or can be turned into a `&[u8]` with `.as_vec()` or a
39 `Option<&str>` with `.as_str()`. Similarly, attributes of the path can be queried
40 with methods such as `.filename()`. There are also methods that return a new
41 path instead of modifying the receiver, such as `.join()` or `.dir_path()`.
43 Paths are always kept in normalized form. This means that creating the path
44 `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt
45 to mutate the path will always leave it in normalized form.
47 When rendering a path to some form of output, there is a method `.display()`
48 which is compatible with the `format!()` parameter `{}`. This will render the
49 path as a string, replacing all non-utf8 sequences with the Replacement
50 Character (U+FFFD). As such it is not suitable for passing to any API that
51 actually operates on the path; it is only intended for display.
56 let mut path = Path::new("/tmp/path");
57 println!("path: {}", path.display());
58 path.set_filename("foo");
60 println!("new path: {}", path.display());
61 println!("path exists: {}", path.exists());
68 use collections::{Collection, MutableSeq};
73 use option::{Option, None, Some};
75 use str::{MaybeOwned, Str, StrSlice};
78 use slice::{ImmutableEqSlice, ImmutableSlice};
81 /// Typedef for POSIX file paths.
82 /// See `posix::Path` for more info.
83 pub use PosixPath = self::posix::Path;
85 /// Typedef for Windows file paths.
86 /// See `windows::Path` for more info.
87 pub use WindowsPath = self::windows::Path;
89 /// Typedef for the platform-native path type
91 pub use Path = self::posix::Path;
92 /// Typedef for the platform-native path type
94 pub use Path = self::windows::Path;
96 /// Typedef for the platform-native component iterator
98 pub use Components = self::posix::Components;
99 /// Typedef for the platform-native component iterator
101 pub use Components = self::windows::Components;
103 /// Typedef for the platform-native str component iterator
105 pub use StrComponents = self::posix::StrComponents;
106 /// Typedef for the platform-native str component iterator
108 pub use StrComponents = self::windows::StrComponents;
110 /// Alias for the platform-native separator character.
112 pub use SEP = self::posix::SEP;
113 /// Alias for the platform-native separator character.
115 pub use SEP = self::windows::SEP;
117 /// Alias for the platform-native separator byte.
119 pub use SEP_BYTE = self::posix::SEP_BYTE;
120 /// Alias for the platform-native separator byte.
122 pub use SEP_BYTE = self::windows::SEP_BYTE;
124 /// Typedef for the platform-native separator char func
126 pub use is_sep = self::posix::is_sep;
127 /// Typedef for the platform-native separator char func
129 pub use is_sep = self::windows::is_sep;
130 /// Typedef for the platform-native separator byte func
132 pub use is_sep_byte = self::posix::is_sep_byte;
133 /// Typedef for the platform-native separator byte func
135 pub use is_sep_byte = self::windows::is_sep_byte;
140 /// A trait that represents the generic operations available on paths
141 pub trait GenericPath: Clone + GenericPathUnsafe {
142 /// Creates a new Path from a byte vector or string.
143 /// The resulting Path will always be normalized.
149 /// # #[cfg(windows)] fn foo() {}
150 /// # #[cfg(unix)] fn foo() {
151 /// let path = Path::new("foo/bar");
157 /// Fails the task if the path contains a NUL.
159 /// See individual Path impls for additional restrictions.
161 fn new<T: BytesContainer>(path: T) -> Self {
162 assert!(!contains_nul(&path));
163 unsafe { GenericPathUnsafe::new_unchecked(path) }
166 /// Creates a new Path from a byte vector or string, if possible.
167 /// The resulting Path will always be normalized.
173 /// # #[cfg(windows)] fn foo() {}
174 /// # #[cfg(unix)] fn foo() {
175 /// let x: &[u8] = b"foo\0";
176 /// assert!(Path::new_opt(x).is_none());
180 fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
181 if contains_nul(&path) {
184 Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
188 /// Returns the path as a string, if possible.
189 /// If the path is not representable in utf-8, this returns None.
195 /// # #[cfg(windows)] fn foo() {}
196 /// # #[cfg(unix)] fn foo() {
197 /// let p = Path::new("/abc/def");
198 /// assert_eq!(p.as_str(), Some("/abc/def"));
202 fn as_str<'a>(&'a self) -> Option<&'a str> {
203 str::from_utf8(self.as_vec())
206 /// Returns the path as a byte vector
212 /// # #[cfg(windows)] fn foo() {}
213 /// # #[cfg(unix)] fn foo() {
214 /// let p = Path::new("abc/def");
215 /// assert_eq!(p.as_vec(), b"abc/def");
218 fn as_vec<'a>(&'a self) -> &'a [u8];
220 /// Converts the Path into an owned byte vector
226 /// # #[cfg(windows)] fn foo() {}
227 /// # #[cfg(unix)] fn foo() {
228 /// let p = Path::new("abc/def");
229 /// assert_eq!(p.into_vec(), b"abc/def".to_vec());
230 /// // attempting to use p now results in "error: use of moved value"
233 fn into_vec(self) -> Vec<u8>;
235 /// Returns an object that implements `Show` for printing paths
241 /// # #[cfg(windows)] fn foo() {}
242 /// # #[cfg(unix)] fn foo() {
243 /// let p = Path::new("abc/def");
244 /// println!("{}", p.display()); // prints "abc/def"
247 fn display<'a>(&'a self) -> Display<'a, Self> {
248 Display{ path: self, filename: false }
251 /// Returns an object that implements `Show` for printing filenames
253 /// If there is no filename, nothing will be printed.
259 /// # #[cfg(windows)] fn foo() {}
260 /// # #[cfg(unix)] fn foo() {
261 /// let p = Path::new("abc/def");
262 /// println!("{}", p.filename_display()); // prints "def"
265 fn filename_display<'a>(&'a self) -> Display<'a, Self> {
266 Display{ path: self, filename: true }
269 /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
270 /// If `self` has no directory component, returns ['.'].
276 /// # #[cfg(windows)] fn foo() {}
277 /// # #[cfg(unix)] fn foo() {
278 /// let p = Path::new("abc/def/ghi");
279 /// assert_eq!(p.dirname(), b"abc/def");
282 fn dirname<'a>(&'a self) -> &'a [u8];
284 /// Returns the directory component of `self`, as a string, if possible.
285 /// See `dirname` for details.
291 /// # #[cfg(windows)] fn foo() {}
292 /// # #[cfg(unix)] fn foo() {
293 /// let p = Path::new("abc/def/ghi");
294 /// assert_eq!(p.dirname_str(), Some("abc/def"));
298 fn dirname_str<'a>(&'a self) -> Option<&'a str> {
299 str::from_utf8(self.dirname())
302 /// Returns the file component of `self`, as a byte vector.
303 /// If `self` represents the root of the file hierarchy, returns None.
304 /// If `self` is "." or "..", returns None.
310 /// # #[cfg(windows)] fn foo() {}
311 /// # #[cfg(unix)] fn foo() {
312 /// let p = Path::new("abc/def/ghi");
313 /// assert_eq!(p.filename(), Some(b"ghi"));
316 fn filename<'a>(&'a self) -> Option<&'a [u8]>;
318 /// Returns the file component of `self`, as a string, if possible.
319 /// See `filename` for details.
325 /// # #[cfg(windows)] fn foo() {}
326 /// # #[cfg(unix)] fn foo() {
327 /// let p = Path::new("abc/def/ghi");
328 /// assert_eq!(p.filename_str(), Some("ghi"));
332 fn filename_str<'a>(&'a self) -> Option<&'a str> {
333 self.filename().and_then(str::from_utf8)
336 /// Returns the stem of the filename of `self`, as a byte vector.
337 /// The stem is the portion of the filename just before the last '.'.
338 /// If there is no '.', the entire filename is returned.
344 /// # #[cfg(windows)] fn foo() {}
345 /// # #[cfg(unix)] fn foo() {
346 /// let p = Path::new("/abc/def.txt");
347 /// assert_eq!(p.filestem(), Some(b"def"));
350 fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
351 match self.filename() {
355 match name.rposition_elem(&dot) {
356 None | Some(0) => name,
357 Some(1) if name == b".." => name,
358 Some(pos) => name.slice_to(pos)
364 /// Returns the stem of the filename of `self`, as a string, if possible.
365 /// See `filestem` for details.
371 /// # #[cfg(windows)] fn foo() {}
372 /// # #[cfg(unix)] fn foo() {
373 /// let p = Path::new("/abc/def.txt");
374 /// assert_eq!(p.filestem_str(), Some("def"));
378 fn filestem_str<'a>(&'a self) -> Option<&'a str> {
379 self.filestem().and_then(str::from_utf8)
382 /// Returns the extension of the filename of `self`, as an optional byte vector.
383 /// The extension is the portion of the filename just after the last '.'.
384 /// If there is no extension, None is returned.
385 /// If the filename ends in '.', the empty vector is returned.
391 /// # #[cfg(windows)] fn foo() {}
392 /// # #[cfg(unix)] fn foo() {
393 /// let p = Path::new("abc/def.txt");
394 /// assert_eq!(p.extension(), Some(b"txt"));
397 fn extension<'a>(&'a self) -> Option<&'a [u8]> {
398 match self.filename() {
402 match name.rposition_elem(&dot) {
403 None | Some(0) => None,
404 Some(1) if name == b".." => None,
405 Some(pos) => Some(name.slice_from(pos+1))
411 /// Returns the extension of the filename of `self`, as a string, if possible.
412 /// See `extension` for details.
418 /// # #[cfg(windows)] fn foo() {}
419 /// # #[cfg(unix)] fn foo() {
420 /// let p = Path::new("abc/def.txt");
421 /// assert_eq!(p.extension_str(), Some("txt"));
425 fn extension_str<'a>(&'a self) -> Option<&'a str> {
426 self.extension().and_then(str::from_utf8)
429 /// Replaces the filename portion of the path with the given byte vector or string.
430 /// If the replacement name is [], this is equivalent to popping the path.
436 /// # #[cfg(windows)] fn foo() {}
437 /// # #[cfg(unix)] fn foo() {
438 /// let mut p = Path::new("abc/def.txt");
439 /// p.set_filename("foo.dat");
440 /// assert!(p == Path::new("abc/foo.dat"));
446 /// Fails the task if the filename contains a NUL.
448 fn set_filename<T: BytesContainer>(&mut self, filename: T) {
449 assert!(!contains_nul(&filename));
450 unsafe { self.set_filename_unchecked(filename) }
453 /// Replaces the extension with the given byte vector or string.
454 /// If there is no extension in `self`, this adds one.
455 /// If the argument is [] or "", this removes the extension.
456 /// If `self` has no filename, this is a no-op.
462 /// # #[cfg(windows)] fn foo() {}
463 /// # #[cfg(unix)] fn foo() {
464 /// let mut p = Path::new("abc/def.txt");
465 /// p.set_extension("csv");
466 /// assert!(p == Path::new("abc/def.csv"));
472 /// Fails the task if the extension contains a NUL.
473 fn set_extension<T: BytesContainer>(&mut self, extension: T) {
474 assert!(!contains_nul(&extension));
476 let val = self.filename().and_then(|name| {
478 let extlen = extension.container_as_bytes().len();
479 match (name.rposition_elem(&dot), extlen) {
480 (None, 0) | (Some(0), 0) => None,
481 (Some(idx), 0) => Some(Vec::from_slice(name.slice_to(idx))),
483 let idx = match idx {
484 None | Some(0) => name.len(),
489 v = Vec::with_capacity(idx + extlen + 1);
490 v.push_all(name.slice_to(idx));
492 v.push_all(extension.container_as_bytes());
500 Some(v) => unsafe { self.set_filename_unchecked(v) }
504 /// Returns a new Path constructed by replacing the filename with the given
505 /// byte vector or string.
506 /// See `set_filename` for details.
512 /// # #[cfg(windows)] fn foo() {}
513 /// # #[cfg(unix)] fn foo() {
514 /// let mut p = Path::new("abc/def.txt");
515 /// assert!(p.with_filename("foo.dat") == Path::new("abc/foo.dat"));
521 /// Fails the task if the filename contains a NUL.
523 fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
524 let mut p = self.clone();
525 p.set_filename(filename);
529 /// Returns a new Path constructed by setting the extension to the given
530 /// byte vector or string.
531 /// See `set_extension` for details.
537 /// # #[cfg(windows)] fn foo() {}
538 /// # #[cfg(unix)] fn foo() {
539 /// let mut p = Path::new("abc/def.txt");
540 /// assert!(p.with_extension("csv") == Path::new("abc/def.csv"));
546 /// Fails the task if the extension contains a NUL.
548 fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
549 let mut p = self.clone();
550 p.set_extension(extension);
554 /// Returns the directory component of `self`, as a Path.
555 /// If `self` represents the root of the filesystem hierarchy, returns `self`.
561 /// # #[cfg(windows)] fn foo() {}
562 /// # #[cfg(unix)] fn foo() {
563 /// let p = Path::new("abc/def/ghi");
564 /// assert!(p.dir_path() == Path::new("abc/def"));
567 fn dir_path(&self) -> Self {
568 // self.dirname() returns a NUL-free vector
569 unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
572 /// Returns a Path that represents the filesystem root that `self` is rooted in.
574 /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
580 /// # #[cfg(windows)] fn foo() {}
581 /// # #[cfg(unix)] fn foo() {
582 /// assert!(Path::new("abc/def").root_path() == None);
583 /// assert!(Path::new("/abc/def").root_path() == Some(Path::new("/")));
586 fn root_path(&self) -> Option<Self>;
588 /// Pushes a path (as a byte vector or string) onto `self`.
589 /// If the argument represents an absolute path, it replaces `self`.
595 /// # #[cfg(windows)] fn foo() {}
596 /// # #[cfg(unix)] fn foo() {
597 /// let mut p = Path::new("foo/bar");
598 /// p.push("baz.txt");
599 /// assert!(p == Path::new("foo/bar/baz.txt"));
605 /// Fails the task if the path contains a NUL.
607 fn push<T: BytesContainer>(&mut self, path: T) {
608 assert!(!contains_nul(&path));
609 unsafe { self.push_unchecked(path) }
612 /// Pushes multiple paths (as byte vectors or strings) onto `self`.
613 /// See `push` for details.
619 /// # #[cfg(windows)] fn foo() {}
620 /// # #[cfg(unix)] fn foo() {
621 /// let mut p = Path::new("foo");
622 /// p.push_many(&["bar", "baz.txt"]);
623 /// assert!(p == Path::new("foo/bar/baz.txt"));
627 fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
628 let t: Option<T> = None;
629 if BytesContainer::is_str(t) {
630 for p in paths.iter() {
631 self.push(p.container_as_str().unwrap())
634 for p in paths.iter() {
635 self.push(p.container_as_bytes())
640 /// Removes the last path component from the receiver.
641 /// Returns `true` if the receiver was modified, or `false` if it already
642 /// represented the root of the file hierarchy.
648 /// # #[cfg(windows)] fn foo() {}
649 /// # #[cfg(unix)] fn foo() {
650 /// let mut p = Path::new("foo/bar/baz.txt");
652 /// assert!(p == Path::new("foo/bar"));
655 fn pop(&mut self) -> bool;
657 /// Returns a new Path constructed by joining `self` with the given path
658 /// (as a byte vector or string).
659 /// If the given path is absolute, the new Path will represent just that.
665 /// # #[cfg(windows)] fn foo() {}
666 /// # #[cfg(unix)] fn foo() {
667 /// let p = Path::new("/foo");
668 /// assert!(p.join("bar.txt") == Path::new("/foo/bar.txt"));
674 /// Fails the task if the path contains a NUL.
676 fn join<T: BytesContainer>(&self, path: T) -> Self {
677 let mut p = self.clone();
682 /// Returns a new Path constructed by joining `self` with the given paths
683 /// (as byte vectors or strings).
684 /// See `join` for details.
690 /// # #[cfg(windows)] fn foo() {}
691 /// # #[cfg(unix)] fn foo() {
692 /// let p = Path::new("foo");
693 /// let fbbq = Path::new("foo/bar/baz/quux.txt");
694 /// assert!(p.join_many(&["bar", "baz", "quux.txt"]) == fbbq);
698 fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
699 let mut p = self.clone();
704 /// Returns whether `self` represents an absolute path.
705 /// An absolute path is defined as one that, when joined to another path, will
706 /// yield back the same absolute path.
712 /// # #[cfg(windows)] fn foo() {}
713 /// # #[cfg(unix)] fn foo() {
714 /// let p = Path::new("/abc/def");
715 /// assert!(p.is_absolute());
718 fn is_absolute(&self) -> bool;
720 /// Returns whether `self` represents a relative path.
721 /// Typically this is the inverse of `is_absolute`.
722 /// But for Windows paths, it also means the path is not volume-relative or
723 /// relative to the current working directory.
729 /// # #[cfg(windows)] fn foo() {}
730 /// # #[cfg(unix)] fn foo() {
731 /// let p = Path::new("abc/def");
732 /// assert!(p.is_relative());
735 fn is_relative(&self) -> bool {
739 /// Returns whether `self` is equal to, or is an ancestor of, the given path.
740 /// If both paths are relative, they are compared as though they are relative
741 /// to the same parent path.
747 /// # #[cfg(windows)] fn foo() {}
748 /// # #[cfg(unix)] fn foo() {
749 /// let p = Path::new("foo/bar/baz/quux.txt");
750 /// let fb = Path::new("foo/bar");
751 /// let bq = Path::new("baz/quux.txt");
752 /// assert!(fb.is_ancestor_of(&p));
755 fn is_ancestor_of(&self, other: &Self) -> bool;
757 /// Returns the Path that, were it joined to `base`, would yield `self`.
758 /// If no such path exists, None is returned.
759 /// If `self` is absolute and `base` is relative, or on Windows if both
760 /// paths refer to separate drives, an absolute path is returned.
766 /// # #[cfg(windows)] fn foo() {}
767 /// # #[cfg(unix)] fn foo() {
768 /// let p = Path::new("foo/bar/baz/quux.txt");
769 /// let fb = Path::new("foo/bar");
770 /// let bq = Path::new("baz/quux.txt");
771 /// assert!(p.path_relative_from(&fb) == Some(bq));
774 fn path_relative_from(&self, base: &Self) -> Option<Self>;
776 /// Returns whether the relative path `child` is a suffix of `self`.
782 /// # #[cfg(windows)] fn foo() {}
783 /// # #[cfg(unix)] fn foo() {
784 /// let p = Path::new("foo/bar/baz/quux.txt");
785 /// let bq = Path::new("baz/quux.txt");
786 /// assert!(p.ends_with_path(&bq));
789 fn ends_with_path(&self, child: &Self) -> bool;
792 /// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
793 pub trait BytesContainer {
794 /// Returns a &[u8] representing the receiver
795 fn container_as_bytes<'a>(&'a self) -> &'a [u8];
796 /// Consumes the receiver and converts it into Vec<u8>
798 fn container_into_owned_bytes(self) -> Vec<u8> {
799 Vec::from_slice(self.container_as_bytes())
801 /// Returns the receiver interpreted as a utf-8 string, if possible
803 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
804 str::from_utf8(self.container_as_bytes())
806 /// Returns whether .container_as_str() is guaranteed to not fail
807 // FIXME (#8888): Remove unused arg once ::<for T> works
809 fn is_str(_: Option<Self>) -> bool { false }
812 /// A trait that represents the unsafe operations on GenericPaths
813 pub trait GenericPathUnsafe {
814 /// Creates a new Path without checking for null bytes.
815 /// The resulting Path will always be normalized.
816 unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
818 /// Replaces the filename portion of the path without checking for null
820 /// See `set_filename` for details.
821 unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
823 /// Pushes a path onto `self` without checking for null bytes.
824 /// See `push` for details.
825 unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
828 /// Helper struct for printing paths with format!()
829 pub struct Display<'a, P> {
834 impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
835 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
836 self.as_maybe_owned().as_slice().fmt(f)
840 impl<'a, P: GenericPath> Display<'a, P> {
841 /// Returns the path as a possibly-owned string.
843 /// If the path is not UTF-8, invalid sequences will be replaced with the
844 /// unicode replacement char. This involves allocation.
846 pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
847 String::from_utf8_lossy(if self.filename {
848 match self.path.filename() {
858 impl<'a> BytesContainer for &'a str {
860 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
864 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
868 fn is_str(_: Option<&'a str>) -> bool { true }
871 impl BytesContainer for String {
873 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
877 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
878 Some(self.as_slice())
881 fn is_str(_: Option<String>) -> bool { true }
884 impl<'a> BytesContainer for &'a [u8] {
886 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
891 impl BytesContainer for Vec<u8> {
893 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
897 fn container_into_owned_bytes(self) -> Vec<u8> {
902 impl BytesContainer for CString {
904 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
905 self.as_bytes_no_nul()
909 impl<'a> BytesContainer for str::MaybeOwned<'a> {
911 fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
912 self.as_slice().as_bytes()
915 fn container_as_str<'b>(&'b self) -> Option<&'b str> {
916 Some(self.as_slice())
919 fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
923 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
924 v.container_as_bytes().iter().any(|&x| x == 0)
930 use super::{GenericPath, PosixPath, WindowsPath};
935 let input = "/foo/bar/baz";
936 let path: PosixPath = PosixPath::new(input.to_c_str());
937 assert_eq!(path.as_vec(), input.as_bytes());
939 let input = r"\foo\bar\baz";
940 let path: WindowsPath = WindowsPath::new(input.to_c_str());
941 assert_eq!(path.as_str().unwrap(), input.as_slice());