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::{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 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.
147 /// Fails the task if the path contains a NUL.
149 /// See individual Path impls for additional restrictions.
151 fn new<T: BytesContainer>(path: T) -> Self {
152 assert!(!contains_nul(&path));
153 unsafe { GenericPathUnsafe::new_unchecked(path) }
156 /// Creates a new Path from a byte vector or string, if possible.
157 /// The resulting Path will always be normalized.
159 fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
160 if contains_nul(&path) {
163 Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
167 /// Returns the path as a string, if possible.
168 /// If the path is not representable in utf-8, this returns None.
170 fn as_str<'a>(&'a self) -> Option<&'a str> {
171 str::from_utf8(self.as_vec())
174 /// Returns the path as a byte vector
175 fn as_vec<'a>(&'a self) -> &'a [u8];
177 /// Converts the Path into an owned byte vector
178 fn into_vec(self) -> Vec<u8>;
180 /// Returns an object that implements `Show` for printing paths
181 fn display<'a>(&'a self) -> Display<'a, Self> {
182 Display{ path: self, filename: false }
185 /// Returns an object that implements `Show` for printing filenames
187 /// If there is no filename, nothing will be printed.
188 fn filename_display<'a>(&'a self) -> Display<'a, Self> {
189 Display{ path: self, filename: true }
192 /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
193 /// If `self` has no directory component, returns ['.'].
194 fn dirname<'a>(&'a self) -> &'a [u8];
195 /// Returns the directory component of `self`, as a string, if possible.
196 /// See `dirname` for details.
198 fn dirname_str<'a>(&'a self) -> Option<&'a str> {
199 str::from_utf8(self.dirname())
201 /// Returns the file component of `self`, as a byte vector.
202 /// If `self` represents the root of the file hierarchy, returns None.
203 /// If `self` is "." or "..", returns None.
204 fn filename<'a>(&'a self) -> Option<&'a [u8]>;
205 /// Returns the file component of `self`, as a string, if possible.
206 /// See `filename` for details.
208 fn filename_str<'a>(&'a self) -> Option<&'a str> {
209 self.filename().and_then(str::from_utf8)
211 /// Returns the stem of the filename of `self`, as a byte vector.
212 /// The stem is the portion of the filename just before the last '.'.
213 /// If there is no '.', the entire filename is returned.
214 fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
215 match self.filename() {
219 match name.rposition_elem(&dot) {
220 None | Some(0) => name,
221 Some(1) if name == b".." => name,
222 Some(pos) => name.slice_to(pos)
227 /// Returns the stem of the filename of `self`, as a string, if possible.
228 /// See `filestem` for details.
230 fn filestem_str<'a>(&'a self) -> Option<&'a str> {
231 self.filestem().and_then(str::from_utf8)
233 /// Returns the extension of the filename of `self`, as an optional byte vector.
234 /// The extension is the portion of the filename just after the last '.'.
235 /// If there is no extension, None is returned.
236 /// If the filename ends in '.', the empty vector is returned.
237 fn extension<'a>(&'a self) -> Option<&'a [u8]> {
238 match self.filename() {
242 match name.rposition_elem(&dot) {
243 None | Some(0) => None,
244 Some(1) if name == b".." => None,
245 Some(pos) => Some(name.slice_from(pos+1))
250 /// Returns the extension of the filename of `self`, as a string, if possible.
251 /// See `extension` for details.
253 fn extension_str<'a>(&'a self) -> Option<&'a str> {
254 self.extension().and_then(str::from_utf8)
257 /// Replaces the filename portion of the path with the given byte vector or string.
258 /// If the replacement name is [], this is equivalent to popping the path.
262 /// Fails the task if the filename contains a NUL.
264 fn set_filename<T: BytesContainer>(&mut self, filename: T) {
265 assert!(!contains_nul(&filename));
266 unsafe { self.set_filename_unchecked(filename) }
268 /// Replaces the extension with the given byte vector or string.
269 /// If there is no extension in `self`, this adds one.
270 /// If the argument is [] or "", this removes the extension.
271 /// If `self` has no filename, this is a no-op.
275 /// Fails the task if the extension contains a NUL.
276 fn set_extension<T: BytesContainer>(&mut self, extension: T) {
277 assert!(!contains_nul(&extension));
279 let val = self.filename().and_then(|name| {
281 let extlen = extension.container_as_bytes().len();
282 match (name.rposition_elem(&dot), extlen) {
283 (None, 0) | (Some(0), 0) => None,
284 (Some(idx), 0) => Some(Vec::from_slice(name.slice_to(idx))),
286 let idx = match idx {
287 None | Some(0) => name.len(),
292 v = Vec::with_capacity(idx + extlen + 1);
293 v.push_all(name.slice_to(idx));
295 v.push_all(extension.container_as_bytes());
303 Some(v) => unsafe { self.set_filename_unchecked(v) }
307 /// Returns a new Path constructed by replacing the filename with the given
308 /// byte vector or string.
309 /// See `set_filename` for details.
313 /// Fails the task if the filename contains a NUL.
315 fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
316 let mut p = self.clone();
317 p.set_filename(filename);
320 /// Returns a new Path constructed by setting the extension to the given
321 /// byte vector or string.
322 /// See `set_extension` for details.
326 /// Fails the task if the extension contains a NUL.
328 fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
329 let mut p = self.clone();
330 p.set_extension(extension);
334 /// Returns the directory component of `self`, as a Path.
335 /// If `self` represents the root of the filesystem hierarchy, returns `self`.
336 fn dir_path(&self) -> Self {
337 // self.dirname() returns a NUL-free vector
338 unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
341 /// Returns a Path that represents the filesystem root that `self` is rooted in.
343 /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
344 fn root_path(&self) -> Option<Self>;
346 /// Pushes a path (as a byte vector or string) onto `self`.
347 /// If the argument represents an absolute path, it replaces `self`.
351 /// Fails the task if the path contains a NUL.
353 fn push<T: BytesContainer>(&mut self, path: T) {
354 assert!(!contains_nul(&path));
355 unsafe { self.push_unchecked(path) }
357 /// Pushes multiple paths (as byte vectors or strings) onto `self`.
358 /// See `push` for details.
360 fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
361 let t: Option<T> = None;
362 if BytesContainer::is_str(t) {
363 for p in paths.iter() {
364 self.push(p.container_as_str().unwrap())
367 for p in paths.iter() {
368 self.push(p.container_as_bytes())
372 /// Removes the last path component from the receiver.
373 /// Returns `true` if the receiver was modified, or `false` if it already
374 /// represented the root of the file hierarchy.
375 fn pop(&mut self) -> bool;
377 /// Returns a new Path constructed by joining `self` with the given path
378 /// (as a byte vector or string).
379 /// If the given path is absolute, the new Path will represent just that.
383 /// Fails the task if the path contains a NUL.
385 fn join<T: BytesContainer>(&self, path: T) -> Self {
386 let mut p = self.clone();
390 /// Returns a new Path constructed by joining `self` with the given paths
391 /// (as byte vectors or strings).
392 /// See `join` for details.
394 fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
395 let mut p = self.clone();
400 /// Returns whether `self` represents an absolute path.
401 /// An absolute path is defined as one that, when joined to another path, will
402 /// yield back the same absolute path.
403 fn is_absolute(&self) -> bool;
405 /// Returns whether `self` represents a relative path.
406 /// Typically this is the inverse of `is_absolute`.
407 /// But for Windows paths, it also means the path is not volume-relative or
408 /// relative to the current working directory.
409 fn is_relative(&self) -> bool {
413 /// Returns whether `self` is equal to, or is an ancestor of, the given path.
414 /// If both paths are relative, they are compared as though they are relative
415 /// to the same parent path.
416 fn is_ancestor_of(&self, other: &Self) -> bool;
418 /// Returns the Path that, were it joined to `base`, would yield `self`.
419 /// If no such path exists, None is returned.
420 /// If `self` is absolute and `base` is relative, or on Windows if both
421 /// paths refer to separate drives, an absolute path is returned.
422 fn path_relative_from(&self, base: &Self) -> Option<Self>;
424 /// Returns whether the relative path `child` is a suffix of `self`.
425 fn ends_with_path(&self, child: &Self) -> bool;
428 /// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
429 pub trait BytesContainer {
430 /// Returns a &[u8] representing the receiver
431 fn container_as_bytes<'a>(&'a self) -> &'a [u8];
432 /// Consumes the receiver and converts it into Vec<u8>
434 fn container_into_owned_bytes(self) -> Vec<u8> {
435 Vec::from_slice(self.container_as_bytes())
437 /// Returns the receiver interpreted as a utf-8 string, if possible
439 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
440 str::from_utf8(self.container_as_bytes())
442 /// Returns whether .container_as_str() is guaranteed to not fail
443 // FIXME (#8888): Remove unused arg once ::<for T> works
445 fn is_str(_: Option<Self>) -> bool { false }
448 /// A trait that represents the unsafe operations on GenericPaths
449 pub trait GenericPathUnsafe {
450 /// Creates a new Path without checking for null bytes.
451 /// The resulting Path will always be normalized.
452 unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
454 /// Replaces the filename portion of the path without checking for null
456 /// See `set_filename` for details.
457 unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
459 /// Pushes a path onto `self` without checking for null bytes.
460 /// See `push` for details.
461 unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
464 /// Helper struct for printing paths with format!()
465 pub struct Display<'a, P> {
470 impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 self.as_maybe_owned().as_slice().fmt(f)
476 impl<'a, P: GenericPath> Display<'a, P> {
477 /// Returns the path as a possibly-owned string.
479 /// If the path is not UTF-8, invalid sequences will be replaced with the
480 /// unicode replacement char. This involves allocation.
482 pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
483 String::from_utf8_lossy(if self.filename {
484 match self.path.filename() {
494 impl<'a> BytesContainer for &'a str {
496 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
500 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
504 fn is_str(_: Option<&'a str>) -> bool { true }
507 impl BytesContainer for String {
509 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
513 fn container_as_str<'a>(&'a self) -> Option<&'a str> {
514 Some(self.as_slice())
517 fn is_str(_: Option<String>) -> bool { true }
520 impl<'a> BytesContainer for &'a [u8] {
522 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
527 impl BytesContainer for Vec<u8> {
529 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
533 fn container_into_owned_bytes(self) -> Vec<u8> {
538 impl BytesContainer for CString {
540 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
541 self.as_bytes_no_nul()
545 impl<'a> BytesContainer for str::MaybeOwned<'a> {
547 fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
548 self.as_slice().as_bytes()
551 fn container_as_str<'b>(&'b self) -> Option<&'b str> {
552 Some(self.as_slice())
555 fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
559 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
560 v.container_as_bytes().iter().any(|&x| x == 0)
566 use super::{GenericPath, PosixPath, WindowsPath};
571 let input = "/foo/bar/baz";
572 let path: PosixPath = PosixPath::new(input.to_c_str());
573 assert_eq!(path.as_vec(), input.as_bytes());
575 let input = r"\foo\bar\baz";
576 let path: WindowsPath = WindowsPath::new(input.to_c_str());
577 assert_eq!(path.as_str().unwrap(), input.as_slice());