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());
66 #![deny(deprecated_owned_vector)]
68 use container::Container;
73 use option::{Option, None, Some};
75 use str::{MaybeOwned, Str, StrSlice, from_utf8_lossy};
78 use slice::{ImmutableEqVector, ImmutableVector};
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 reverse component iterator
101 pub use RevComponents = self::posix::RevComponents;
102 /// Typedef for the platform-native component iterator
104 pub use Components = self::windows::Components;
105 /// Typedef for the platform-native reverse component iterator
107 pub use RevComponents = self::windows::RevComponents;
109 /// Typedef for the platform-native str component iterator
111 pub use StrComponents = self::posix::StrComponents;
112 /// Typedef for the platform-native reverse str component iterator
114 pub use RevStrComponents = self::posix::RevStrComponents;
115 /// Typedef for the platform-native str component iterator
117 pub use StrComponents = self::windows::StrComponents;
118 /// Typedef for the platform-native reverse str component iterator
120 pub use RevStrComponents = self::windows::RevStrComponents;
122 /// Alias for the platform-native separator character.
124 pub use SEP = self::posix::SEP;
125 /// Alias for the platform-native separator character.
127 pub use SEP = self::windows::SEP;
129 /// Alias for the platform-native separator byte.
131 pub use SEP_BYTE = self::posix::SEP_BYTE;
132 /// Alias for the platform-native separator byte.
134 pub use SEP_BYTE = self::windows::SEP_BYTE;
136 /// Typedef for the platform-native separator char func
138 pub use is_sep = self::posix::is_sep;
139 /// Typedef for the platform-native separator char func
141 pub use is_sep = self::windows::is_sep;
142 /// Typedef for the platform-native separator byte func
144 pub use is_sep_byte = self::posix::is_sep_byte;
145 /// Typedef for the platform-native separator byte func
147 pub use is_sep_byte = self::windows::is_sep_byte;
152 /// A trait that represents the generic operations available on paths
153 pub trait GenericPath: Clone + GenericPathUnsafe {
154 /// Creates a new Path from a byte vector or string.
155 /// The resulting Path will always be normalized.
159 /// Fails the task if the path contains a NUL.
161 /// See individual Path impls for additional restrictions.
163 fn new<T: BytesContainer>(path: T) -> Self {
164 assert!(!contains_nul(&path));
165 unsafe { GenericPathUnsafe::new_unchecked(path) }
168 /// Creates a new Path from a byte vector or string, if possible.
169 /// The resulting Path will always be normalized.
171 fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
172 if contains_nul(&path) {
175 Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
179 /// Returns the path as a string, if possible.
180 /// If the path is not representable in utf-8, this returns None.
182 fn as_str<'a>(&'a self) -> Option<&'a str> {
183 str::from_utf8(self.as_vec())
186 /// Returns the path as a byte vector
187 fn as_vec<'a>(&'a self) -> &'a [u8];
189 /// Converts the Path into an owned byte vector
190 fn into_vec(self) -> Vec<u8>;
192 /// Returns an object that implements `Show` for printing paths
194 /// This will print the equivalent of `to_display_str()` when used with a {} format parameter.
195 fn display<'a>(&'a self) -> Display<'a, Self> {
196 Display{ path: self, filename: false }
199 /// Returns an object that implements `Show` for printing filenames
201 /// This will print the equivalent of `to_filename_display_str()` when used with a {}
202 /// format parameter. If there is no filename, nothing will be printed.
203 fn filename_display<'a>(&'a self) -> Display<'a, Self> {
204 Display{ path: self, filename: true }
207 /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
208 /// If `self` has no directory component, returns ['.'].
209 fn dirname<'a>(&'a self) -> &'a [u8];
210 /// Returns the directory component of `self`, as a string, if possible.
211 /// See `dirname` for details.
213 fn dirname_str<'a>(&'a self) -> Option<&'a str> {
214 str::from_utf8(self.dirname())
216 /// Returns the file component of `self`, as a byte vector.
217 /// If `self` represents the root of the file hierarchy, returns None.
218 /// If `self` is "." or "..", returns None.
219 fn filename<'a>(&'a self) -> Option<&'a [u8]>;
220 /// Returns the file component of `self`, as a string, if possible.
221 /// See `filename` for details.
223 fn filename_str<'a>(&'a self) -> Option<&'a str> {
224 self.filename().and_then(str::from_utf8)
226 /// Returns the stem of the filename of `self`, as a byte vector.
227 /// The stem is the portion of the filename just before the last '.'.
228 /// If there is no '.', the entire filename is returned.
229 fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
230 match self.filename() {
234 match name.rposition_elem(&dot) {
235 None | Some(0) => name,
236 Some(1) if name == bytes!("..") => name,
237 Some(pos) => name.slice_to(pos)
242 /// Returns the stem of the filename of `self`, as a string, if possible.
243 /// See `filestem` for details.
245 fn filestem_str<'a>(&'a self) -> Option<&'a str> {
246 self.filestem().and_then(str::from_utf8)
248 /// Returns the extension of the filename of `self`, as an optional byte vector.
249 /// The extension is the portion of the filename just after the last '.'.
250 /// If there is no extension, None is returned.
251 /// If the filename ends in '.', the empty vector is returned.
252 fn extension<'a>(&'a self) -> Option<&'a [u8]> {
253 match self.filename() {
257 match name.rposition_elem(&dot) {
258 None | Some(0) => None,
259 Some(1) if name == bytes!("..") => None,
260 Some(pos) => Some(name.slice_from(pos+1))
265 /// Returns the extension of the filename of `self`, as a string, if possible.
266 /// See `extension` for details.
268 fn extension_str<'a>(&'a self) -> Option<&'a str> {
269 self.extension().and_then(str::from_utf8)
272 /// Replaces the filename portion of the path with the given byte vector or string.
273 /// If the replacement name is [], this is equivalent to popping the path.
277 /// Fails the task if the filename contains a NUL.
279 fn set_filename<T: BytesContainer>(&mut self, filename: T) {
280 assert!(!contains_nul(&filename));
281 unsafe { self.set_filename_unchecked(filename) }
283 /// Replaces the extension with the given byte vector or string.
284 /// If there is no extension in `self`, this adds one.
285 /// If the argument is [] or "", this removes the extension.
286 /// If `self` has no filename, this is a no-op.
290 /// Fails the task if the extension contains a NUL.
291 fn set_extension<T: BytesContainer>(&mut self, extension: T) {
292 assert!(!contains_nul(&extension));
294 let val = self.filename().and_then(|name| {
296 let extlen = extension.container_as_bytes().len();
297 match (name.rposition_elem(&dot), extlen) {
298 (None, 0) | (Some(0), 0) => None,
299 (Some(idx), 0) => Some(Vec::from_slice(name.slice_to(idx))),
301 let idx = match idx {
302 None | Some(0) => name.len(),
307 v = Vec::with_capacity(idx + extlen + 1);
308 v.push_all(name.slice_to(idx));
310 v.push_all(extension.container_as_bytes());
318 Some(v) => unsafe { self.set_filename_unchecked(v) }
322 /// Returns a new Path constructed by replacing the filename with the given
323 /// byte vector or string.
324 /// See `set_filename` for details.
328 /// Fails the task if the filename contains a NUL.
330 fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
331 let mut p = self.clone();
332 p.set_filename(filename);
335 /// Returns a new Path constructed by setting the extension to the given
336 /// byte vector or string.
337 /// See `set_extension` for details.
341 /// Fails the task if the extension contains a NUL.
343 fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
344 let mut p = self.clone();
345 p.set_extension(extension);
349 /// Returns the directory component of `self`, as a Path.
350 /// If `self` represents the root of the filesystem hierarchy, returns `self`.
351 fn dir_path(&self) -> Self {
352 // self.dirname() returns a NUL-free vector
353 unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
356 /// Returns a Path that represents the filesystem root that `self` is rooted in.
358 /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
359 fn root_path(&self) -> Option<Self>;
361 /// Pushes a path (as a byte vector or string) onto `self`.
362 /// If the argument represents an absolute path, it replaces `self`.
366 /// Fails the task if the path contains a NUL.
368 fn push<T: BytesContainer>(&mut self, path: T) {
369 assert!(!contains_nul(&path));
370 unsafe { self.push_unchecked(path) }
372 /// Pushes multiple paths (as byte vectors or strings) onto `self`.
373 /// See `push` for details.
375 fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
376 let t: Option<T> = None;
377 if BytesContainer::is_str(t) {
378 for p in paths.iter() {
379 self.push(p.container_as_str().unwrap())
382 for p in paths.iter() {
383 self.push(p.container_as_bytes())
387 /// Removes the last path component from the receiver.
388 /// Returns `true` if the receiver was modified, or `false` if it already
389 /// represented the root of the file hierarchy.
390 fn pop(&mut self) -> bool;
392 /// Returns a new Path constructed by joining `self` with the given path
393 /// (as a byte vector or string).
394 /// If the given path is absolute, the new Path will represent just that.
398 /// Fails the task if the path contains a NUL.
400 fn join<T: BytesContainer>(&self, path: T) -> Self {
401 let mut p = self.clone();
405 /// Returns a new Path constructed by joining `self` with the given paths
406 /// (as byte vectors or strings).
407 /// See `join` for details.
409 fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
410 let mut p = self.clone();
415 /// Returns whether `self` represents an absolute path.
416 /// An absolute path is defined as one that, when joined to another path, will
417 /// yield back the same absolute path.
418 fn is_absolute(&self) -> bool;
420 /// Returns whether `self` represents a relative path.
421 /// Typically this is the inverse of `is_absolute`.
422 /// But for Windows paths, it also means the path is not volume-relative or
423 /// relative to the current working directory.
424 fn is_relative(&self) -> bool {
428 /// Returns whether `self` is equal to, or is an ancestor of, the given path.
429 /// If both paths are relative, they are compared as though they are relative
430 /// to the same parent path.
431 fn is_ancestor_of(&self, other: &Self) -> bool;
433 /// Returns the Path that, were it joined to `base`, would yield `self`.
434 /// If no such path exists, None is returned.
435 /// If `self` is absolute and `base` is relative, or on Windows if both
436 /// paths refer to separate drives, an absolute path is returned.
437 fn path_relative_from(&self, base: &Self) -> Option<Self>;
439 /// Returns whether the relative path `child` is a suffix of `self`.
440 fn ends_with_path(&self, child: &Self) -> bool;
443 /// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
444 pub trait BytesContainer {
445 /// Returns a &[u8] representing the receiver
446 fn container_as_bytes<'a>(&'a self) -> &'a [u8];
447 /// Consumes the receiver and converts it into Vec<u8>
449 fn container_into_owned_bytes(self) -> Vec<u8> {
450 Vec::from_slice(self.container_as_bytes())
452 /// Returns the receiver interpreted as a utf-8 string, if possible
454 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
455 str::from_utf8(self.container_as_bytes())
457 /// Returns whether .container_as_str() is guaranteed to not fail
458 // FIXME (#8888): Remove unused arg once ::<for T> works
460 fn is_str(_: Option<Self>) -> bool { false }
463 /// A trait that represents the unsafe operations on GenericPaths
464 pub trait GenericPathUnsafe {
465 /// Creates a new Path without checking for null bytes.
466 /// The resulting Path will always be normalized.
467 unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
469 /// Replaces the filename portion of the path without checking for null
471 /// See `set_filename` for details.
472 unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
474 /// Pushes a path onto `self` without checking for null bytes.
475 /// See `push` for details.
476 unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
479 /// Helper struct for printing paths with format!()
480 pub struct Display<'a, P> {
485 impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
486 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
487 self.as_maybe_owned().as_slice().fmt(f)
491 impl<'a, P: GenericPath> Display<'a, P> {
492 /// Returns the path as a possibly-owned string.
494 /// If the path is not UTF-8, invalid sequences will be replaced with the
495 /// unicode replacement char. This involves allocation.
497 pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
498 from_utf8_lossy(if self.filename {
499 match self.path.filename() {
509 impl<'a> BytesContainer for &'a str {
511 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
515 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
519 fn is_str(_: Option<&'a str>) -> bool { true }
522 impl BytesContainer for ~str {
524 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
528 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
529 Some(self.as_slice())
532 fn is_str(_: Option<~str>) -> bool { true }
534 impl BytesContainer for StrBuf {
536 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
540 fn container_into_owned_bytes(self) -> Vec<u8> {
544 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
545 Some(self.as_slice())
548 fn is_str(_: Option<StrBuf>) -> bool { true }
551 impl<'a> BytesContainer for &'a [u8] {
553 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
558 impl BytesContainer for ~[u8] {
560 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
565 impl BytesContainer for Vec<u8> {
567 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
571 fn container_into_owned_bytes(self) -> Vec<u8> {
576 impl BytesContainer for CString {
578 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
579 self.as_bytes_no_nul()
583 impl<'a> BytesContainer for str::MaybeOwned<'a> {
585 fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
586 self.as_slice().as_bytes()
589 fn container_as_str<'b>(&'b self) -> Option<&'b str> {
590 Some(self.as_slice())
593 fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
597 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
598 v.container_as_bytes().iter().any(|&x| x == 0)
604 use super::{GenericPath, PosixPath, WindowsPath};
609 let input = "/foo/bar/baz";
610 let path: PosixPath = PosixPath::new(input.to_c_str());
611 assert_eq!(path.as_vec(), input.as_bytes());
613 let input = r"\foo\bar\baz";
614 let path: WindowsPath = WindowsPath::new(input.to_c_str());
615 assert_eq!(path.as_str().unwrap(), input.as_slice());