]> git.lizzy.rs Git - rust.git/blob - src/libstd/path/mod.rs
auto merge of #15999 : Kimundi/rust/fix_folder, r=nikomatsakis
[rust.git] / src / libstd / path / mod.rs
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.
4 //
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.
10
11 /*!
12
13 Cross-platform path support
14
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.
19
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()`.
24
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).
29
30 ## Usage
31
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.
34
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()`.
42
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.
46
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.
52
53 ## Example
54
55 ```rust
56 let mut path = Path::new("/tmp/path");
57 println!("path: {}", path.display());
58 path.set_filename("foo");
59 path.push("bar");
60 println!("new path: {}", path.display());
61 println!("path exists: {}", path.exists());
62 ```
63
64 */
65
66 #![experimental]
67
68 use collections::{Collection, MutableSeq};
69 use c_str::CString;
70 use clone::Clone;
71 use fmt;
72 use iter::Iterator;
73 use option::{Option, None, Some};
74 use str;
75 use str::{MaybeOwned, Str, StrSlice};
76 use string::String;
77 use slice::Vector;
78 use slice::{ImmutableEqVector, ImmutableVector};
79 use vec::Vec;
80
81 /// Typedef for POSIX file paths.
82 /// See `posix::Path` for more info.
83 pub use PosixPath = self::posix::Path;
84
85 /// Typedef for Windows file paths.
86 /// See `windows::Path` for more info.
87 pub use WindowsPath = self::windows::Path;
88
89 /// Typedef for the platform-native path type
90 #[cfg(unix)]
91 pub use Path = self::posix::Path;
92 /// Typedef for the platform-native path type
93 #[cfg(windows)]
94 pub use Path = self::windows::Path;
95
96 /// Typedef for the platform-native component iterator
97 #[cfg(unix)]
98 pub use Components = self::posix::Components;
99 /// Typedef for the platform-native component iterator
100 #[cfg(windows)]
101 pub use Components = self::windows::Components;
102
103 /// Typedef for the platform-native str component iterator
104 #[cfg(unix)]
105 pub use StrComponents = self::posix::StrComponents;
106 /// Typedef for the platform-native str component iterator
107 #[cfg(windows)]
108 pub use StrComponents = self::windows::StrComponents;
109
110 /// Alias for the platform-native separator character.
111 #[cfg(unix)]
112 pub use SEP = self::posix::SEP;
113 /// Alias for the platform-native separator character.
114 #[cfg(windows)]
115 pub use SEP = self::windows::SEP;
116
117 /// Alias for the platform-native separator byte.
118 #[cfg(unix)]
119 pub use SEP_BYTE = self::posix::SEP_BYTE;
120 /// Alias for the platform-native separator byte.
121 #[cfg(windows)]
122 pub use SEP_BYTE = self::windows::SEP_BYTE;
123
124 /// Typedef for the platform-native separator char func
125 #[cfg(unix)]
126 pub use is_sep = self::posix::is_sep;
127 /// Typedef for the platform-native separator char func
128 #[cfg(windows)]
129 pub use is_sep = self::windows::is_sep;
130 /// Typedef for the platform-native separator byte func
131 #[cfg(unix)]
132 pub use is_sep_byte = self::posix::is_sep_byte;
133 /// Typedef for the platform-native separator byte func
134 #[cfg(windows)]
135 pub use is_sep_byte = self::windows::is_sep_byte;
136
137 pub mod posix;
138 pub mod windows;
139
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.
144     ///
145     /// # Failure
146     ///
147     /// Fails the task if the path contains a NUL.
148     ///
149     /// See individual Path impls for additional restrictions.
150     #[inline]
151     fn new<T: BytesContainer>(path: T) -> Self {
152         assert!(!contains_nul(&path));
153         unsafe { GenericPathUnsafe::new_unchecked(path) }
154     }
155
156     /// Creates a new Path from a byte vector or string, if possible.
157     /// The resulting Path will always be normalized.
158     #[inline]
159     fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
160         if contains_nul(&path) {
161             None
162         } else {
163             Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
164         }
165     }
166
167     /// Returns the path as a string, if possible.
168     /// If the path is not representable in utf-8, this returns None.
169     #[inline]
170     fn as_str<'a>(&'a self) -> Option<&'a str> {
171         str::from_utf8(self.as_vec())
172     }
173
174     /// Returns the path as a byte vector
175     fn as_vec<'a>(&'a self) -> &'a [u8];
176
177     /// Converts the Path into an owned byte vector
178     fn into_vec(self) -> Vec<u8>;
179
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 }
183     }
184
185     /// Returns an object that implements `Show` for printing filenames
186     ///
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 }
190     }
191
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.
197     #[inline]
198     fn dirname_str<'a>(&'a self) -> Option<&'a str> {
199         str::from_utf8(self.dirname())
200     }
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.
207     #[inline]
208     fn filename_str<'a>(&'a self) -> Option<&'a str> {
209         self.filename().and_then(str::from_utf8)
210     }
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() {
216             None => None,
217             Some(name) => Some({
218                 let dot = '.' as u8;
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)
223                 }
224             })
225         }
226     }
227     /// Returns the stem of the filename of `self`, as a string, if possible.
228     /// See `filestem` for details.
229     #[inline]
230     fn filestem_str<'a>(&'a self) -> Option<&'a str> {
231         self.filestem().and_then(str::from_utf8)
232     }
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() {
239             None => None,
240             Some(name) => {
241                 let dot = '.' as u8;
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))
246                 }
247             }
248         }
249     }
250     /// Returns the extension of the filename of `self`, as a string, if possible.
251     /// See `extension` for details.
252     #[inline]
253     fn extension_str<'a>(&'a self) -> Option<&'a str> {
254         self.extension().and_then(str::from_utf8)
255     }
256
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.
259     ///
260     /// # Failure
261     ///
262     /// Fails the task if the filename contains a NUL.
263     #[inline]
264     fn set_filename<T: BytesContainer>(&mut self, filename: T) {
265         assert!(!contains_nul(&filename));
266         unsafe { self.set_filename_unchecked(filename) }
267     }
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.
272     ///
273     /// # Failure
274     ///
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));
278
279         let val = self.filename().and_then(|name| {
280             let dot = '.' as u8;
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))),
285                 (idx, extlen) => {
286                     let idx = match idx {
287                         None | Some(0) => name.len(),
288                         Some(val) => val
289                     };
290
291                     let mut v;
292                     v = Vec::with_capacity(idx + extlen + 1);
293                     v.push_all(name.slice_to(idx));
294                     v.push(dot);
295                     v.push_all(extension.container_as_bytes());
296                     Some(v)
297                 }
298             }
299         });
300
301         match val {
302             None => (),
303             Some(v) => unsafe { self.set_filename_unchecked(v) }
304         }
305     }
306
307     /// Returns a new Path constructed by replacing the filename with the given
308     /// byte vector or string.
309     /// See `set_filename` for details.
310     ///
311     /// # Failure
312     ///
313     /// Fails the task if the filename contains a NUL.
314     #[inline]
315     fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
316         let mut p = self.clone();
317         p.set_filename(filename);
318         p
319     }
320     /// Returns a new Path constructed by setting the extension to the given
321     /// byte vector or string.
322     /// See `set_extension` for details.
323     ///
324     /// # Failure
325     ///
326     /// Fails the task if the extension contains a NUL.
327     #[inline]
328     fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
329         let mut p = self.clone();
330         p.set_extension(extension);
331         p
332     }
333
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()) }
339     }
340
341     /// Returns a Path that represents the filesystem root that `self` is rooted in.
342     ///
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>;
345
346     /// Pushes a path (as a byte vector or string) onto `self`.
347     /// If the argument represents an absolute path, it replaces `self`.
348     ///
349     /// # Failure
350     ///
351     /// Fails the task if the path contains a NUL.
352     #[inline]
353     fn push<T: BytesContainer>(&mut self, path: T) {
354         assert!(!contains_nul(&path));
355         unsafe { self.push_unchecked(path) }
356     }
357     /// Pushes multiple paths (as byte vectors or strings) onto `self`.
358     /// See `push` for details.
359     #[inline]
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())
365             }
366         } else {
367             for p in paths.iter() {
368                 self.push(p.container_as_bytes())
369             }
370         }
371     }
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;
376
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.
380     ///
381     /// # Failure
382     ///
383     /// Fails the task if the path contains a NUL.
384     #[inline]
385     fn join<T: BytesContainer>(&self, path: T) -> Self {
386         let mut p = self.clone();
387         p.push(path);
388         p
389     }
390     /// Returns a new Path constructed by joining `self` with the given paths
391     /// (as byte vectors or strings).
392     /// See `join` for details.
393     #[inline]
394     fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
395         let mut p = self.clone();
396         p.push_many(paths);
397         p
398     }
399
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;
404
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 {
410         !self.is_absolute()
411     }
412
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;
417
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>;
423
424     /// Returns whether the relative path `child` is a suffix of `self`.
425     fn ends_with_path(&self, child: &Self) -> bool;
426 }
427
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>
433     #[inline]
434     fn container_into_owned_bytes(self) -> Vec<u8> {
435         Vec::from_slice(self.container_as_bytes())
436     }
437     /// Returns the receiver interpreted as a utf-8 string, if possible
438     #[inline]
439     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
440         str::from_utf8(self.container_as_bytes())
441     }
442     /// Returns whether .container_as_str() is guaranteed to not fail
443     // FIXME (#8888): Remove unused arg once ::<for T> works
444     #[inline]
445     fn is_str(_: Option<Self>) -> bool { false }
446 }
447
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;
453
454     /// Replaces the filename portion of the path without checking for null
455     /// bytes.
456     /// See `set_filename` for details.
457     unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
458
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);
462 }
463
464 /// Helper struct for printing paths with format!()
465 pub struct Display<'a, P> {
466     path: &'a P,
467     filename: bool
468 }
469
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)
473     }
474 }
475
476 impl<'a, P: GenericPath> Display<'a, P> {
477     /// Returns the path as a possibly-owned string.
478     ///
479     /// If the path is not UTF-8, invalid sequences will be replaced with the
480     /// unicode replacement char. This involves allocation.
481     #[inline]
482     pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
483         String::from_utf8_lossy(if self.filename {
484             match self.path.filename() {
485                 None => &[],
486                 Some(v) => v
487             }
488         } else {
489             self.path.as_vec()
490         })
491     }
492 }
493
494 impl<'a> BytesContainer for &'a str {
495     #[inline]
496     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
497         self.as_bytes()
498     }
499     #[inline]
500     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
501         Some(*self)
502     }
503     #[inline]
504     fn is_str(_: Option<&'a str>) -> bool { true }
505 }
506
507 impl BytesContainer for String {
508     #[inline]
509     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
510         self.as_bytes()
511     }
512     #[inline]
513     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
514         Some(self.as_slice())
515     }
516     #[inline]
517     fn is_str(_: Option<String>) -> bool { true }
518 }
519
520 impl<'a> BytesContainer for &'a [u8] {
521     #[inline]
522     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
523         *self
524     }
525 }
526
527 impl BytesContainer for Vec<u8> {
528     #[inline]
529     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
530         self.as_slice()
531     }
532     #[inline]
533     fn container_into_owned_bytes(self) -> Vec<u8> {
534         self
535     }
536 }
537
538 impl BytesContainer for CString {
539     #[inline]
540     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
541         self.as_bytes_no_nul()
542     }
543 }
544
545 impl<'a> BytesContainer for str::MaybeOwned<'a> {
546     #[inline]
547     fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
548         self.as_slice().as_bytes()
549     }
550     #[inline]
551     fn container_as_str<'b>(&'b self) -> Option<&'b str> {
552         Some(self.as_slice())
553     }
554     #[inline]
555     fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
556 }
557
558 #[inline(always)]
559 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
560     v.container_as_bytes().iter().any(|&x| x == 0)
561 }
562
563 #[cfg(test)]
564 mod tests {
565     use prelude::*;
566     use super::{GenericPath, PosixPath, WindowsPath};
567     use c_str::ToCStr;
568
569     #[test]
570     fn test_cstring() {
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());
574
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());
578     }
579 }