]> git.lizzy.rs Git - rust.git/blob - src/libstd/path/mod.rs
2960d55f337c731ea73ab334193feba9bac2ccbe
[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 #![deny(deprecated_owned_vector)]
67
68 use container::Container;
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, from_utf8_lossy};
76 use strbuf::StrBuf;
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 reverse component iterator
100 #[cfg(unix)]
101 pub use RevComponents = self::posix::RevComponents;
102 /// Typedef for the platform-native component iterator
103 #[cfg(windows)]
104 pub use Components = self::windows::Components;
105 /// Typedef for the platform-native reverse component iterator
106 #[cfg(windows)]
107 pub use RevComponents = self::windows::RevComponents;
108
109 /// Typedef for the platform-native str component iterator
110 #[cfg(unix)]
111 pub use StrComponents = self::posix::StrComponents;
112 /// Typedef for the platform-native reverse str component iterator
113 #[cfg(unix)]
114 pub use RevStrComponents = self::posix::RevStrComponents;
115 /// Typedef for the platform-native str component iterator
116 #[cfg(windows)]
117 pub use StrComponents = self::windows::StrComponents;
118 /// Typedef for the platform-native reverse str component iterator
119 #[cfg(windows)]
120 pub use RevStrComponents = self::windows::RevStrComponents;
121
122 /// Alias for the platform-native separator character.
123 #[cfg(unix)]
124 pub use SEP = self::posix::SEP;
125 /// Alias for the platform-native separator character.
126 #[cfg(windows)]
127 pub use SEP = self::windows::SEP;
128
129 /// Alias for the platform-native separator byte.
130 #[cfg(unix)]
131 pub use SEP_BYTE = self::posix::SEP_BYTE;
132 /// Alias for the platform-native separator byte.
133 #[cfg(windows)]
134 pub use SEP_BYTE = self::windows::SEP_BYTE;
135
136 /// Typedef for the platform-native separator char func
137 #[cfg(unix)]
138 pub use is_sep = self::posix::is_sep;
139 /// Typedef for the platform-native separator char func
140 #[cfg(windows)]
141 pub use is_sep = self::windows::is_sep;
142 /// Typedef for the platform-native separator byte func
143 #[cfg(unix)]
144 pub use is_sep_byte = self::posix::is_sep_byte;
145 /// Typedef for the platform-native separator byte func
146 #[cfg(windows)]
147 pub use is_sep_byte = self::windows::is_sep_byte;
148
149 pub mod posix;
150 pub mod windows;
151
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.
156     ///
157     /// # Failure
158     ///
159     /// Fails the task if the path contains a NUL.
160     ///
161     /// See individual Path impls for additional restrictions.
162     #[inline]
163     fn new<T: BytesContainer>(path: T) -> Self {
164         assert!(!contains_nul(&path));
165         unsafe { GenericPathUnsafe::new_unchecked(path) }
166     }
167
168     /// Creates a new Path from a byte vector or string, if possible.
169     /// The resulting Path will always be normalized.
170     #[inline]
171     fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
172         if contains_nul(&path) {
173             None
174         } else {
175             Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
176         }
177     }
178
179     /// Returns the path as a string, if possible.
180     /// If the path is not representable in utf-8, this returns None.
181     #[inline]
182     fn as_str<'a>(&'a self) -> Option<&'a str> {
183         str::from_utf8(self.as_vec())
184     }
185
186     /// Returns the path as a byte vector
187     fn as_vec<'a>(&'a self) -> &'a [u8];
188
189     /// Converts the Path into an owned byte vector
190     fn into_vec(self) -> Vec<u8>;
191
192     /// Returns an object that implements `Show` for printing paths
193     ///
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 }
197     }
198
199     /// Returns an object that implements `Show` for printing filenames
200     ///
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 }
205     }
206
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.
212     #[inline]
213     fn dirname_str<'a>(&'a self) -> Option<&'a str> {
214         str::from_utf8(self.dirname())
215     }
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.
222     #[inline]
223     fn filename_str<'a>(&'a self) -> Option<&'a str> {
224         self.filename().and_then(str::from_utf8)
225     }
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() {
231             None => None,
232             Some(name) => Some({
233                 let dot = '.' as u8;
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)
238                 }
239             })
240         }
241     }
242     /// Returns the stem of the filename of `self`, as a string, if possible.
243     /// See `filestem` for details.
244     #[inline]
245     fn filestem_str<'a>(&'a self) -> Option<&'a str> {
246         self.filestem().and_then(str::from_utf8)
247     }
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() {
254             None => None,
255             Some(name) => {
256                 let dot = '.' as u8;
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))
261                 }
262             }
263         }
264     }
265     /// Returns the extension of the filename of `self`, as a string, if possible.
266     /// See `extension` for details.
267     #[inline]
268     fn extension_str<'a>(&'a self) -> Option<&'a str> {
269         self.extension().and_then(str::from_utf8)
270     }
271
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.
274     ///
275     /// # Failure
276     ///
277     /// Fails the task if the filename contains a NUL.
278     #[inline]
279     fn set_filename<T: BytesContainer>(&mut self, filename: T) {
280         assert!(!contains_nul(&filename));
281         unsafe { self.set_filename_unchecked(filename) }
282     }
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.
287     ///
288     /// # Failure
289     ///
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));
293
294         let val = self.filename().and_then(|name| {
295             let dot = '.' as u8;
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))),
300                 (idx, extlen) => {
301                     let idx = match idx {
302                         None | Some(0) => name.len(),
303                         Some(val) => val
304                     };
305
306                     let mut v;
307                     v = Vec::with_capacity(idx + extlen + 1);
308                     v.push_all(name.slice_to(idx));
309                     v.push(dot);
310                     v.push_all(extension.container_as_bytes());
311                     Some(v)
312                 }
313             }
314         });
315
316         match val {
317             None => (),
318             Some(v) => unsafe { self.set_filename_unchecked(v) }
319         }
320     }
321
322     /// Returns a new Path constructed by replacing the filename with the given
323     /// byte vector or string.
324     /// See `set_filename` for details.
325     ///
326     /// # Failure
327     ///
328     /// Fails the task if the filename contains a NUL.
329     #[inline]
330     fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
331         let mut p = self.clone();
332         p.set_filename(filename);
333         p
334     }
335     /// Returns a new Path constructed by setting the extension to the given
336     /// byte vector or string.
337     /// See `set_extension` for details.
338     ///
339     /// # Failure
340     ///
341     /// Fails the task if the extension contains a NUL.
342     #[inline]
343     fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
344         let mut p = self.clone();
345         p.set_extension(extension);
346         p
347     }
348
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()) }
354     }
355
356     /// Returns a Path that represents the filesystem root that `self` is rooted in.
357     ///
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>;
360
361     /// Pushes a path (as a byte vector or string) onto `self`.
362     /// If the argument represents an absolute path, it replaces `self`.
363     ///
364     /// # Failure
365     ///
366     /// Fails the task if the path contains a NUL.
367     #[inline]
368     fn push<T: BytesContainer>(&mut self, path: T) {
369         assert!(!contains_nul(&path));
370         unsafe { self.push_unchecked(path) }
371     }
372     /// Pushes multiple paths (as byte vectors or strings) onto `self`.
373     /// See `push` for details.
374     #[inline]
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())
380             }
381         } else {
382             for p in paths.iter() {
383                 self.push(p.container_as_bytes())
384             }
385         }
386     }
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;
391
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.
395     ///
396     /// # Failure
397     ///
398     /// Fails the task if the path contains a NUL.
399     #[inline]
400     fn join<T: BytesContainer>(&self, path: T) -> Self {
401         let mut p = self.clone();
402         p.push(path);
403         p
404     }
405     /// Returns a new Path constructed by joining `self` with the given paths
406     /// (as byte vectors or strings).
407     /// See `join` for details.
408     #[inline]
409     fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
410         let mut p = self.clone();
411         p.push_many(paths);
412         p
413     }
414
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;
419
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 {
425         !self.is_absolute()
426     }
427
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;
432
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>;
438
439     /// Returns whether the relative path `child` is a suffix of `self`.
440     fn ends_with_path(&self, child: &Self) -> bool;
441 }
442
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>
448     #[inline]
449     fn container_into_owned_bytes(self) -> Vec<u8> {
450         Vec::from_slice(self.container_as_bytes())
451     }
452     /// Returns the receiver interpreted as a utf-8 string, if possible
453     #[inline]
454     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
455         str::from_utf8(self.container_as_bytes())
456     }
457     /// Returns whether .container_as_str() is guaranteed to not fail
458     // FIXME (#8888): Remove unused arg once ::<for T> works
459     #[inline]
460     fn is_str(_: Option<Self>) -> bool { false }
461 }
462
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;
468
469     /// Replaces the filename portion of the path without checking for null
470     /// bytes.
471     /// See `set_filename` for details.
472     unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
473
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);
477 }
478
479 /// Helper struct for printing paths with format!()
480 pub struct Display<'a, P> {
481     path: &'a P,
482     filename: bool
483 }
484
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)
488     }
489 }
490
491 impl<'a, P: GenericPath> Display<'a, P> {
492     /// Returns the path as a possibly-owned string.
493     ///
494     /// If the path is not UTF-8, invalid sequences will be replaced with the
495     /// unicode replacement char. This involves allocation.
496     #[inline]
497     pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
498         from_utf8_lossy(if self.filename {
499             match self.path.filename() {
500                 None => &[],
501                 Some(v) => v
502             }
503         } else {
504             self.path.as_vec()
505         })
506     }
507 }
508
509 impl<'a> BytesContainer for &'a str {
510     #[inline]
511     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
512         self.as_bytes()
513     }
514     #[inline]
515     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
516         Some(*self)
517     }
518     #[inline]
519     fn is_str(_: Option<&'a str>) -> bool { true }
520 }
521
522 impl BytesContainer for ~str {
523     #[inline]
524     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
525         self.as_bytes()
526     }
527     #[inline]
528     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
529         Some(self.as_slice())
530     }
531     #[inline]
532     fn is_str(_: Option<~str>) -> bool { true }
533 }
534 impl BytesContainer for StrBuf {
535     #[inline]
536     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
537         self.as_bytes()
538     }
539     #[inline]
540     fn container_into_owned_bytes(self) -> Vec<u8> {
541         self.into_bytes()
542     }
543     #[inline]
544     fn container_as_str<'a>(&'a self) -> Option<&'a str> {
545         Some(self.as_slice())
546     }
547     #[inline]
548     fn is_str(_: Option<StrBuf>) -> bool { true }
549 }
550
551 impl<'a> BytesContainer for &'a [u8] {
552     #[inline]
553     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
554         *self
555     }
556 }
557
558 impl BytesContainer for ~[u8] {
559     #[inline]
560     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
561         self.as_slice()
562     }
563 }
564
565 impl BytesContainer for Vec<u8> {
566     #[inline]
567     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
568         self.as_slice()
569     }
570     #[inline]
571     fn container_into_owned_bytes(self) -> Vec<u8> {
572         self
573     }
574 }
575
576 impl BytesContainer for CString {
577     #[inline]
578     fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
579         self.as_bytes_no_nul()
580     }
581 }
582
583 impl<'a> BytesContainer for str::MaybeOwned<'a> {
584     #[inline]
585     fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
586         self.as_slice().as_bytes()
587     }
588     #[inline]
589     fn container_as_str<'b>(&'b self) -> Option<&'b str> {
590         Some(self.as_slice())
591     }
592     #[inline]
593     fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
594 }
595
596 #[inline(always)]
597 fn contains_nul<T: BytesContainer>(v: &T) -> bool {
598     v.container_as_bytes().iter().any(|&x| x == 0)
599 }
600
601 #[cfg(test)]
602 mod tests {
603     use prelude::*;
604     use super::{GenericPath, PosixPath, WindowsPath};
605     use c_str::ToCStr;
606
607     #[test]
608     fn test_cstring() {
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());
612
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());
616     }
617 }