mod.rs 22.0 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 38 39 40 41 42
`.pop()` (and other setters). The resulting Path can either be passed to another
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
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 67 68

use container::Container;
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::{OwnedStr, Str, StrSlice};
74
use to_str::ToStr;
75
use vec;
76
use vec::{CloneableVector, OwnedCloneableVector, OwnedVector, Vector};
77 78 79 80
use vec::{ImmutableEqVector, ImmutableVector};

/// 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 reverse component iterator
#[cfg(unix)]
P
Palmer Cox 已提交
99
pub use RevComponents = self::posix::RevComponents;
100 101
/// Typedef for the platform-native component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
102
pub use Components = self::windows::Components;
103 104
/// Typedef for the platform-native reverse component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
105
pub use RevComponents = self::windows::RevComponents;
106 107 108

/// Typedef for the platform-native str component iterator
#[cfg(unix)]
P
Palmer Cox 已提交
109
pub use StrComponents = self::posix::StrComponents;
110 111
/// Typedef for the platform-native reverse str component iterator
#[cfg(unix)]
P
Palmer Cox 已提交
112
pub use RevStrComponents = self::posix::RevStrComponents;
113 114
/// Typedef for the platform-native str component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
115
pub use StrComponents = self::windows::StrComponents;
116 117
/// Typedef for the platform-native reverse str component iterator
#[cfg(windows)]
P
Palmer Cox 已提交
118
pub use RevStrComponents = self::windows::RevStrComponents;
119

120 121 122
/// Alias for the platform-native separator character.
#[cfg(unix)]
pub use SEP = self::posix::SEP;
123
/// Alias for the platform-native separator character.
124 125 126
#[cfg(windows)]
pub use SEP = self::windows::SEP;

127
/// Alias for the platform-native separator byte.
128 129 130 131 132 133
#[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;

134 135 136 137 138 139 140 141 142 143 144 145 146
/// 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;

147 148
pub mod posix;
pub mod windows;
149 150 151

/// A trait that represents the generic operations available on paths
pub trait GenericPath: Clone + GenericPathUnsafe {
152
    /// Creates a new Path from a byte vector or string.
153 154 155 156
    /// The resulting Path will always be normalized.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
157
    /// Fails the task if the path contains a NUL.
K
Kevin Ballard 已提交
158 159
    ///
    /// See individual Path impls for additional restrictions.
160
    #[inline]
161
    fn new<T: BytesContainer>(path: T) -> Self {
A
Alex Crichton 已提交
162 163
        assert!(!contains_nul(path.container_as_bytes()));
        unsafe { GenericPathUnsafe::new_unchecked(path) }
164 165
    }

166
    /// Creates a new Path from a byte vector or string, if possible.
167 168
    /// The resulting Path will always be normalized.
    #[inline]
169
    fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
170
        if contains_nul(path.container_as_bytes()) {
171 172
            None
        } else {
173
            Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
174 175 176
        }
    }

177 178 179 180
    /// 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> {
181
        str::from_utf8(self.as_vec())
182 183 184 185 186
    }

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

187 188 189
    /// Converts the Path into an owned byte vector
    fn into_vec(self) -> ~[u8];

190
    /// Returns an object that implements `Show` for printing paths
191 192 193
    ///
    /// This will print the equivalent of `to_display_str()` when used with a {} format parameter.
    fn display<'a>(&'a self) -> Display<'a, Self> {
194
        Display{ path: self, filename: false }
195 196
    }

197
    /// Returns an object that implements `Show` for printing filenames
198 199 200
    ///
    /// 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.
201 202
    fn filename_display<'a>(&'a self) -> Display<'a, Self> {
        Display{ path: self, filename: true }
203 204
    }

205 206 207 208 209 210 211
    /// 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> {
212
        str::from_utf8(self.dirname())
213 214
    }
    /// Returns the file component of `self`, as a byte vector.
215 216 217
    /// 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]>;
218 219 220 221
    /// 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> {
222
        self.filename().and_then(str::from_utf8)
223 224 225 226
    }
    /// 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.
227 228 229 230 231 232 233 234 235 236 237
    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,
                    Some(1) if name == bytes!("..") => name,
                    Some(pos) => name.slice_to(pos)
                }
            })
238 239 240 241 242 243
        }
    }
    /// 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> {
244
        self.filestem().and_then(str::from_utf8)
245 246 247 248 249 250
    }
    /// 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]> {
251 252 253 254 255 256 257 258 259 260
        match self.filename() {
            None => None,
            Some(name) => {
                let dot = '.' as u8;
                match name.rposition_elem(&dot) {
                    None | Some(0) => None,
                    Some(1) if name == bytes!("..") => None,
                    Some(pos) => Some(name.slice_from(pos+1))
                }
            }
261 262 263 264 265 266
        }
    }
    /// 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> {
267
        self.extension().and_then(str::from_utf8)
268 269
    }

270
    /// Replaces the filename portion of the path with the given byte vector or string.
271 272 273 274
    /// If the replacement name is [], this is equivalent to popping the path.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
275
    /// Fails the task if the filename contains a NUL.
276
    #[inline]
277
    fn set_filename<T: BytesContainer>(&mut self, filename: T) {
A
Alex Crichton 已提交
278 279
        assert!(!contains_nul(filename.container_as_bytes()));
        unsafe { self.set_filename_unchecked(filename) }
280
    }
281
    /// Replaces the extension with the given byte vector or string.
282
    /// If there is no extension in `self`, this adds one.
283
    /// If the argument is [] or "", this removes the extension.
284 285 286 287
    /// If `self` has no filename, this is a no-op.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
288
    /// Fails the task if the extension contains a NUL.
289
    fn set_extension<T: BytesContainer>(&mut self, extension: T) {
A
Alex Crichton 已提交
290
        assert!(!contains_nul(extension.container_as_bytes()));
291 292
        // borrowck causes problems here too
        let val = {
293 294 295 296 297 298
            match self.filename() {
                None => None,
                Some(name) => {
                    let dot = '.' as u8;
                    match name.rposition_elem(&dot) {
                        None | Some(0) => {
299
                            if extension.container_as_bytes().is_empty() {
300
                                None
301
                            } else {
302
                                let mut v;
A
Alex Crichton 已提交
303 304 305 306 307
                                let extension = extension.container_as_bytes();
                                v = vec::with_capacity(name.len() + extension.len() + 1);
                                v.push_all(name);
                                v.push(dot);
                                v.push_all(extension);
308
                                Some(v)
309 310
                            }
                        }
311
                        Some(idx) => {
312
                            if extension.container_as_bytes().is_empty() {
313
                                Some(name.slice_to(idx).to_owned())
314
                            } else {
315
                                let mut v;
A
Alex Crichton 已提交
316 317 318 319
                                let extension = extension.container_as_bytes();
                                v = vec::with_capacity(idx + extension.len() + 1);
                                v.push_all(name.slice_to(idx+1));
                                v.push_all(extension);
320
                                Some(v)
321 322 323 324
                            }
                        }
                    }
                }
325
            }
326 327 328 329 330 331 332
        };
        match val {
            None => (),
            Some(v) => unsafe { self.set_filename_unchecked(v) }
        }
    }

333 334
    /// Returns a new Path constructed by replacing the filename with the given
    /// byte vector or string.
335 336 337 338
    /// See `set_filename` for details.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
339
    /// Fails the task if the filename contains a NUL.
340
    #[inline]
341
    fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
342 343 344 345
        let mut p = self.clone();
        p.set_filename(filename);
        p
    }
346 347
    /// Returns a new Path constructed by setting the extension to the given
    /// byte vector or string.
348 349 350 351
    /// See `set_extension` for details.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
352
    /// Fails the task if the extension contains a NUL.
353
    #[inline]
354
    fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
355 356 357 358 359 360 361 362
        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 已提交
363
        // self.dirname() returns a NUL-free vector
364
        unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
365 366
    }

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

372
    /// Pushes a path (as a byte vector or string) onto `self`.
373 374 375 376
    /// If the argument represents an absolute path, it replaces `self`.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
377
    /// Fails the task if the path contains a NUL.
378
    #[inline]
379
    fn push<T: BytesContainer>(&mut self, path: T) {
A
Alex Crichton 已提交
380 381
        assert!(!contains_nul(path.container_as_bytes()));
        unsafe { self.push_unchecked(path) }
382
    }
383
    /// Pushes multiple paths (as byte vectors or strings) onto `self`.
384 385
    /// See `push` for details.
    #[inline]
386 387 388 389
    fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
        let t: Option<T> = None;
        if BytesContainer::is_str(t) {
            for p in paths.iter() {
390
                self.push(p.container_as_str().unwrap())
391 392 393 394 395
            }
        } else {
            for p in paths.iter() {
                self.push(p.container_as_bytes())
            }
396 397
        }
    }
K
Kevin Ballard 已提交
398 399 400 401
    /// 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;
402

403 404
    /// Returns a new Path constructed by joining `self` with the given path
    /// (as a byte vector or string).
405 406 407 408
    /// If the given path is absolute, the new Path will represent just that.
    ///
    /// # Failure
    ///
A
Alex Crichton 已提交
409
    /// Fails the task if the path contains a NUL.
410
    #[inline]
411
    fn join<T: BytesContainer>(&self, path: T) -> Self {
412 413 414 415
        let mut p = self.clone();
        p.push(path);
        p
    }
416 417
    /// Returns a new Path constructed by joining `self` with the given paths
    /// (as byte vectors or strings).
418 419
    /// See `join` for details.
    #[inline]
420
    fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
421 422 423 424
        let mut p = self.clone();
        p.push_many(paths);
        p
    }
425 426

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

431 432 433 434 435 436 437 438
    /// 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()
    }

439 440 441 442 443 444 445 446 447 448
    /// 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>;
449

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    /// 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];
    /// Consumes the receiver and converts it into ~[u8]
    #[inline]
    fn container_into_owned_bytes(self) -> ~[u8] {
        self.container_as_bytes().to_owned()
    }
    /// Returns the receiver interpreted as a utf-8 string, if possible
    #[inline]
465
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
466
        str::from_utf8(self.container_as_bytes())
467
    }
468
    /// Returns whether .container_as_str() is guaranteed to not fail
469 470 471
    // FIXME (#8888): Remove unused arg once ::<for T> works
    #[inline]
    fn is_str(_: Option<Self>) -> bool { false }
472 473 474 475
}

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

480 481
    /// Replaces the filename portion of the path without checking for null
    /// bytes.
482
    /// See `set_filename` for details.
483
    unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
K
Kevin Ballard 已提交
484

485
    /// Pushes a path onto `self` without checking for null bytes.
486
    /// See `push` for details.
487
    unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
488 489
}

490
/// Helper struct for printing paths with format!()
E
Erik Price 已提交
491 492
pub struct Display<'a, P> {
    priv path: &'a P,
493
    priv filename: bool
494 495
}

496
impl<'a, P: GenericPath> fmt::Show for Display<'a, P> {
497 498
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.with_str(|s| f.pad(s))
499 500 501
    }
}

E
Erik Price 已提交
502
impl<'a, P: GenericPath> ToStr for Display<'a, P> {
503 504 505 506 507
    /// Returns the path as a string
    ///
    /// If the path is not UTF-8, invalid sequences with be replaced with the
    /// unicode replacement char. This involves allocation.
    fn to_str(&self) -> ~str {
508 509 510
        if self.filename {
            match self.path.filename() {
                None => ~"",
511
                Some(v) => str::from_utf8_lossy(v).into_owned()
512 513
            }
        } else {
514
            str::from_utf8_lossy(self.path.as_vec()).into_owned()
515
        }
516 517 518
    }
}

E
Erik Price 已提交
519
impl<'a, P: GenericPath> Display<'a, P> {
520 521 522 523 524
    /// Provides the path as a string to a closure
    ///
    /// If the path is not UTF-8, invalid sequences will be replaced with the
    /// unicode replacement char. This involves allocation.
    #[inline]
525
    pub fn with_str<T>(&self, f: |&str| -> T) -> T {
526 527 528
        let opt = if self.filename { self.path.filename_str() }
                  else { self.path.as_str() };
        match opt {
529 530 531 532 533
            Some(s) => f(s),
            None => {
                let s = self.to_str();
                f(s.as_slice())
            }
534 535 536 537
        }
    }
}

E
Erik Price 已提交
538
impl<'a> BytesContainer for &'a str {
539 540 541 542 543
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_bytes()
    }
    #[inline]
544
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
545 546 547
        Some(*self)
    }
    #[inline]
E
Erik Price 已提交
548
    fn is_str(_: Option<&'a str>) -> bool { true }
549 550 551 552 553 554 555 556 557 558 559 560
}

impl BytesContainer for ~str {
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_bytes()
    }
    #[inline]
    fn container_into_owned_bytes(self) -> ~[u8] {
        self.into_bytes()
    }
    #[inline]
561
    fn container_as_str<'a>(&'a self) -> Option<&'a str> {
562 563 564 565 566 567
        Some(self.as_slice())
    }
    #[inline]
    fn is_str(_: Option<~str>) -> bool { true }
}

E
Erik Price 已提交
568
impl<'a> BytesContainer for &'a [u8] {
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        *self
    }
}

impl BytesContainer for ~[u8] {
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        self.as_slice()
    }
    #[inline]
    fn container_into_owned_bytes(self) -> ~[u8] {
        self
    }
}

586 587 588 589 590 591 592 593
impl BytesContainer for CString {
    #[inline]
    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
        let s = self.as_bytes();
        s.slice_to(s.len()-1)
    }
}

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
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_into_owned_bytes(self) -> ~[u8] {
        self.into_owned().into_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 }
}

611 612 613 614
#[inline(always)]
fn contains_nul(v: &[u8]) -> bool {
    v.iter().any(|&x| x == 0)
}
K
Kevin Ballard 已提交
615

616 617
#[cfg(test)]
mod tests {
618
    use prelude::*;
619 620 621 622
    use super::{GenericPath, PosixPath, WindowsPath};
    use c_str::ToCStr;

    #[test]
623
    fn test_cstring() {
624
        let input = "/foo/bar/baz";
625
        let path: PosixPath = PosixPath::new(input.to_c_str());
626 627
        assert_eq!(path.as_vec(), input.as_bytes());

628
        let input = r"\foo\bar\baz";
629
        let path: WindowsPath = WindowsPath::new(input.to_c_str());
630
        assert_eq!(path.as_str().unwrap(), input.as_slice());
631 632
    }
}