]> git.lizzy.rs Git - rust.git/blob - src/libstd/path.rs
Auto merge of #22517 - brson:relnotes, r=Gankro
[rust.git] / src / libstd / path.rs
1 // Copyright 2015 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 //! Cross-platform path manipulation.
12 //!
13 //! This module provides two types, `PathBuf` and `Path` (akin to `String` and
14 //! `str`), for working with paths abstractly. These types are thin wrappers
15 //! around `OsString` and `OsStr` respectively, meaning that they work directly
16 //! on strings according to the local platform's path syntax.
17 //!
18 //! ## Simple usage
19 //!
20 //! Path manipulation involves both parsing components from slices and building
21 //! new owned paths.
22 //!
23 //! To parse a path, you can create a `Path` slice from a `str`
24 //! slice and start asking questions:
25 //!
26 //! ```rust
27 //! use std::path::Path;
28 //!
29 //! let path = Path::new("/tmp/foo/bar.txt");
30 //! let file = path.file_name();
31 //! let extension = path.extension();
32 //! let parent_dir = path.parent();
33 //! ```
34 //!
35 //! To build or modify paths, use `PathBuf`:
36 //!
37 //! ```rust
38 //! use std::path::PathBuf;
39 //!
40 //! let mut path = PathBuf::new("c:\\");
41 //! path.push("windows");
42 //! path.push("system32");
43 //! path.set_extension("dll");
44 //! ```
45 //!
46 //! ## Path components and normalization
47 //!
48 //! The path APIs are built around the notion of "components", which roughly
49 //! correspond to the substrings between path separators (`/` and, on Windows,
50 //! `\`). The APIs for path parsing are largely specified in terms of the path's
51 //! components, so it's important to clearly understand how those are determined.
52 //!
53 //! A path can always be reconstructed into an equivalent path by putting
54 //! together its components via `push`. Syntactically, the paths may differ by
55 //! the normalization described below.
56 //!
57 //! ### Component types
58 //!
59 //! Components come in several types:
60 //!
61 //! * Normal components are the default: standard references to files or
62 //! directories. The path `a/b` has two normal components, `a` and `b`.
63 //!
64 //! * Current directory components represent the `.` character. For example,
65 //! `a/.` has a normal component `a` and a current directory component.
66 //!
67 //! * The root directory component represents a separator that designates
68 //!   starting from root. For example, `/a/b` has a root directory component
69 //!   followed by normal components `a` and `b`.
70 //!
71 //! On Windows, two additional component types come into play:
72 //!
73 //! * Prefix components, of which there is a large variety. For example, `C:`
74 //! and `\\server\share` are prefixes. The path `C:windows` has a prefix
75 //! component `C:` and a normal component `windows`; the path `C:\windows` has a
76 //! prefix component `C:`, a root directory component, and a normal component
77 //! `windows`.
78 //!
79 //! * Empty components, a special case for so-called "verbatim" paths where very
80 //! little normalization is allowed. For example, `\\?\C:\` has a "verbatim"
81 //! prefix `\\?\C:`, a root component, and an empty component (as a way of
82 //! representing the trailing `\`. Such a trailing `\` is in fact the only
83 //! situation in which an empty component is produced.
84 //!
85 //! ### Normalization
86 //!
87 //! Aside from splitting on the separator(s), there is a small amount of
88 //! "normalization":
89 //!
90 //! * Repeated separators are ignored: `a/b` and `a//b` both have components `a`
91 //!   and `b`.
92 //!
93 //! * Paths ending in a separator are treated as if they has a current directory
94 //!   component at the end (or, in verbatim paths, an empty component).  For
95 //!   example, while `a/b` has components `a` and `b`, the paths `a/b/` and
96 //!   `a/b/.` both have components `a`, `b`, and `.` (current directory).  The
97 //!   reason for this normalization is that `a/b` and `a/b/` are treated
98 //!   differently in some contexts, but `a/b/` and `a/b/.` are always treated
99 //!   the same.
100 //!
101 //! No other normalization takes place by default. In particular, `a/./b/` and
102 //! `a/b` are treated distinctly in terms of components, as are `a/c` and
103 //! `a/b/../c`. Further normalization is possible to build on top of the
104 //! components APIs, and will be included in this library very soon.
105
106 #![unstable(feature = "path")]
107
108 use core::prelude::*;
109
110 use ascii::*;
111 use borrow::BorrowFrom;
112 use cmp;
113 use iter;
114 use mem;
115 use ops::{self, Deref};
116 use string::CowString;
117 use vec::Vec;
118 use fmt;
119
120 use ffi::{OsStr, OsString, AsOsStr};
121
122 use self::platform::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix};
123
124 ////////////////////////////////////////////////////////////////////////////////
125 // GENERAL NOTES
126 ////////////////////////////////////////////////////////////////////////////////
127 //
128 // Parsing in this module is done by directly transmuting OsStr to [u8] slices,
129 // taking advantage of the fact that OsStr always encodes ASCII characters
130 // as-is.  Eventually, this transmutation should be replaced by direct uses of
131 // OsStr APIs for parsing, but it will take a while for those to become
132 // available.
133
134 ////////////////////////////////////////////////////////////////////////////////
135 // Platform-specific definitions
136 ////////////////////////////////////////////////////////////////////////////////
137
138 // The following modules give the most basic tools for parsing paths on various
139 // platforms. The bulk of the code is devoted to parsing prefixes on Windows.
140
141 #[cfg(unix)]
142 mod platform {
143     use super::Prefix;
144     use core::prelude::*;
145     use ffi::OsStr;
146
147     #[inline]
148     pub fn is_sep_byte(b: u8) -> bool {
149         b == b'/'
150     }
151
152     #[inline]
153     pub fn is_verbatim_sep(b: u8) -> bool {
154         b == b'/'
155     }
156
157     pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
158         None
159     }
160
161     pub const MAIN_SEP_STR: &'static str = "/";
162     pub const MAIN_SEP: char = '/';
163 }
164
165 #[cfg(windows)]
166 mod platform {
167     use core::prelude::*;
168     use ascii::*;
169
170     use char::CharExt as UnicodeCharExt;
171     use super::{os_str_as_u8_slice, u8_slice_as_os_str, Prefix};
172     use ffi::OsStr;
173
174     #[inline]
175     pub fn is_sep_byte(b: u8) -> bool {
176         b == b'/' || b == b'\\'
177     }
178
179     #[inline]
180     pub fn is_verbatim_sep(b: u8) -> bool {
181         b == b'\\'
182     }
183
184     pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> {
185         use super::Prefix::*;
186         unsafe {
187             // The unsafety here stems from converting between &OsStr and &[u8]
188             // and back. This is safe to do because (1) we only look at ASCII
189             // contents of the encoding and (2) new &OsStr values are produced
190             // only from ASCII-bounded slices of existing &OsStr values.
191             let mut path = os_str_as_u8_slice(path);
192
193             if path.starts_with(br"\\") {
194                 // \\
195                 path = &path[2..];
196                 if path.starts_with(br"?\") {
197                     // \\?\
198                     path = &path[2..];
199                     if path.starts_with(br"UNC\") {
200                         // \\?\UNC\server\share
201                         path = &path[4..];
202                         let (server, share) = match parse_two_comps(path, is_verbatim_sep) {
203                             Some((server, share)) => (u8_slice_as_os_str(server),
204                                                       u8_slice_as_os_str(share)),
205                             None => (u8_slice_as_os_str(path),
206                                      u8_slice_as_os_str(&[])),
207                         };
208                         return Some(VerbatimUNC(server, share));
209                     } else {
210                         // \\?\path
211                         let idx = path.position_elem(&b'\\');
212                         if idx == Some(2) && path[1] == b':' {
213                             let c = path[0];
214                             if c.is_ascii() && (c as char).is_alphabetic() {
215                                 // \\?\C:\ path
216                                 return Some(VerbatimDisk(c.to_ascii_uppercase()));
217                             }
218                         }
219                         let slice = &path[.. idx.unwrap_or(path.len())];
220                         return Some(Verbatim(u8_slice_as_os_str(slice)));
221                     }
222                 } else if path.starts_with(b".\\") {
223                     // \\.\path
224                     path = &path[2..];
225                     let slice = &path[.. path.position_elem(&b'\\').unwrap_or(path.len())];
226                     return Some(DeviceNS(u8_slice_as_os_str(slice)));
227                 }
228                 match parse_two_comps(path, is_sep_byte) {
229                     Some((server, share)) if server.len() > 0 && share.len() > 0 => {
230                         // \\server\share
231                         return Some(UNC(u8_slice_as_os_str(server),
232                                         u8_slice_as_os_str(share)));
233                     }
234                     _ => ()
235                 }
236             } else if path.len() > 1 && path[1] == b':' {
237                 // C:
238                 let c = path[0];
239                 if c.is_ascii() && (c as char).is_alphabetic() {
240                     return Some(Disk(c.to_ascii_uppercase()));
241                 }
242             }
243             return None;
244         }
245
246         fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
247             let first = match path.iter().position(|x| f(*x)) {
248                 None => return None,
249                 Some(x) => &path[.. x]
250             };
251             path = &path[(first.len()+1)..];
252             let idx = path.iter().position(|x| f(*x));
253             let second = &path[.. idx.unwrap_or(path.len())];
254             Some((first, second))
255         }
256     }
257
258     pub const MAIN_SEP_STR: &'static str = "\\";
259     pub const MAIN_SEP: char = '\\';
260 }
261
262 ////////////////////////////////////////////////////////////////////////////////
263 // Windows Prefixes
264 ////////////////////////////////////////////////////////////////////////////////
265
266 /// Path prefixes (Windows only).
267 ///
268 /// Windows uses a variety of path styles, including references to drive
269 /// volumes (like `C:`), network shared (like `\\server\share`) and
270 /// others. In addition, some path prefixes are "verbatim", in which case
271 /// `/` is *not* treated as a separator and essentially no normalization is
272 /// performed.
273 #[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
274 pub enum Prefix<'a> {
275     /// Prefix `\\?\`, together with the given component immediately following it.
276     Verbatim(&'a OsStr),
277
278     /// Prefix `\\?\UNC\`, with the "server" and "share" components following it.
279     VerbatimUNC(&'a OsStr, &'a OsStr),
280
281     /// Prefix like `\\?\C:\`, for the given drive letter
282     VerbatimDisk(u8),
283
284     /// Prefix `\\.\`, together with the given component immediately following it.
285     DeviceNS(&'a OsStr),
286
287     /// Prefix `\\server\share`, with the given "server" and "share" components.
288     UNC(&'a OsStr, &'a OsStr),
289
290     /// Prefix `C:` for the given disk drive.
291     Disk(u8),
292 }
293
294 impl<'a> Prefix<'a> {
295     #[inline]
296     fn len(&self) -> usize {
297         use self::Prefix::*;
298         fn os_str_len(s: &OsStr) -> usize {
299             os_str_as_u8_slice(s).len()
300         }
301         match *self {
302             Verbatim(x) => 4 + os_str_len(x),
303             VerbatimUNC(x,y) => 8 + os_str_len(x) +
304                 if os_str_len(y) > 0 { 1 + os_str_len(y) }
305                 else { 0 },
306             VerbatimDisk(_) => 6,
307             UNC(x,y) => 2 + os_str_len(x) +
308                 if os_str_len(y) > 0 { 1 + os_str_len(y) }
309                 else { 0 },
310             DeviceNS(x) => 4 + os_str_len(x),
311             Disk(_) => 2
312         }
313
314     }
315
316     /// Determine if the prefix is verbatim, i.e. begins `\\?\`.
317     #[inline]
318     pub fn is_verbatim(&self) -> bool {
319         use self::Prefix::*;
320         match *self {
321             Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(_, _) => true,
322             _ => false
323         }
324     }
325
326     #[inline]
327     fn is_drive(&self) -> bool {
328         match *self {
329             Prefix::Disk(_) => true,
330             _ => false,
331         }
332     }
333
334     #[inline]
335     fn has_implicit_root(&self) -> bool {
336         !self.is_drive()
337     }
338 }
339
340 ////////////////////////////////////////////////////////////////////////////////
341 // Exposed parsing helpers
342 ////////////////////////////////////////////////////////////////////////////////
343
344 /// Determine whether the character is one of the permitted path
345 /// separators for the current platform.
346 pub fn is_separator(c: char) -> bool {
347     use ascii::*;
348     c.is_ascii() && is_sep_byte(c as u8)
349 }
350
351 /// The primary sperator for the current platform
352 pub const MAIN_SEPARATOR: char = platform::MAIN_SEP;
353
354 ////////////////////////////////////////////////////////////////////////////////
355 // Misc helpers
356 ////////////////////////////////////////////////////////////////////////////////
357
358 // Iterate through `iter` while it matches `prefix`; return `None` if `prefix`
359 // is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving
360 // `iter` after having exhausted `prefix`.
361 fn iter_after<A, I, J>(mut iter: I, mut prefix: J) -> Option<I> where
362     I: Iterator<Item=A> + Clone, J: Iterator<Item=A>, A: PartialEq
363 {
364     loop {
365         let mut iter_next = iter.clone();
366         match (iter_next.next(), prefix.next()) {
367             (Some(x), Some(y)) => {
368                 if x != y { return None }
369             }
370             (Some(_), None) => return Some(iter),
371             (None, None) => return Some(iter),
372             (None, Some(_)) => return None,
373         }
374         iter = iter_next;
375     }
376 }
377
378 // See note at the top of this module to understand why these are used:
379 fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
380     unsafe { mem::transmute(s) }
381 }
382 unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
383     mem::transmute(s)
384 }
385
386 ////////////////////////////////////////////////////////////////////////////////
387 // Cross-platform parsing
388 ////////////////////////////////////////////////////////////////////////////////
389
390 /// Says whether the path ends in a separator character and therefore needs to
391 /// be treated as if it ended with an additional `.`
392 fn has_suffix(s: &[u8], prefix: Option<Prefix>) -> bool {
393     let (prefix_len, verbatim) = if let Some(p) = prefix {
394         (p.len(), p.is_verbatim())
395     } else { (0, false) };
396     if prefix_len > 0 && prefix_len == s.len() && !verbatim { return true; }
397     let mut splits = s[prefix_len..].split(|b| is_sep_byte(*b));
398     let last = splits.next_back().unwrap();
399     let more = splits.next_back().is_some();
400     more && last == b""
401 }
402
403 /// Says whether the first byte after the prefix is a separator.
404 fn has_physical_root(s: &[u8], prefix: Option<Prefix>) -> bool {
405     let path = if let Some(p) = prefix { &s[p.len()..] } else { s };
406     path.len() > 0 && is_sep_byte(path[0])
407 }
408
409 fn parse_single_component(comp: &[u8]) -> Option<Component> {
410     match comp {
411         b"." => Some(Component::CurDir),
412         b".." => Some(Component::ParentDir),
413         b"" => None,
414         _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) }))
415     }
416 }
417
418 // basic workhorse for splitting stem and extension
419 #[allow(unused_unsafe)] // FIXME
420 fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
421     unsafe {
422         if os_str_as_u8_slice(file) == b".." { return (Some(file), None) }
423
424         // The unsafety here stems from converting between &OsStr and &[u8]
425         // and back. This is safe to do because (1) we only look at ASCII
426         // contents of the encoding and (2) new &OsStr values are produced
427         // only from ASCII-bounded slices of existing &OsStr values.
428
429         let mut iter = os_str_as_u8_slice(file).rsplitn(1, |b| *b == b'.');
430         let after = iter.next();
431         let before = iter.next();
432         if before == Some(b"") {
433             (Some(file), None)
434         } else {
435             (before.map(|s| u8_slice_as_os_str(s)),
436              after.map(|s| u8_slice_as_os_str(s)))
437         }
438     }
439 }
440
441 ////////////////////////////////////////////////////////////////////////////////
442 // The core iterators
443 ////////////////////////////////////////////////////////////////////////////////
444
445 /// Component parsing works by a double-ended state machine; the cursors at the
446 /// front and back of the path each keep track of what parts of the path have
447 /// been consumed so far.
448 ///
449 /// Going front to back, a path is made up of a prefix, a root component, a body
450 /// (of normal components), and a suffix/emptycomponent (normalized `.` or ``
451 /// for a path ending with the separator)
452 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
453 enum State {
454     Prefix = 0,         // c:
455     Root = 1,           // /
456     Body = 2,           // foo/bar/baz
457     Suffix = 3,         // .
458     Done = 4,
459 }
460
461 /// A single component of a path.
462 ///
463 /// See the module documentation for an in-depth explanation of components and
464 /// their role in the API.
465 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
466 pub enum Component<'a> {
467     /// A Windows path prefix, e.g. `C:` or `\server\share`.
468     ///
469     /// Does not occur on Unix.
470     Prefix {
471         /// The prefix as an unparsed `OsStr` slice.
472         raw: &'a OsStr,
473
474         /// The parsed prefix data.
475         parsed: Prefix<'a>
476     },
477
478     /// An empty component. Only used on Windows for the last component of
479     /// verbatim paths ending with a separator (e.g. the last component of
480     /// `\\?\C:\windows\` but not `\\?\C:\windows` or `C:\windows`).
481     Empty,
482
483     /// The root directory component, appears after any prefix and before anything else
484     RootDir,
485
486     /// A reference to the current directory, i.e. `.`
487     CurDir,
488
489     /// A reference to the parent directory, i.e. `..`
490     ParentDir,
491
492     /// A normal component, i.e. `a` and `b` in `a/b`
493     Normal(&'a OsStr),
494 }
495
496 impl<'a> Component<'a> {
497     /// Extract the underlying `OsStr` slice
498     pub fn as_os_str(self) -> &'a OsStr {
499         match self {
500             Component::Prefix { raw, .. } => &raw,
501             Component::Empty => OsStr::from_str(""),
502             Component::RootDir => OsStr::from_str(MAIN_SEP_STR),
503             Component::CurDir => OsStr::from_str("."),
504             Component::ParentDir => OsStr::from_str(".."),
505             Component::Normal(path) => path,
506         }
507     }
508 }
509
510 /// The core iterator giving the components of a path.
511 ///
512 /// See the module documentation for an in-depth explanation of components and
513 /// their role in the API.
514 #[derive(Clone)]
515 pub struct Components<'a> {
516     // The path left to parse components from
517     path: &'a [u8],
518
519     // The prefix as it was originally parsed, if any
520     prefix: Option<Prefix<'a>>,
521
522     // true if path *physically* has a root separator; for most Windows
523     // prefixes, it may have a "logical" rootseparator for the purposes of
524     // normalization, e.g.  \\server\share == \\server\share\.
525     has_physical_root: bool,
526
527     // The iterator is double-ended, and these two states keep track of what has
528     // been produced from either end
529     front: State,
530     back: State,
531 }
532
533 /// An iterator over the components of a path, as `OsStr` slices.
534 #[derive(Clone)]
535 pub struct Iter<'a> {
536     inner: Components<'a>
537 }
538
539 impl<'a> Components<'a> {
540     // how long is the prefix, if any?
541     #[inline]
542     fn prefix_len(&self) -> usize {
543         self.prefix.as_ref().map(Prefix::len).unwrap_or(0)
544     }
545
546     #[inline]
547     fn prefix_verbatim(&self) -> bool {
548         self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false)
549     }
550
551     /// how much of the prefix is left from the point of view of iteration?
552     #[inline]
553     fn prefix_remaining(&self) -> usize {
554         if self.front == State::Prefix { self.prefix_len() }
555         else { 0 }
556     }
557
558     fn prefix_and_root(&self) -> usize {
559         let root = if self.front <= State::Root && self.has_physical_root { 1 } else { 0 };
560         self.prefix_remaining() + root
561     }
562
563     // is the iteration complete?
564     #[inline]
565     fn finished(&self) -> bool {
566         self.front == State::Done || self.back == State::Done || self.front > self.back
567     }
568
569     #[inline]
570     fn is_sep_byte(&self, b: u8) -> bool {
571         if self.prefix_verbatim() {
572             is_verbatim_sep(b)
573         } else {
574             is_sep_byte(b)
575         }
576     }
577
578     /// Extract a slice corresponding to the portion of the path remaining for iteration.
579     pub fn as_path(&self) -> &'a Path {
580         let mut comps = self.clone();
581         if comps.front == State::Body { comps.trim_left(); }
582         if comps.back == State::Body { comps.trim_right(); }
583         if comps.path.is_empty() && comps.front < comps.back && comps.back == State::Suffix {
584             Path::new(".")
585         } else {
586             unsafe { Path::from_u8_slice(comps.path) }
587         }
588     }
589
590     /// Is the *original* path rooted?
591     fn has_root(&self) -> bool {
592         if self.has_physical_root { return true }
593         if let Some(p) = self.prefix {
594             if p.has_implicit_root() { return true }
595         }
596         false
597     }
598
599     // parse a component from the left, saying how many bytes to consume to
600     // remove the component
601     fn parse_next_component(&self) -> (usize, Option<Component<'a>>) {
602         debug_assert!(self.front == State::Body);
603         let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) {
604             None => (0, self.path),
605             Some(i) => (1, &self.path[.. i]),
606         };
607         (comp.len() + extra, parse_single_component(comp))
608     }
609
610     // parse a component from the right, saying how many bytes to consume to
611     // remove the component
612     fn parse_next_component_back(&self) -> (usize, Option<Component<'a>>) {
613         debug_assert!(self.back == State::Body);
614         let start = self.prefix_and_root();
615         let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) {
616             None => (0, &self.path[start ..]),
617             Some(i) => (1, &self.path[start + i + 1 ..]),
618         };
619         (comp.len() + extra, parse_single_component(comp))
620     }
621
622     // trim away repeated separators (i.e. emtpy components) on the left
623     fn trim_left(&mut self) {
624         while !self.path.is_empty() {
625             let (size, comp) = self.parse_next_component();
626             if comp.is_some() {
627                 return;
628             } else {
629                 self.path = &self.path[size ..];
630             }
631         }
632     }
633
634     // trim away repeated separators (i.e. emtpy components) on the right
635     fn trim_right(&mut self) {
636         while self.path.len() > self.prefix_and_root() {
637             let (size, comp) = self.parse_next_component_back();
638             if comp.is_some() {
639                 return;
640             } else {
641                 self.path = &self.path[.. self.path.len() - size];
642             }
643         }
644     }
645
646     /// Examine the next component without consuming it.
647     pub fn peek(&self) -> Option<Component<'a>> {
648         self.clone().next()
649     }
650 }
651
652 impl<'a> Iter<'a> {
653     /// Extract a slice corresponding to the portion of the path remaining for iteration.
654     pub fn as_path(&self) -> &'a Path {
655         self.inner.as_path()
656     }
657 }
658
659 impl<'a> Iterator for Iter<'a> {
660     type Item = &'a OsStr;
661
662     fn next(&mut self) -> Option<&'a OsStr> {
663         self.inner.next().map(Component::as_os_str)
664     }
665 }
666
667 impl<'a> DoubleEndedIterator for Iter<'a> {
668     fn next_back(&mut self) -> Option<&'a OsStr> {
669         self.inner.next_back().map(Component::as_os_str)
670     }
671 }
672
673 impl<'a> Iterator for Components<'a> {
674     type Item = Component<'a>;
675
676     fn next(&mut self) -> Option<Component<'a>> {
677         while !self.finished() {
678             match self.front {
679                 State::Prefix if self.prefix_len() > 0 => {
680                     self.front = State::Root;
681                     debug_assert!(self.prefix_len() <= self.path.len());
682                     let raw = &self.path[.. self.prefix_len()];
683                     self.path = &self.path[self.prefix_len() .. ];
684                     return Some(Component::Prefix {
685                         raw: unsafe { u8_slice_as_os_str(raw) },
686                         parsed: self.prefix.unwrap()
687                     })
688                 }
689                 State::Prefix => {
690                     self.front = State::Root;
691                 }
692                 State::Root => {
693                     self.front = State::Body;
694                     if self.has_physical_root {
695                         debug_assert!(self.path.len() > 0);
696                         self.path = &self.path[1..];
697                         return Some(Component::RootDir)
698                     } else if let Some(p) = self.prefix {
699                         if p.has_implicit_root() && !p.is_verbatim() {
700                             return Some(Component::RootDir)
701                         }
702                     }
703                 }
704                 State::Body if !self.path.is_empty() => {
705                     let (size, comp) = self.parse_next_component();
706                     self.path = &self.path[size ..];
707                     if comp.is_some() { return comp }
708                 }
709                 State::Body => {
710                     self.front = State::Suffix;
711                 }
712                 State::Suffix => {
713                     self.front = State::Done;
714                     if self.prefix_verbatim() {
715                         return Some(Component::Empty)
716                     } else {
717                         return Some(Component::CurDir)
718                     }
719                 }
720                 State::Done => unreachable!()
721             }
722         }
723         None
724     }
725 }
726
727 impl<'a> DoubleEndedIterator for Components<'a> {
728     fn next_back(&mut self) -> Option<Component<'a>> {
729         while !self.finished() {
730             match self.back {
731                 State::Suffix => {
732                     self.back = State::Body;
733                     if self.prefix_verbatim() {
734                         return Some(Component::Empty)
735                     } else {
736                         return Some(Component::CurDir)
737                     }
738                 }
739                 State::Body if self.path.len() > self.prefix_and_root() => {
740                     let (size, comp) = self.parse_next_component_back();
741                     self.path = &self.path[.. self.path.len() - size];
742                     if comp.is_some() { return comp }
743                 }
744                 State::Body => {
745                     self.back = State::Root;
746                 }
747                 State::Root => {
748                     self.back = State::Prefix;
749                     if self.has_physical_root {
750                         self.path = &self.path[.. self.path.len() - 1];
751                         return Some(Component::RootDir)
752                     } else if let Some(p) = self.prefix {
753                         if p.has_implicit_root() && !p.is_verbatim() {
754                             return Some(Component::RootDir)
755                         }
756                     }
757                 }
758                 State::Prefix if self.prefix_len() > 0 => {
759                     self.back = State::Done;
760                     return Some(Component::Prefix {
761                         raw: unsafe { u8_slice_as_os_str(self.path) },
762                         parsed: self.prefix.unwrap()
763                     })
764                 }
765                 State::Prefix => {
766                     self.back = State::Done;
767                     return None
768                 }
769                 State::Done => unreachable!()
770             }
771         }
772         None
773     }
774 }
775
776 fn optional_path(path: &Path) -> Option<&Path> {
777     if path.as_u8_slice().is_empty() { None } else { Some(path) }
778 }
779
780 impl<'a> cmp::PartialEq for Components<'a> {
781     fn eq(&self, other: &Components<'a>) -> bool {
782         iter::order::eq(self.clone(), other.clone())
783     }
784 }
785
786 impl<'a> cmp::Eq for Components<'a> {}
787
788 impl<'a> cmp::PartialOrd for Components<'a> {
789     fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> {
790         iter::order::partial_cmp(self.clone(), other.clone())
791     }
792 }
793
794 impl<'a> cmp::Ord for Components<'a> {
795     fn cmp(&self, other: &Components<'a>) -> cmp::Ordering {
796         iter::order::cmp(self.clone(), other.clone())
797     }
798 }
799
800 ////////////////////////////////////////////////////////////////////////////////
801 // Basic types and traits
802 ////////////////////////////////////////////////////////////////////////////////
803
804 /// An owned, mutable path (akin to `String`).
805 ///
806 /// This type provides methods like `push` and `set_extension` that mutate the
807 /// path in place. It also implements `Deref` to `Path`, meaning that all
808 /// methods on `Path` slices are available on `PathBuf` values as well.
809 ///
810 /// More details about the overall approach can be found in
811 /// the module documentation.
812 ///
813 /// # Example
814 ///
815 /// ```rust
816 /// use std::path::PathBuf;
817 ///
818 /// let mut path = PathBuf::new("c:\\");
819 /// path.push("windows");
820 /// path.push("system32");
821 /// path.set_extension("dll");
822 /// ```
823 #[derive(Clone, Hash)]
824 pub struct PathBuf {
825     inner: OsString
826 }
827
828 impl PathBuf {
829     fn as_mut_vec(&mut self) -> &mut Vec<u8> {
830         unsafe { mem::transmute(self) }
831     }
832
833     /// Allocate a `PathBuf` with initial contents given by the
834     /// argument.
835     pub fn new<S: ?Sized + AsOsStr>(s: &S) -> PathBuf {
836         PathBuf { inner: s.as_os_str().to_os_string() }
837     }
838
839     /// Extend `self` with `path`.
840     ///
841     /// If `path` is absolute, it replaces the current path.
842     ///
843     /// On Windows:
844     ///
845     /// * if `path` has a root but no prefix (e.g. `\windows`), it
846     ///   replaces everything except for the prefix (if any) of `self`.
847     /// * if `path` has a prefix but no root, it replaces `self.
848     pub fn push<P: ?Sized>(&mut self, path: &P) where P: AsPath {
849         // in general, a separator is needed if the rightmost byte is not a separator
850         let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
851
852         // in the special case of `C:` on Windows, do *not* add a separator
853         {
854             let comps = self.components();
855             if comps.prefix_len() > 0 &&
856                 comps.prefix_len() == comps.path.len() &&
857                 comps.prefix.unwrap().is_drive()
858             {
859                 need_sep = false
860             }
861         }
862
863         let path = path.as_path();
864
865         // absolute `path` replaces `self`
866         if path.is_absolute() || path.prefix().is_some() {
867             self.as_mut_vec().truncate(0);
868
869         // `path` has a root but no prefix, e.g. `\windows` (Windows only)
870         } else if path.has_root() {
871             let prefix_len = self.components().prefix_remaining();
872             self.as_mut_vec().truncate(prefix_len);
873
874         // `path` is a pure relative path
875         } else if need_sep {
876             self.inner.push_os_str(OsStr::from_str(MAIN_SEP_STR));
877         }
878
879         self.inner.push_os_str(path.as_os_str());
880     }
881
882     /// Truncate `self` to `self.parent()`.
883     ///
884     /// Returns `false` and does nothing if `self.parent()` is `None`.
885     /// Otherwise, returns `true`.
886     pub fn pop(&mut self) -> bool {
887         match self.parent().map(|p| p.as_u8_slice().len()) {
888             Some(len) => {
889                 self.as_mut_vec().truncate(len);
890                 true
891             }
892             None => false
893         }
894     }
895
896     /// Updates `self.file_name()` to `file_name`.
897     ///
898     /// If `self.file_name()` was `None`, this is equivalent to pushing
899     /// `file_name`.
900     ///
901     /// # Examples
902     ///
903     /// ```rust
904     /// use std::path::{Path, PathBuf};
905     ///
906     /// let mut buf = PathBuf::new("/foo/");
907     /// assert!(buf.file_name() == None);
908     /// buf.set_file_name("bar");
909     /// assert!(buf == PathBuf::new("/foo/bar"));
910     /// assert!(buf.file_name().is_some());
911     /// buf.set_file_name("baz.txt");
912     /// assert!(buf == PathBuf::new("/foo/baz.txt"));
913     /// ```
914     pub fn set_file_name<S: ?Sized>(&mut self, file_name: &S) where S: AsOsStr {
915         if self.file_name().is_some() && !self.pop() {
916             // Given that there is a file name, this is reachable only for
917             // Windows paths like c:file or paths like `foo`, but not `c:\` or
918             // `/`.
919             let prefix_len = self.components().prefix_remaining();
920             self.as_mut_vec().truncate(prefix_len);
921         }
922         self.push(file_name.as_os_str());
923     }
924
925     /// Updates `self.extension()` to `extension`.
926     ///
927     /// If `self.file_name()` is `None`, does nothing and returns `false`.
928     ///
929     /// Otherwise, returns `true`; if `self.extension()` is `None`, the extension
930     /// is added; otherwise it is replaced.
931     pub fn set_extension<S: ?Sized + AsOsStr>(&mut self, extension: &S) -> bool {
932         if self.file_name().is_none() { return false; }
933
934         let mut stem = match self.file_stem() {
935             Some(stem) => stem.to_os_string(),
936             None => OsString::from_str(""),
937         };
938
939         let extension = extension.as_os_str();
940         if os_str_as_u8_slice(extension).len() > 0 {
941             stem.push_os_str(OsStr::from_str("."));
942             stem.push_os_str(extension.as_os_str());
943         }
944         self.set_file_name(&stem);
945
946         true
947     }
948
949     /// Consume the `PathBuf`, yielding its internal `OsString` storage
950     pub fn into_os_string(self) -> OsString {
951         self.inner
952     }
953 }
954
955 impl<'a, P: ?Sized + 'a> iter::FromIterator<&'a P> for PathBuf where P: AsPath {
956     fn from_iter<I: Iterator<Item = &'a P>>(iter: I) -> PathBuf {
957         let mut buf = PathBuf::new("");
958         buf.extend(iter);
959         buf
960     }
961 }
962
963 impl<'a, P: ?Sized + 'a> iter::Extend<&'a P> for PathBuf where P: AsPath {
964     fn extend<I: Iterator<Item = &'a P>>(&mut self, iter: I) {
965         for p in iter {
966             self.push(p)
967         }
968     }
969 }
970
971 impl fmt::Debug for PathBuf {
972     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
973         fmt::Debug::fmt(&**self, formatter)
974     }
975 }
976
977 impl ops::Deref for PathBuf {
978     type Target = Path;
979
980     fn deref(&self) -> &Path {
981         unsafe { mem::transmute(&self.inner[]) }
982     }
983 }
984
985 impl BorrowFrom<PathBuf> for Path {
986     fn borrow_from(owned: &PathBuf) -> &Path {
987         owned.deref()
988     }
989 }
990
991 impl cmp::PartialEq for PathBuf {
992     fn eq(&self, other: &PathBuf) -> bool {
993         self.components() == other.components()
994     }
995 }
996
997 impl cmp::Eq for PathBuf {}
998
999 impl cmp::PartialOrd for PathBuf {
1000     fn partial_cmp(&self, other: &PathBuf) -> Option<cmp::Ordering> {
1001         self.components().partial_cmp(&other.components())
1002     }
1003 }
1004
1005 impl cmp::Ord for PathBuf {
1006     fn cmp(&self, other: &PathBuf) -> cmp::Ordering {
1007         self.components().cmp(&other.components())
1008     }
1009 }
1010
1011 impl AsOsStr for PathBuf {
1012     fn as_os_str(&self) -> &OsStr {
1013         &self.inner[]
1014     }
1015 }
1016
1017 /// A slice of a path (akin to `str`).
1018 ///
1019 /// This type supports a number of operations for inspecting a path, including
1020 /// breaking the path into its components (separated by `/` or `\`, depending on
1021 /// the platform), extracting the file name, determining whether the path is
1022 /// absolute, and so on. More details about the overall approach can be found in
1023 /// the module documentation.
1024 ///
1025 /// This is an *unsized* type, meaning that it must always be used with behind a
1026 /// pointer like `&` or `Box`.
1027 ///
1028 /// # Example
1029 ///
1030 /// ```rust
1031 /// use std::path::Path;
1032 ///
1033 /// let path = Path::new("/tmp/foo/bar.txt");
1034 /// let file = path.file_name();
1035 /// let extension = path.extension();
1036 /// let parent_dir = path.parent();
1037 /// ```
1038 ///
1039 #[derive(Hash)]
1040 pub struct Path {
1041     inner: OsStr
1042 }
1043
1044 impl Path {
1045     // The following (private!) function allows construction of a path from a u8
1046     // slice, which is only safe when it is known to follow the OsStr encoding.
1047     unsafe fn from_u8_slice(s: &[u8]) -> &Path {
1048         mem::transmute(s)
1049     }
1050     // The following (private!) function reveals the byte encoding used for OsStr.
1051     fn as_u8_slice(&self) -> &[u8] {
1052         unsafe { mem::transmute(self) }
1053     }
1054
1055     /// Directly wrap a string slice as a `Path` slice.
1056     ///
1057     /// This is a cost-free conversion.
1058     pub fn new<S: ?Sized + AsOsStr>(s: &S) -> &Path {
1059         unsafe { mem::transmute(s.as_os_str()) }
1060     }
1061
1062     /// Yield a `&str` slice if the `Path` is valid unicode.
1063     ///
1064     /// This conversion may entail doing a check for UTF-8 validity.
1065     pub fn to_str(&self) -> Option<&str> {
1066         self.inner.to_str()
1067     }
1068
1069     /// Convert a `Path` to a `CowString`.
1070     ///
1071     /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
1072     pub fn to_string_lossy(&self) -> CowString {
1073         self.inner.to_string_lossy()
1074     }
1075
1076     /// Convert a `Path` to an owned `PathBuf`.
1077     pub fn to_path_buf(&self) -> PathBuf {
1078         PathBuf::new(self)
1079     }
1080
1081     /// A path is *absolute* if it is independent of the current directory.
1082     ///
1083     /// * On Unix, a path is absolute if it starts with the root, so
1084     /// `is_absolute` and `has_root` are equivalent.
1085     ///
1086     /// * On Windows, a path is absolute if it has a prefix and starts with the
1087     /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. In
1088     /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`.
1089     pub fn is_absolute(&self) -> bool {
1090         self.has_root() &&
1091             (cfg!(unix) || self.prefix().is_some())
1092     }
1093
1094     /// A path is *relative* if it is not absolute.
1095     pub fn is_relative(&self) -> bool {
1096         !self.is_absolute()
1097     }
1098
1099     /// Returns the *prefix* of a path, if any.
1100     ///
1101     /// Prefixes are relevant only for Windows paths, and consist of volumes
1102     /// like `C:`, UNC prefixes like `\\server`, and others described in more
1103     /// detail in `std::os::windows::PathExt`.
1104     pub fn prefix(&self) -> Option<&Path> {
1105         let iter = self.components();
1106         optional_path(unsafe {
1107             Path::from_u8_slice(
1108                 &self.as_u8_slice()[.. iter.prefix_remaining()])
1109         })
1110     }
1111
1112     /// A path has a root if the body of the path begins with the directory separator.
1113     ///
1114     /// * On Unix, a path has a root if it begins with `/`.
1115     ///
1116     /// * On Windows, a path has a root if it:
1117     ///     * has no prefix and begins with a separator, e.g. `\\windows`
1118     ///     * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows`
1119     ///     * has any non-disk prefix, e.g. `\\server\share`
1120     pub fn has_root(&self) -> bool {
1121          self.components().has_root()
1122     }
1123
1124     /// The path without its final component.
1125     ///
1126     /// Does nothing, returning `None` if the path consists of just a prefix
1127     /// and/or root directory reference.
1128     ///
1129     /// # Examples
1130     ///
1131     /// ```rust
1132     /// use std::path::Path;
1133     ///
1134     /// let path = Path::new("/foo/bar");
1135     /// let foo = path.parent().unwrap();
1136     /// assert!(foo == Path::new("/foo"));
1137     /// let root = foo.parent().unwrap();
1138     /// assert!(root == Path::new("/"));
1139     /// assert!(root.parent() == None);
1140     /// ```
1141     pub fn parent(&self) -> Option<&Path> {
1142         let mut comps = self.components();
1143         let comp = comps.next_back();
1144         let rest = optional_path(comps.as_path());
1145
1146         match (comp, comps.next_back()) {
1147             (Some(Component::CurDir), Some(Component::RootDir)) => None,
1148             (Some(Component::CurDir), Some(Component::Prefix { .. })) => None,
1149             (Some(Component::Empty), Some(Component::RootDir)) => None,
1150             (Some(Component::Empty), Some(Component::Prefix { .. })) => None,
1151             (Some(Component::Prefix { .. }), None) => None,
1152             (Some(Component::RootDir), Some(Component::Prefix { .. })) => None,
1153             _ => rest
1154         }
1155     }
1156
1157     /// The final component of the path, if it is a normal file.
1158     ///
1159     /// If the path terminates in `.`, `..`, or consists solely or a root of
1160     /// prefix, `file` will return `None`.
1161     pub fn file_name(&self) -> Option<&OsStr> {
1162         self.components().next_back().and_then(|p| match p {
1163             Component::Normal(p) => Some(p.as_os_str()),
1164             _ => None
1165         })
1166     }
1167
1168     /// Returns a path that, when joined onto `base`, yields `self`.
1169     pub fn relative_from<'a, P: ?Sized>(&'a self, base: &'a P) -> Option<&Path> where
1170         P: AsPath
1171     {
1172         iter_after(self.components(), base.as_path().components()).map(|c| c.as_path())
1173     }
1174
1175     /// Determines whether `base` is a prefix of `self`.
1176     pub fn starts_with<P: ?Sized>(&self, base: &P) -> bool where P: AsPath {
1177         iter_after(self.components(), base.as_path().components()).is_some()
1178     }
1179
1180     /// Determines whether `base` is a suffix of `self`.
1181     pub fn ends_with<P: ?Sized>(&self, child: &P) -> bool where P: AsPath {
1182         iter_after(self.components().rev(), child.as_path().components().rev()).is_some()
1183     }
1184
1185     /// Extract the stem (non-extension) portion of `self.file()`.
1186     ///
1187     /// The stem is:
1188     ///
1189     /// * None, if there is no file name;
1190     /// * The entire file name if there is no embedded `.`;
1191     /// * The entire file name if the file name begins with `.` and has no other `.`s within;
1192     /// * Otherwise, the portion of the file name before the final `.`
1193     pub fn file_stem(&self) -> Option<&OsStr> {
1194         self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after))
1195     }
1196
1197     /// Extract the extension of `self.file()`, if possible.
1198     ///
1199     /// The extension is:
1200     ///
1201     /// * None, if there is no file name;
1202     /// * None, if there is no embedded `.`;
1203     /// * None, if the file name begins with `.` and has no other `.`s within;
1204     /// * Otherwise, the portion of the file name after the final `.`
1205     pub fn extension(&self) -> Option<&OsStr> {
1206         self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after))
1207     }
1208
1209     /// Creates an owned `PathBuf` with `path` adjoined to `self`.
1210     ///
1211     /// See `PathBuf::push` for more details on what it means to adjoin a path.
1212     pub fn join<P: ?Sized>(&self, path: &P) -> PathBuf where P: AsPath {
1213         let mut buf = self.to_path_buf();
1214         buf.push(path);
1215         buf
1216     }
1217
1218     /// Creates an owned `PathBuf` like `self` but with the given file name.
1219     ///
1220     /// See `PathBuf::set_file_name` for more details.
1221     pub fn with_file_name<S: ?Sized>(&self, file_name: &S) -> PathBuf where S: AsOsStr {
1222         let mut buf = self.to_path_buf();
1223         buf.set_file_name(file_name);
1224         buf
1225     }
1226
1227     /// Creates an owned `PathBuf` like `self` but with the given extension.
1228     ///
1229     /// See `PathBuf::set_extension` for more details.
1230     pub fn with_extension<S: ?Sized>(&self, extension: &S) -> PathBuf where S: AsOsStr {
1231         let mut buf = self.to_path_buf();
1232         buf.set_extension(extension);
1233         buf
1234     }
1235
1236     /// Produce an iterator over the components of the path.
1237     pub fn components(&self) -> Components {
1238         let prefix = parse_prefix(self.as_os_str());
1239         Components {
1240             path: self.as_u8_slice(),
1241             prefix: prefix,
1242             has_physical_root: has_physical_root(self.as_u8_slice(), prefix),
1243             front: State::Prefix,
1244             back: if has_suffix(self.as_u8_slice(), prefix) { State::Suffix }
1245                   else { State::Body },
1246         }
1247     }
1248
1249     /// Produce an iterator over the path's components viewed as `OsStr` slices.
1250     pub fn iter(&self) -> Iter {
1251         Iter { inner: self.components() }
1252     }
1253
1254     /// Returns an object that implements `Display` for safely printing paths
1255     /// that may contain non-Unicode data.
1256     pub fn display(&self) -> Display {
1257         Display { path: self }
1258     }
1259 }
1260
1261 impl AsOsStr for Path {
1262     fn as_os_str(&self) -> &OsStr {
1263         &self.inner
1264     }
1265 }
1266
1267 impl fmt::Debug for Path {
1268     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
1269         self.inner.fmt(formatter)
1270     }
1271 }
1272
1273 /// Helper struct for safely printing paths with `format!()` and `{}`
1274 pub struct Display<'a> {
1275     path: &'a Path
1276 }
1277
1278 impl<'a> fmt::Debug for Display<'a> {
1279     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1280         fmt::Debug::fmt(&self.path.to_string_lossy(), f)
1281     }
1282 }
1283
1284 impl<'a> fmt::Display for Display<'a> {
1285     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1286         fmt::Display::fmt(&self.path.to_string_lossy(), f)
1287     }
1288 }
1289
1290 impl cmp::PartialEq for Path {
1291     fn eq(&self, other: &Path) -> bool {
1292         iter::order::eq(self.components(), other.components())
1293     }
1294 }
1295
1296 impl cmp::Eq for Path {}
1297
1298 impl cmp::PartialOrd for Path {
1299     fn partial_cmp(&self, other: &Path) -> Option<cmp::Ordering> {
1300         self.components().partial_cmp(&other.components())
1301     }
1302 }
1303
1304 impl cmp::Ord for Path {
1305     fn cmp(&self, other: &Path) -> cmp::Ordering {
1306         self.components().cmp(&other.components())
1307     }
1308 }
1309
1310 /// Freely convertible to a `Path`.
1311 pub trait AsPath {
1312     /// Convert to a `Path`.
1313     fn as_path(&self) -> &Path;
1314 }
1315
1316 impl<T: AsOsStr + ?Sized> AsPath for T {
1317     fn as_path(&self) -> &Path { Path::new(self.as_os_str()) }
1318 }
1319
1320 #[cfg(test)]
1321 mod tests {
1322     use super::*;
1323     use ffi::OsStr;
1324     use core::prelude::*;
1325     use string::{ToString, String};
1326     use vec::Vec;
1327
1328     macro_rules! t(
1329         ($path:expr, iter: $iter:expr) => (
1330             {
1331                 let path = Path::new($path);
1332
1333                 // Forward iteration
1334                 let comps = path.iter()
1335                     .map(|p| p.to_string_lossy().into_owned())
1336                     .collect::<Vec<String>>();
1337                 let exp: &[&str] = &$iter;
1338                 let exps = exp.iter().map(|s| s.to_string()).collect::<Vec<String>>();
1339                 assert!(comps == exps, "iter: Expected {:?}, found {:?}",
1340                         exps, comps);
1341
1342                 // Reverse iteration
1343                 let comps = Path::new($path).iter().rev()
1344                     .map(|p| p.to_string_lossy().into_owned())
1345                     .collect::<Vec<String>>();
1346                 let exps = exps.into_iter().rev().collect::<Vec<String>>();
1347                 assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}",
1348                         exps, comps);
1349             }
1350         );
1351
1352         ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => (
1353             {
1354                 let path = Path::new($path);
1355
1356                 let act_root = path.has_root();
1357                 assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}",
1358                         $has_root, act_root);
1359
1360                 let act_abs = path.is_absolute();
1361                 assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}",
1362                         $is_absolute, act_abs);
1363             }
1364         );
1365
1366         ($path:expr, parent: $parent:expr, file_name: $file:expr) => (
1367             {
1368                 let path = Path::new($path);
1369
1370                 let parent = path.parent().map(|p| p.to_str().unwrap());
1371                 let exp_parent: Option<&str> = $parent;
1372                 assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}",
1373                         exp_parent, parent);
1374
1375                 let file = path.file_name().map(|p| p.to_str().unwrap());
1376                 let exp_file: Option<&str> = $file;
1377                 assert!(file == exp_file, "file_name: Expected {:?}, found {:?}",
1378                         exp_file, file);
1379             }
1380         );
1381
1382         ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => (
1383             {
1384                 let path = Path::new($path);
1385
1386                 let stem = path.file_stem().map(|p| p.to_str().unwrap());
1387                 let exp_stem: Option<&str> = $file_stem;
1388                 assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}",
1389                         exp_stem, stem);
1390
1391                 let ext = path.extension().map(|p| p.to_str().unwrap());
1392                 let exp_ext: Option<&str> = $extension;
1393                 assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}",
1394                         exp_ext, ext);
1395             }
1396         );
1397
1398         ($path:expr, iter: $iter:expr,
1399                      has_root: $has_root:expr, is_absolute: $is_absolute:expr,
1400                      parent: $parent:expr, file_name: $file:expr,
1401                      file_stem: $file_stem:expr, extension: $extension:expr) => (
1402             {
1403                 t!($path, iter: $iter);
1404                 t!($path, has_root: $has_root, is_absolute: $is_absolute);
1405                 t!($path, parent: $parent, file_name: $file);
1406                 t!($path, file_stem: $file_stem, extension: $extension);
1407             }
1408         );
1409     );
1410
1411     #[test]
1412     #[cfg(unix)]
1413     pub fn test_decompositions_unix() {
1414         t!("",
1415            iter: [],
1416            has_root: false,
1417            is_absolute: false,
1418            parent: None,
1419            file_name: None,
1420            file_stem: None,
1421            extension: None
1422            );
1423
1424         t!("foo",
1425            iter: ["foo"],
1426            has_root: false,
1427            is_absolute: false,
1428            parent: None,
1429            file_name: Some("foo"),
1430            file_stem: Some("foo"),
1431            extension: None
1432            );
1433
1434         t!("/",
1435            iter: ["/", "."],
1436            has_root: true,
1437            is_absolute: true,
1438            parent: None,
1439            file_name: None,
1440            file_stem: None,
1441            extension: None
1442            );
1443
1444         t!("/foo",
1445            iter: ["/", "foo"],
1446            has_root: true,
1447            is_absolute: true,
1448            parent: Some("/"),
1449            file_name: Some("foo"),
1450            file_stem: Some("foo"),
1451            extension: None
1452            );
1453
1454         t!("foo/",
1455            iter: ["foo", "."],
1456            has_root: false,
1457            is_absolute: false,
1458            parent: Some("foo"),
1459            file_name: None,
1460            file_stem: None,
1461            extension: None
1462            );
1463
1464         t!("/foo/",
1465            iter: ["/", "foo", "."],
1466            has_root: true,
1467            is_absolute: true,
1468            parent: Some("/foo"),
1469            file_name: None,
1470            file_stem: None,
1471            extension: None
1472            );
1473
1474         t!("foo/bar",
1475            iter: ["foo", "bar"],
1476            has_root: false,
1477            is_absolute: false,
1478            parent: Some("foo"),
1479            file_name: Some("bar"),
1480            file_stem: Some("bar"),
1481            extension: None
1482            );
1483
1484         t!("/foo/bar",
1485            iter: ["/", "foo", "bar"],
1486            has_root: true,
1487            is_absolute: true,
1488            parent: Some("/foo"),
1489            file_name: Some("bar"),
1490            file_stem: Some("bar"),
1491            extension: None
1492            );
1493
1494         t!("///foo///",
1495            iter: ["/", "foo", "."],
1496            has_root: true,
1497            is_absolute: true,
1498            parent: Some("///foo"),
1499            file_name: None,
1500            file_stem: None,
1501            extension: None
1502            );
1503
1504         t!("///foo///bar",
1505            iter: ["/", "foo", "bar"],
1506            has_root: true,
1507            is_absolute: true,
1508            parent: Some("///foo"),
1509            file_name: Some("bar"),
1510            file_stem: Some("bar"),
1511            extension: None
1512            );
1513
1514         t!("./.",
1515            iter: [".", "."],
1516            has_root: false,
1517            is_absolute: false,
1518            parent: Some("."),
1519            file_name: None,
1520            file_stem: None,
1521            extension: None
1522            );
1523
1524         t!("./.",
1525            iter: [".", "."],
1526            has_root: false,
1527            is_absolute: false,
1528            parent: Some("."),
1529            file_name: None,
1530            file_stem: None,
1531            extension: None
1532            );
1533
1534         t!("/..",
1535            iter: ["/", ".."],
1536            has_root: true,
1537            is_absolute: true,
1538            parent: Some("/"),
1539            file_name: None,
1540            file_stem: None,
1541            extension: None
1542            );
1543
1544         t!("../",
1545            iter: ["..", "."],
1546            has_root: false,
1547            is_absolute: false,
1548            parent: Some(".."),
1549            file_name: None,
1550            file_stem: None,
1551            extension: None
1552            );
1553
1554         t!("foo/.",
1555            iter: ["foo", "."],
1556            has_root: false,
1557            is_absolute: false,
1558            parent: Some("foo"),
1559            file_name: None,
1560            file_stem: None,
1561            extension: None
1562            );
1563
1564         t!("foo/..",
1565            iter: ["foo", ".."],
1566            has_root: false,
1567            is_absolute: false,
1568            parent: Some("foo"),
1569            file_name: None,
1570            file_stem: None,
1571            extension: None
1572            );
1573
1574         t!("foo/./",
1575            iter: ["foo", ".", "."],
1576            has_root: false,
1577            is_absolute: false,
1578            parent: Some("foo/."),
1579            file_name: None,
1580            file_stem: None,
1581            extension: None
1582            );
1583
1584         t!("foo/./bar",
1585            iter: ["foo", ".", "bar"],
1586            has_root: false,
1587            is_absolute: false,
1588            parent: Some("foo/."),
1589            file_name: Some("bar"),
1590            file_stem: Some("bar"),
1591            extension: None
1592            );
1593
1594         t!("foo/../",
1595            iter: ["foo", "..", "."],
1596            has_root: false,
1597            is_absolute: false,
1598            parent: Some("foo/.."),
1599            file_name: None,
1600            file_stem: None,
1601            extension: None
1602            );
1603
1604         t!("foo/../bar",
1605            iter: ["foo", "..", "bar"],
1606            has_root: false,
1607            is_absolute: false,
1608            parent: Some("foo/.."),
1609            file_name: Some("bar"),
1610            file_stem: Some("bar"),
1611            extension: None
1612            );
1613
1614         t!("./a",
1615            iter: [".", "a"],
1616            has_root: false,
1617            is_absolute: false,
1618            parent: Some("."),
1619            file_name: Some("a"),
1620            file_stem: Some("a"),
1621            extension: None
1622            );
1623
1624         t!(".",
1625            iter: ["."],
1626            has_root: false,
1627            is_absolute: false,
1628            parent: None,
1629            file_name: None,
1630            file_stem: None,
1631            extension: None
1632            );
1633
1634         t!("./",
1635            iter: [".", "."],
1636            has_root: false,
1637            is_absolute: false,
1638            parent: Some("."),
1639            file_name: None,
1640            file_stem: None,
1641            extension: None
1642            );
1643
1644         t!("a/b",
1645            iter: ["a", "b"],
1646            has_root: false,
1647            is_absolute: false,
1648            parent: Some("a"),
1649            file_name: Some("b"),
1650            file_stem: Some("b"),
1651            extension: None
1652            );
1653
1654         t!("a//b",
1655            iter: ["a", "b"],
1656            has_root: false,
1657            is_absolute: false,
1658            parent: Some("a"),
1659            file_name: Some("b"),
1660            file_stem: Some("b"),
1661            extension: None
1662            );
1663
1664         t!("a/./b",
1665            iter: ["a", ".", "b"],
1666            has_root: false,
1667            is_absolute: false,
1668            parent: Some("a/."),
1669            file_name: Some("b"),
1670            file_stem: Some("b"),
1671            extension: None
1672            );
1673
1674         t!("a/b/c",
1675            iter: ["a", "b", "c"],
1676            has_root: false,
1677            is_absolute: false,
1678            parent: Some("a/b"),
1679            file_name: Some("c"),
1680            file_stem: Some("c"),
1681            extension: None
1682            );
1683     }
1684
1685     #[test]
1686     #[cfg(windows)]
1687     pub fn test_decompositions_windows() {
1688         t!("",
1689            iter: [],
1690            has_root: false,
1691            is_absolute: false,
1692            parent: None,
1693            file_name: None,
1694            file_stem: None,
1695            extension: None
1696            );
1697
1698         t!("foo",
1699            iter: ["foo"],
1700            has_root: false,
1701            is_absolute: false,
1702            parent: None,
1703            file_name: Some("foo"),
1704            file_stem: Some("foo"),
1705            extension: None
1706            );
1707
1708         t!("/",
1709            iter: ["\\", "."],
1710            has_root: true,
1711            is_absolute: false,
1712            parent: None,
1713            file_name: None,
1714            file_stem: None,
1715            extension: None
1716            );
1717
1718         t!("\\",
1719            iter: ["\\", "."],
1720            has_root: true,
1721            is_absolute: false,
1722            parent: None,
1723            file_name: None,
1724            file_stem: None,
1725            extension: None
1726            );
1727
1728         t!("c:",
1729            iter: ["c:", "."],
1730            has_root: false,
1731            is_absolute: false,
1732            parent: None,
1733            file_name: None,
1734            file_stem: None,
1735            extension: None
1736            );
1737
1738         t!("c:\\",
1739            iter: ["c:", "\\", "."],
1740            has_root: true,
1741            is_absolute: true,
1742            parent: None,
1743            file_name: None,
1744            file_stem: None,
1745            extension: None
1746            );
1747
1748         t!("c:\\",
1749            iter: ["c:", "\\", "."],
1750            has_root: true,
1751            is_absolute: true,
1752            parent: None,
1753            file_name: None,
1754            file_stem: None,
1755            extension: None
1756            );
1757
1758         t!("c:/",
1759            iter: ["c:", "\\", "."],
1760            has_root: true,
1761            is_absolute: true,
1762            parent: None,
1763            file_name: None,
1764            file_stem: None,
1765            extension: None
1766            );
1767
1768         t!("/foo",
1769            iter: ["\\", "foo"],
1770            has_root: true,
1771            is_absolute: false,
1772            parent: Some("/"),
1773            file_name: Some("foo"),
1774            file_stem: Some("foo"),
1775            extension: None
1776            );
1777
1778         t!("foo/",
1779            iter: ["foo", "."],
1780            has_root: false,
1781            is_absolute: false,
1782            parent: Some("foo"),
1783            file_name: None,
1784            file_stem: None,
1785            extension: None
1786            );
1787
1788         t!("/foo/",
1789            iter: ["\\", "foo", "."],
1790            has_root: true,
1791            is_absolute: false,
1792            parent: Some("/foo"),
1793            file_name: None,
1794            file_stem: None,
1795            extension: None
1796            );
1797
1798         t!("foo/bar",
1799            iter: ["foo", "bar"],
1800            has_root: false,
1801            is_absolute: false,
1802            parent: Some("foo"),
1803            file_name: Some("bar"),
1804            file_stem: Some("bar"),
1805            extension: None
1806            );
1807
1808         t!("/foo/bar",
1809            iter: ["\\", "foo", "bar"],
1810            has_root: true,
1811            is_absolute: false,
1812            parent: Some("/foo"),
1813            file_name: Some("bar"),
1814            file_stem: Some("bar"),
1815            extension: None
1816            );
1817
1818         t!("///foo///",
1819            iter: ["\\", "foo", "."],
1820            has_root: true,
1821            is_absolute: false,
1822            parent: Some("///foo"),
1823            file_name: None,
1824            file_stem: None,
1825            extension: None
1826            );
1827
1828         t!("///foo///bar",
1829            iter: ["\\", "foo", "bar"],
1830            has_root: true,
1831            is_absolute: false,
1832            parent: Some("///foo"),
1833            file_name: Some("bar"),
1834            file_stem: Some("bar"),
1835            extension: None
1836            );
1837
1838         t!("./.",
1839            iter: [".", "."],
1840            has_root: false,
1841            is_absolute: false,
1842            parent: Some("."),
1843            file_name: None,
1844            file_stem: None,
1845            extension: None
1846            );
1847
1848         t!("./.",
1849            iter: [".", "."],
1850            has_root: false,
1851            is_absolute: false,
1852            parent: Some("."),
1853            file_name: None,
1854            file_stem: None,
1855            extension: None
1856            );
1857
1858         t!("/..",
1859            iter: ["\\", ".."],
1860            has_root: true,
1861            is_absolute: false,
1862            parent: Some("/"),
1863            file_name: None,
1864            file_stem: None,
1865            extension: None
1866            );
1867
1868         t!("../",
1869            iter: ["..", "."],
1870            has_root: false,
1871            is_absolute: false,
1872            parent: Some(".."),
1873            file_name: None,
1874            file_stem: None,
1875            extension: None
1876            );
1877
1878         t!("foo/.",
1879            iter: ["foo", "."],
1880            has_root: false,
1881            is_absolute: false,
1882            parent: Some("foo"),
1883            file_name: None,
1884            file_stem: None,
1885            extension: None
1886            );
1887
1888         t!("foo/..",
1889            iter: ["foo", ".."],
1890            has_root: false,
1891            is_absolute: false,
1892            parent: Some("foo"),
1893            file_name: None,
1894            file_stem: None,
1895            extension: None
1896            );
1897
1898         t!("foo/./",
1899            iter: ["foo", ".", "."],
1900            has_root: false,
1901            is_absolute: false,
1902            parent: Some("foo/."),
1903            file_name: None,
1904            file_stem: None,
1905            extension: None
1906            );
1907
1908         t!("foo/./bar",
1909            iter: ["foo", ".", "bar"],
1910            has_root: false,
1911            is_absolute: false,
1912            parent: Some("foo/."),
1913            file_name: Some("bar"),
1914            file_stem: Some("bar"),
1915            extension: None
1916            );
1917
1918         t!("foo/../",
1919            iter: ["foo", "..", "."],
1920            has_root: false,
1921            is_absolute: false,
1922            parent: Some("foo/.."),
1923            file_name: None,
1924            file_stem: None,
1925            extension: None
1926            );
1927
1928         t!("foo/../bar",
1929            iter: ["foo", "..", "bar"],
1930            has_root: false,
1931            is_absolute: false,
1932            parent: Some("foo/.."),
1933            file_name: Some("bar"),
1934            file_stem: Some("bar"),
1935            extension: None
1936            );
1937
1938         t!("./a",
1939            iter: [".", "a"],
1940            has_root: false,
1941            is_absolute: false,
1942            parent: Some("."),
1943            file_name: Some("a"),
1944            file_stem: Some("a"),
1945            extension: None
1946            );
1947
1948         t!(".",
1949            iter: ["."],
1950            has_root: false,
1951            is_absolute: false,
1952            parent: None,
1953            file_name: None,
1954            file_stem: None,
1955            extension: None
1956            );
1957
1958         t!("./",
1959            iter: [".", "."],
1960            has_root: false,
1961            is_absolute: false,
1962            parent: Some("."),
1963            file_name: None,
1964            file_stem: None,
1965            extension: None
1966            );
1967
1968         t!("a/b",
1969            iter: ["a", "b"],
1970            has_root: false,
1971            is_absolute: false,
1972            parent: Some("a"),
1973            file_name: Some("b"),
1974            file_stem: Some("b"),
1975            extension: None
1976            );
1977
1978         t!("a//b",
1979            iter: ["a", "b"],
1980            has_root: false,
1981            is_absolute: false,
1982            parent: Some("a"),
1983            file_name: Some("b"),
1984            file_stem: Some("b"),
1985            extension: None
1986            );
1987
1988         t!("a/./b",
1989            iter: ["a", ".", "b"],
1990            has_root: false,
1991            is_absolute: false,
1992            parent: Some("a/."),
1993            file_name: Some("b"),
1994            file_stem: Some("b"),
1995            extension: None
1996            );
1997
1998         t!("a/b/c",
1999            iter: ["a", "b", "c"],
2000            has_root: false,
2001            is_absolute: false,
2002            parent: Some("a/b"),
2003            file_name: Some("c"),
2004            file_stem: Some("c"),
2005            extension: None);
2006
2007         t!("a\\b\\c",
2008            iter: ["a", "b", "c"],
2009            has_root: false,
2010            is_absolute: false,
2011            parent: Some("a\\b"),
2012            file_name: Some("c"),
2013            file_stem: Some("c"),
2014            extension: None
2015            );
2016
2017         t!("\\a",
2018            iter: ["\\", "a"],
2019            has_root: true,
2020            is_absolute: false,
2021            parent: Some("\\"),
2022            file_name: Some("a"),
2023            file_stem: Some("a"),
2024            extension: None
2025            );
2026
2027         t!("c:\\foo.txt",
2028            iter: ["c:", "\\", "foo.txt"],
2029            has_root: true,
2030            is_absolute: true,
2031            parent: Some("c:\\"),
2032            file_name: Some("foo.txt"),
2033            file_stem: Some("foo"),
2034            extension: Some("txt")
2035            );
2036
2037         t!("\\\\server\\share\\foo.txt",
2038            iter: ["\\\\server\\share", "\\", "foo.txt"],
2039            has_root: true,
2040            is_absolute: true,
2041            parent: Some("\\\\server\\share\\"),
2042            file_name: Some("foo.txt"),
2043            file_stem: Some("foo"),
2044            extension: Some("txt")
2045            );
2046
2047         t!("\\\\server\\share",
2048            iter: ["\\\\server\\share", "\\", "."],
2049            has_root: true,
2050            is_absolute: true,
2051            parent: None,
2052            file_name: None,
2053            file_stem: None,
2054            extension: None
2055            );
2056
2057         t!("\\\\server",
2058            iter: ["\\", "server"],
2059            has_root: true,
2060            is_absolute: false,
2061            parent: Some("\\"),
2062            file_name: Some("server"),
2063            file_stem: Some("server"),
2064            extension: None
2065            );
2066
2067         t!("\\\\?\\bar\\foo.txt",
2068            iter: ["\\\\?\\bar", "\\", "foo.txt"],
2069            has_root: true,
2070            is_absolute: true,
2071            parent: Some("\\\\?\\bar\\"),
2072            file_name: Some("foo.txt"),
2073            file_stem: Some("foo"),
2074            extension: Some("txt")
2075            );
2076
2077         t!("\\\\?\\bar",
2078            iter: ["\\\\?\\bar"],
2079            has_root: true,
2080            is_absolute: true,
2081            parent: None,
2082            file_name: None,
2083            file_stem: None,
2084            extension: None
2085            );
2086
2087         t!("\\\\?\\",
2088            iter: ["\\\\?\\"],
2089            has_root: true,
2090            is_absolute: true,
2091            parent: None,
2092            file_name: None,
2093            file_stem: None,
2094            extension: None
2095            );
2096
2097         t!("\\\\?\\UNC\\server\\share\\foo.txt",
2098            iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"],
2099            has_root: true,
2100            is_absolute: true,
2101            parent: Some("\\\\?\\UNC\\server\\share\\"),
2102            file_name: Some("foo.txt"),
2103            file_stem: Some("foo"),
2104            extension: Some("txt")
2105            );
2106
2107         t!("\\\\?\\UNC\\server",
2108            iter: ["\\\\?\\UNC\\server"],
2109            has_root: true,
2110            is_absolute: true,
2111            parent: None,
2112            file_name: None,
2113            file_stem: None,
2114            extension: None
2115            );
2116
2117         t!("\\\\?\\UNC\\",
2118            iter: ["\\\\?\\UNC\\"],
2119            has_root: true,
2120            is_absolute: true,
2121            parent: None,
2122            file_name: None,
2123            file_stem: None,
2124            extension: None
2125            );
2126
2127         t!("\\\\?\\C:\\foo.txt",
2128            iter: ["\\\\?\\C:", "\\", "foo.txt"],
2129            has_root: true,
2130            is_absolute: true,
2131            parent: Some("\\\\?\\C:\\"),
2132            file_name: Some("foo.txt"),
2133            file_stem: Some("foo"),
2134            extension: Some("txt")
2135            );
2136
2137
2138         t!("\\\\?\\C:\\",
2139            iter: ["\\\\?\\C:", "\\", ""],
2140            has_root: true,
2141            is_absolute: true,
2142            parent: None,
2143            file_name: None,
2144            file_stem: None,
2145            extension: None
2146            );
2147
2148
2149         t!("\\\\?\\C:",
2150            iter: ["\\\\?\\C:"],
2151            has_root: true,
2152            is_absolute: true,
2153            parent: None,
2154            file_name: None,
2155            file_stem: None,
2156            extension: None
2157            );
2158
2159
2160         t!("\\\\?\\foo/bar",
2161            iter: ["\\\\?\\foo/bar"],
2162            has_root: true,
2163            is_absolute: true,
2164            parent: None,
2165            file_name: None,
2166            file_stem: None,
2167            extension: None
2168            );
2169
2170
2171         t!("\\\\?\\C:/foo",
2172            iter: ["\\\\?\\C:/foo"],
2173            has_root: true,
2174            is_absolute: true,
2175            parent: None,
2176            file_name: None,
2177            file_stem: None,
2178            extension: None
2179            );
2180
2181
2182         t!("\\\\.\\foo\\bar",
2183            iter: ["\\\\.\\foo", "\\", "bar"],
2184            has_root: true,
2185            is_absolute: true,
2186            parent: Some("\\\\.\\foo\\"),
2187            file_name: Some("bar"),
2188            file_stem: Some("bar"),
2189            extension: None
2190            );
2191
2192
2193         t!("\\\\.\\foo",
2194            iter: ["\\\\.\\foo", "\\", "."],
2195            has_root: true,
2196            is_absolute: true,
2197            parent: None,
2198            file_name: None,
2199            file_stem: None,
2200            extension: None
2201            );
2202
2203
2204         t!("\\\\.\\foo/bar",
2205            iter: ["\\\\.\\foo/bar", "\\", "."],
2206            has_root: true,
2207            is_absolute: true,
2208            parent: None,
2209            file_name: None,
2210            file_stem: None,
2211            extension: None
2212            );
2213
2214
2215         t!("\\\\.\\foo\\bar/baz",
2216            iter: ["\\\\.\\foo", "\\", "bar", "baz"],
2217            has_root: true,
2218            is_absolute: true,
2219            parent: Some("\\\\.\\foo\\bar"),
2220            file_name: Some("baz"),
2221            file_stem: Some("baz"),
2222            extension: None
2223            );
2224
2225
2226         t!("\\\\.\\",
2227            iter: ["\\\\.\\", "\\", "."],
2228            has_root: true,
2229            is_absolute: true,
2230            parent: None,
2231            file_name: None,
2232            file_stem: None,
2233            extension: None
2234            );
2235
2236         t!("\\\\?\\a\\b\\",
2237            iter: ["\\\\?\\a", "\\", "b", ""],
2238            has_root: true,
2239            is_absolute: true,
2240            parent: Some("\\\\?\\a\\b"),
2241            file_name: None,
2242            file_stem: None,
2243            extension: None
2244            );
2245     }
2246
2247     #[test]
2248     pub fn test_stem_ext() {
2249         t!("foo",
2250            file_stem: Some("foo"),
2251            extension: None
2252            );
2253
2254         t!("foo.",
2255            file_stem: Some("foo"),
2256            extension: Some("")
2257            );
2258
2259         t!(".foo",
2260            file_stem: Some(".foo"),
2261            extension: None
2262            );
2263
2264         t!("foo.txt",
2265            file_stem: Some("foo"),
2266            extension: Some("txt")
2267            );
2268
2269         t!("foo.bar.txt",
2270            file_stem: Some("foo.bar"),
2271            extension: Some("txt")
2272            );
2273
2274         t!("foo.bar.",
2275            file_stem: Some("foo.bar"),
2276            extension: Some("")
2277            );
2278
2279         t!(".",
2280            file_stem: None,
2281            extension: None
2282            );
2283
2284         t!("..",
2285            file_stem: None,
2286            extension: None
2287            );
2288
2289         t!("",
2290            file_stem: None,
2291            extension: None
2292            );
2293     }
2294
2295     #[test]
2296     pub fn test_push() {
2297         macro_rules! tp(
2298             ($path:expr, $push:expr, $expected:expr) => ( {
2299                 let mut actual = PathBuf::new($path);
2300                 actual.push($push);
2301                 assert!(actual.to_str() == Some($expected),
2302                         "pushing {:?} onto {:?}: Expected {:?}, got {:?}",
2303                         $push, $path, $expected, actual.to_str().unwrap());
2304             });
2305         );
2306
2307         if cfg!(unix) {
2308             tp!("", "foo", "foo");
2309             tp!("foo", "bar", "foo/bar");
2310             tp!("foo/", "bar", "foo/bar");
2311             tp!("foo//", "bar", "foo//bar");
2312             tp!("foo/.", "bar", "foo/./bar");
2313             tp!("foo./.", "bar", "foo././bar");
2314             tp!("foo", "", "foo/");
2315             tp!("foo", ".", "foo/.");
2316             tp!("foo", "..", "foo/..");
2317             tp!("foo", "/", "/");
2318             tp!("/foo/bar", "/", "/");
2319             tp!("/foo/bar", "/baz", "/baz");
2320             tp!("/foo/bar", "./baz", "/foo/bar/./baz");
2321         } else {
2322             tp!("", "foo", "foo");
2323             tp!("foo", "bar", r"foo\bar");
2324             tp!("foo/", "bar", r"foo/bar");
2325             tp!(r"foo\", "bar", r"foo\bar");
2326             tp!("foo//", "bar", r"foo//bar");
2327             tp!(r"foo\\", "bar", r"foo\\bar");
2328             tp!("foo/.", "bar", r"foo/.\bar");
2329             tp!("foo./.", "bar", r"foo./.\bar");
2330             tp!(r"foo\.", "bar", r"foo\.\bar");
2331             tp!(r"foo.\.", "bar", r"foo.\.\bar");
2332             tp!("foo", "", "foo\\");
2333             tp!("foo", ".", r"foo\.");
2334             tp!("foo", "..", r"foo\..");
2335             tp!("foo", "/", "/");
2336             tp!("foo", r"\", r"\");
2337             tp!("/foo/bar", "/", "/");
2338             tp!(r"\foo\bar", r"\", r"\");
2339             tp!("/foo/bar", "/baz", "/baz");
2340             tp!("/foo/bar", r"\baz", r"\baz");
2341             tp!("/foo/bar", "./baz", r"/foo/bar\./baz");
2342             tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz");
2343
2344             tp!("c:\\", "windows", "c:\\windows");
2345             tp!("c:", "windows", "c:windows");
2346
2347             tp!("a\\b\\c", "d", "a\\b\\c\\d");
2348             tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d");
2349             tp!("a\\b", "c\\d", "a\\b\\c\\d");
2350             tp!("a\\b", "\\c\\d", "\\c\\d");
2351             tp!("a\\b", ".", "a\\b\\.");
2352             tp!("a\\b", "..\\c", "a\\b\\..\\c");
2353             tp!("a\\b", "C:a.txt", "C:a.txt");
2354             tp!("a\\b", "C:\\a.txt", "C:\\a.txt");
2355             tp!("C:\\a", "C:\\b.txt", "C:\\b.txt");
2356             tp!("C:\\a\\b\\c", "C:d", "C:d");
2357             tp!("C:a\\b\\c", "C:d", "C:d");
2358             tp!("C:", r"a\b\c", r"C:a\b\c");
2359             tp!("C:", r"..\a", r"C:..\a");
2360             tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
2361             tp!("\\\\server\\share\\foo", "C:baz", "C:baz");
2362             tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d");
2363             tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
2364             tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
2365             tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
2366             tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
2367             tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
2368             tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a");
2369
2370             // Note: modified from old path API
2371             tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo");
2372
2373             tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
2374             tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
2375             tp!("\\\\.\\foo\\bar", "C:a", "C:a");
2376             // again, not sure about the following, but I'm assuming \\.\ should be verbatim
2377             tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
2378
2379             tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
2380         }
2381     }
2382
2383     #[test]
2384     pub fn test_pop() {
2385         macro_rules! tp(
2386             ($path:expr, $expected:expr, $output:expr) => ( {
2387                 let mut actual = PathBuf::new($path);
2388                 let output = actual.pop();
2389                 assert!(actual.to_str() == Some($expected) && output == $output,
2390                         "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
2391                         $path, $expected, $output,
2392                         actual.to_str().unwrap(), output);
2393             });
2394         );
2395
2396         tp!("", "", false);
2397         tp!("/", "/", false);
2398         tp!("foo", "foo", false);
2399         tp!(".", ".", false);
2400         tp!("/foo", "/", true);
2401         tp!("/foo/bar", "/foo", true);
2402         tp!("foo/bar", "foo", true);
2403         tp!("foo/.", "foo", true);
2404         tp!("foo//bar", "foo", true);
2405
2406         if cfg!(windows) {
2407             tp!("a\\b\\c", "a\\b", true);
2408             tp!("\\a", "\\", true);
2409             tp!("\\", "\\", false);
2410
2411             tp!("C:\\a\\b", "C:\\a", true);
2412             tp!("C:\\a", "C:\\", true);
2413             tp!("C:\\", "C:\\", false);
2414             tp!("C:a\\b", "C:a", true);
2415             tp!("C:a", "C:", true);
2416             tp!("C:", "C:", false);
2417             tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
2418             tp!("\\\\server\\share\\a", "\\\\server\\share\\", true);
2419             tp!("\\\\server\\share", "\\\\server\\share", false);
2420             tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
2421             tp!("\\\\?\\a\\b", "\\\\?\\a\\", true);
2422             tp!("\\\\?\\a", "\\\\?\\a", false);
2423             tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
2424             tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true);
2425             tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false);
2426             tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
2427             tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true);
2428             tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
2429             tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
2430             tp!("\\\\.\\a\\b", "\\\\.\\a\\", true);
2431             tp!("\\\\.\\a", "\\\\.\\a", false);
2432
2433             tp!("\\\\?\\a\\b\\", "\\\\?\\a\\b", true);
2434         }
2435     }
2436
2437     #[test]
2438     pub fn test_set_file_name() {
2439         macro_rules! tfn(
2440                 ($path:expr, $file:expr, $expected:expr) => ( {
2441                 let mut p = PathBuf::new($path);
2442                 p.set_file_name($file);
2443                 assert!(p.to_str() == Some($expected),
2444                         "setting file name of {:?} to {:?}: Expected {:?}, got {:?}",
2445                         $path, $file, $expected,
2446                         p.to_str().unwrap());
2447             });
2448         );
2449
2450         tfn!("foo", "foo", "foo");
2451         tfn!("foo", "bar", "bar");
2452         tfn!("foo", "", "");
2453         tfn!("", "foo", "foo");
2454         if cfg!(unix) {
2455             tfn!(".", "foo", "./foo");
2456             tfn!("foo/", "bar", "foo/bar");
2457             tfn!("foo/.", "bar", "foo/./bar");
2458             tfn!("..", "foo", "../foo");
2459             tfn!("foo/..", "bar", "foo/../bar");
2460             tfn!("/", "foo", "/foo");
2461         } else {
2462             tfn!(".", "foo", r".\foo");
2463             tfn!(r"foo\", "bar", r"foo\bar");
2464             tfn!(r"foo\.", "bar", r"foo\.\bar");
2465             tfn!("..", "foo", r"..\foo");
2466             tfn!(r"foo\..", "bar", r"foo\..\bar");
2467             tfn!(r"\", "foo", r"\foo");
2468         }
2469     }
2470
2471     #[test]
2472     pub fn test_set_extension() {
2473         macro_rules! tfe(
2474                 ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
2475                 let mut p = PathBuf::new($path);
2476                 let output = p.set_extension($ext);
2477                 assert!(p.to_str() == Some($expected) && output == $output,
2478                         "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
2479                         $path, $ext, $expected, $output,
2480                         p.to_str().unwrap(), output);
2481             });
2482         );
2483
2484         tfe!("foo", "txt", "foo.txt", true);
2485         tfe!("foo.bar", "txt", "foo.txt", true);
2486         tfe!("foo.bar.baz", "txt", "foo.bar.txt", true);
2487         tfe!(".test", "txt", ".test.txt", true);
2488         tfe!("foo.txt", "", "foo", true);
2489         tfe!("foo", "", "foo", true);
2490         tfe!("", "foo", "", false);
2491         tfe!(".", "foo", ".", false);
2492         tfe!("foo/", "bar", "foo/", false);
2493         tfe!("foo/.", "bar", "foo/.", false);
2494         tfe!("..", "foo", "..",  false);
2495         tfe!("foo/..", "bar", "foo/..", false);
2496         tfe!("/", "foo", "/", false);
2497     }
2498
2499     #[test]
2500     pub fn test_compare() {
2501         macro_rules! tc(
2502             ($path1:expr, $path2:expr, eq: $eq:expr,
2503              starts_with: $starts_with:expr, ends_with: $ends_with:expr,
2504              relative_from: $relative_from:expr) => ({
2505                  let path1 = Path::new($path1);
2506                  let path2 = Path::new($path2);
2507
2508                  let eq = path1 == path2;
2509                  assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
2510                          $path1, $path2, $eq, eq);
2511
2512                  let starts_with = path1.starts_with(path2);
2513                  assert!(starts_with == $starts_with,
2514                          "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2,
2515                          $starts_with, starts_with);
2516
2517                  let ends_with = path1.ends_with(path2);
2518                  assert!(ends_with == $ends_with,
2519                          "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2,
2520                          $ends_with, ends_with);
2521
2522                  let relative_from = path1.relative_from(path2).map(|p| p.to_str().unwrap());
2523                  let exp: Option<&str> = $relative_from;
2524                  assert!(relative_from == exp,
2525                          "{:?}.relative_from({:?}), expected {:?}, got {:?}", $path1, $path2,
2526                          exp, relative_from);
2527             });
2528         );
2529
2530         tc!("", "",
2531             eq: true,
2532             starts_with: true,
2533             ends_with: true,
2534             relative_from: Some("")
2535             );
2536
2537         tc!("foo", "",
2538             eq: false,
2539             starts_with: true,
2540             ends_with: true,
2541             relative_from: Some("foo")
2542             );
2543
2544         tc!("", "foo",
2545             eq: false,
2546             starts_with: false,
2547             ends_with: false,
2548             relative_from: None
2549             );
2550
2551         tc!("foo", "foo",
2552             eq: true,
2553             starts_with: true,
2554             ends_with: true,
2555             relative_from: Some("")
2556             );
2557
2558         tc!("foo/", "foo",
2559             eq: false,
2560             starts_with: true,
2561             ends_with: false,
2562             relative_from: Some(".")
2563             );
2564
2565         tc!("foo/bar", "foo",
2566             eq: false,
2567             starts_with: true,
2568             ends_with: false,
2569             relative_from: Some("bar")
2570             );
2571
2572         tc!("foo/bar/baz", "foo/bar",
2573             eq: false,
2574             starts_with: true,
2575             ends_with: false,
2576             relative_from: Some("baz")
2577             );
2578
2579         tc!("foo/bar", "foo/bar/baz",
2580             eq: false,
2581             starts_with: false,
2582             ends_with: false,
2583             relative_from: None
2584             );
2585
2586         tc!("./foo/bar/", ".",
2587             eq: false,
2588             starts_with: true,
2589             ends_with: true,
2590             relative_from: Some("foo/bar/")
2591             );
2592     }
2593 }