mod.rs 19.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13 14 15 16 17 18 19 20 21 22
/*!

Cross-platform path support

This module implements support for two flavors of paths. `PosixPath` represents
a path on any unix-like system, whereas `WindowsPath` represents a path on
Windows. This module also exposes a typedef `Path` which is equal to the
appropriate platform-specific path variant.

Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which
contains the set of methods that behave the same for both paths. They each also
implement some methods that could not be expressed in `GenericPath`, yet behave
23
identically for both path flavors, such as `.components()`.
24 25 26 27 28 29 30 31 32

The three main design goals of this module are 1) to avoid unnecessary
allocation, 2) to behave the same regardless of which flavor of path is being
used, and 3) to support paths that cannot be represented in UTF-8 (as Linux has
no restriction on paths beyond disallowing NUL).

## Usage

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 36
Creation of a path is typically done with either `Path::new(some_str)` or
`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 39
API that expects a path, or can be turned into a `&[u8]` with `.as_vec()` or a
`Option<&str>` with `.as_str()`. Similarly, attributes of the path can be queried
40 41 42
with methods such as `.filename()`. There are also methods that return a new
path instead of modifying the receiver, such as `.join()` or `.dir_path()`.

K
Kevin Ballard 已提交
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
K
Kevin Ballard 已提交
45 46
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 49 50 51 52 53 54 55
which is compatible with the `format!()` parameter `{}`. This will render the
path as a string, replacing all non-utf8 sequences with the Replacement
Character (U+FFFD). As such it is not suitable for passing to any API that
actually operates on the path; it is only intended for display.

## Example

```rust
56
let mut path = Path::new("/tmp/path");
A
Alex Crichton 已提交
57
println!("path: {}", path.display());
58 59
path.set_filename("foo");
path.push("bar");
A
Alex Crichton 已提交
60 61
println!("new path: {}", path.display());
println!("path exists: {}", path.exists());
62 63 64
```

*/
65

66
use collections::Collection;
67 68
use c_str::CString;
use clone::Clone;
69
use fmt;
70
use iter::Iterator;
71 72
use option::{Option, None, Some};
use str;
73
use str::{MaybeOwned, Str, StrSlice, from_utf8_lossy};
74
use string::String;
75
use slice::Vector;
D
Daniel Micay 已提交
76
use slice::{ImmutableEqVector, ImmutableVector};
77
use vec::Vec;
78 79 80

/// Typedef for POSIX file paths.
/// See `posix::Path` for more info.
81
pub use PosixPath = self::posix::Path;
82

K
Kevin Ballard 已提交
83 84
/// Typedef for Windows file paths.
/// See `windows::Path` for more info.
85
pub use WindowsPath = self::windows::Path;
86 87 88

/// Typedef for the platform-native path type
#[cfg(unix)]
89
pub use Path = self::posix::Path;
K
Kevin Ballard 已提交
90 91
/// Typedef for the platform-native path type
#[cfg(windows)]
92
pub use Path = self::windows::Path;
93 94 95

/// Typedef for the platform-native component iterator
#[cfg(unix)]
P
Palmer Cox 已提交
96
pub use Components = self::posix::Components;
97 98
/// Typedef for the platform-native component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
99
pub use Components = self::windows::Components;
100 101 102

/// Typedef for the platform-native str component iterator
#[cfg(unix)]
P
Palmer Cox 已提交
103
pub use StrComponents = self::posix::StrComponents;
104 105
/// Typedef for the platform-native str component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
106
pub use StrComponents = self::windows::StrComponents;
107

108 109 110
/// Alias for the platform-native separator character.
#[cfg(unix)]
pub use SEP = self::posix::SEP;
111
/// Alias for the platform-native separator character.
112 113 114
#[cfg(windows)]
pub use SEP = self::windows::SEP;

115
/// Alias for the platform-native separator byte.
116 117 118 119 120 121
#[cfg(unix)]
pub use SEP_BYTE = self::posix::SEP_BYTE;
/// Alias for the platform-native separator byte.
#[cfg(windows)]
pub use SEP_BYTE = self::windows::SEP_BYTE;

122 123 124 125 126 127 128 129 130 131 132 133 134
/// Typedef for the platform-native separator char func
#[cfg(unix)]
pub use is_sep = self::posix::is_sep;
/// Typedef for the platform-native separator char func
#[cfg(windows)]
pub use is_sep = self::windows::is_sep;
/// Typedef for the platform-native separator byte func
#[cfg(unix)]
pub use is_sep_byte = self::posix::is_sep_byte;
/// Typedef for the platform-native separator byte func
#[cfg(windows)]
pub use is_sep_byte = self::windows::is_sep_byte;

135 136
pub mod posix;
pub mod windows;
137 138 139

/// A trait that represents the generic operations available on paths
pub trait GenericPath: Clone + GenericPathUnsafe {
140
    /// Creates a new Path from a byte vector or string.
141 142 143 144
    /// The resulting Path will always be normalized.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
145
    /// Fails the task if the path contains a NUL.
K
Kevin Ballard 已提交
146 147
    ///
    /// See individual Path impls for additional restrictions.
148
    #[inline]
149
    fn new<T: BytesContainer>(path: T) -> Self {
150
        assert!(!contains_nul(&path));
A
Alex Crichton 已提交
151
        unsafe { GenericPathUnsafe::new_unchecked(path) }
152 153
    }

154
    /// Creates a new Path from a byte vector or string, if possible.
155 156
    /// The resulting Path will always be normalized.
    #[inline]
157
    fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
158
        if contains_nul(&path) {
159 160
            None
        } else {
161
            Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
162 163 164
        }
    }

165 166 167 168
    /// Returns the path as a string, if possible.
    /// If the path is not representable in utf-8, this returns None.
    #[inline]
    fn as_str<'a>(&'a self) -> Option<&'a str> {
169
        str::from_utf8(self.as_vec())
170 171 172 173 174
    }

    /// Returns the path as a byte vector
    fn as_vec<'a>(&'a self) -> &'a [u8];

175
    /// Converts the Path into an owned byte vector
176
    fn into_vec(self) -> Vec<u8>;
177

178
    /// Returns an object that implements `Show` for printing paths
179 180 181
    ///
    /// This will print the equivalent of `to_display_str()` when used with a {} format parameter.
    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 188
    ///
    /// This will print the equivalent of `to_filename_display_str()` when used with a {}
    /// format parameter. If there is no filename, nothing will be printed.
189 190
    fn filename_display<'a>(&'a self) -> Display<'a, Self> {
        Display{ path: self, filename: true }
191 192
    }

193 194 195 196 197 198 199
    /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
    /// If `self` has no directory component, returns ['.'].
    fn dirname<'a>(&'a self) -> &'a [u8];
    /// Returns the directory component of `self`, as a string, if possible.
    /// See `dirname` for details.
    #[inline]
    fn dirname_str<'a>(&'a self) -> Option<&'a str> {
200
        str::from_utf8(self.dirname())
201 202
    }
    /// Returns the file component of `self`, as a byte vector.
203 204 205
    /// If `self` represents the root of the file hierarchy, returns None.
    /// If `self` is "." or "..", returns None.
    fn filename<'a>(&'a self) -> Option<&'a [u8]>;
206 207 208 209
    /// Returns the file component of `self`, as a string, if possible.
    /// See `filename` for details.
    #[inline]
    fn filename_str<'a>(&'a self) -> Option<&'a str> {
210
        self.filename().and_then(str::from_utf8)
211 212 213 214
    }
    /// Returns the stem of the filename of `self`, as a byte vector.
    /// The stem is the portion of the filename just before the last '.'.
    /// If there is no '.', the entire filename is returned.
215 216 217 218 219 220 221
    fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
        match self.filename() {
            None => None,
            Some(name) => Some({
                let dot = '.' as u8;
                match name.rposition_elem(&dot) {
                    None | Some(0) => name,
S
Simon Sapin 已提交
222
                    Some(1) if name == b".." => name,
223 224 225
                    Some(pos) => name.slice_to(pos)
                }
            })
226 227 228 229 230 231
        }
    }
    /// Returns the stem of the filename of `self`, as a string, if possible.
    /// See `filestem` for details.
    #[inline]
    fn filestem_str<'a>(&'a self) -> Option<&'a str> {
232
        self.filestem().and_then(str::from_utf8)
233 234 235 236 237 238
    }
    /// Returns the extension of the filename of `self`, as an optional byte vector.
    /// The extension is the portion of the filename just after the last '.'.
    /// If there is no extension, None is returned.
    /// If the filename ends in '.', the empty vector is returned.
    fn extension<'a>(&'a self) -> Option<&'a [u8]> {
239 240 241 242 243 244
        match self.filename() {
            None => None,
            Some(name) => {
                let dot = '.' as u8;
                match name.rposition_elem(&dot) {
                    None | Some(0) => None,
S
Simon Sapin 已提交
245
                    Some(1) if name == b".." => None,
246 247 248
                    Some(pos) => Some(name.slice_from(pos+1))
                }
            }
249 250 251 252 253 254
        }
    }
    /// Returns the extension of the filename of `self`, as a string, if possible.
    /// See `extension` for details.
    #[inline]
    fn extension_str<'a>(&'a self) -> Option<&'a str> {
255
        self.extension().and_then(str::from_utf8)
256 257
    }

258
    /// Replaces the filename portion of the path with the given byte vector or string.
259 260 261 262
    /// If the replacement name is [], this is equivalent to popping the path.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
263
    /// Fails the task if the filename contains a NUL.
264
    #[inline]
265
    fn set_filename<T: BytesContainer>(&mut self, filename: T) {
266
        assert!(!contains_nul(&filename));
A
Alex Crichton 已提交
267
        unsafe { self.set_filename_unchecked(filename) }
268
    }
269
    /// Replaces the extension with the given byte vector or string.
270
    /// If there is no extension in `self`, this adds one.
271
    /// If the argument is [] or "", this removes the extension.
272 273 274 275
    /// If `self` has no filename, this is a no-op.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
276
    /// Fails the task if the extension contains a NUL.
277
    fn set_extension<T: BytesContainer>(&mut self, extension: T) {
278
        assert!(!contains_nul(&extension));
279 280 281 282 283 284

        let val = self.filename().and_then(|name| {
            let dot = '.' as u8;
            let extlen = extension.container_as_bytes().len();
            match (name.rposition_elem(&dot), extlen) {
                (None, 0) | (Some(0), 0) => None,
285
                (Some(idx), 0) => Some(Vec::from_slice(name.slice_to(idx))),
286 287 288 289 290 291 292
                (idx, extlen) => {
                    let idx = match idx {
                        None | Some(0) => name.len(),
                        Some(val) => val
                    };

                    let mut v;
293
                    v = Vec::with_capacity(idx + extlen + 1);
294 295 296 297
                    v.push_all(name.slice_to(idx));
                    v.push(dot);
                    v.push_all(extension.container_as_bytes());
                    Some(v)
298
                }
299
            }
300 301
        });

302 303 304 305 306 307
        match val {
            None => (),
            Some(v) => unsafe { self.set_filename_unchecked(v) }
        }
    }

308 309
    /// Returns a new Path constructed by replacing the filename with the given
    /// byte vector or string.
310 311 312 313
    /// See `set_filename` for details.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
314
    /// Fails the task if the filename contains a NUL.
315
    #[inline]
316
    fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
317 318 319 320
        let mut p = self.clone();
        p.set_filename(filename);
        p
    }
321 322
    /// Returns a new Path constructed by setting the extension to the given
    /// byte vector or string.
323 324 325 326
    /// See `set_extension` for details.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
327
    /// Fails the task if the extension contains a NUL.
328
    #[inline]
329
    fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
330 331 332 333 334 335 336 337
        let mut p = self.clone();
        p.set_extension(extension);
        p
    }

    /// Returns the directory component of `self`, as a Path.
    /// If `self` represents the root of the filesystem hierarchy, returns `self`.
    fn dir_path(&self) -> Self {
K
Kevin Ballard 已提交
338
        // self.dirname() returns a NUL-free vector
339
        unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
340 341
    }

342 343
    /// Returns a Path that represents the filesystem root that `self` is rooted in.
    ///
344
    /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
345 346
    fn root_path(&self) -> Option<Self>;

347
    /// Pushes a path (as a byte vector or string) onto `self`.
348 349 350 351
    /// If the argument represents an absolute path, it replaces `self`.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
352
    /// Fails the task if the path contains a NUL.
353
    #[inline]
354
    fn push<T: BytesContainer>(&mut self, path: T) {
355
        assert!(!contains_nul(&path));
A
Alex Crichton 已提交
356
        unsafe { self.push_unchecked(path) }
357
    }
358
    /// Pushes multiple paths (as byte vectors or strings) onto `self`.
359 360
    /// See `push` for details.
    #[inline]
361 362 363 364
    fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
        let t: Option<T> = None;
        if BytesContainer::is_str(t) {
            for p in paths.iter() {
365
                self.push(p.container_as_str().unwrap())
366 367 368 369 370
            }
        } else {
            for p in paths.iter() {
                self.push(p.container_as_bytes())
            }
371 372
        }
    }
K
Kevin Ballard 已提交
373 374 375 376
    /// Removes the last path component from the receiver.
    /// Returns `true` if the receiver was modified, or `false` if it already
    /// represented the root of the file hierarchy.
    fn pop(&mut self) -> bool;
377

378 379
    /// Returns a new Path constructed by joining `self` with the given path
    /// (as a byte vector or string).
380 381 382 383
    /// If the given path is absolute, the new Path will represent just that.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
384
    /// Fails the task if the path contains a NUL.
385
    #[inline]
386
    fn join<T: BytesContainer>(&self, path: T) -> Self {
387 388 389 390
        let mut p = self.clone();
        p.push(path);
        p
    }
391 392
    /// Returns a new Path constructed by joining `self` with the given paths
    /// (as byte vectors or strings).
393 394
    /// See `join` for details.
    #[inline]
395
    fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
396 397 398 399
        let mut p = self.clone();
        p.push_many(paths);
        p
    }
400 401

    /// Returns whether `self` represents an absolute path.
K
Kevin Ballard 已提交
402 403
    /// An absolute path is defined as one that, when joined to another path, will
    /// yield back the same absolute path.
404 405
    fn is_absolute(&self) -> bool;

406 407 408 409 410 411 412 413
    /// Returns whether `self` represents a relative path.
    /// Typically this is the inverse of `is_absolute`.
    /// But for Windows paths, it also means the path is not volume-relative or
    /// relative to the current working directory.
    fn is_relative(&self) -> bool {
        !self.is_absolute()
    }

414 415 416 417 418 419 420 421 422 423
    /// Returns whether `self` is equal to, or is an ancestor of, the given path.
    /// If both paths are relative, they are compared as though they are relative
    /// to the same parent path.
    fn is_ancestor_of(&self, other: &Self) -> bool;

    /// Returns the Path that, were it joined to `base`, would yield `self`.
    /// If no such path exists, None is returned.
    /// If `self` is absolute and `base` is relative, or on Windows if both
    /// paths refer to separate drives, an absolute path is returned.
    fn path_relative_from(&self, base: &Self) -> Option<Self>;
424

425 426 427 428 429 430 431 432
    /// Returns whether the relative path `child` is a suffix of `self`.
    fn ends_with_path(&self, child: &Self) -> bool;
}

/// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
pub trait BytesContainer {
    /// Returns a &[u8] representing the receiver
    fn container_as_bytes<'a>(&'a self) -> &'a [u8];
433
    /// Consumes the receiver and converts it into Vec<u8>
434
    #[inline]
435 436
    fn container_into_owned_bytes(self) -> Vec<u8> {
        Vec::from_slice(self.container_as_bytes())
437 438 439
    }
    /// Returns the receiver interpreted as a utf-8 string, if possible
    #[inline]
440
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
441
        str::from_utf8(self.container_as_bytes())
442
    }
443
    /// Returns whether .container_as_str() is guaranteed to not fail
444 445 446
    // FIXME (#8888): Remove unused arg once ::<for T> works
    #[inline]
    fn is_str(_: Option<Self>) -> bool { false }
447 448 449 450
}

/// A trait that represents the unsafe operations on GenericPaths
pub trait GenericPathUnsafe {
451
    /// Creates a new Path without checking for null bytes.
452
    /// The resulting Path will always be normalized.
453
    unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
454

455 456
    /// Replaces the filename portion of the path without checking for null
    /// bytes.
457
    /// See `set_filename` for details.
458
    unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
K
Kevin Ballard 已提交
459

460
    /// Pushes a path onto `self` without checking for null bytes.
461
    /// See `push` for details.
462
    unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
463 464
}

465
/// Helper struct for printing paths with format!()
E
Erik Price 已提交
466
pub struct Display<'a, P> {
467 468
    path: &'a P,
    filename: bool
469 470
}

471
impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
472
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473
        self.as_maybe_owned().as_slice().fmt(f)
474 475 476
    }
}

E
Erik Price 已提交
477
impl<'a, P: GenericPath> Display<'a, P> {
478
    /// Returns the path as a possibly-owned string.
479 480 481 482
    ///
    /// If the path is not UTF-8, invalid sequences will be replaced with the
    /// unicode replacement char. This involves allocation.
    #[inline]
483 484 485 486 487
    pub fn as_maybe_owned(&self) -> MaybeOwned<'a> {
        from_utf8_lossy(if self.filename {
            match self.path.filename() {
                None => &[],
                Some(v) => v
488
            }
489 490 491
        } else {
            self.path.as_vec()
        })
492 493 494
    }
}

E
Erik Price 已提交
495
impl<'a> BytesContainer for &'a str {
496 497 498 499 500
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_bytes()
    }
    #[inline]
501
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
502 503 504
        Some(*self)
    }
    #[inline]
E
Erik Price 已提交
505
    fn is_str(_: Option<&'a str>) -> bool { true }
506 507
}

508
impl BytesContainer for String {
509 510 511 512 513 514 515 516 517
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_bytes()
    }
    #[inline]
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
        Some(self.as_slice())
    }
    #[inline]
518
    fn is_str(_: Option<String>) -> bool { true }
519
}
520

E
Erik Price 已提交
521
impl<'a> BytesContainer for &'a [u8] {
522 523 524 525 526 527
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        *self
    }
}

528 529 530 531 532
impl BytesContainer for Vec<u8> {
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_slice()
    }
533
    #[inline]
534
    fn container_into_owned_bytes(self) -> Vec<u8> {
535 536 537 538
        self
    }
}

539 540 541
impl BytesContainer for CString {
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
542
        self.as_bytes_no_nul()
543 544 545
    }
}

546 547 548 549 550 551 552 553 554 555 556 557 558
impl<'a> BytesContainer for str::MaybeOwned<'a> {
    #[inline]
    fn container_as_bytes<'b>(&'b self) -> &'b [u8] {
        self.as_slice().as_bytes()
    }
    #[inline]
    fn container_as_str<'b>(&'b self) -> Option<&'b str> {
        Some(self.as_slice())
    }
    #[inline]
    fn is_str(_: Option<str::MaybeOwned>) -> bool { true }
}

559
#[inline(always)]
560 561
fn contains_nul<T: BytesContainer>(v: &T) -> bool {
    v.container_as_bytes().iter().any(|&x| x == 0)
562
}
K
Kevin Ballard 已提交
563

564 565
#[cfg(test)]
mod tests {
566
    use prelude::*;
567 568 569 570
    use super::{GenericPath, PosixPath, WindowsPath};
    use c_str::ToCStr;

    #[test]
571
    fn test_cstring() {
572
        let input = "/foo/bar/baz";
573
        let path: PosixPath = PosixPath::new(input.to_c_str());
574 575
        assert_eq!(path.as_vec(), input.as_bytes());

576
        let input = r"\foo\bar\baz";
577
        let path: WindowsPath = WindowsPath::new(input.to_c_str());
578
        assert_eq!(path.as_str().unwrap(), input.as_slice());
579 580
    }
}