(index<- ) ./libstd/path/posix.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2013-2014 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 //! POSIX file path handling
12
13 use container::Container;
14 use c_str::{CString, ToCStr};
15 use clone::Clone;
16 use cmp::{Eq, TotalEq};
17 use from_str::FromStr;
18 use io::Writer;
19 use iter::{DoubleEndedIterator, Rev, AdditiveIterator, Extendable, Iterator, Map};
20 use option::{Option, None, Some};
21 use str;
22 use str::Str;
23 use slice::{CloneableVector, Splits, Vector, VectorVector,
24 ImmutableEqVector, OwnedVector, ImmutableVector};
25 use vec::Vec;
26
27 use super::{BytesContainer, GenericPath, GenericPathUnsafe};
28
29 /// Iterator that yields successive components of a Path as &[u8]
30 pub type Components<'a> = Splits<'a, u8>;
31 /// Iterator that yields components of a Path in reverse as &[u8]
32 #[deprecated = "replaced by Rev<Components<'a>>"]
33 pub type RevComponents<'a> = Rev<Components<'a>>;
34
35 /// Iterator that yields successive components of a Path as Option<&str>
36 pub type StrComponents<'a> = Map<'a, &'a [u8], Option<&'a str>,
37 Components<'a>>;
38 /// Iterator that yields components of a Path in reverse as Option<&str>
39 #[deprecated = "replaced by Rev<StrComponents<'a>>"]
40 pub type RevStrComponents<'a> = Rev<StrComponents<'a>>;
41
42 /// Represents a POSIX file path
43 #[deriving(Clone)]
44 pub struct Path {
45 repr: Vec<u8>, // assumed to never be empty or contain NULs
46 sepidx: Option<uint> // index of the final separator in repr
47 }
48
49 /// The standard path separator character
50 pub static SEP: char = '/';
51
52 /// The standard path separator byte
53 pub static SEP_BYTE: u8 = SEP as u8;
54
55 /// Returns whether the given byte is a path separator
56 #[inline]
57 pub fn is_sep_byte(u: &u8) -> bool {
58 *u as char == SEP
59 }
60
61 /// Returns whether the given char is a path separator
62 #[inline]
63 pub fn is_sep(c: char) -> bool {
64 c == SEP
65 }
66
67 impl Eq for Path {
68 #[inline]
69 fn eq(&self, other: &Path) -> bool {
70 self.repr == other.repr
71 }
72 }
73
74 impl TotalEq for Path {}
75
76 impl FromStr for Path {
77 fn from_str(s: &str) -> Option<Path> {
78 Path::new_opt(s)
79 }
80 }
81
82 impl ToCStr for Path {
83 #[inline]
84 fn to_c_str(&self) -> CString {
85 // The Path impl guarantees no internal NUL
86 unsafe { self.as_vec().to_c_str_unchecked() }
87 }
88
89 #[inline]
90 unsafe fn to_c_str_unchecked(&self) -> CString {
91 self.as_vec().to_c_str_unchecked()
92 }
93 }
94
95 impl<S: Writer> ::hash::Hash<S> for Path {
96 #[inline]
97 fn hash(&self, state: &mut S) {
98 self.repr.hash(state)
99 }
100 }
101
102 impl BytesContainer for Path {
103 #[inline]
104 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
105 self.as_vec()
106 }
107 #[inline]
108 fn container_into_owned_bytes(self) -> Vec<u8> {
109 self.into_vec()
110 }
111 }
112
113 impl<'a> BytesContainer for &'a Path {
114 #[inline]
115 fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
116 self.as_vec()
117 }
118 }
119
120 impl GenericPathUnsafe for Path {
121 unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
122 let path = Path::normalize(path.container_as_bytes());
123 assert!(!path.is_empty());
124 let idx = path.as_slice().rposition_elem(&SEP_BYTE);
125 Path{ repr: path, sepidx: idx }
126 }
127
128 unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
129 let filename = filename.container_as_bytes();
130 match self.sepidx {
131 None if bytes!("..") == self.repr.as_slice() => {
132 let mut v = Vec::with_capacity(3 + filename.len());
133 v.push_all(dot_dot_static);
134 v.push(SEP_BYTE);
135 v.push_all(filename);
136 // FIXME: this is slow
137 self.repr = Path::normalize(v.as_slice());
138 }
139 None => {
140 self.repr = Path::normalize(filename);
141 }
142 Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => {
143 let mut v = Vec::with_capacity(self.repr.len() + 1 + filename.len());
144 v.push_all(self.repr.as_slice());
145 v.push(SEP_BYTE);
146 v.push_all(filename);
147 // FIXME: this is slow
148 self.repr = Path::normalize(v.as_slice());
149 }
150 Some(idx) => {
151 let mut v = Vec::with_capacity(idx + 1 + filename.len());
152 v.push_all(self.repr.slice_to(idx+1));
153 v.push_all(filename);
154 // FIXME: this is slow
155 self.repr = Path::normalize(v.as_slice());
156 }
157 }
158 self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE);
159 }
160
161 unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
162 let path = path.container_as_bytes();
163 if !path.is_empty() {
164 if path[0] == SEP_BYTE {
165 self.repr = Path::normalize(path);
166 } else {
167 let mut v = Vec::with_capacity(self.repr.len() + path.len() + 1);
168 v.push_all(self.repr.as_slice());
169 v.push(SEP_BYTE);
170 v.push_all(path);
171 // FIXME: this is slow
172 self.repr = Path::normalize(v.as_slice());
173 }
174 self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE);
175 }
176 }
177 }
178
179 impl GenericPath for Path {
180 #[inline]
181 fn as_vec<'a>(&'a self) -> &'a [u8] {
182 self.repr.as_slice()
183 }
184
185 fn into_vec(self) -> Vec<u8> {
186 self.repr
187 }
188
189 fn dirname<'a>(&'a self) -> &'a [u8] {
190 match self.sepidx {
191 None if bytes!("..") == self.repr.as_slice() => self.repr.as_slice(),
192 None => dot_static,
193 Some(0) => self.repr.slice_to(1),
194 Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => self.repr.as_slice(),
195 Some(idx) => self.repr.slice_to(idx)
196 }
197 }
198
199 fn filename<'a>(&'a self) -> Option<&'a [u8]> {
200 match self.sepidx {
201 None if bytes!(".") == self.repr.as_slice() ||
202 bytes!("..") == self.repr.as_slice() => None,
203 None => Some(self.repr.as_slice()),
204 Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => None,
205 Some(0) if self.repr.slice_from(1).is_empty() => None,
206 Some(idx) => Some(self.repr.slice_from(idx+1))
207 }
208 }
209
210 fn pop(&mut self) -> bool {
211 match self.sepidx {
212 None if bytes!(".") == self.repr.as_slice() => false,
213 None => {
214 self.repr = vec!['.' as u8];
215 self.sepidx = None;
216 true
217 }
218 Some(0) if bytes!("/") == self.repr.as_slice() => false,
219 Some(idx) => {
220 if idx == 0 {
221 self.repr.truncate(idx+1);
222 } else {
223 self.repr.truncate(idx);
224 }
225 self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE);
226 true
227 }
228 }
229 }
230
231 fn root_path(&self) -> Option<Path> {
232 if self.is_absolute() {
233 Some(Path::new("/"))
234 } else {
235 None
236 }
237 }
238
239 #[inline]
240 fn is_absolute(&self) -> bool {
241 *self.repr.get(0) == SEP_BYTE
242 }
243
244 fn is_ancestor_of(&self, other: &Path) -> bool {
245 if self.is_absolute() != other.is_absolute() {
246 false
247 } else {
248 let mut ita = self.components();
249 let mut itb = other.components();
250 if bytes!(".") == self.repr.as_slice() {
251 return match itb.next() {
252 None => true,
253 Some(b) => b != bytes!("..")
254 };
255 }
256 loop {
257 match (ita.next(), itb.next()) {
258 (None, _) => break,
259 (Some(a), Some(b)) if a == b => { continue },
260 (Some(a), _) if a == bytes!("..") => {
261 // if ita contains only .. components, it's an ancestor
262 return ita.all(|x| x == bytes!(".."));
263 }
264 _ => return false
265 }
266 }
267 true
268 }
269 }
270
271 #[allow(deprecated_owned_vector)]
272 fn path_relative_from(&self, base: &Path) -> Option<Path> {
273 if self.is_absolute() != base.is_absolute() {
274 if self.is_absolute() {
275 Some(self.clone())
276 } else {
277 None
278 }
279 } else {
280 let mut ita = self.components();
281 let mut itb = base.components();
282 let mut comps = vec![];
283 loop {
284 match (ita.next(), itb.next()) {
285 (None, None) => break,
286 (Some(a), None) => {
287 comps.push(a);
288 comps.extend(ita.by_ref());
289 break;
290 }
291 (None, _) => comps.push(dot_dot_static),
292 (Some(a), Some(b)) if comps.is_empty() && a == b => (),
293 (Some(a), Some(b)) if b == bytes!(".") => comps.push(a),
294 (Some(_), Some(b)) if b == bytes!("..") => return None,
295 (Some(a), Some(_)) => {
296 comps.push(dot_dot_static);
297 for _ in itb {
298 comps.push(dot_dot_static);
299 }
300 comps.push(a);
301 comps.extend(ita.by_ref());
302 break;
303 }
304 }
305 }
306 Some(Path::new(comps.as_slice().connect_vec(&SEP_BYTE)))
307 }
308 }
309
310 fn ends_with_path(&self, child: &Path) -> bool {
311 if !child.is_relative() { return false; }
312 let mut selfit = self.components().rev();
313 let mut childit = child.components().rev();
314 loop {
315 match (selfit.next(), childit.next()) {
316 (Some(a), Some(b)) => if a != b { return false; },
317 (Some(_), None) => break,
318 (None, Some(_)) => return false,
319 (None, None) => break
320 }
321 }
322 true
323 }
324 }
325
326 impl Path {
327 /// Returns a new Path from a byte vector or string
328 ///
329 /// # Failure
330 ///
331 /// Fails the task if the vector contains a NUL.
332 #[inline]
333 pub fn new<T: BytesContainer>(path: T) -> Path {
334 GenericPath::new(path)
335 }
336
337 /// Returns a new Path from a byte vector or string, if possible
338 #[inline]
339 pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
340 GenericPath::new_opt(path)
341 }
342
343 /// Returns a normalized byte vector representation of a path, by removing all empty
344 /// components, and unnecessary . and .. components.
345 fn normalize<V: Vector<u8>+CloneableVector<u8>>(v: V) -> Vec<u8> {
346 // borrowck is being very picky
347 let val = {
348 let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == SEP_BYTE;
349 let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() };
350 let comps = normalize_helper(v_, is_abs);
351 match comps {
352 None => None,
353 Some(comps) => {
354 if is_abs && comps.is_empty() {
355 Some(vec![SEP_BYTE])
356 } else {
357 let n = if is_abs { comps.len() } else { comps.len() - 1} +
358 comps.iter().map(|v| v.len()).sum();
359 let mut v = Vec::with_capacity(n);
360 let mut it = comps.move_iter();
361 if !is_abs {
362 match it.next() {
363 None => (),
364 Some(comp) => v.push_all(comp)
365 }
366 }
367 for comp in it {
368 v.push(SEP_BYTE);
369 v.push_all(comp);
370 }
371 Some(v)
372 }
373 }
374 }
375 };
376 match val {
377 None => Vec::from_slice(v.as_slice()),
378 Some(val) => val
379 }
380 }
381
382 /// Returns an iterator that yields each component of the path in turn.
383 /// Does not distinguish between absolute and relative paths, e.g.
384 /// /a/b/c and a/b/c yield the same set of components.
385 /// A path of "/" yields no components. A path of "." yields one component.
386 pub fn components<'a>(&'a self) -> Components<'a> {
387 let v = if *self.repr.get(0) == SEP_BYTE {
388 self.repr.slice_from(1)
389 } else { self.repr.as_slice() };
390 let mut ret = v.split(is_sep_byte);
391 if v.is_empty() {
392 // consume the empty "" component
393 ret.next();
394 }
395 ret
396 }
397
398 /// Returns an iterator that yields each component of the path in reverse.
399 /// See components() for details.
400 #[deprecated = "replaced by .components().rev()"]
401 pub fn rev_components<'a>(&'a self) -> Rev<Components<'a>> {
402 self.components().rev()
403 }
404
405 /// Returns an iterator that yields each component of the path as Option<&str>.
406 /// See components() for details.
407 pub fn str_components<'a>(&'a self) -> StrComponents<'a> {
408 self.components().map(str::from_utf8)
409 }
410
411 /// Returns an iterator that yields each component of the path in reverse as Option<&str>.
412 /// See components() for details.
413 #[deprecated = "replaced by .str_components().rev()"]
414 pub fn rev_str_components<'a>(&'a self) -> Rev<StrComponents<'a>> {
415 self.str_components().rev()
416 }
417 }
418
419 // None result means the byte vector didn't need normalizing
420 fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<Vec<&'a [u8]>> {
421 if is_abs && v.as_slice().is_empty() {
422 return None;
423 }
424 let mut comps: Vec<&'a [u8]> = vec![];
425 let mut n_up = 0u;
426 let mut changed = false;
427 for comp in v.split(is_sep_byte) {
428 if comp.is_empty() { changed = true }
429 else if comp == bytes!(".") { changed = true }
430 else if comp == bytes!("..") {
431 if is_abs && comps.is_empty() { changed = true }
432 else if comps.len() == n_up { comps.push(dot_dot_static); n_up += 1 }
433 else { comps.pop().unwrap(); changed = true }
434 } else { comps.push(comp) }
435 }
436 if changed {
437 if comps.is_empty() && !is_abs {
438 if v == bytes!(".") {
439 return None;
440 }
441 comps.push(dot_static);
442 }
443 Some(comps)
444 } else {
445 None
446 }
447 }
448
449 static dot_static: &'static [u8] = bytes!(".");
450 static dot_dot_static: &'static [u8] = bytes!("..");
451
452 #[cfg(test)]
453 mod tests {
454 use prelude::*;
455 use super::*;
456 use str;
457 use str::StrSlice;
458
459 macro_rules! t(
460 (s: $path:expr, $exp:expr) => (
461 {
462 let path = $path;
463 assert!(path.as_str() == Some($exp));
464 }
465 );
466 (v: $path:expr, $exp:expr) => (
467 {
468 let path = $path;
469 assert!(path.as_vec() == $exp);
470 }
471 )
472 )
473
474 macro_rules! b(
475 ($($arg:expr),+) => (
476 {
477 static the_bytes: &'static [u8] = bytes!($($arg),+);
478 the_bytes
479 }
480 )
481 )
482
483 #[test]
484 fn test_paths() {
485 let empty: &[u8] = [];
486 t!(v: Path::new(empty), b!("."));
487 t!(v: Path::new(b!("/")), b!("/"));
488 t!(v: Path::new(b!("a/b/c")), b!("a/b/c"));
489 t!(v: Path::new(b!("a/b/c", 0xff)), b!("a/b/c", 0xff));
490 t!(v: Path::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80));
491 let p = Path::new(b!("a/b/c", 0xff));
492 assert!(p.as_str() == None);
493
494 t!(s: Path::new(""), ".");
495 t!(s: Path::new("/"), "/");
496 t!(s: Path::new("hi"), "hi");
497 t!(s: Path::new("hi/"), "hi");
498 t!(s: Path::new("/lib"), "/lib");
499 t!(s: Path::new("/lib/"), "/lib");
500 t!(s: Path::new("hi/there"), "hi/there");
501 t!(s: Path::new("hi/there.txt"), "hi/there.txt");
502
503 t!(s: Path::new("hi/there/"), "hi/there");
504 t!(s: Path::new("hi/../there"), "there");
505 t!(s: Path::new("../hi/there"), "../hi/there");
506 t!(s: Path::new("/../hi/there"), "/hi/there");
507 t!(s: Path::new("foo/.."), ".");
508 t!(s: Path::new("/foo/.."), "/");
509 t!(s: Path::new("/foo/../.."), "/");
510 t!(s: Path::new("/foo/../../bar"), "/bar");
511 t!(s: Path::new("/./hi/./there/."), "/hi/there");
512 t!(s: Path::new("/./hi/./there/./.."), "/hi");
513 t!(s: Path::new("foo/../.."), "..");
514 t!(s: Path::new("foo/../../.."), "../..");
515 t!(s: Path::new("foo/../../bar"), "../bar");
516
517 assert_eq!(Path::new(b!("foo/bar")).into_vec().as_slice(), b!("foo/bar"));
518 assert_eq!(Path::new(b!("/foo/../../bar")).into_vec().as_slice(),
519 b!("/bar"));
520
521 let p = Path::new(b!("foo/bar", 0x80));
522 assert!(p.as_str() == None);
523 }
524
525 #[test]
526 fn test_opt_paths() {
527 assert!(Path::new_opt(b!("foo/bar", 0)) == None);
528 t!(v: Path::new_opt(b!("foo/bar")).unwrap(), b!("foo/bar"));
529 assert!(Path::new_opt("foo/bar\0") == None);
530 t!(s: Path::new_opt("foo/bar").unwrap(), "foo/bar");
531 }
532
533 #[test]
534 fn test_null_byte() {
535 use task;
536 let result = task::try(proc() {
537 Path::new(b!("foo/bar", 0))
538 });
539 assert!(result.is_err());
540
541 let result = task::try(proc() {
542 Path::new("test").set_filename(b!("f", 0, "o"))
543 });
544 assert!(result.is_err());
545
546 let result = task::try(proc() {
547 Path::new("test").push(b!("f", 0, "o"));
548 });
549 assert!(result.is_err());
550 }
551
552 #[test]
553 fn test_display_str() {
554 macro_rules! t(
555 ($path:expr, $disp:ident, $exp:expr) => (
556 {
557 let path = Path::new($path);
558 assert!(path.$disp().to_str().as_slice() == $exp);
559 }
560 )
561 )
562 t!("foo", display, "foo");
563 t!(b!("foo", 0x80), display, "foo\uFFFD");
564 t!(b!("foo", 0xff, "bar"), display, "foo\uFFFDbar");
565 t!(b!("foo", 0xff, "/bar"), filename_display, "bar");
566 t!(b!("foo/", 0xff, "bar"), filename_display, "\uFFFDbar");
567 t!(b!("/"), filename_display, "");
568
569 macro_rules! t(
570 ($path:expr, $exp:expr) => (
571 {
572 let path = Path::new($path);
573 let mo = path.display().as_maybe_owned();
574 assert!(mo.as_slice() == $exp);
575 }
576 );
577 ($path:expr, $exp:expr, filename) => (
578 {
579 let path = Path::new($path);
580 let mo = path.filename_display().as_maybe_owned();
581 assert!(mo.as_slice() == $exp);
582 }
583 )
584 )
585
586 t!("foo", "foo");
587 t!(b!("foo", 0x80), "foo\uFFFD");
588 t!(b!("foo", 0xff, "bar"), "foo\uFFFDbar");
589 t!(b!("foo", 0xff, "/bar"), "bar", filename);
590 t!(b!("foo/", 0xff, "bar"), "\uFFFDbar", filename);
591 t!(b!("/"), "", filename);
592 }
593
594 #[test]
595 fn test_display() {
596 macro_rules! t(
597 ($path:expr, $exp:expr, $expf:expr) => (
598 {
599 let path = Path::new($path);
600 let f = format!("{}", path.display());
601 assert!(f.as_slice() == $exp);
602 let f = format!("{}", path.filename_display());
603 assert!(f.as_slice() == $expf);
604 }
605 )
606 )
607
608 t!(b!("foo"), "foo", "foo");
609 t!(b!("foo/bar"), "foo/bar", "bar");
610 t!(b!("/"), "/", "");
611 t!(b!("foo", 0xff), "foo\uFFFD", "foo\uFFFD");
612 t!(b!("foo", 0xff, "/bar"), "foo\uFFFD/bar", "bar");
613 t!(b!("foo/", 0xff, "bar"), "foo/\uFFFDbar", "\uFFFDbar");
614 t!(b!(0xff, "foo/bar", 0xff), "\uFFFDfoo/bar\uFFFD", "bar\uFFFD");
615 }
616
617 #[test]
618 fn test_components() {
619 macro_rules! t(
620 (s: $path:expr, $op:ident, $exp:expr) => (
621 {
622 let path = Path::new($path);
623 assert!(path.$op() == ($exp).as_bytes());
624 }
625 );
626 (s: $path:expr, $op:ident, $exp:expr, opt) => (
627 {
628 let path = Path::new($path);
629 let left = path.$op().map(|x| str::from_utf8(x).unwrap());
630 assert!(left == $exp);
631 }
632 );
633 (v: $path:expr, $op:ident, $exp:expr) => (
634 {
635 let arg = $path;
636 let path = Path::new(arg);
637 assert!(path.$op() == $exp);
638 }
639 );
640 )
641
642 t!(v: b!("a/b/c"), filename, Some(b!("c")));
643 t!(v: b!("a/b/c", 0xff), filename, Some(b!("c", 0xff)));
644 t!(v: b!("a/b", 0xff, "/c"), filename, Some(b!("c")));
645 t!(s: "a/b/c", filename, Some("c"), opt);
646 t!(s: "/a/b/c", filename, Some("c"), opt);
647 t!(s: "a", filename, Some("a"), opt);
648 t!(s: "/a", filename, Some("a"), opt);
649 t!(s: ".", filename, None, opt);
650 t!(s: "/", filename, None, opt);
651 t!(s: "..", filename, None, opt);
652 t!(s: "../..", filename, None, opt);
653
654 t!(v: b!("a/b/c"), dirname, b!("a/b"));
655 t!(v: b!("a/b/c", 0xff), dirname, b!("a/b"));
656 t!(v: b!("a/b", 0xff, "/c"), dirname, b!("a/b", 0xff));
657 t!(s: "a/b/c", dirname, "a/b");
658 t!(s: "/a/b/c", dirname, "/a/b");
659 t!(s: "a", dirname, ".");
660 t!(s: "/a", dirname, "/");
661 t!(s: ".", dirname, ".");
662 t!(s: "/", dirname, "/");
663 t!(s: "..", dirname, "..");
664 t!(s: "../..", dirname, "../..");
665
666 t!(v: b!("hi/there.txt"), filestem, Some(b!("there")));
667 t!(v: b!("hi/there", 0x80, ".txt"), filestem, Some(b!("there", 0x80)));
668 t!(v: b!("hi/there.t", 0x80, "xt"), filestem, Some(b!("there")));
669 t!(s: "hi/there.txt", filestem, Some("there"), opt);
670 t!(s: "hi/there", filestem, Some("there"), opt);
671 t!(s: "there.txt", filestem, Some("there"), opt);
672 t!(s: "there", filestem, Some("there"), opt);
673 t!(s: ".", filestem, None, opt);
674 t!(s: "/", filestem, None, opt);
675 t!(s: "foo/.bar", filestem, Some(".bar"), opt);
676 t!(s: ".bar", filestem, Some(".bar"), opt);
677 t!(s: "..bar", filestem, Some("."), opt);
678 t!(s: "hi/there..txt", filestem, Some("there."), opt);
679 t!(s: "..", filestem, None, opt);
680 t!(s: "../..", filestem, None, opt);
681
682 t!(v: b!("hi/there.txt"), extension, Some(b!("txt")));
683 t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt")));
684 t!(v: b!("hi/there.t", 0x80, "xt"), extension, Some(b!("t", 0x80, "xt")));
685 t!(v: b!("hi/there"), extension, None);
686 t!(v: b!("hi/there", 0x80), extension, None);
687 t!(s: "hi/there.txt", extension, Some("txt"), opt);
688 t!(s: "hi/there", extension, None, opt);
689 t!(s: "there.txt", extension, Some("txt"), opt);
690 t!(s: "there", extension, None, opt);
691 t!(s: ".", extension, None, opt);
692 t!(s: "/", extension, None, opt);
693 t!(s: "foo/.bar", extension, None, opt);
694 t!(s: ".bar", extension, None, opt);
695 t!(s: "..bar", extension, Some("bar"), opt);
696 t!(s: "hi/there..txt", extension, Some("txt"), opt);
697 t!(s: "..", extension, None, opt);
698 t!(s: "../..", extension, None, opt);
699 }
700
701 #[test]
702 fn test_push() {
703 macro_rules! t(
704 (s: $path:expr, $join:expr) => (
705 {
706 let path = $path;
707 let join = $join;
708 let mut p1 = Path::new(path);
709 let p2 = p1.clone();
710 p1.push(join);
711 assert!(p1 == p2.join(join));
712 }
713 )
714 )
715
716 t!(s: "a/b/c", "..");
717 t!(s: "/a/b/c", "d");
718 t!(s: "a/b", "c/d");
719 t!(s: "a/b", "/c/d");
720 }
721
722 #[test]
723 fn test_push_path() {
724 macro_rules! t(
725 (s: $path:expr, $push:expr, $exp:expr) => (
726 {
727 let mut p = Path::new($path);
728 let push = Path::new($push);
729 p.push(&push);
730 assert!(p.as_str() == Some($exp));
731 }
732 )
733 )
734
735 t!(s: "a/b/c", "d", "a/b/c/d");
736 t!(s: "/a/b/c", "d", "/a/b/c/d");
737 t!(s: "a/b", "c/d", "a/b/c/d");
738 t!(s: "a/b", "/c/d", "/c/d");
739 t!(s: "a/b", ".", "a/b");
740 t!(s: "a/b", "../c", "a/c");
741 }
742
743 #[test]
744 fn test_push_many() {
745 macro_rules! t(
746 (s: $path:expr, $push:expr, $exp:expr) => (
747 {
748 let mut p = Path::new($path);
749 p.push_many($push);
750 assert!(p.as_str() == Some($exp));
751 }
752 );
753 (v: $path:expr, $push:expr, $exp:expr) => (
754 {
755 let mut p = Path::new($path);
756 p.push_many($push);
757 assert!(p.as_vec() == $exp);
758 }
759 )
760 )
761
762 t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
763 t!(s: "a/b/c", ["d", "/e"], "/e");
764 t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
765 t!(s: "a/b/c", ["d".to_owned(), "e".to_owned()], "a/b/c/d/e");
766 t!(v: b!("a/b/c"), [b!("d"), b!("e")], b!("a/b/c/d/e"));
767 t!(v: b!("a/b/c"), [b!("d"), b!("/e"), b!("f")], b!("/e/f"));
768 t!(v: b!("a/b/c"), [Vec::from_slice(b!("d")), Vec::from_slice(b!("e"))], b!("a/b/c/d/e"));
769 }
770
771 #[test]
772 fn test_pop() {
773 macro_rules! t(
774 (s: $path:expr, $left:expr, $right:expr) => (
775 {
776 let mut p = Path::new($path);
777 let result = p.pop();
778 assert!(p.as_str() == Some($left));
779 assert!(result == $right);
780 }
781 );
782 (v: [$($path:expr),+], [$($left:expr),+], $right:expr) => (
783 {
784 let mut p = Path::new(b!($($path),+));
785 let result = p.pop();
786 assert!(p.as_vec() == b!($($left),+));
787 assert!(result == $right);
788 }
789 )
790 )
791
792 t!(v: ["a/b/c"], ["a/b"], true);
793 t!(v: ["a"], ["."], true);
794 t!(v: ["."], ["."], false);
795 t!(v: ["/a"], ["/"], true);
796 t!(v: ["/"], ["/"], false);
797 t!(v: ["a/b/c", 0x80], ["a/b"], true);
798 t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], true);
799 t!(v: [0xff], ["."], true);
800 t!(v: ["/", 0xff], ["/"], true);
801 t!(s: "a/b/c", "a/b", true);
802 t!(s: "a", ".", true);
803 t!(s: ".", ".", false);
804 t!(s: "/a", "/", true);
805 t!(s: "/", "/", false);
806 }
807
808 #[test]
809 fn test_root_path() {
810 assert!(Path::new(b!("a/b/c")).root_path() == None);
811 assert!(Path::new(b!("/a/b/c")).root_path() == Some(Path::new("/")));
812 }
813
814 #[test]
815 fn test_join() {
816 t!(v: Path::new(b!("a/b/c")).join(b!("..")), b!("a/b"));
817 t!(v: Path::new(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d"));
818 t!(v: Path::new(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff));
819 t!(s: Path::new("a/b/c").join(".."), "a/b");
820 t!(s: Path::new("/a/b/c").join("d"), "/a/b/c/d");
821 t!(s: Path::new("a/b").join("c/d"), "a/b/c/d");
822 t!(s: Path::new("a/b").join("/c/d"), "/c/d");
823 t!(s: Path::new(".").join("a/b"), "a/b");
824 t!(s: Path::new("/").join("a/b"), "/a/b");
825 }
826
827 #[test]
828 fn test_join_path() {
829 macro_rules! t(
830 (s: $path:expr, $join:expr, $exp:expr) => (
831 {
832 let path = Path::new($path);
833 let join = Path::new($join);
834 let res = path.join(&join);
835 assert!(res.as_str() == Some($exp));
836 }
837 )
838 )
839
840 t!(s: "a/b/c", "..", "a/b");
841 t!(s: "/a/b/c", "d", "/a/b/c/d");
842 t!(s: "a/b", "c/d", "a/b/c/d");
843 t!(s: "a/b", "/c/d", "/c/d");
844 t!(s: ".", "a/b", "a/b");
845 t!(s: "/", "a/b", "/a/b");
846 }
847
848 #[test]
849 fn test_join_many() {
850 macro_rules! t(
851 (s: $path:expr, $join:expr, $exp:expr) => (
852 {
853 let path = Path::new($path);
854 let res = path.join_many($join);
855 assert!(res.as_str() == Some($exp));
856 }
857 );
858 (v: $path:expr, $join:expr, $exp:expr) => (
859 {
860 let path = Path::new($path);
861 let res = path.join_many($join);
862 assert!(res.as_vec() == $exp);
863 }
864 )
865 )
866
867 t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
868 t!(s: "a/b/c", ["..", "d"], "a/b/d");
869 t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
870 t!(s: "a/b/c", ["d".to_owned(), "e".to_owned()], "a/b/c/d/e");
871 t!(v: b!("a/b/c"), [b!("d"), b!("e")], b!("a/b/c/d/e"));
872 t!(v: b!("a/b/c"), [Vec::from_slice(b!("d")), Vec::from_slice(b!("e"))], b!("a/b/c/d/e"));
873 }
874
875 #[test]
876 fn test_with_helpers() {
877 let empty: &[u8] = [];
878
879 t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d"));
880 t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80));
881 t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)),
882 b!("/", 0xff, "/", 0xcd));
883 t!(s: Path::new("a/b/c").with_filename("d"), "a/b/d");
884 t!(s: Path::new(".").with_filename("foo"), "foo");
885 t!(s: Path::new("/a/b/c").with_filename("d"), "/a/b/d");
886 t!(s: Path::new("/").with_filename("foo"), "/foo");
887 t!(s: Path::new("/a").with_filename("foo"), "/foo");
888 t!(s: Path::new("foo").with_filename("bar"), "bar");
889 t!(s: Path::new("/").with_filename("foo/"), "/foo");
890 t!(s: Path::new("/a").with_filename("foo/"), "/foo");
891 t!(s: Path::new("a/b/c").with_filename(""), "a/b");
892 t!(s: Path::new("a/b/c").with_filename("."), "a/b");
893 t!(s: Path::new("a/b/c").with_filename(".."), "a");
894 t!(s: Path::new("/a").with_filename(""), "/");
895 t!(s: Path::new("foo").with_filename(""), ".");
896 t!(s: Path::new("a/b/c").with_filename("d/e"), "a/b/d/e");
897 t!(s: Path::new("a/b/c").with_filename("/d"), "a/b/d");
898 t!(s: Path::new("..").with_filename("foo"), "../foo");
899 t!(s: Path::new("../..").with_filename("foo"), "../../foo");
900 t!(s: Path::new("..").with_filename(""), "..");
901 t!(s: Path::new("../..").with_filename(""), "../..");
902
903 t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")),
904 b!("hi/there", 0x80, ".exe"));
905 t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)),
906 b!("hi/there.", 0xff));
907 t!(v: Path::new(b!("hi/there", 0x80)).with_extension(b!(0xff)),
908 b!("hi/there", 0x80, ".", 0xff));
909 t!(v: Path::new(b!("hi/there.", 0xff)).with_extension(empty), b!("hi/there"));
910 t!(s: Path::new("hi/there.txt").with_extension("exe"), "hi/there.exe");
911 t!(s: Path::new("hi/there.txt").with_extension(""), "hi/there");
912 t!(s: Path::new("hi/there.txt").with_extension("."), "hi/there..");
913 t!(s: Path::new("hi/there.txt").with_extension(".."), "hi/there...");
914 t!(s: Path::new("hi/there").with_extension("txt"), "hi/there.txt");
915 t!(s: Path::new("hi/there").with_extension("."), "hi/there..");
916 t!(s: Path::new("hi/there").with_extension(".."), "hi/there...");
917 t!(s: Path::new("hi/there.").with_extension("txt"), "hi/there.txt");
918 t!(s: Path::new("hi/.foo").with_extension("txt"), "hi/.foo.txt");
919 t!(s: Path::new("hi/there.txt").with_extension(".foo"), "hi/there..foo");
920 t!(s: Path::new("/").with_extension("txt"), "/");
921 t!(s: Path::new("/").with_extension("."), "/");
922 t!(s: Path::new("/").with_extension(".."), "/");
923 t!(s: Path::new(".").with_extension("txt"), ".");
924 }
925
926 #[test]
927 fn test_setters() {
928 macro_rules! t(
929 (s: $path:expr, $set:ident, $with:ident, $arg:expr) => (
930 {
931 let path = $path;
932 let arg = $arg;
933 let mut p1 = Path::new(path);
934 p1.$set(arg);
935 let p2 = Path::new(path);
936 assert!(p1 == p2.$with(arg));
937 }
938 );
939 (v: $path:expr, $set:ident, $with:ident, $arg:expr) => (
940 {
941 let path = $path;
942 let arg = $arg;
943 let mut p1 = Path::new(path);
944 p1.$set(arg);
945 let p2 = Path::new(path);
946 assert!(p1 == p2.$with(arg));
947 }
948 )
949 )
950
951 t!(v: b!("a/b/c"), set_filename, with_filename, b!("d"));
952 t!(v: b!("/"), set_filename, with_filename, b!("foo"));
953 t!(v: b!(0x80), set_filename, with_filename, b!(0xff));
954 t!(s: "a/b/c", set_filename, with_filename, "d");
955 t!(s: "/", set_filename, with_filename, "foo");
956 t!(s: ".", set_filename, with_filename, "foo");
957 t!(s: "a/b", set_filename, with_filename, "");
958 t!(s: "a", set_filename, with_filename, "");
959
960 t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe"));
961 t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff));
962 t!(s: "hi/there.txt", set_extension, with_extension, "exe");
963 t!(s: "hi/there.", set_extension, with_extension, "txt");
964 t!(s: "hi/there", set_extension, with_extension, "txt");
965 t!(s: "hi/there.txt", set_extension, with_extension, "");
966 t!(s: "hi/there", set_extension, with_extension, "");
967 t!(s: ".", set_extension, with_extension, "txt");
968 }
969
970 #[test]
971 fn test_getters() {
972 macro_rules! t(
973 (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
974 {
975 let path = $path;
976 let filename = $filename;
977 assert!(path.filename_str() == filename,
978 "{}.filename_str(): Expected `{:?}`, found {:?}",
979 path.as_str().unwrap(), filename, path.filename_str());
980 let dirname = $dirname;
981 assert!(path.dirname_str() == dirname,
982 "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`",
983 path.as_str().unwrap(), dirname, path.dirname_str());
984 let filestem = $filestem;
985 assert!(path.filestem_str() == filestem,
986 "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`",
987 path.as_str().unwrap(), filestem, path.filestem_str());
988 let ext = $ext;
989 assert!(path.extension_str() == ext,
990 "`{}`.extension_str(): Expected `{:?}`, found `{:?}`",
991 path.as_str().unwrap(), ext, path.extension_str());
992 }
993 );
994 (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
995 {
996 let path = $path;
997 assert!(path.filename() == $filename);
998 assert!(path.dirname() == $dirname);
999 assert!(path.filestem() == $filestem);
1000 assert!(path.extension() == $ext);
1001 }
1002 )
1003 )
1004
1005 t!(v: Path::new(b!("a/b/c")), Some(b!("c")), b!("a/b"), Some(b!("c")), None);
1006 t!(v: Path::new(b!("a/b/", 0xff)), Some(b!(0xff)), b!("a/b"), Some(b!(0xff)), None);
1007 t!(v: Path::new(b!("hi/there.", 0xff)), Some(b!("there.", 0xff)), b!("hi"),
1008 Some(b!("there")), Some(b!(0xff)));
1009 t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None);
1010 t!(s: Path::new("."), None, Some("."), None, None);
1011 t!(s: Path::new("/"), None, Some("/"), None, None);
1012 t!(s: Path::new(".."), None, Some(".."), None, None);
1013 t!(s: Path::new("../.."), None, Some("../.."), None, None);
1014 t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"),
1015 Some("there"), Some("txt"));
1016 t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None);
1017 t!(s: Path::new("hi/there."), Some("there."), Some("hi"),
1018 Some("there"), Some(""));
1019 t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None);
1020 t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"),
1021 Some("."), Some("there"));
1022 t!(s: Path::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None);
1023 t!(s: Path::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt"));
1024 t!(s: Path::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None);
1025 t!(s: Path::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None);
1026 }
1027
1028 #[test]
1029 fn test_dir_path() {
1030 t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi"));
1031 t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff));
1032 t!(s: Path::new("hi/there").dir_path(), "hi");
1033 t!(s: Path::new("hi").dir_path(), ".");
1034 t!(s: Path::new("/hi").dir_path(), "/");
1035 t!(s: Path::new("/").dir_path(), "/");
1036 t!(s: Path::new("..").dir_path(), "..");
1037 t!(s: Path::new("../..").dir_path(), "../..");
1038 }
1039
1040 #[test]
1041 fn test_is_absolute() {
1042 macro_rules! t(
1043 (s: $path:expr, $abs:expr, $rel:expr) => (
1044 {
1045 let path = Path::new($path);
1046 assert_eq!(path.is_absolute(), $abs);
1047 assert_eq!(path.is_relative(), $rel);
1048 }
1049 )
1050 )
1051 t!(s: "a/b/c", false, true);
1052 t!(s: "/a/b/c", true, false);
1053 t!(s: "a", false, true);
1054 t!(s: "/a", true, false);
1055 t!(s: ".", false, true);
1056 t!(s: "/", true, false);
1057 t!(s: "..", false, true);
1058 t!(s: "../..", false, true);
1059 }
1060
1061 #[test]
1062 fn test_is_ancestor_of() {
1063 macro_rules! t(
1064 (s: $path:expr, $dest:expr, $exp:expr) => (
1065 {
1066 let path = Path::new($path);
1067 let dest = Path::new($dest);
1068 assert_eq!(path.is_ancestor_of(&dest), $exp);
1069 }
1070 )
1071 )
1072
1073 t!(s: "a/b/c", "a/b/c/d", true);
1074 t!(s: "a/b/c", "a/b/c", true);
1075 t!(s: "a/b/c", "a/b", false);
1076 t!(s: "/a/b/c", "/a/b/c", true);
1077 t!(s: "/a/b", "/a/b/c", true);
1078 t!(s: "/a/b/c/d", "/a/b/c", false);
1079 t!(s: "/a/b", "a/b/c", false);
1080 t!(s: "a/b", "/a/b/c", false);
1081 t!(s: "a/b/c", "a/b/d", false);
1082 t!(s: "../a/b/c", "a/b/c", false);
1083 t!(s: "a/b/c", "../a/b/c", false);
1084 t!(s: "a/b/c", "a/b/cd", false);
1085 t!(s: "a/b/cd", "a/b/c", false);
1086 t!(s: "../a/b", "../a/b/c", true);
1087 t!(s: ".", "a/b", true);
1088 t!(s: ".", ".", true);
1089 t!(s: "/", "/", true);
1090 t!(s: "/", "/a/b", true);
1091 t!(s: "..", "a/b", true);
1092 t!(s: "../..", "a/b", true);
1093 }
1094
1095 #[test]
1096 fn test_ends_with_path() {
1097 macro_rules! t(
1098 (s: $path:expr, $child:expr, $exp:expr) => (
1099 {
1100 let path = Path::new($path);
1101 let child = Path::new($child);
1102 assert_eq!(path.ends_with_path(&child), $exp);
1103 }
1104 );
1105 (v: $path:expr, $child:expr, $exp:expr) => (
1106 {
1107 let path = Path::new($path);
1108 let child = Path::new($child);
1109 assert_eq!(path.ends_with_path(&child), $exp);
1110 }
1111 )
1112 )
1113
1114 t!(s: "a/b/c", "c", true);
1115 t!(s: "a/b/c", "d", false);
1116 t!(s: "foo/bar/quux", "bar", false);
1117 t!(s: "foo/bar/quux", "barquux", false);
1118 t!(s: "a/b/c", "b/c", true);
1119 t!(s: "a/b/c", "a/b/c", true);
1120 t!(s: "a/b/c", "foo/a/b/c", false);
1121 t!(s: "/a/b/c", "a/b/c", true);
1122 t!(s: "/a/b/c", "/a/b/c", false); // child must be relative
1123 t!(s: "/a/b/c", "foo/a/b/c", false);
1124 t!(s: "a/b/c", "", false);
1125 t!(s: "", "", true);
1126 t!(s: "/a/b/c", "d/e/f", false);
1127 t!(s: "a/b/c", "a/b", false);
1128 t!(s: "a/b/c", "b", false);
1129 t!(v: b!("a/b/c"), b!("b/c"), true);
1130 t!(v: b!("a/b/", 0xff), b!(0xff), true);
1131 t!(v: b!("a/b/", 0xff), b!("b/", 0xff), true);
1132 }
1133
1134 #[test]
1135 fn test_path_relative_from() {
1136 macro_rules! t(
1137 (s: $path:expr, $other:expr, $exp:expr) => (
1138 {
1139 let path = Path::new($path);
1140 let other = Path::new($other);
1141 let res = path.path_relative_from(&other);
1142 assert_eq!(res.as_ref().and_then(|x| x.as_str()), $exp);
1143 }
1144 )
1145 )
1146
1147 t!(s: "a/b/c", "a/b", Some("c"));
1148 t!(s: "a/b/c", "a/b/d", Some("../c"));
1149 t!(s: "a/b/c", "a/b/c/d", Some(".."));
1150 t!(s: "a/b/c", "a/b/c", Some("."));
1151 t!(s: "a/b/c", "a/b/c/d/e", Some("../.."));
1152 t!(s: "a/b/c", "a/d/e", Some("../../b/c"));
1153 t!(s: "a/b/c", "d/e/f", Some("../../../a/b/c"));
1154 t!(s: "a/b/c", "/a/b/c", None);
1155 t!(s: "/a/b/c", "a/b/c", Some("/a/b/c"));
1156 t!(s: "/a/b/c", "/a/b/c/d", Some(".."));
1157 t!(s: "/a/b/c", "/a/b", Some("c"));
1158 t!(s: "/a/b/c", "/a/b/c/d/e", Some("../.."));
1159 t!(s: "/a/b/c", "/a/d/e", Some("../../b/c"));
1160 t!(s: "/a/b/c", "/d/e/f", Some("../../../a/b/c"));
1161 t!(s: "hi/there.txt", "hi/there", Some("../there.txt"));
1162 t!(s: ".", "a", Some(".."));
1163 t!(s: ".", "a/b", Some("../.."));
1164 t!(s: ".", ".", Some("."));
1165 t!(s: "a", ".", Some("a"));
1166 t!(s: "a/b", ".", Some("a/b"));
1167 t!(s: "..", ".", Some(".."));
1168 t!(s: "a/b/c", "a/b/c", Some("."));
1169 t!(s: "/a/b/c", "/a/b/c", Some("."));
1170 t!(s: "/", "/", Some("."));
1171 t!(s: "/", ".", Some("/"));
1172 t!(s: "../../a", "b", Some("../../../a"));
1173 t!(s: "a", "../../b", None);
1174 t!(s: "../../a", "../../b", Some("../a"));
1175 t!(s: "../../a", "../../a/b", Some(".."));
1176 t!(s: "../../a/b", "../../a", Some("b"));
1177 }
1178
1179 #[test]
1180 fn test_components_iter() {
1181 macro_rules! t(
1182 (s: $path:expr, $exp:expr) => (
1183 {
1184 let path = Path::new($path);
1185 let comps = path.components().collect::<Vec<&[u8]>>();
1186 let exp: &[&str] = $exp;
1187 let exps = exp.iter().map(|x| x.as_bytes()).collect::<Vec<&[u8]>>();
1188 assert!(comps == exps, "components: Expected {:?}, found {:?}",
1189 comps, exps);
1190 let comps = path.components().rev().collect::<Vec<&[u8]>>();
1191 let exps = exps.move_iter().rev().collect::<Vec<&[u8]>>();
1192 assert!(comps == exps, "rev_components: Expected {:?}, found {:?}",
1193 comps, exps);
1194 }
1195 );
1196 (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => (
1197 {
1198 let path = Path::new(b!($($arg),+));
1199 let comps = path.components().collect::<Vec<&[u8]>>();
1200 let exp: &[&[u8]] = [$(b!($($exp),*)),*];
1201 assert_eq!(comps.as_slice(), exp);
1202 let comps = path.components().rev().collect::<Vec<&[u8]>>();
1203 let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>();
1204 assert_eq!(comps, exp)
1205 }
1206 )
1207 )
1208
1209 t!(v: ["a/b/c"], [["a"], ["b"], ["c"]]);
1210 t!(v: ["/", 0xff, "/a/", 0x80], [[0xff], ["a"], [0x80]]);
1211 t!(v: ["../../foo", 0xcd, "bar"], [[".."], [".."], ["foo", 0xcd, "bar"]]);
1212 t!(s: "a/b/c", ["a", "b", "c"]);
1213 t!(s: "a/b/d", ["a", "b", "d"]);
1214 t!(s: "a/b/cd", ["a", "b", "cd"]);
1215 t!(s: "/a/b/c", ["a", "b", "c"]);
1216 t!(s: "a", ["a"]);
1217 t!(s: "/a", ["a"]);
1218 t!(s: "/", []);
1219 t!(s: ".", ["."]);
1220 t!(s: "..", [".."]);
1221 t!(s: "../..", ["..", ".."]);
1222 t!(s: "../../foo", ["..", "..", "foo"]);
1223 }
1224
1225 #[test]
1226 fn test_str_components() {
1227 macro_rules! t(
1228 (v: [$($arg:expr),+], $exp:expr) => (
1229 {
1230 let path = Path::new(b!($($arg),+));
1231 let comps = path.str_components().collect::<Vec<Option<&str>>>();
1232 let exp: &[Option<&str>] = $exp;
1233 assert_eq!(comps.as_slice(), exp);
1234 let comps = path.str_components().rev().collect::<Vec<Option<&str>>>();
1235 let exp = exp.iter().rev().map(|&x|x).collect::<Vec<Option<&str>>>();
1236 assert_eq!(comps, exp);
1237 }
1238 )
1239 )
1240
1241 t!(v: ["a/b/c"], [Some("a"), Some("b"), Some("c")]);
1242 t!(v: ["/", 0xff, "/a/", 0x80], [None, Some("a"), None]);
1243 t!(v: ["../../foo", 0xcd, "bar"], [Some(".."), Some(".."), None]);
1244 // str_components is a wrapper around components, so no need to do
1245 // the full set of tests
1246 }
1247 }
1248
1249 #[cfg(test)]
1250 mod bench {
1251 extern crate test;
1252 use self::test::Bencher;
1253 use super::*;
1254 use prelude::*;
1255
1256 #[bench]
1257 fn join_home_dir(b: &mut Bencher) {
1258 let posix_path = Path::new("/");
1259 b.iter(|| {
1260 posix_path.join("home");
1261 });
1262 }
1263
1264 #[bench]
1265 fn join_abs_path_home_dir(b: &mut Bencher) {
1266 let posix_path = Path::new("/");
1267 b.iter(|| {
1268 posix_path.join("/home");
1269 });
1270 }
1271
1272 #[bench]
1273 fn join_many_home_dir(b: &mut Bencher) {
1274 let posix_path = Path::new("/");
1275 b.iter(|| {
1276 posix_path.join_many(&["home"]);
1277 });
1278 }
1279
1280 #[bench]
1281 fn join_many_abs_path_home_dir(b: &mut Bencher) {
1282 let posix_path = Path::new("/");
1283 b.iter(|| {
1284 posix_path.join_many(&["/home"]);
1285 });
1286 }
1287
1288 #[bench]
1289 fn push_home_dir(b: &mut Bencher) {
1290 let mut posix_path = Path::new("/");
1291 b.iter(|| {
1292 posix_path.push("home");
1293 });
1294 }
1295
1296 #[bench]
1297 fn push_abs_path_home_dir(b: &mut Bencher) {
1298 let mut posix_path = Path::new("/");
1299 b.iter(|| {
1300 posix_path.push("/home");
1301 });
1302 }
1303
1304 #[bench]
1305 fn push_many_home_dir(b: &mut Bencher) {
1306 let mut posix_path = Path::new("/");
1307 b.iter(|| {
1308 posix_path.push_many(&["home"]);
1309 });
1310 }
1311
1312 #[bench]
1313 fn push_many_abs_path_home_dir(b: &mut Bencher) {
1314 let mut posix_path = Path::new("/");
1315 b.iter(|| {
1316 posix_path.push_many(&["/home"]);
1317 });
1318 }
1319
1320 #[bench]
1321 fn ends_with_path_home_dir(b: &mut Bencher) {
1322 let posix_home_path = Path::new("/home");
1323 b.iter(|| {
1324 posix_home_path.ends_with_path(&Path::new("home"));
1325 });
1326 }
1327
1328 #[bench]
1329 fn ends_with_path_missmatch_jome_home(b: &mut Bencher) {
1330 let posix_home_path = Path::new("/home");
1331 b.iter(|| {
1332 posix_home_path.ends_with_path(&Path::new("jome"));
1333 });
1334 }
1335
1336 #[bench]
1337 fn is_ancestor_of_path_with_10_dirs(b: &mut Bencher) {
1338 let path = Path::new("/home/1/2/3/4/5/6/7/8/9");
1339 let mut sub = path.clone();
1340 sub.pop();
1341 b.iter(|| {
1342 path.is_ancestor_of(&sub);
1343 });
1344 }
1345
1346 #[bench]
1347 fn path_relative_from_forward(b: &mut Bencher) {
1348 let path = Path::new("/a/b/c");
1349 let mut other = path.clone();
1350 other.pop();
1351 b.iter(|| {
1352 path.path_relative_from(&other);
1353 });
1354 }
1355
1356 #[bench]
1357 fn path_relative_from_same_level(b: &mut Bencher) {
1358 let path = Path::new("/a/b/c");
1359 let mut other = path.clone();
1360 other.pop();
1361 other.push("d");
1362 b.iter(|| {
1363 path.path_relative_from(&other);
1364 });
1365 }
1366
1367 #[bench]
1368 fn path_relative_from_backward(b: &mut Bencher) {
1369 let path = Path::new("/a/b");
1370 let mut other = path.clone();
1371 other.push("c");
1372 b.iter(|| {
1373 path.path_relative_from(&other);
1374 });
1375 }
1376 }