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.
11 //! Cross-platform path support
13 //! This module implements support for two flavors of paths. `PosixPath` represents a path on any
14 //! unix-like system, whereas `WindowsPath` represents a path on Windows. This module also exposes
15 //! a typedef `Path` which is equal to the appropriate platform-specific path variant.
17 //! Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which contains the set of
18 //! methods that behave the same for both paths. They each also implement some methods that could
19 //! not be expressed in `GenericPath`, yet behave identically for both path flavors, such as
22 //! The three main design goals of this module are 1) to avoid unnecessary allocation, 2) to behave
23 //! the same regardless of which flavor of path is being used, and 3) to support paths that cannot
24 //! be represented in UTF-8 (as Linux has no restriction on paths beyond disallowing NUL).
28 //! Usage of this module is fairly straightforward. Unless writing platform-specific code, `Path`
29 //! should be used to refer to the platform-native path.
31 //! Creation of a path is typically done with either `Path::new(some_str)` or
32 //! `Path::new(some_vec)`. This path can be modified with `.push()` and `.pop()` (and other
33 //! setters). The resulting Path can either be passed to another API that expects a path, or can be
34 //! turned into a `&[u8]` with `.as_vec()` or a `Option<&str>` with `.as_str()`. Similarly,
35 //! attributes of the path can be queried with methods such as `.filename()`. There are also
36 //! methods that return a new path instead of modifying the receiver, such as `.join()` or
39 //! Paths are always kept in normalized form. This means that creating the path
40 //! `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt to mutate the path
41 //! will always leave it in normalized form.
43 //! When rendering a path to some form of output, there is a method `.display()` which is
44 //! compatible with the `format!()` parameter `{}`. This will render the path as a string,
45 //! replacing all non-utf8 sequences with the Replacement Character (U+FFFD). As such it is not
46 //! suitable for passing to any API that actually operates on the path; it is only intended for
52 //! use std::io::fs::PathExtensions;
54 //! let mut path = Path::new("/tmp/path");
55 //! println!("path: {}", path.display());
56 //! path.set_filename("foo");
58 //! println!("new path: {}", path.display());
59 //! println!("path exists: {}", path.exists());
64 use core::kinds::Sized;
68 use iter::IteratorExt;
70 use option::Option::{None, Some};
73 use string::{String, CowString};
77 /// Typedef for POSIX file paths.
78 /// See `posix::Path` for more info.
79 pub use self::posix::Path as PosixPath;
81 /// Typedef for Windows file paths.
82 /// See `windows::Path` for more info.
83 pub use self::windows::Path as WindowsPath;
85 /// Typedef for the platform-native path type
87 pub use self::posix::Path as Path;
88 /// Typedef for the platform-native path type
90 pub use self::windows::Path as Path;
92 /// Typedef for the platform-native component iterator
94 pub use self::posix::Components as Components;
95 /// Typedef for the platform-native component iterator
97 pub use self::windows::Components as Components;
99 /// Typedef for the platform-native str component iterator
101 pub use self::posix::StrComponents as StrComponents;
102 /// Typedef for the platform-native str component iterator
104 pub use self::windows::StrComponents as StrComponents;
106 /// Alias for the platform-native separator character.
108 pub use self::posix::SEP as SEP;
109 /// Alias for the platform-native separator character.
111 pub use self::windows::SEP as SEP;
113 /// Alias for the platform-native separator byte.
115 pub use self::posix::SEP_BYTE as SEP_BYTE;
116 /// Alias for the platform-native separator byte.
118 pub use self::windows::SEP_BYTE as SEP_BYTE;
120 /// Typedef for the platform-native separator char func
122 pub use self::posix::is_sep as is_sep;
123 /// Typedef for the platform-native separator char func
125 pub use self::windows::is_sep as is_sep;
126 /// Typedef for the platform-native separator byte func
128 pub use self::posix::is_sep_byte as is_sep_byte;
129 /// Typedef for the platform-native separator byte func
131 pub use self::windows::is_sep_byte as is_sep_byte;
136 /// A trait that represents the generic operations available on paths
137 pub trait GenericPath: Clone + GenericPathUnsafe {
138 /// Creates a new Path from a byte vector or string.
139 /// The resulting Path will always be normalized.
145 /// # #[cfg(windows)] fn foo() {}
146 /// # #[cfg(unix)] fn foo() {
147 /// let path = Path::new("foo/bar");
153 /// Panics the task if the path contains a NUL.
155 /// See individual Path impls for additional restrictions.
157 fn new<T: BytesContainer>(path: T) -> Self {
158 assert!(!contains_nul(&path));
159 unsafe { GenericPathUnsafe::new_unchecked(path) }
162 /// Creates a new Path from a byte vector or string, if possible.
163 /// The resulting Path will always be normalized.
169 /// # #[cfg(windows)] fn foo() {}
170 /// # #[cfg(unix)] fn foo() {
171 /// let x: &[u8] = b"foo\0";
172 /// assert!(Path::new_opt(x).is_none());
176 fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
177 if contains_nul(&path) {
180 Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
184 /// Returns the path as a string, if possible.
185 /// If the path is not representable in utf-8, this returns None.
191 /// # #[cfg(windows)] fn foo() {}
192 /// # #[cfg(unix)] fn foo() {
193 /// let p = Path::new("/abc/def");
194 /// assert_eq!(p.as_str(), Some("/abc/def"));
198 fn as_str<'a>(&'a self) -> Option<&'a str> {
199 str::from_utf8(self.as_vec()).ok()
202 /// Returns the path as a byte vector
208 /// # #[cfg(windows)] fn foo() {}
209 /// # #[cfg(unix)] fn foo() {
210 /// let p = Path::new("abc/def");
211 /// assert_eq!(p.as_vec(), b"abc/def");
214 fn as_vec<'a>(&'a self) -> &'a [u8];
216 /// Converts the Path into an owned byte vector
222 /// # #[cfg(windows)] fn foo() {}
223 /// # #[cfg(unix)] fn foo() {
224 /// let p = Path::new("abc/def");
225 /// assert_eq!(p.into_vec(), b"abc/def".to_vec());
226 /// // attempting to use p now results in "error: use of moved value"
229 fn into_vec(self) -> Vec<u8>;
231 /// Returns an object that implements `Show` for printing paths
237 /// # #[cfg(windows)] fn foo() {}
238 /// # #[cfg(unix)] fn foo() {
239 /// let p = Path::new("abc/def");
240 /// println!("{}", p.display()); // prints "abc/def"
243 fn display<'a>(&'a self) -> Display<'a, Self> {
244 Display{ path: self, filename: false }
247 /// Returns an object that implements `Show` for printing filenames
249 /// If there is no filename, nothing will be printed.
255 /// # #[cfg(windows)] fn foo() {}
256 /// # #[cfg(unix)] fn foo() {
257 /// let p = Path::new("abc/def");
258 /// println!("{}", p.filename_display()); // prints "def"
261 fn filename_display<'a>(&'a self) -> Display<'a, Self> {
262 Display{ path: self, filename: true }
265 /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
266 /// If `self` has no directory component, returns ['.'].
272 /// # #[cfg(windows)] fn foo() {}
273 /// # #[cfg(unix)] fn foo() {
274 /// let p = Path::new("abc/def/ghi");
275 /// assert_eq!(p.dirname(), b"abc/def");
278 fn dirname<'a>(&'a self) -> &'a [u8];
280 /// Returns the directory component of `self`, as a string, if possible.
281 /// See `dirname` for details.
287 /// # #[cfg(windows)] fn foo() {}
288 /// # #[cfg(unix)] fn foo() {
289 /// let p = Path::new("abc/def/ghi");
290 /// assert_eq!(p.dirname_str(), Some("abc/def"));
294 fn dirname_str<'a>(&'a self) -> Option<&'a str> {
295 str::from_utf8(self.dirname()).ok()
298 /// Returns the file component of `self`, as a byte vector.
299 /// If `self` represents the root of the file hierarchy, returns None.
300 /// If `self` is "." or "..", returns None.
306 /// # #[cfg(windows)] fn foo() {}
307 /// # #[cfg(unix)] fn foo() {
308 /// let p = Path::new("abc/def/ghi");
309 /// assert_eq!(p.filename(), Some(b"ghi"));
312 fn filename<'a>(&'a self) -> Option<&'a [u8]>;
314 /// Returns the file component of `self`, as a string, if possible.
315 /// See `filename` for details.
321 /// # #[cfg(windows)] fn foo() {}
322 /// # #[cfg(unix)] fn foo() {
323 /// let p = Path::new("abc/def/ghi");
324 /// assert_eq!(p.filename_str(), Some("ghi"));
328 fn filename_str<'a>(&'a self) -> Option<&'a str> {
329 self.filename().and_then(|s| str::from_utf8(s).ok())
332 /// Returns the stem of the filename of `self`, as a byte vector.
333 /// The stem is the portion of the filename just before the last '.'.
334 /// If there is no '.', the entire filename is returned.
340 /// # #[cfg(windows)] fn foo() {}
341 /// # #[cfg(unix)] fn foo() {
342 /// let p = Path::new("/abc/def.txt");
343 /// assert_eq!(p.filestem(), Some(b"def"));
346 fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
347 match self.filename() {
351 match name.rposition_elem(&dot) {
352 None | Some(0) => name,
353 Some(1) if name == b".." => name,
354 Some(pos) => name[..pos]
360 /// Returns the stem of the filename of `self`, as a string, if possible.
361 /// See `filestem` for details.
367 /// # #[cfg(windows)] fn foo() {}
368 /// # #[cfg(unix)] fn foo() {
369 /// let p = Path::new("/abc/def.txt");
370 /// assert_eq!(p.filestem_str(), Some("def"));
374 fn filestem_str<'a>(&'a self) -> Option<&'a str> {
375 self.filestem().and_then(|s| str::from_utf8(s).ok())
378 /// Returns the extension of the filename of `self`, as an optional byte vector.
379 /// The extension is the portion of the filename just after the last '.'.
380 /// If there is no extension, None is returned.
381 /// If the filename ends in '.', the empty vector is returned.
387 /// # #[cfg(windows)] fn foo() {}
388 /// # #[cfg(unix)] fn foo() {
389 /// let p = Path::new("abc/def.txt");
390 /// assert_eq!(p.extension(), Some(b"txt"));
393 fn extension<'a>(&'a self) -> Option<&'a [u8]> {
394 match self.filename() {
398 match name.rposition_elem(&dot) {
399 None | Some(0) => None,
400 Some(1) if name == b".." => None,
401 Some(pos) => Some(name[pos+1..])
407 /// Returns the extension of the filename of `self`, as a string, if possible.
408 /// See `extension` for details.
414 /// # #[cfg(windows)] fn foo() {}
415 /// # #[cfg(unix)] fn foo() {
416 /// let p = Path::new("abc/def.txt");
417 /// assert_eq!(p.extension_str(), Some("txt"));
421 fn extension_str<'a>(&'a self) -> Option<&'a str> {
422 self.extension().and_then(|s| str::from_utf8(s).ok())
425 /// Replaces the filename portion of the path with the given byte vector or string.
426 /// If the replacement name is [], this is equivalent to popping the path.
432 /// # #[cfg(windows)] fn foo() {}
433 /// # #[cfg(unix)] fn foo() {
434 /// let mut p = Path::new("abc/def.txt");
435 /// p.set_filename("foo.dat");
436 /// assert!(p == Path::new("abc/foo.dat"));
442 /// Panics the task if the filename contains a NUL.
444 fn set_filename<T: BytesContainer>(&mut self, filename: T) {
445 assert!(!contains_nul(&filename));
446 unsafe { self.set_filename_unchecked(filename) }
449 /// Replaces the extension with the given byte vector or string.
450 /// If there is no extension in `self`, this adds one.
451 /// If the argument is [] or "", this removes the extension.
452 /// If `self` has no filename, this is a no-op.
458 /// # #[cfg(windows)] fn foo() {}
459 /// # #[cfg(unix)] fn foo() {
460 /// let mut p = Path::new("abc/def.txt");
461 /// p.set_extension("csv");
462 /// assert!(p == Path::new("abc/def.csv"));
468 /// Panics the task if the extension contains a NUL.
469 fn set_extension<T: BytesContainer>(&mut self, extension: T) {
470 assert!(!contains_nul(&extension));
472 let val = self.filename().and_then(|name| {
474 let extlen = extension.container_as_bytes().len();
475 match (name.rposition_elem(&dot), extlen) {
476 (None, 0) | (Some(0), 0) => None,
477 (Some(idx), 0) => Some(name[..idx].to_vec()),
479 let idx = match idx {
480 None | Some(0) => name.len(),
485 v = Vec::with_capacity(idx + extlen + 1);
486 v.push_all(name[..idx]);
488 v.push_all(extension.container_as_bytes());
496 Some(v) => unsafe { self.set_filename_unchecked(v) }
500 /// Returns a new Path constructed by replacing the filename with the given
501 /// byte vector or string.
502 /// See `set_filename` for details.
508 /// # #[cfg(windows)] fn foo() {}
509 /// # #[cfg(unix)] fn foo() {
510 /// let mut p = Path::new("abc/def.txt");
511 /// assert!(p.with_filename("foo.dat") == Path::new("abc/foo.dat"));
517 /// Panics the task if the filename contains a NUL.
519 fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
520 let mut p = self.clone();
521 p.set_filename(filename);
525 /// Returns a new Path constructed by setting the extension to the given
526 /// byte vector or string.
527 /// See `set_extension` for details.
533 /// # #[cfg(windows)] fn foo() {}
534 /// # #[cfg(unix)] fn foo() {
535 /// let mut p = Path::new("abc/def.txt");
536 /// assert!(p.with_extension("csv") == Path::new("abc/def.csv"));
542 /// Panics the task if the extension contains a NUL.
544 fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
545 let mut p = self.clone();
546 p.set_extension(extension);
550 /// Returns the directory component of `self`, as a Path.
551 /// If `self` represents the root of the filesystem hierarchy, returns `self`.
557 /// # #[cfg(windows)] fn foo() {}
558 /// # #[cfg(unix)] fn foo() {
559 /// let p = Path::new("abc/def/ghi");
560 /// assert!(p.dir_path() == Path::new("abc/def"));
563 fn dir_path(&self) -> Self {
564 // self.dirname() returns a NUL-free vector
565 unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
568 /// Returns a Path that represents the filesystem root that `self` is rooted in.
570 /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
576 /// # #[cfg(windows)] fn foo() {}
577 /// # #[cfg(unix)] fn foo() {
578 /// assert!(Path::new("abc/def").root_path() == None);
579 /// assert!(Path::new("/abc/def").root_path() == Some(Path::new("/")));
582 fn root_path(&self) -> Option<Self>;
584 /// Pushes a path (as a byte vector or string) onto `self`.
585 /// If the argument represents an absolute path, it replaces `self`.
591 /// # #[cfg(windows)] fn foo() {}
592 /// # #[cfg(unix)] fn foo() {
593 /// let mut p = Path::new("foo/bar");
594 /// p.push("baz.txt");
595 /// assert!(p == Path::new("foo/bar/baz.txt"));
601 /// Panics the task if the path contains a NUL.
603 fn push<T: BytesContainer>(&mut self, path: T) {
604 assert!(!contains_nul(&path));
605 unsafe { self.push_unchecked(path) }
608 /// Pushes multiple paths (as byte vectors or strings) onto `self`.
609 /// See `push` for details.
615 /// # #[cfg(windows)] fn foo() {}
616 /// # #[cfg(unix)] fn foo() {
617 /// let mut p = Path::new("foo");
618 /// p.push_many(&["bar", "baz.txt"]);
619 /// assert!(p == Path::new("foo/bar/baz.txt"));
623 fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
624 let t: Option<&T> = None;
625 if BytesContainer::is_str(t) {
626 for p in paths.iter() {
627 self.push(p.container_as_str().unwrap())
630 for p in paths.iter() {
631 self.push(p.container_as_bytes())
636 /// Removes the last path component from the receiver.
637 /// Returns `true` if the receiver was modified, or `false` if it already
638 /// represented the root of the file hierarchy.
644 /// # #[cfg(windows)] fn foo() {}
645 /// # #[cfg(unix)] fn foo() {
646 /// let mut p = Path::new("foo/bar/baz.txt");
648 /// assert!(p == Path::new("foo/bar"));
651 fn pop(&mut self) -> bool;
653 /// Returns a new Path constructed by joining `self` with the given path
654 /// (as a byte vector or string).
655 /// If the given path is absolute, the new Path will represent just that.
661 /// # #[cfg(windows)] fn foo() {}
662 /// # #[cfg(unix)] fn foo() {
663 /// let p = Path::new("/foo");
664 /// assert!(p.join("bar.txt") == Path::new("/foo/bar.txt"));
670 /// Panics the task if the path contains a NUL.
672 fn join<T: BytesContainer>(&self, path: T) -> Self {
673 let mut p = self.clone();
678 /// Returns a new Path constructed by joining `self` with the given paths
679 /// (as byte vectors or strings).
680 /// See `join` for details.
686 /// # #[cfg(windows)] fn foo() {}
687 /// # #[cfg(unix)] fn foo() {
688 /// let p = Path::new("foo");
689 /// let fbbq = Path::new("foo/bar/baz/quux.txt");
690 /// assert!(p.join_many(&["bar", "baz", "quux.txt"]) == fbbq);
694 fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
695 let mut p = self.clone();
700 /// Returns whether `self` represents an absolute path.
701 /// An absolute path is defined as one that, when joined to another path, will
702 /// yield back the same absolute path.
708 /// # #[cfg(windows)] fn foo() {}
709 /// # #[cfg(unix)] fn foo() {
710 /// let p = Path::new("/abc/def");
711 /// assert!(p.is_absolute());
714 fn is_absolute(&self) -> bool;
716 /// Returns whether `self` represents a relative path.
717 /// Typically this is the inverse of `is_absolute`.
718 /// But for Windows paths, it also means the path is not volume-relative or
719 /// relative to the current working directory.
725 /// # #[cfg(windows)] fn foo() {}
726 /// # #[cfg(unix)] fn foo() {
727 /// let p = Path::new("abc/def");
728 /// assert!(p.is_relative());
731 fn is_relative(&self) -> bool {
735 /// Returns whether `self` is equal to, or is an ancestor of, the given path.
736 /// If both paths are relative, they are compared as though they are relative
737 /// to the same parent path.
743 /// # #[cfg(windows)] fn foo() {}
744 /// # #[cfg(unix)] fn foo() {
745 /// let p = Path::new("foo/bar/baz/quux.txt");
746 /// let fb = Path::new("foo/bar");
747 /// let bq = Path::new("baz/quux.txt");
748 /// assert!(fb.is_ancestor_of(&p));
751 fn is_ancestor_of(&self, other: &Self) -> bool;
753 /// Returns the Path that, were it joined to `base`, would yield `self`.
754 /// If no such path exists, None is returned.
755 /// If `self` is absolute and `base` is relative, or on Windows if both
756 /// paths refer to separate drives, an absolute path is returned.
762 /// # #[cfg(windows)] fn foo() {}
763 /// # #[cfg(unix)] fn foo() {
764 /// let p = Path::new("foo/bar/baz/quux.txt");
765 /// let fb = Path::new("foo/bar");
766 /// let bq = Path::new("baz/quux.txt");
767 /// assert!(p.path_relative_from(&fb) == Some(bq));
770 fn path_relative_from(&self, base: &Self) -> Option<Self>;
772 /// Returns whether the relative path `child` is a suffix of `self`.
778 /// # #[cfg(windows)] fn foo() {}
779 /// # #[cfg(unix)] fn foo() {
780 /// let p = Path::new("foo/bar/baz/quux.txt");
781 /// let bq = Path::new("baz/quux.txt");
782 /// assert!(p.ends_with_path(&bq));
785 fn ends_with_path(&self, child: &Self) -> bool;
788 /// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
789 pub trait BytesContainer for Sized? {
790 /// Returns a &[u8] representing the receiver
791 fn container_as_bytes<'a>(&'a self) -> &'a [u8];
792 /// Returns the receiver interpreted as a utf-8 string, if possible
794 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
795 str::from_utf8(self.container_as_bytes()).ok()
797 /// Returns whether .container_as_str() is guaranteed to not fail
798 // FIXME (#8888): Remove unused arg once ::<for T> works
800 fn is_str(_: Option<&Self>) -> bool { false }
803 /// A trait that represents the unsafe operations on GenericPaths
804 pub trait GenericPathUnsafe {
805 /// Creates a new Path without checking for null bytes.
806 /// The resulting Path will always be normalized.
807 unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
809 /// Replaces the filename portion of the path without checking for null
811 /// See `set_filename` for details.
812 unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
814 /// Pushes a path onto `self` without checking for null bytes.
815 /// See `push` for details.
816 unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
819 /// Helper struct for printing paths with format!()
820 pub struct Display<'a, P:'a> {
825 impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
826 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
831 impl<'a, P: GenericPath> Display<'a, P> {
832 /// Returns the path as a possibly-owned string.
834 /// If the path is not UTF-8, invalid sequences will be replaced with the
835 /// Unicode replacement char. This involves allocation.
837 pub fn as_cow(&self) -> CowString<'a> {
838 String::from_utf8_lossy(if self.filename {
839 match self.path.filename() {
841 let result: &[u8] = &[];
852 impl BytesContainer for str {
854 fn container_as_bytes(&self) -> &[u8] {
858 fn container_as_str(&self) -> Option<&str> {
862 fn is_str(_: Option<&str>) -> bool { true }
865 impl BytesContainer for String {
867 fn container_as_bytes(&self) -> &[u8] {
871 fn container_as_str(&self) -> Option<&str> {
875 fn is_str(_: Option<&String>) -> bool { true }
878 impl BytesContainer for [u8] {
880 fn container_as_bytes(&self) -> &[u8] {
885 impl BytesContainer for Vec<u8> {
887 fn container_as_bytes(&self) -> &[u8] {
892 impl BytesContainer for CString {
894 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
895 self.as_bytes_no_nul()
899 impl<'a, Sized? T: BytesContainer> BytesContainer for &'a T {
901 fn container_as_bytes(&self) -> &[u8] {
902 (**self).container_as_bytes()
905 fn container_as_str(&self) -> Option<&str> {
906 (**self).container_as_str()
909 fn is_str(_: Option<& &'a T>) -> bool { BytesContainer::is_str(None::<&T>) }
913 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
914 v.container_as_bytes().iter().any(|&x| x == 0)
921 use path::{WindowsPath, PosixPath};
925 let input = "/foo/bar/baz";
926 let path: PosixPath = PosixPath::new(input.to_c_str());
927 assert_eq!(path.as_vec(), input.as_bytes());
929 let input = r"\foo\bar\baz";
930 let path: WindowsPath = WindowsPath::new(input.to_c_str());
931 assert_eq!(path.as_str().unwrap(), input);