This commit is an implementation of [RFC 576][rfc] which adds back the `std::io`
module to the standard library. No functionality in `std::old_io` has been
deprecated just yet, and the new `std::io` module is behind the same `io`
feature gate.
[rfc]: https://github.com/rust-lang/rfcs/pull/576
A good bit of functionality was copied over from `std::old_io`, but many tweaks
were required for the new method signatures. Behavior such as precisely when
buffered objects call to the underlying object may have been tweaked slightly in
the transition. All implementations were audited to use composition wherever
possible. For example the custom `pos` and `cap` cursors in `BufReader` were
removed in favor of just using `Cursor<Vec<u8>>`.
A few liberties were taken during this implementation which were not explicitly
spelled out in the RFC:
* The old `LineBufferedWriter` is now named `LineWriter`
* The internal representation of `Error` now favors OS error codes (a
0-allocation path) and contains a `Box` for extra semantic data.
* The io prelude currently reexports `Seek` as `NewSeek` to prevent conflicts
with the real prelude reexport of `old_io::Seek`
* The `chars` method was moved from `BufReadExt` to `ReadExt`.
* The `chars` iterator returns a custom error with a variant that explains that
the data was not valid UTF-8.
.collect();
match strs.len() {
- 1u => (strs.pop().unwrap(), "".to_string()),
- 2u => {
+ 1 => (strs.pop().unwrap(), "".to_string()),
+ 2 => {
let end = strs.pop().unwrap();
(strs.pop().unwrap(), end)
}
let s = File::open(&filepath).read_to_end().unwrap();
String::from_utf8(s).unwrap()
}
- None => { srcs[srcs.len() - 2u].clone() }
+ None => { srcs[srcs.len() - 2].clone() }
};
- let mut actual = srcs[srcs.len() - 1u].clone();
+ let mut actual = srcs[srcs.len() - 1].clone();
if props.pp_exact.is_some() {
// Now we have to care about line endings
}).collect();
// check if each line in props.check_lines appears in the
// output (in order)
- let mut i = 0u;
+ let mut i = 0;
for line in debugger_run_result.stdout.lines() {
let mut rest = line.trim();
let mut first = true;
first = false;
}
if !failed && rest.len() == 0 {
- i += 1u;
+ i += 1;
}
if i == num_check_lines {
// all lines checked
fatal(format!("no error pattern specified in {:?}",
testfile.display()).as_slice());
}
- let mut next_err_idx = 0u;
+ let mut next_err_idx = 0;
let mut next_err_pat = &props.error_patterns[next_err_idx];
let mut done = false;
for line in output_to_check.lines() {
if line.contains(next_err_pat.as_slice()) {
debug!("found error pattern {}", next_err_pat);
- next_err_idx += 1u;
+ next_err_idx += 1;
if next_err_idx == props.error_patterns.len() {
debug!("found all error patterns");
done = true;
if done { return; }
let missing_patterns = &props.error_patterns[next_err_idx..];
- if missing_patterns.len() == 1u {
+ if missing_patterns.len() == 1 {
fatal_proc_rec(format!("error pattern '{}' not found!",
missing_patterns[0]).as_slice(),
proc_res);
}
fn is_compiler_error_or_warning(line: &str) -> bool {
- let mut i = 0u;
+ let mut i = 0;
return
scan_until_char(line, ':', &mut i) &&
scan_char(line, ':', &mut i) &&
fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
let mut haystack_i = *idx;
- let mut needle_i = 0u;
+ let mut needle_i = 0;
while needle_i < needle.len() {
if haystack_i >= haystack.len() {
return false;
impl Arena {
/// Allocates a new Arena with 32 bytes preallocated.
pub fn new() -> Arena {
- Arena::new_with_size(32u)
+ Arena::new_with_size(32)
}
/// Allocates a new Arena with `initial_size` bytes preallocated.
fn chunk(size: uint, is_copy: bool) -> Chunk {
Chunk {
data: Rc::new(RefCell::new(Vec::with_capacity(size))),
- fill: Cell::new(0u),
+ fill: Cell::new(0),
is_copy: Cell::new(is_copy),
}
}
self.chunks.borrow_mut().push(self.copy_head.borrow().clone());
*self.copy_head.borrow_mut() =
- chunk((new_min_chunk_size + 1u).next_power_of_two(), true);
+ chunk((new_min_chunk_size + 1).next_power_of_two(), true);
return self.alloc_copy_inner(n_bytes, align);
}
self.chunks.borrow_mut().push(self.head.borrow().clone());
*self.head.borrow_mut() =
- chunk((new_min_chunk_size + 1u).next_power_of_two(), false);
+ chunk((new_min_chunk_size + 1).next_power_of_two(), false);
return self.alloc_noncopy_inner(n_bytes, align);
}
#[test]
fn test_arena_destructors() {
let arena = Arena::new();
- for i in 0u..10 {
+ for i in 0..10 {
// Arena allocate something with drop glue to make sure it
// doesn't leak.
arena.alloc(|| Rc::new(i));
fn test_arena_destructors_fail() {
let arena = Arena::new();
// Put some stuff in the arena.
- for i in 0u..10 {
+ for i in 0..10 {
// Arena allocate something with drop glue to make sure it
// doesn't leak.
arena.alloc(|| { Rc::new(i) });
#[test]
pub fn test_copy() {
let arena = TypedArena::new();
- for _ in 0u..100000 {
+ for _ in 0..100000 {
arena.alloc(Point {
x: 1,
y: 2,
#[test]
pub fn test_noncopy() {
let arena = TypedArena::new();
- for _ in 0u..100000 {
+ for _ in 0..100000 {
arena.alloc(Noncopy {
string: "hello world".to_string(),
array: vec!( 1, 2, 3, 4, 5 ),
//! use std::error::FromError;
//! use std::old_io::{File, IoError};
//! use std::os::{MemoryMap, MapError};
-//! use std::path::Path;
+//! use std::old_path::Path;
//!
//! enum MyError {
//! Io(IoError),
fn test_flate_round_trip() {
let mut r = rand::thread_rng();
let mut words = vec!();
- for _ in 0u..20 {
- let range = r.gen_range(1u, 10);
+ for _ in 0..20 {
+ let range = r.gen_range(1, 10);
let v = r.gen_iter::<u8>().take(range).collect::<Vec<u8>>();
words.push(v);
}
- for _ in 0u..20 {
+ for _ in 0..20 {
let mut input = vec![];
- for _ in 0u..2000 {
+ for _ in 0..2000 {
input.push_all(r.choose(words.as_slice()).unwrap().as_slice());
}
debug!("de/inflate of {} bytes of random word-sequences",
impl Name {
fn from_str(nm: &str) -> Name {
- if nm.len() == 1u {
- Short(nm.char_at(0u))
+ if nm.len() == 1 {
+ Short(nm.char_at(0))
} else {
Long(nm.to_string())
}
}
i += 1;
}
- for i in 0u..n_opts {
+ for i in 0..n_opts {
let n = vals[i].len();
let occ = opts[i].occur;
if occ == Req && n == 0 {
impl<'a> GraphWalk<'a, Node, &'a Edge> for LabelledGraph {
fn nodes(&'a self) -> Nodes<'a,Node> {
- (0u..self.node_labels.len()).collect()
+ (0..self.node_labels.len()).collect()
}
fn edges(&'a self) -> Edges<'a,&'a Edge> {
self.edges.iter().collect()
use std::default::Default;
use std::fmt;
use std::iter::FromIterator;
-use std::path::BytesContainer;
+use std::old_path::BytesContainer;
use std::slice;
// Note 1: It is not clear whether the flexibility of providing both
pub iSecurityScheme: c_int,
pub dwMessageSize: DWORD,
pub dwProviderReserved: DWORD,
- pub szProtocol: [u8; (WSAPROTOCOL_LEN as uint) + 1u],
+ pub szProtocol: [u8; (WSAPROTOCOL_LEN as uint) + 1us],
}
pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO;
/// fn main() {
/// log!(log::WARN, "this is a warning {}", "message");
/// log!(log::DEBUG, "this is a debug message");
-/// log!(6, "this is a custom logging level: {level}", level=6u);
+/// log!(6, "this is a custom logging level: {level}", level=6);
/// }
/// ```
///
/// #[macro_use] extern crate log;
///
/// fn main() {
-/// let error = 3u;
+/// let error = 3;
/// error!("the build has failed with error code: {}", error);
/// }
/// ```
/// #[macro_use] extern crate log;
///
/// fn main() {
-/// let code = 3u;
+/// let code = 3;
/// warn!("you may like to know that a process exited with: {}", code);
/// }
/// ```
// Store the 17*i-th 32-bit word,
// i.e., the i-th word of the i-th 16-word block
let mut v : Vec<u32> = Vec::new();
- for _ in 0u..16 {
+ for _ in 0..16 {
v.push(ra.next_u32());
- for _ in 0u..16 {
+ for _ in 0..16 {
ra.next_u32();
}
}
let seed : &[_] = &[0u32; 8];
let mut rng: ChaChaRng = SeedableRng::from_seed(seed);
let mut clone = rng.clone();
- for _ in 0u..16 {
+ for _ in 0..16 {
assert_eq!(rng.next_u64(), clone.next_u64());
}
}
fn test_exp() {
let mut exp = Exp::new(10.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
assert!(exp.sample(&mut rng) >= 0.0);
assert!(exp.ind_sample(&mut rng) >= 0.0);
}
fn test_chi_squared_one() {
let mut chi = ChiSquared::new(1.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
chi.sample(&mut rng);
chi.ind_sample(&mut rng);
}
fn test_chi_squared_small() {
let mut chi = ChiSquared::new(0.5);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
chi.sample(&mut rng);
chi.ind_sample(&mut rng);
}
fn test_chi_squared_large() {
let mut chi = ChiSquared::new(30.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
chi.sample(&mut rng);
chi.ind_sample(&mut rng);
}
fn test_f() {
let mut f = FisherF::new(2.0, 32.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
f.sample(&mut rng);
f.ind_sample(&mut rng);
}
fn test_t() {
let mut t = StudentT::new(11.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
t.sample(&mut rng);
t.ind_sample(&mut rng);
}
/// Weighted { weight: 1, item: 'c' });
/// let wc = WeightedChoice::new(items.as_mut_slice());
/// let mut rng = rand::thread_rng();
-/// for _ in 0u..16 {
+/// for _ in 0..16 {
/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice.
/// println!("{}", wc.ind_sample(&mut rng));
/// }
// strictly speaking, this is subsumed by the total weight == 0 case
assert!(!items.is_empty(), "WeightedChoice::new called with no items");
- let mut running_total = 0u;
+ let mut running_total = 0;
// we convert the list from individual weights to cumulative
// weights so we can binary search. This *could* drop elements
fn test_normal() {
let mut norm = Normal::new(10.0, 10.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
norm.sample(&mut rng);
norm.ind_sample(&mut rng);
}
fn test_log_normal() {
let mut lnorm = LogNormal::new(10.0, 10.0);
let mut rng = ::test::rng();
- for _ in 0u..1000 {
+ for _ in 0..1000 {
lnorm.sample(&mut rng);
lnorm.ind_sample(&mut rng);
}
/// use std::rand::distributions::{IndependentSample, Range};
///
/// fn main() {
-/// let between = Range::new(10u, 10000u);
+/// let between = Range::new(10, 10000);
/// let mut rng = std::rand::thread_rng();
/// let mut sum = 0;
-/// for _ in 0u..1000 {
+/// for _ in 0..1000 {
/// sum += between.ind_sample(&mut rng);
/// }
/// println!("{}", sum);
(Int::min_value(), Int::max_value())];
for &(low, high) in v {
let mut sampler: Range<$ty> = Range::new(low, high);
- for _ in 0u..1000 {
+ for _ in 0..1000 {
let v = sampler.sample(&mut rng);
assert!(low <= v && v < high);
let v = sampler.ind_sample(&mut rng);
(-1e35, 1e35)];
for &(low, high) in v {
let mut sampler: Range<$ty> = Range::new(low, high);
- for _ in 0u..1000 {
+ for _ in 0..1000 {
let v = sampler.sample(&mut rng);
assert!(low <= v && v < high);
let v = sampler.ind_sample(&mut rng);
}}
}
- for _ in 0u..4 {
+ for _ in 0..4 {
mix!();
}
}}
}
- for i in range_step(0u, MIDPOINT, 4) {
+ for i in range_step(0, MIDPOINT, 4) {
rngstepp!(i + 0, 13);
rngstepn!(i + 1, 6);
rngstepp!(i + 2, 2);
}}
}
- for _ in 0u..4 {
+ for _ in 0..4 {
mix!();
}
}}
}
- rngstepp!(0u, 21);
- rngstepn!(1u, 5);
- rngstepp!(2u, 12);
- rngstepn!(3u, 33);
+ rngstepp!(0, 21);
+ rngstepn!(1, 5);
+ rngstepp!(2, 12);
+ rngstepn!(3, 33);
}
}
let seed: &[_] = &[12345, 67890, 54321, 9876];
let mut rb: IsaacRng = SeedableRng::from_seed(seed);
// skip forward to the 10000th number
- for _ in 0u..10000 { rb.next_u32(); }
+ for _ in 0..10000 { rb.next_u32(); }
let v = (0..10).map(|_| rb.next_u32()).collect::<Vec<_>>();
assert_eq!(v,
let seed: &[_] = &[12345, 67890, 54321, 9876];
let mut rb: Isaac64Rng = SeedableRng::from_seed(seed);
// skip forward to the 10000th number
- for _ in 0u..10000 { rb.next_u64(); }
+ for _ in 0..10000 { rb.next_u64(); }
let v = (0..10).map(|_| rb.next_u64()).collect::<Vec<_>>();
assert_eq!(v,
let seed: &[_] = &[1, 23, 456, 7890, 12345];
let mut rng: Isaac64Rng = SeedableRng::from_seed(seed);
let mut clone = rng.clone();
- for _ in 0u..16 {
+ for _ in 0..16 {
assert_eq!(rng.next_u64(), clone.next_u64());
}
}
/// use std::rand::{thread_rng, Rng};
///
/// let mut rng = thread_rng();
- /// let n: uint = rng.gen_range(0u, 10);
+ /// let n: uint = rng.gen_range(0, 10);
/// println!("{}", n);
/// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64);
/// println!("{}", m);
if values.is_empty() {
None
} else {
- Some(&values[self.gen_range(0u, values.len())])
+ Some(&values[self.gen_range(0, values.len())])
}
}
/// ```
fn shuffle<T>(&mut self, values: &mut [T]) {
let mut i = values.len();
- while i >= 2u {
+ while i >= 2 {
// invariant: elements with index >= i have been locked in place.
- i -= 1u;
+ i -= 1;
// lock element i in place.
- values.swap(i, self.gen_range(0u, i + 1u));
+ values.swap(i, self.gen_range(0, i + 1));
}
}
}
// this is unlikely to catch an incorrect implementation that
// generates exactly 0 or 1, but it keeps it sane.
let mut rng = thread_rng();
- for _ in 0u..1_000 {
+ for _ in 0..1_000 {
// strict inequalities
let Open01(f) = rng.gen::<Open01<f64>>();
assert!(0.0 < f && f < 1.0);
#[test]
fn rand_closed() {
let mut rng = thread_rng();
- for _ in 0u..1_000 {
+ for _ in 0..1_000 {
// strict inequalities
let Closed01(f) = rng.gen::<Closed01<f64>>();
assert!(0.0 <= f && f <= 1.0);
let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault);
let mut i = 0;
- for _ in 0u..1000 {
+ for _ in 0..1000 {
assert_eq!(rs.next_u32(), i % 100);
i += 1;
}
impl<'doc> Doc<'doc> {
pub fn new(data: &'doc [u8]) -> Doc<'doc> {
- Doc { data: data, start: 0u, end: data.len() }
+ Doc { data: data, start: 0, end: data.len() }
}
pub fn get<'a>(&'a self, tag: uint) -> Doc<'a> {
fn vuint_at_slow(data: &[u8], start: uint) -> DecodeResult<Res> {
let a = data[start];
if a & 0x80u8 != 0u8 {
- return Ok(Res {val: (a & 0x7fu8) as uint, next: start + 1u});
+ return Ok(Res {val: (a & 0x7fu8) as uint, next: start + 1});
}
if a & 0x40u8 != 0u8 {
- return Ok(Res {val: ((a & 0x3fu8) as uint) << 8u |
- (data[start + 1u] as uint),
- next: start + 2u});
+ return Ok(Res {val: ((a & 0x3fu8) as uint) << 8 |
+ (data[start + 1] as uint),
+ next: start + 2});
}
if a & 0x20u8 != 0u8 {
- return Ok(Res {val: ((a & 0x1fu8) as uint) << 16u |
- (data[start + 1u] as uint) << 8u |
- (data[start + 2u] as uint),
- next: start + 3u});
+ return Ok(Res {val: ((a & 0x1fu8) as uint) << 16 |
+ (data[start + 1] as uint) << 8 |
+ (data[start + 2] as uint),
+ next: start + 3});
}
if a & 0x10u8 != 0u8 {
- return Ok(Res {val: ((a & 0x0fu8) as uint) << 24u |
- (data[start + 1u] as uint) << 16u |
- (data[start + 2u] as uint) << 8u |
- (data[start + 3u] as uint),
- next: start + 4u});
+ return Ok(Res {val: ((a & 0x0fu8) as uint) << 24 |
+ (data[start + 1] as uint) << 16 |
+ (data[start + 2] as uint) << 8 |
+ (data[start + 3] as uint),
+ next: start + 4});
}
Err(IntTooBig(a as uint))
}
let ptr = data.as_ptr().offset(start as int) as *const u32;
let val = Int::from_be(*ptr);
- let i = (val >> 28u) as uint;
+ let i = (val >> 28) as uint;
let (shift, mask) = SHIFT_MASK_TABLE[i];
Ok(Res {
val: ((val >> shift) & mask) as uint,
pub fn doc_as_u8(d: Doc) -> u8 {
- assert_eq!(d.end, d.start + 1u);
+ assert_eq!(d.end, d.start + 1);
d.data[d.start]
}
pub fn doc_as_u16(d: Doc) -> u16 {
- assert_eq!(d.end, d.start + 2u);
- u64_from_be_bytes(d.data, d.start, 2u) as u16
+ assert_eq!(d.end, d.start + 2);
+ u64_from_be_bytes(d.data, d.start, 2) as u16
}
pub fn doc_as_u32(d: Doc) -> u32 {
- assert_eq!(d.end, d.start + 4u);
- u64_from_be_bytes(d.data, d.start, 4u) as u32
+ assert_eq!(d.end, d.start + 4);
+ u64_from_be_bytes(d.data, d.start, 4) as u32
}
pub fn doc_as_u64(d: Doc) -> u64 {
- assert_eq!(d.end, d.start + 8u);
- u64_from_be_bytes(d.data, d.start, 8u)
+ assert_eq!(d.end, d.start + 8);
+ u64_from_be_bytes(d.data, d.start, 8)
}
pub fn doc_as_i8(d: Doc) -> i8 { doc_as_u8(d) as i8 }
fn write_sized_vuint<W: Writer>(w: &mut W, n: uint, size: uint) -> EncodeResult {
match size {
- 1u => w.write_all(&[0x80u8 | (n as u8)]),
- 2u => w.write_all(&[0x40u8 | ((n >> 8_u) as u8), n as u8]),
- 3u => w.write_all(&[0x20u8 | ((n >> 16_u) as u8), (n >> 8_u) as u8,
+ 1 => w.write_all(&[0x80u8 | (n as u8)]),
+ 2 => w.write_all(&[0x40u8 | ((n >> 8) as u8), n as u8]),
+ 3 => w.write_all(&[0x20u8 | ((n >> 16) as u8), (n >> 8_u) as u8,
n as u8]),
- 4u => w.write_all(&[0x10u8 | ((n >> 24_u) as u8), (n >> 16_u) as u8,
+ 4 => w.write_all(&[0x10u8 | ((n >> 24) as u8), (n >> 16_u) as u8,
(n >> 8_u) as u8, n as u8]),
_ => Err(old_io::IoError {
kind: old_io::OtherIoError,
}
fn write_vuint<W: Writer>(w: &mut W, n: uint) -> EncodeResult {
- if n < 0x7f_u { return write_sized_vuint(w, n, 1u); }
- if n < 0x4000_u { return write_sized_vuint(w, n, 2u); }
- if n < 0x200000_u { return write_sized_vuint(w, n, 3u); }
- if n < 0x10000000_u { return write_sized_vuint(w, n, 4u); }
+ if n < 0x7f { return write_sized_vuint(w, n, 1); }
+ if n < 0x4000 { return write_sized_vuint(w, n, 2); }
+ if n < 0x200000 { return write_sized_vuint(w, n, 3); }
+ if n < 0x10000000 { return write_sized_vuint(w, n, 4); }
Err(old_io::IoError {
kind: old_io::OtherIoError,
desc: "int too big",
let cur_pos = try!(self.writer.tell());
try!(self.writer.seek(last_size_pos as i64, old_io::SeekSet));
let size = cur_pos as uint - last_size_pos - 4;
- try!(write_sized_vuint(self.writer, size, 4u));
+ try!(write_sized_vuint(self.writer, size, 4));
let r = try!(self.writer.seek(cur_pos as i64, old_io::SeekSet));
debug!("End tag (size = {:?})", size);
}
pub fn wr_tagged_u64(&mut self, tag_id: uint, v: u64) -> EncodeResult {
- u64_to_be_bytes(v, 8u, |v| {
+ u64_to_be_bytes(v, 8, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
pub fn wr_tagged_u32(&mut self, tag_id: uint, v: u32) -> EncodeResult{
- u64_to_be_bytes(v as u64, 4u, |v| {
+ u64_to_be_bytes(v as u64, 4, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
pub fn wr_tagged_u16(&mut self, tag_id: uint, v: u16) -> EncodeResult {
- u64_to_be_bytes(v as u64, 2u, |v| {
+ u64_to_be_bytes(v as u64, 2, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
}
pub fn wr_tagged_i64(&mut self, tag_id: uint, v: i64) -> EncodeResult {
- u64_to_be_bytes(v as u64, 8u, |v| {
+ u64_to_be_bytes(v as u64, 8, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
pub fn wr_tagged_i32(&mut self, tag_id: uint, v: i32) -> EncodeResult {
- u64_to_be_bytes(v as u64, 4u, |v| {
+ u64_to_be_bytes(v as u64, 4, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
pub fn wr_tagged_i16(&mut self, tag_id: uint, v: i16) -> EncodeResult {
- u64_to_be_bytes(v as u64, 2u, |v| {
+ u64_to_be_bytes(v as u64, 2, |v| {
self.wr_tagged_bytes(tag_id, v)
})
}
_ => i as u8,
}
}).collect::<Vec<_>>();
- let mut sum = 0u;
+ let mut sum = 0;
b.iter(|| {
let mut i = 0;
while i < data.len() {
_ => i as u8
}
}).collect::<Vec<_>>();
- let mut sum = 0u;
+ let mut sum = 0;
b.iter(|| {
let mut i = 1;
while i < data.len() {
_ => 0u8
}
}).collect::<Vec<_>>();
- let mut sum = 0u;
+ let mut sum = 0;
b.iter(|| {
let mut i = 0;
while i < data.len() {
_ => 0u8
}
}).collect::<Vec<_>>();
- let mut sum = 0u;
+ let mut sum = 0;
b.iter(|| {
let mut i = 1;
while i < data.len() {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-pub const BOX_FIELD_DROP_GLUE: uint = 1u;
-pub const BOX_FIELD_BODY: uint = 4u;
+pub const BOX_FIELD_DROP_GLUE: uint = 1;
+pub const BOX_FIELD_BODY: uint = 4;
/// The first half of a fat pointer.
/// - For a closure, this is the code address.
/// - For a closure, this is the address of the environment.
/// - For an object or trait instance, this is the address of the vtable.
/// - For a slice, this is the length.
-pub const FAT_PTR_EXTRA: uint = 1u;
+pub const FAT_PTR_EXTRA: uint = 1;
/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format.
fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
assert!(dst.len() * 4 == input.len());
- let mut pos = 0u;
+ let mut pos = 0;
for chunk in input.chunks(4) {
dst[pos] = read_u32_be(chunk);
pos += 1;
// Putting the message schedule inside the same loop as the round calculations allows for
// the compiler to generate better code.
- for t in range_step(0u, 48, 8) {
+ for t in range_step(0, 48, 8) {
schedule_round!(t + 16);
schedule_round!(t + 17);
schedule_round!(t + 18);
sha2_round!(b, c, d, e, f, g, h, a, K32, t + 7);
}
- for t in range_step(48u, 64, 8) {
+ for t in range_step(48, 64, 8) {
sha2_round!(a, b, c, d, e, f, g, h, K32, t);
sha2_round!(h, a, b, c, d, e, f, g, K32, t + 1);
sha2_round!(g, h, a, b, c, d, e, f, K32, t + 2);
sh.reset();
let len = t.input.len();
let mut left = len;
- while left > 0u {
- let take = (left + 1u) / 2u;
+ while left > 0 {
+ let take = (left + 1) / 2;
sh.input_str(&t.input[len - left..take + len - left]);
left = left - take;
}
let hash = state.finish();
return Svh {
- hash: range_step(0u, 64u, 4u).map(|i| hex(hash >> i)).collect()
+ hash: range_step(0, 64, 4).map(|i| hex(hash >> i)).collect()
};
fn hex(b: u64) -> char {
use std::env;
use std::ffi::OsString;
use std::old_io::File;
- use std::path::Path;
+ use std::old_path::Path;
use serialize::json;
fn load_file(path: &Path) -> Result<Target, String> {
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
let ofile = matches.opt_str("o").map(|o| Path::new(o));
let (input, input_file_path) = match matches.free.len() {
- 0u => {
+ 0 => {
if sopts.describe_lints {
let mut ls = lint::LintStore::new();
ls.register_builtin(None);
}
early_error("no input filename given");
}
- 1u => {
+ 1 => {
let ifile = &matches.free[0][];
if ifile == "-" {
let contents = old_io::stdin().read_to_end().unwrap();
};
let mut saw_node = ast::DUMMY_NODE_ID;
- let mut seen = 0u;
+ let mut seen = 0;
for node in self.all_matching_node_ids(map) {
saw_node = node;
seen += 1;
/// Resolves all imports for the crate. This method performs the fixed-
/// point iteration.
fn resolve_imports(&mut self) {
- let mut i = 0u;
+ let mut i = 0;
let mut prev_unresolved_imports = 0;
loop {
debug!("(resolving imports) iteration {}, {} imports left",
Some(ref p) if p.is_relative() => {
// prepend "./" if necessary
let dotdot = b"..";
- let prefix: &[u8] = &[dotdot[0], ::std::path::SEP_BYTE];
+ let prefix: &[u8] = &[dotdot[0], ::std::old_path::SEP_BYTE];
let mut path_bytes = p.as_vec().to_vec();
if &path_bytes[..2] != prefix &&
// a different region or mutability, but we don't care here. It might
// also be just in case we need to unsize. But if there are no nested
// adjustments then it should be a no-op).
- Some(ty::AutoPtr(_, _, None)) if adj.autoderefs == 1 => {
+ Some(ty::AutoPtr(_, _, None)) |
+ Some(ty::AutoUnsafe(_, None)) if adj.autoderefs == 1 => {
match datum.ty.sty {
// Don't skip a conversion from Box<T> to &T, etc.
ty::ty_rptr(..) => {
let num_supplied_types = supplied_method_types.len();
let num_method_types = pick.method_ty.generics.types.len(subst::FnSpace);
let method_types = {
- if num_supplied_types == 0u {
+ if num_supplied_types == 0 {
self.fcx.infcx().next_ty_vars(num_method_types)
- } else if num_method_types == 0u {
+ } else if num_method_types == 0 {
span_err!(self.tcx().sess, self.span, E0035,
"does not take type parameters");
self.fcx.infcx().next_ty_vars(num_method_types)
callee_expr: &ast::Expr,
error: MethodError)
{
+ // avoid suggestions when we don't know what's going on.
+ if ty::type_is_error(rcvr_ty) {
+ return
+ }
+
match error {
MethodError::NoMatch(static_sources, out_of_scope_traits) => {
let cx = fcx.tcx();
span_note!(fcx.sess(), method_span,
"candidate #{} is defined in an impl{} for the type `{}`",
- idx + 1u,
+ idx + 1,
insertion,
impl_ty.user_string(fcx.tcx()));
}
let method_span = fcx.tcx().map.def_id_span(method.def_id, span);
span_note!(fcx.sess(), method_span,
"candidate #{} is defined in the trait `{}`",
- idx + 1u,
+ idx + 1,
ty::item_path_str(fcx.tcx(), trait_did));
}
}
fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
- _rcvr_ty: Ty<'tcx>,
+ rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
valid_out_of_scope_traits: Vec<ast::DefId>)
{
return
}
- // there's no implemented traits, so lets suggest some traits to implement
+ let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty);
+
+ // there's no implemented traits, so lets suggest some traits to
+ // implement, by finding ones that have the method name, and are
+ // legal to implement.
let mut candidates = all_traits(fcx.ccx)
- .filter(|info| trait_method(tcx, info.def_id, method_name).is_some())
+ .filter(|info| {
+ // we approximate the coherence rules to only suggest
+ // traits that are legal to implement by requiring that
+ // either the type or trait is local. Multidispatch means
+ // this isn't perfect (that is, there are cases when
+ // implementing a trait would be legal but is rejected
+ // here).
+ (type_is_local || ast_util::is_local(info.def_id))
+ && trait_method(tcx, info.def_id, method_name).is_some()
+ })
.collect::<Vec<_>>();
if candidates.len() > 0 {
candidates.sort_by(|a, b| a.cmp(b).reverse());
candidates.dedup();
+ // FIXME #21673 this help message could be tuned to the case
+ // of a type parameter: suggest adding a trait bound rather
+ // than implementing.
let msg = format!(
"methods from traits can only be called if the trait is implemented and in scope; \
the following {traits_define} a method `{name}`, \
}
}
+/// Checks whether there is a local type somewhere in the chain of
+/// autoderefs of `rcvr_ty`.
+fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+ span: Span,
+ rcvr_ty: Ty<'tcx>) -> bool {
+ check::autoderef(fcx, span, rcvr_ty, None,
+ check::UnresolvedTypeAction::Ignore, check::NoPreference,
+ |&: ty, _| {
+ let is_local = match ty.sty {
+ ty::ty_enum(did, _) | ty::ty_struct(did, _) => ast_util::is_local(did),
+
+ ty::ty_trait(ref tr) => ast_util::is_local(tr.principal_def_id()),
+
+ ty::ty_param(_) => true,
+
+ // the user cannot implement traits for unboxed closures, so
+ // there's no point suggesting anything at all, local or not.
+ ty::ty_closure(..) => return Some(false),
+
+ // everything else (primitive types etc.) is effectively
+ // non-local (there are "edge" cases, e.g. (LocalType,), but
+ // the noise from these sort of types is usually just really
+ // annoying, rather than any sort of help).
+ _ => false
+ };
+ if is_local {
+ Some(true)
+ } else {
+ None
+ }
+ }).2.unwrap_or(false)
+}
+
#[derive(Copy)]
pub struct TraitInfo {
pub def_id: ast::DefId,
tps.len(), ppaux::ty_to_string(ccx.tcx, ty));
// make a vector of booleans initially false, set to true when used
- if tps.len() == 0u { return; }
+ if tps.len() == 0 { return; }
let mut tps_used: Vec<_> = repeat(false).take(tps.len()).collect();
ty::walk_ty(ty, |t| {
let (n_tps, inputs, output) = match name.get() {
"breakpoint" => (0, Vec::new(), ty::mk_nil(tcx)),
"size_of" |
- "pref_align_of" | "min_align_of" => (1u, Vec::new(), ccx.tcx.types.uint),
- "init" => (1u, Vec::new(), param(ccx, 0)),
- "uninit" => (1u, Vec::new(), param(ccx, 0)),
- "forget" => (1u, vec!( param(ccx, 0) ), ty::mk_nil(tcx)),
+ "pref_align_of" | "min_align_of" => (1, Vec::new(), ccx.tcx.types.uint),
+ "init" => (1, Vec::new(), param(ccx, 0)),
+ "uninit" => (1, Vec::new(), param(ccx, 0)),
+ "forget" => (1, vec!( param(ccx, 0) ), ty::mk_nil(tcx)),
"transmute" => (2, vec!( param(ccx, 0) ), param(ccx, 1)),
"move_val_init" => {
- (1u,
+ (1,
vec!(
ty::mk_mut_rptr(tcx,
tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
),
ty::mk_nil(tcx))
}
- "needs_drop" => (1u, Vec::new(), ccx.tcx.types.bool),
- "owns_managed" => (1u, Vec::new(), ccx.tcx.types.bool),
+ "needs_drop" => (1, Vec::new(), ccx.tcx.types.bool),
+ "owns_managed" => (1, Vec::new(), ccx.tcx.types.bool),
"get_tydesc" => {
let tydesc_ty = match ty::get_tydesc_ty(ccx.tcx) {
ty: tydesc_ty,
mutbl: ast::MutImmutable
});
- (1u, Vec::new(), td_ptr)
+ (1, Vec::new(), td_ptr)
}
- "type_id" => (1u, Vec::new(), ccx.tcx.types.u64),
+ "type_id" => (1, Vec::new(), ccx.tcx.types.u64),
"offset" => {
(1,
vec!(
derefd_ty.repr(rcx.tcx()));
let r_deref_expr = ty::ReScope(CodeExtent::from_node_id(deref_expr.id));
- for i in 0u..derefs {
+ for i in 0..derefs {
let method_call = MethodCall::autoderef(deref_expr.id, i);
debug!("constrain_autoderefs: method_call={:?} (of {:?} total)", method_call, derefs);
use std::rc::Rc;
use std::u32;
use std::str::Str as StrTrait; // Conflicts with Str variant
-use std::path::Path as FsPath; // Conflicts with Path struct
+use std::old_path::Path as FsPath; // Conflicts with Path struct
use core::DocContext;
use doctree;
fn decode<D: Decoder>(d: &mut D) -> Result<DList<T>, D::Error> {
d.read_seq(|d, len| {
let mut list = DList::new();
- for i in 0u..len {
+ for i in 0..len {
list.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
}
Ok(list)
fn decode<D: Decoder>(d: &mut D) -> Result<RingBuf<T>, D::Error> {
d.read_seq(|d, len| {
let mut deque: RingBuf<T> = RingBuf::new();
- for i in 0u..len {
+ for i in 0..len {
deque.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
}
Ok(deque)
fn decode<D: Decoder>(d: &mut D) -> Result<BTreeMap<K, V>, D::Error> {
d.read_map(|d, len| {
let mut map = BTreeMap::new();
- for i in 0u..len {
+ for i in 0..len {
let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
map.insert(key, val);
fn decode<D: Decoder>(d: &mut D) -> Result<BTreeSet<T>, D::Error> {
d.read_seq(|d, len| {
let mut set = BTreeSet::new();
- for i in 0u..len {
+ for i in 0..len {
set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
}
Ok(set)
d.read_map(|d, len| {
let state = Default::default();
let mut map = HashMap::with_capacity_and_hash_state(len, state);
- for i in 0u..len {
+ for i in 0..len {
let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
map.insert(key, val);
d.read_seq(|d, len| {
let state = Default::default();
let mut set = HashSet::with_capacity_and_hash_state(len, state);
- for i in 0u..len {
+ for i in 0..len {
set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
}
Ok(set)
fn decode<D: Decoder>(d: &mut D) -> Result<VecMap<V>, D::Error> {
d.read_map(|d, len| {
let mut map = VecMap::new();
- for i in 0u..len {
+ for i in 0..len {
let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
map.insert(key, val);
#[test]
pub fn test_to_hex_all_bytes() {
- for i in 0u..256 {
+ for i in 0..256 {
assert_eq!([i as u8].to_hex(), format!("{:02x}", i as uint));
}
}
#[test]
pub fn test_from_hex_all_bytes() {
- for i in 0u..256 {
+ for i in 0..256 {
let ii: &[u8] = &[i as u8];
assert_eq!(format!("{:02x}", i as uint).from_hex()
.unwrap(),
fn fmt_number_or_null(v: f64) -> string::String {
match v.classify() {
Fp::Nan | Fp::Infinite => string::String::from_str("null"),
- _ if v.fract() != 0f64 => f64::to_str_digits(v, 6u),
- _ => f64::to_str_digits(v, 6u) + ".0",
+ _ if v.fract() != 0f64 => f64::to_str_digits(v, 6),
+ _ => f64::to_str_digits(v, 6) + ".0",
}
}
self.ch = self.rdr.next();
if self.ch_is('\n') {
- self.line += 1u;
- self.col = 1u;
+ self.line += 1;
+ self.col = 1;
} else {
- self.col += 1u;
+ self.col += 1;
}
}
fn parse_exponent(&mut self, mut res: f64) -> Result<f64, ParserError> {
self.bump();
- let mut exp = 0u;
+ let mut exp = 0;
let mut neg_exp = false;
if self.ch_is('+') {
}
fn decode_hex_escape(&mut self) -> Result<u16, ParserError> {
- let mut i = 0u;
+ let mut i = 0;
let mut n = 0u16;
while i < 4 && !self.eof() {
self.bump();
_ => return self.error(InvalidEscape)
};
- i += 1u;
+ i += 1;
}
// Error out if we didn't parse 4 digits.
fn test_decode_option_some() {
let s = "{ \"opt\": 10 }";
let obj: OptionData = super::decode(s).unwrap();
- assert_eq!(obj, OptionData { opt: Some(10u) });
+ assert_eq!(obj, OptionData { opt: Some(10) });
}
#[test]
#[test]
fn test_decode_tuple() {
let t: (uint, uint, uint) = super::decode("[1, 2, 3]").unwrap();
- assert_eq!(t, (1u, 2, 3));
+ assert_eq!(t, (1, 2, 3));
let t: (uint, string::String) = super::decode("[1, \"two\"]").unwrap();
- assert_eq!(t, (1u, "two".to_string()));
+ assert_eq!(t, (1, "two".to_string()));
}
#[test]
#[test]
fn test_multiline_errors() {
assert_eq!(from_str("{\n \"foo\":\n \"bar\""),
- Err(SyntaxError(EOFWhileParsingObject, 3u, 8u)));
+ Err(SyntaxError(EOFWhileParsingObject, 3, 8)));
}
#[derive(RustcDecodable)]
}
// Test up to 4 spaces of indents (more?)
- for i in 0..4u {
+ for i in 0..4 {
let mut writer = Vec::new();
write!(&mut writer, "{}",
super::as_pretty_json(&json).indent(i)).unwrap();
assert_eq!(false.to_json(), Boolean(false));
assert_eq!("abc".to_json(), String("abc".to_string()));
assert_eq!("abc".to_string().to_json(), String("abc".to_string()));
- assert_eq!((1u, 2u).to_json(), array2);
- assert_eq!((1u, 2u, 3u).to_json(), array3);
- assert_eq!([1u, 2].to_json(), array2);
- assert_eq!((&[1u, 2, 3]).to_json(), array3);
- assert_eq!((vec![1u, 2]).to_json(), array2);
- assert_eq!(vec!(1u, 2, 3).to_json(), array3);
+ assert_eq!((1us, 2us).to_json(), array2);
+ assert_eq!((1us, 2us, 3us).to_json(), array3);
+ assert_eq!([1us, 2us].to_json(), array2);
+ assert_eq!((&[1us, 2us, 3us]).to_json(), array3);
+ assert_eq!((vec![1us, 2us]).to_json(), array2);
+ assert_eq!(vec!(1us, 2us, 3us).to_json(), array3);
let mut tree_map = BTreeMap::new();
- tree_map.insert("a".to_string(), 1u);
+ tree_map.insert("a".to_string(), 1us);
tree_map.insert("b".to_string(), 2);
assert_eq!(tree_map.to_json(), object);
let mut hash_map = HashMap::new();
- hash_map.insert("a".to_string(), 1u);
+ hash_map.insert("a".to_string(), 1us);
hash_map.insert("b".to_string(), 2);
assert_eq!(hash_map.to_json(), object);
assert_eq!(Some(15).to_json(), I64(15));
- assert_eq!(Some(15u).to_json(), U64(15));
+ assert_eq!(Some(15us).to_json(), U64(15));
assert_eq!(None::<int>.to_json(), Null);
}
Core encoding and decoding interfaces.
*/
-use std::path;
+use std::old_path;
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::sync::Arc;
/// Evaluates to the number of identifiers passed to it, for example: `count_idents!(a, b, c) == 3
macro_rules! count_idents {
- () => { 0u };
+ () => { 0 };
($_i:ident, $($rest:ident,)*) => { 1 + count_idents!($($rest,)*) }
}
tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
-impl Encodable for path::posix::Path {
+impl Encodable for old_path::posix::Path {
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
self.as_vec().encode(e)
}
}
-impl Decodable for path::posix::Path {
- fn decode<D: Decoder>(d: &mut D) -> Result<path::posix::Path, D::Error> {
+impl Decodable for old_path::posix::Path {
+ fn decode<D: Decoder>(d: &mut D) -> Result<old_path::posix::Path, D::Error> {
let bytes: Vec<u8> = try!(Decodable::decode(d));
- Ok(path::posix::Path::new(bytes))
+ Ok(old_path::posix::Path::new(bytes))
}
}
-impl Encodable for path::windows::Path {
+impl Encodable for old_path::windows::Path {
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
self.as_vec().encode(e)
}
}
-impl Decodable for path::windows::Path {
- fn decode<D: Decoder>(d: &mut D) -> Result<path::windows::Path, D::Error> {
+impl Decodable for old_path::windows::Path {
+ fn decode<D: Decoder>(d: &mut D) -> Result<old_path::windows::Path, D::Error> {
let bytes: Vec<u8> = try!(Decodable::decode(d));
- Ok(path::windows::Path::new(bytes))
+ Ok(old_path::windows::Path::new(bytes))
}
}
use super::state::HashState;
const INITIAL_LOG2_CAP: uint = 5;
+#[unstable(feature = "std_misc")]
pub const INITIAL_CAPACITY: uint = 1 << INITIAL_LOG2_CAP; // 2^5
/// The default behavior of HashMap implements a load factor of 90.9%.
/// typically declare an ability to explicitly hash into this particular type,
/// but rather in a `H: hash::Writer` type parameter.
#[allow(missing_copy_implementations)]
+#[unstable(feature = "std_misc",
+ reason = "hashing an hash maps may be altered")]
pub struct Hasher { inner: SipHasher }
impl hash::Writer for Hasher {
/// algorithm can implement the `Default` trait and create hash maps with the
/// `DefaultState` structure. This state is 0-sized and will simply delegate
/// to `Default` when asked to create a hasher.
+#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub trait HashState {
type Hasher: hash::Hasher;
/// default trait.
///
/// This struct has is 0-sized and does not need construction.
+#[unstable(feature = "std_misc", reason = "hasher stuff is unclear")]
pub struct DefaultState<H>;
impl<H: Default + hash::Hasher> HashState for DefaultState<H> {
///
/// ```rust
/// use std::env;
-/// use std::path::Path;
+/// use std::old_path::Path;
///
/// let root = Path::new("/");
/// assert!(env::set_current_dir(&root).is_ok());
mod c_str;
mod os_str;
+// FIXME (#21670): these should be defined in the os_str module
/// Freely convertible to an `&OsStr` slice.
pub trait AsOsStr {
/// Convert to an `&OsStr` slice.
use ops;
use cmp;
use hash::{Hash, Hasher, Writer};
-use path::{Path, GenericPath};
+use old_path::{Path, GenericPath};
use sys::os_str::{Buf, Slice};
use sys_common::{AsInner, IntoInner, FromInner};
pub mod os;
pub mod env;
pub mod path;
+pub mod old_path;
pub mod rand;
pub mod time;
use iter::{Iterator, Extend};
use option::Option;
use option::Option::{Some, None};
-use path::{Path, GenericPath};
-use path;
+use old_path::{Path, GenericPath};
+use old_path;
use result::Result::{Err, Ok};
use slice::SliceExt;
use string::String;
fn is_dir(&self) -> bool;
}
-impl PathExtensions for path::Path {
+impl PathExtensions for old_path::Path {
fn stat(&self) -> IoResult<FileStat> { stat(self) }
fn lstat(&self) -> IoResult<FileStat> { lstat(self) }
fn exists(&self) -> bool {
use prelude::v1::*;
use ffi::CString;
-use path::BytesContainer;
+use old_path::BytesContainer;
use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
use sys::pipe::UnixAcceptor as UnixAcceptorImp;
use sys::pipe::UnixListener as UnixListenerImp;
use old_io;
use libc;
use os;
-use path::BytesContainer;
+use old_path::BytesContainer;
use sync::mpsc::{channel, Receiver};
use sys::fs::FileDesc;
use sys::process::Process as ProcessImp;
use ops::Drop;
use option::Option::{None, Some};
use option::Option;
-use path::{Path, GenericPath};
+use old_path::{Path, GenericPath};
use rand::{Rng, thread_rng};
use result::Result::{Ok, Err};
use str::StrExt;
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Cross-platform path support
+//!
+//! This module implements support for two flavors of paths. `PosixPath` represents a path on any
+//! unix-like system, whereas `WindowsPath` represents a path on Windows. This module also exposes
+//! a typedef `Path` which is equal to the appropriate platform-specific path variant.
+//!
+//! Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which contains the set of
+//! methods that behave the same for both paths. They each also implement some methods that could
+//! not be expressed in `GenericPath`, yet behave identically for both path flavors, such as
+//! `.components()`.
+//!
+//! The three main design goals of this module are 1) to avoid unnecessary allocation, 2) to behave
+//! the same regardless of which flavor of path is being used, and 3) to support paths that cannot
+//! be represented in UTF-8 (as Linux has no restriction on paths beyond disallowing NUL).
+//!
+//! ## Usage
+//!
+//! Usage of this module is fairly straightforward. Unless writing platform-specific code, `Path`
+//! should be used to refer to the platform-native path.
+//!
+//! Creation of a path is typically done with either `Path::new(some_str)` or
+//! `Path::new(some_vec)`. This path can be modified with `.push()` and `.pop()` (and other
+//! setters). The resulting Path can either be passed to another API that expects a path, or can be
+//! turned into a `&[u8]` with `.as_vec()` or a `Option<&str>` with `.as_str()`. Similarly,
+//! attributes of the path can be queried with methods such as `.filename()`. There are also
+//! methods that return a new path instead of modifying the receiver, such as `.join()` or
+//! `.dir_path()`.
+//!
+//! Paths are always kept in normalized form. This means that creating the path
+//! `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt to mutate the path
+//! will always leave it in normalized form.
+//!
+//! When rendering a path to some form of output, there is a method `.display()` which is
+//! compatible with the `format!()` parameter `{}`. This will render the path as a string,
+//! replacing all non-utf8 sequences with the Replacement Character (U+FFFD). As such it is not
+//! suitable for passing to any API that actually operates on the path; it is only intended for
+//! display.
+//!
+//! ## Example
+//!
+//! ```rust
+//! use std::old_io::fs::PathExtensions;
+//!
+//! let mut path = Path::new("/tmp/path");
+//! println!("path: {}", path.display());
+//! path.set_filename("foo");
+//! path.push("bar");
+//! println!("new path: {}", path.display());
+//! println!("path exists: {}", path.exists());
+//! ```
+
+#![unstable(feature = "path")]
+
+use core::marker::Sized;
+use ffi::CString;
+use clone::Clone;
+use fmt;
+use iter::IteratorExt;
+use option::Option;
+use option::Option::{None, Some};
+use str;
+use str::StrExt;
+use string::{String, CowString};
+use slice::SliceExt;
+use vec::Vec;
+
+/// Typedef for POSIX file paths.
+/// See `posix::Path` for more info.
+pub use self::posix::Path as PosixPath;
+
+/// Typedef for Windows file paths.
+/// See `windows::Path` for more info.
+pub use self::windows::Path as WindowsPath;
+
+/// Typedef for the platform-native path type
+#[cfg(unix)]
+pub use self::posix::Path as Path;
+/// Typedef for the platform-native path type
+#[cfg(windows)]
+pub use self::windows::Path as Path;
+
+/// Typedef for the platform-native component iterator
+#[cfg(unix)]
+pub use self::posix::Components as Components;
+/// Typedef for the platform-native component iterator
+#[cfg(windows)]
+pub use self::windows::Components as Components;
+
+/// Typedef for the platform-native str component iterator
+#[cfg(unix)]
+pub use self::posix::StrComponents as StrComponents;
+/// Typedef for the platform-native str component iterator
+#[cfg(windows)]
+pub use self::windows::StrComponents as StrComponents;
+
+/// Alias for the platform-native separator character.
+#[cfg(unix)]
+pub use self::posix::SEP as SEP;
+/// Alias for the platform-native separator character.
+#[cfg(windows)]
+pub use self::windows::SEP as SEP;
+
+/// Alias for the platform-native separator byte.
+#[cfg(unix)]
+pub use self::posix::SEP_BYTE as SEP_BYTE;
+/// Alias for the platform-native separator byte.
+#[cfg(windows)]
+pub use self::windows::SEP_BYTE as SEP_BYTE;
+
+/// Typedef for the platform-native separator char func
+#[cfg(unix)]
+pub use self::posix::is_sep as is_sep;
+/// Typedef for the platform-native separator char func
+#[cfg(windows)]
+pub use self::windows::is_sep as is_sep;
+/// Typedef for the platform-native separator byte func
+#[cfg(unix)]
+pub use self::posix::is_sep_byte as is_sep_byte;
+/// Typedef for the platform-native separator byte func
+#[cfg(windows)]
+pub use self::windows::is_sep_byte as is_sep_byte;
+
+pub mod posix;
+pub mod windows;
+
+/// A trait that represents the generic operations available on paths
+pub trait GenericPath: Clone + GenericPathUnsafe {
+ /// Creates a new Path from a byte vector or string.
+ /// The resulting Path will always be normalized.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let path = Path::new("foo/bar");
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the path contains a NUL.
+ ///
+ /// See individual Path impls for additional restrictions.
+ #[inline]
+ fn new<T: BytesContainer>(path: T) -> Self {
+ assert!(!contains_nul(&path));
+ unsafe { GenericPathUnsafe::new_unchecked(path) }
+ }
+
+ /// Creates a new Path from a byte vector or string, if possible.
+ /// The resulting Path will always be normalized.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let x: &[u8] = b"foo\0";
+ /// assert!(Path::new_opt(x).is_none());
+ /// # }
+ /// ```
+ #[inline]
+ fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
+ if contains_nul(&path) {
+ None
+ } else {
+ Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
+ }
+ }
+
+ /// Returns the path as a string, if possible.
+ /// If the path is not representable in utf-8, this returns None.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("/abc/def");
+ /// assert_eq!(p.as_str(), Some("/abc/def"));
+ /// # }
+ /// ```
+ #[inline]
+ fn as_str<'a>(&'a self) -> Option<&'a str> {
+ str::from_utf8(self.as_vec()).ok()
+ }
+
+ /// Returns the path as a byte vector
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def");
+ /// assert_eq!(p.as_vec(), b"abc/def");
+ /// # }
+ /// ```
+ fn as_vec<'a>(&'a self) -> &'a [u8];
+
+ /// Converts the Path into an owned byte vector
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def");
+ /// assert_eq!(p.into_vec(), b"abc/def".to_vec());
+ /// // attempting to use p now results in "error: use of moved value"
+ /// # }
+ /// ```
+ fn into_vec(self) -> Vec<u8>;
+
+ /// Returns an object that implements `Show` for printing paths
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def");
+ /// println!("{}", p.display()); // prints "abc/def"
+ /// # }
+ /// ```
+ fn display<'a>(&'a self) -> Display<'a, Self> {
+ Display{ path: self, filename: false }
+ }
+
+ /// Returns an object that implements `Show` for printing filenames
+ ///
+ /// If there is no filename, nothing will be printed.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def");
+ /// println!("{}", p.filename_display()); // prints "def"
+ /// # }
+ /// ```
+ fn filename_display<'a>(&'a self) -> Display<'a, Self> {
+ Display{ path: self, filename: true }
+ }
+
+ /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
+ /// If `self` has no directory component, returns ['.'].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def/ghi");
+ /// assert_eq!(p.dirname(), b"abc/def");
+ /// # }
+ /// ```
+ fn dirname<'a>(&'a self) -> &'a [u8];
+
+ /// Returns the directory component of `self`, as a string, if possible.
+ /// See `dirname` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def/ghi");
+ /// assert_eq!(p.dirname_str(), Some("abc/def"));
+ /// # }
+ /// ```
+ #[inline]
+ fn dirname_str<'a>(&'a self) -> Option<&'a str> {
+ str::from_utf8(self.dirname()).ok()
+ }
+
+ /// Returns the file component of `self`, as a byte vector.
+ /// If `self` represents the root of the file hierarchy, returns None.
+ /// If `self` is "." or "..", returns None.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def/ghi");
+ /// assert_eq!(p.filename(), Some(b"ghi"));
+ /// # }
+ /// ```
+ fn filename<'a>(&'a self) -> Option<&'a [u8]>;
+
+ /// Returns the file component of `self`, as a string, if possible.
+ /// See `filename` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def/ghi");
+ /// assert_eq!(p.filename_str(), Some("ghi"));
+ /// # }
+ /// ```
+ #[inline]
+ fn filename_str<'a>(&'a self) -> Option<&'a str> {
+ self.filename().and_then(|s| str::from_utf8(s).ok())
+ }
+
+ /// Returns the stem of the filename of `self`, as a byte vector.
+ /// The stem is the portion of the filename just before the last '.'.
+ /// If there is no '.', the entire filename is returned.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("/abc/def.txt");
+ /// assert_eq!(p.filestem(), Some(b"def"));
+ /// # }
+ /// ```
+ fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
+ match self.filename() {
+ None => None,
+ Some(name) => Some({
+ let dot = b'.';
+ match name.rposition_elem(&dot) {
+ None | Some(0) => name,
+ Some(1) if name == b".." => name,
+ Some(pos) => &name[..pos]
+ }
+ })
+ }
+ }
+
+ /// Returns the stem of the filename of `self`, as a string, if possible.
+ /// See `filestem` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("/abc/def.txt");
+ /// assert_eq!(p.filestem_str(), Some("def"));
+ /// # }
+ /// ```
+ #[inline]
+ fn filestem_str<'a>(&'a self) -> Option<&'a str> {
+ self.filestem().and_then(|s| str::from_utf8(s).ok())
+ }
+
+ /// Returns the extension of the filename of `self`, as an optional byte vector.
+ /// The extension is the portion of the filename just after the last '.'.
+ /// If there is no extension, None is returned.
+ /// If the filename ends in '.', the empty vector is returned.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def.txt");
+ /// assert_eq!(p.extension(), Some(b"txt"));
+ /// # }
+ /// ```
+ fn extension<'a>(&'a self) -> Option<&'a [u8]> {
+ match self.filename() {
+ None => None,
+ Some(name) => {
+ let dot = b'.';
+ match name.rposition_elem(&dot) {
+ None | Some(0) => None,
+ Some(1) if name == b".." => None,
+ Some(pos) => Some(&name[pos+1..])
+ }
+ }
+ }
+ }
+
+ /// Returns the extension of the filename of `self`, as a string, if possible.
+ /// See `extension` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def.txt");
+ /// assert_eq!(p.extension_str(), Some("txt"));
+ /// # }
+ /// ```
+ #[inline]
+ fn extension_str<'a>(&'a self) -> Option<&'a str> {
+ self.extension().and_then(|s| str::from_utf8(s).ok())
+ }
+
+ /// Replaces the filename portion of the path with the given byte vector or string.
+ /// If the replacement name is [], this is equivalent to popping the path.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("abc/def.txt");
+ /// p.set_filename("foo.dat");
+ /// assert!(p == Path::new("abc/foo.dat"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the filename contains a NUL.
+ #[inline]
+ fn set_filename<T: BytesContainer>(&mut self, filename: T) {
+ assert!(!contains_nul(&filename));
+ unsafe { self.set_filename_unchecked(filename) }
+ }
+
+ /// Replaces the extension with the given byte vector or string.
+ /// If there is no extension in `self`, this adds one.
+ /// If the argument is [] or "", this removes the extension.
+ /// If `self` has no filename, this is a no-op.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("abc/def.txt");
+ /// p.set_extension("csv");
+ /// assert_eq!(p, Path::new("abc/def.csv"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the extension contains a NUL.
+ fn set_extension<T: BytesContainer>(&mut self, extension: T) {
+ assert!(!contains_nul(&extension));
+
+ let val = self.filename().and_then(|name| {
+ let dot = b'.';
+ let extlen = extension.container_as_bytes().len();
+ match (name.rposition_elem(&dot), extlen) {
+ (None, 0) | (Some(0), 0) => None,
+ (Some(idx), 0) => Some(name[..idx].to_vec()),
+ (idx, extlen) => {
+ let idx = match idx {
+ None | Some(0) => name.len(),
+ Some(val) => val
+ };
+
+ let mut v;
+ v = Vec::with_capacity(idx + extlen + 1);
+ v.push_all(&name[..idx]);
+ v.push(dot);
+ v.push_all(extension.container_as_bytes());
+ Some(v)
+ }
+ }
+ });
+
+ match val {
+ None => (),
+ Some(v) => unsafe { self.set_filename_unchecked(v) }
+ }
+ }
+
+ /// Returns a new Path constructed by replacing the filename with the given
+ /// byte vector or string.
+ /// See `set_filename` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("abc/def.txt");
+ /// assert_eq!(p.with_filename("foo.dat"), Path::new("abc/foo.dat"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the filename contains a NUL.
+ #[inline]
+ fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
+ let mut p = self.clone();
+ p.set_filename(filename);
+ p
+ }
+
+ /// Returns a new Path constructed by setting the extension to the given
+ /// byte vector or string.
+ /// See `set_extension` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("abc/def.txt");
+ /// assert_eq!(p.with_extension("csv"), Path::new("abc/def.csv"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the extension contains a NUL.
+ #[inline]
+ fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
+ let mut p = self.clone();
+ p.set_extension(extension);
+ p
+ }
+
+ /// Returns the directory component of `self`, as a Path.
+ /// If `self` represents the root of the filesystem hierarchy, returns `self`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def/ghi");
+ /// assert_eq!(p.dir_path(), Path::new("abc/def"));
+ /// # }
+ /// ```
+ fn dir_path(&self) -> Self {
+ // self.dirname() returns a NUL-free vector
+ unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
+ }
+
+ /// Returns a Path that represents the filesystem root that `self` is rooted in.
+ ///
+ /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// assert_eq!(Path::new("abc/def").root_path(), None);
+ /// assert_eq!(Path::new("/abc/def").root_path(), Some(Path::new("/")));
+ /// # }
+ /// ```
+ fn root_path(&self) -> Option<Self>;
+
+ /// Pushes a path (as a byte vector or string) onto `self`.
+ /// If the argument represents an absolute path, it replaces `self`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("foo/bar");
+ /// p.push("baz.txt");
+ /// assert_eq!(p, Path::new("foo/bar/baz.txt"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the path contains a NUL.
+ #[inline]
+ fn push<T: BytesContainer>(&mut self, path: T) {
+ assert!(!contains_nul(&path));
+ unsafe { self.push_unchecked(path) }
+ }
+
+ /// Pushes multiple paths (as byte vectors or strings) onto `self`.
+ /// See `push` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("foo");
+ /// p.push_many(&["bar", "baz.txt"]);
+ /// assert_eq!(p, Path::new("foo/bar/baz.txt"));
+ /// # }
+ /// ```
+ #[inline]
+ fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
+ let t: Option<&T> = None;
+ if BytesContainer::is_str(t) {
+ for p in paths {
+ self.push(p.container_as_str().unwrap())
+ }
+ } else {
+ for p in paths {
+ self.push(p.container_as_bytes())
+ }
+ }
+ }
+
+ /// Removes the last path component from the receiver.
+ /// Returns `true` if the receiver was modified, or `false` if it already
+ /// represented the root of the file hierarchy.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let mut p = Path::new("foo/bar/baz.txt");
+ /// p.pop();
+ /// assert_eq!(p, Path::new("foo/bar"));
+ /// # }
+ /// ```
+ fn pop(&mut self) -> bool;
+
+ /// Returns a new Path constructed by joining `self` with the given path
+ /// (as a byte vector or string).
+ /// If the given path is absolute, the new Path will represent just that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("/foo");
+ /// assert_eq!(p.join("bar.txt"), Path::new("/foo/bar.txt"));
+ /// # }
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the path contains a NUL.
+ #[inline]
+ fn join<T: BytesContainer>(&self, path: T) -> Self {
+ let mut p = self.clone();
+ p.push(path);
+ p
+ }
+
+ /// Returns a new Path constructed by joining `self` with the given paths
+ /// (as byte vectors or strings).
+ /// See `join` for details.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("foo");
+ /// let fbbq = Path::new("foo/bar/baz/quux.txt");
+ /// assert_eq!(p.join_many(&["bar", "baz", "quux.txt"]), fbbq);
+ /// # }
+ /// ```
+ #[inline]
+ fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
+ let mut p = self.clone();
+ p.push_many(paths);
+ p
+ }
+
+ /// Returns whether `self` represents an absolute path.
+ /// An absolute path is defined as one that, when joined to another path, will
+ /// yield back the same absolute path.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("/abc/def");
+ /// assert!(p.is_absolute());
+ /// # }
+ /// ```
+ fn is_absolute(&self) -> bool;
+
+ /// Returns whether `self` represents a relative path.
+ /// Typically this is the inverse of `is_absolute`.
+ /// But for Windows paths, it also means the path is not volume-relative or
+ /// relative to the current working directory.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("abc/def");
+ /// assert!(p.is_relative());
+ /// # }
+ /// ```
+ fn is_relative(&self) -> bool {
+ !self.is_absolute()
+ }
+
+ /// Returns whether `self` is equal to, or is an ancestor of, the given path.
+ /// If both paths are relative, they are compared as though they are relative
+ /// to the same parent path.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("foo/bar/baz/quux.txt");
+ /// let fb = Path::new("foo/bar");
+ /// let bq = Path::new("baz/quux.txt");
+ /// assert!(fb.is_ancestor_of(&p));
+ /// # }
+ /// ```
+ fn is_ancestor_of(&self, other: &Self) -> bool;
+
+ /// Returns the Path that, were it joined to `base`, would yield `self`.
+ /// If no such path exists, None is returned.
+ /// If `self` is absolute and `base` is relative, or on Windows if both
+ /// paths refer to separate drives, an absolute path is returned.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("foo/bar/baz/quux.txt");
+ /// let fb = Path::new("foo/bar");
+ /// let bq = Path::new("baz/quux.txt");
+ /// assert_eq!(p.path_relative_from(&fb), Some(bq));
+ /// # }
+ /// ```
+ fn path_relative_from(&self, base: &Self) -> Option<Self>;
+
+ /// Returns whether the relative path `child` is a suffix of `self`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # foo();
+ /// # #[cfg(windows)] fn foo() {}
+ /// # #[cfg(unix)] fn foo() {
+ /// let p = Path::new("foo/bar/baz/quux.txt");
+ /// let bq = Path::new("baz/quux.txt");
+ /// assert!(p.ends_with_path(&bq));
+ /// # }
+ /// ```
+ fn ends_with_path(&self, child: &Self) -> bool;
+}
+
+/// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
+pub trait BytesContainer {
+ /// Returns a &[u8] representing the receiver
+ fn container_as_bytes<'a>(&'a self) -> &'a [u8];
+ /// Returns the receiver interpreted as a utf-8 string, if possible
+ #[inline]
+ fn container_as_str<'a>(&'a self) -> Option<&'a str> {
+ str::from_utf8(self.container_as_bytes()).ok()
+ }
+ /// Returns whether .container_as_str() is guaranteed to not fail
+ // FIXME (#8888): Remove unused arg once ::<for T> works
+ #[inline]
+ fn is_str(_: Option<&Self>) -> bool { false }
+}
+
+/// A trait that represents the unsafe operations on GenericPaths
+pub trait GenericPathUnsafe {
+ /// Creates a new Path without checking for null bytes.
+ /// The resulting Path will always be normalized.
+ unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
+
+ /// Replaces the filename portion of the path without checking for null
+ /// bytes.
+ /// See `set_filename` for details.
+ unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
+
+ /// Pushes a path onto `self` without checking for null bytes.
+ /// See `push` for details.
+ unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
+}
+
+/// Helper struct for printing paths with format!()
+pub struct Display<'a, P:'a> {
+ path: &'a P,
+ filename: bool
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, P: GenericPath> fmt::Debug for Display<'a, P> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.as_cow(), f)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, P: GenericPath> fmt::Display for Display<'a, P> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.as_cow().fmt(f)
+ }
+}
+
+impl<'a, P: GenericPath> Display<'a, P> {
+ /// Returns the path as a possibly-owned string.
+ ///
+ /// If the path is not UTF-8, invalid sequences will be replaced with the
+ /// Unicode replacement char. This involves allocation.
+ #[inline]
+ pub fn as_cow(&self) -> CowString<'a> {
+ String::from_utf8_lossy(if self.filename {
+ match self.path.filename() {
+ None => {
+ let result: &[u8] = &[];
+ result
+ }
+ Some(v) => v
+ }
+ } else {
+ self.path.as_vec()
+ })
+ }
+}
+
+impl BytesContainer for str {
+ #[inline]
+ fn container_as_bytes(&self) -> &[u8] {
+ self.as_bytes()
+ }
+ #[inline]
+ fn container_as_str(&self) -> Option<&str> {
+ Some(self)
+ }
+ #[inline]
+ fn is_str(_: Option<&str>) -> bool { true }
+}
+
+impl BytesContainer for String {
+ #[inline]
+ fn container_as_bytes(&self) -> &[u8] {
+ self.as_bytes()
+ }
+ #[inline]
+ fn container_as_str(&self) -> Option<&str> {
+ Some(&self[])
+ }
+ #[inline]
+ fn is_str(_: Option<&String>) -> bool { true }
+}
+
+impl BytesContainer for [u8] {
+ #[inline]
+ fn container_as_bytes(&self) -> &[u8] {
+ self
+ }
+}
+
+impl BytesContainer for Vec<u8> {
+ #[inline]
+ fn container_as_bytes(&self) -> &[u8] {
+ &self[]
+ }
+}
+
+impl BytesContainer for CString {
+ #[inline]
+ fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+ self.as_bytes()
+ }
+}
+
+impl<'a, T: ?Sized + BytesContainer> BytesContainer for &'a T {
+ #[inline]
+ fn container_as_bytes(&self) -> &[u8] {
+ (**self).container_as_bytes()
+ }
+ #[inline]
+ fn container_as_str(&self) -> Option<&str> {
+ (**self).container_as_str()
+ }
+ #[inline]
+ fn is_str(_: Option<& &'a T>) -> bool { BytesContainer::is_str(None::<&T>) }
+}
+
+#[inline(always)]
+fn contains_nul<T: BytesContainer>(v: &T) -> bool {
+ v.container_as_bytes().iter().any(|&x| x == 0)
+}
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! POSIX file path handling
+
+use clone::Clone;
+use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
+use fmt;
+use hash;
+use old_io::Writer;
+use iter::{AdditiveIterator, Extend};
+use iter::{Iterator, IteratorExt, Map};
+use marker::Sized;
+use option::Option::{self, Some, None};
+use result::Result::{self, Ok, Err};
+use slice::{AsSlice, Split, SliceExt, SliceConcatExt};
+use str::{self, FromStr, StrExt};
+use vec::Vec;
+
+use super::{BytesContainer, GenericPath, GenericPathUnsafe};
+
+/// Iterator that yields successive components of a Path as &[u8]
+pub type Components<'a> = Split<'a, u8, fn(&u8) -> bool>;
+
+/// Iterator that yields successive components of a Path as Option<&str>
+pub type StrComponents<'a> =
+ Map<Components<'a>, fn(&[u8]) -> Option<&str>>;
+
+/// Represents a POSIX file path
+#[derive(Clone)]
+pub struct Path {
+ repr: Vec<u8>, // assumed to never be empty or contain NULs
+ sepidx: Option<uint> // index of the final separator in repr
+}
+
+/// The standard path separator character
+pub const SEP: char = '/';
+
+/// The standard path separator byte
+pub const SEP_BYTE: u8 = SEP as u8;
+
+/// Returns whether the given byte is a path separator
+#[inline]
+pub fn is_sep_byte(u: &u8) -> bool {
+ *u as char == SEP
+}
+
+/// Returns whether the given char is a path separator
+#[inline]
+pub fn is_sep(c: char) -> bool {
+ c == SEP
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for Path {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.display(), f)
+ }
+}
+
+impl PartialEq for Path {
+ #[inline]
+ fn eq(&self, other: &Path) -> bool {
+ self.repr == other.repr
+ }
+}
+
+impl Eq for Path {}
+
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.repr.cmp(&other.repr)
+ }
+}
+
+impl FromStr for Path {
+ type Err = ParsePathError;
+ fn from_str(s: &str) -> Result<Path, ParsePathError> {
+ match Path::new_opt(s) {
+ Some(p) => Ok(p),
+ None => Err(ParsePathError),
+ }
+ }
+}
+
+/// Valuelue indicating that a path could not be parsed from a string.
+#[derive(Debug, Clone, PartialEq, Copy)]
+pub struct ParsePathError;
+
+impl<S: hash::Writer + hash::Hasher> hash::Hash<S> for Path {
+ #[inline]
+ fn hash(&self, state: &mut S) {
+ self.repr.hash(state)
+ }
+}
+
+impl BytesContainer for Path {
+ #[inline]
+ fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+ self.as_vec()
+ }
+}
+
+impl GenericPathUnsafe for Path {
+ unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
+ let path = Path::normalize(path.container_as_bytes());
+ assert!(!path.is_empty());
+ let idx = path.rposition_elem(&SEP_BYTE);
+ Path{ repr: path, sepidx: idx }
+ }
+
+ unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
+ let filename = filename.container_as_bytes();
+ match self.sepidx {
+ None if b".." == self.repr => {
+ let mut v = Vec::with_capacity(3 + filename.len());
+ v.push_all(dot_dot_static);
+ v.push(SEP_BYTE);
+ v.push_all(filename);
+ // FIXME: this is slow
+ self.repr = Path::normalize(v.as_slice());
+ }
+ None => {
+ self.repr = Path::normalize(filename);
+ }
+ Some(idx) if &self.repr[idx+1..] == b".." => {
+ let mut v = Vec::with_capacity(self.repr.len() + 1 + filename.len());
+ v.push_all(self.repr.as_slice());
+ v.push(SEP_BYTE);
+ v.push_all(filename);
+ // FIXME: this is slow
+ self.repr = Path::normalize(v.as_slice());
+ }
+ Some(idx) => {
+ let mut v = Vec::with_capacity(idx + 1 + filename.len());
+ v.push_all(&self.repr[..idx+1]);
+ v.push_all(filename);
+ // FIXME: this is slow
+ self.repr = Path::normalize(v.as_slice());
+ }
+ }
+ self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
+ }
+
+ unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
+ let path = path.container_as_bytes();
+ if !path.is_empty() {
+ if path[0] == SEP_BYTE {
+ self.repr = Path::normalize(path);
+ } else {
+ let mut v = Vec::with_capacity(self.repr.len() + path.len() + 1);
+ v.push_all(self.repr.as_slice());
+ v.push(SEP_BYTE);
+ v.push_all(path);
+ // FIXME: this is slow
+ self.repr = Path::normalize(v.as_slice());
+ }
+ self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
+ }
+ }
+}
+
+impl GenericPath for Path {
+ #[inline]
+ fn as_vec<'a>(&'a self) -> &'a [u8] {
+ self.repr.as_slice()
+ }
+
+ fn into_vec(self) -> Vec<u8> {
+ self.repr
+ }
+
+ fn dirname<'a>(&'a self) -> &'a [u8] {
+ match self.sepidx {
+ None if b".." == self.repr => self.repr.as_slice(),
+ None => dot_static,
+ Some(0) => &self.repr[..1],
+ Some(idx) if &self.repr[idx+1..] == b".." => self.repr.as_slice(),
+ Some(idx) => &self.repr[..idx]
+ }
+ }
+
+ fn filename<'a>(&'a self) -> Option<&'a [u8]> {
+ match self.sepidx {
+ None if b"." == self.repr ||
+ b".." == self.repr => None,
+ None => Some(self.repr.as_slice()),
+ Some(idx) if &self.repr[idx+1..] == b".." => None,
+ Some(0) if self.repr[1..].is_empty() => None,
+ Some(idx) => Some(&self.repr[idx+1..])
+ }
+ }
+
+ fn pop(&mut self) -> bool {
+ match self.sepidx {
+ None if b"." == self.repr => false,
+ None => {
+ self.repr = vec![b'.'];
+ self.sepidx = None;
+ true
+ }
+ Some(0) if b"/" == self.repr => false,
+ Some(idx) => {
+ if idx == 0 {
+ self.repr.truncate(idx+1);
+ } else {
+ self.repr.truncate(idx);
+ }
+ self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
+ true
+ }
+ }
+ }
+
+ fn root_path(&self) -> Option<Path> {
+ if self.is_absolute() {
+ Some(Path::new("/"))
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn is_absolute(&self) -> bool {
+ self.repr[0] == SEP_BYTE
+ }
+
+ fn is_ancestor_of(&self, other: &Path) -> bool {
+ if self.is_absolute() != other.is_absolute() {
+ false
+ } else {
+ let mut ita = self.components();
+ let mut itb = other.components();
+ if b"." == self.repr {
+ return match itb.next() {
+ None => true,
+ Some(b) => b != b".."
+ };
+ }
+ loop {
+ match (ita.next(), itb.next()) {
+ (None, _) => break,
+ (Some(a), Some(b)) if a == b => { continue },
+ (Some(a), _) if a == b".." => {
+ // if ita contains only .. components, it's an ancestor
+ return ita.all(|x| x == b"..");
+ }
+ _ => return false
+ }
+ }
+ true
+ }
+ }
+
+ fn path_relative_from(&self, base: &Path) -> Option<Path> {
+ if self.is_absolute() != base.is_absolute() {
+ if self.is_absolute() {
+ Some(self.clone())
+ } else {
+ None
+ }
+ } else {
+ let mut ita = self.components();
+ let mut itb = base.components();
+ let mut comps = vec![];
+ loop {
+ match (ita.next(), itb.next()) {
+ (None, None) => break,
+ (Some(a), None) => {
+ comps.push(a);
+ comps.extend(ita.by_ref());
+ break;
+ }
+ (None, _) => comps.push(dot_dot_static),
+ (Some(a), Some(b)) if comps.is_empty() && a == b => (),
+ (Some(a), Some(b)) if b == b"." => comps.push(a),
+ (Some(_), Some(b)) if b == b".." => return None,
+ (Some(a), Some(_)) => {
+ comps.push(dot_dot_static);
+ for _ in itb {
+ comps.push(dot_dot_static);
+ }
+ comps.push(a);
+ comps.extend(ita.by_ref());
+ break;
+ }
+ }
+ }
+ Some(Path::new(comps.connect(&SEP_BYTE)))
+ }
+ }
+
+ fn ends_with_path(&self, child: &Path) -> bool {
+ if !child.is_relative() { return false; }
+ let mut selfit = self.components().rev();
+ let mut childit = child.components().rev();
+ loop {
+ match (selfit.next(), childit.next()) {
+ (Some(a), Some(b)) => if a != b { return false; },
+ (Some(_), None) => break,
+ (None, Some(_)) => return false,
+ (None, None) => break
+ }
+ }
+ true
+ }
+}
+
+impl Path {
+ /// Returns a new Path from a byte vector or string
+ ///
+ /// # Panics
+ ///
+ /// Panics the task if the vector contains a NUL.
+ #[inline]
+ pub fn new<T: BytesContainer>(path: T) -> Path {
+ GenericPath::new(path)
+ }
+
+ /// Returns a new Path from a byte vector or string, if possible
+ #[inline]
+ pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
+ GenericPath::new_opt(path)
+ }
+
+ /// Returns a normalized byte vector representation of a path, by removing all empty
+ /// components, and unnecessary . and .. components.
+ fn normalize<V: ?Sized + AsSlice<u8>>(v: &V) -> Vec<u8> {
+ // borrowck is being very picky
+ let val = {
+ let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == SEP_BYTE;
+ let v_ = if is_abs { &v.as_slice()[1..] } else { v.as_slice() };
+ let comps = normalize_helper(v_, is_abs);
+ match comps {
+ None => None,
+ Some(comps) => {
+ if is_abs && comps.is_empty() {
+ Some(vec![SEP_BYTE])
+ } else {
+ let n = if is_abs { comps.len() } else { comps.len() - 1} +
+ comps.iter().map(|v| v.len()).sum();
+ let mut v = Vec::with_capacity(n);
+ let mut it = comps.into_iter();
+ if !is_abs {
+ match it.next() {
+ None => (),
+ Some(comp) => v.push_all(comp)
+ }
+ }
+ for comp in it {
+ v.push(SEP_BYTE);
+ v.push_all(comp);
+ }
+ Some(v)
+ }
+ }
+ }
+ };
+ match val {
+ None => v.as_slice().to_vec(),
+ Some(val) => val
+ }
+ }
+
+ /// Returns an iterator that yields each component of the path in turn.
+ /// Does not distinguish between absolute and relative paths, e.g.
+ /// /a/b/c and a/b/c yield the same set of components.
+ /// A path of "/" yields no components. A path of "." yields one component.
+ pub fn components<'a>(&'a self) -> Components<'a> {
+ let v = if self.repr[0] == SEP_BYTE {
+ &self.repr[1..]
+ } else { self.repr.as_slice() };
+ let is_sep_byte: fn(&u8) -> bool = is_sep_byte; // coerce to fn ptr
+ let mut ret = v.split(is_sep_byte);
+ if v.is_empty() {
+ // consume the empty "" component
+ ret.next();
+ }
+ ret
+ }
+
+ /// Returns an iterator that yields each component of the path as Option<&str>.
+ /// See components() for details.
+ pub fn str_components<'a>(&'a self) -> StrComponents<'a> {
+ fn from_utf8(s: &[u8]) -> Option<&str> {
+ str::from_utf8(s).ok()
+ }
+ let f: fn(&[u8]) -> Option<&str> = from_utf8; // coerce to fn ptr
+ self.components().map(f)
+ }
+}
+
+// None result means the byte vector didn't need normalizing
+fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<Vec<&'a [u8]>> {
+ if is_abs && v.is_empty() {
+ return None;
+ }
+ let mut comps: Vec<&'a [u8]> = vec![];
+ let mut n_up = 0u;
+ let mut changed = false;
+ for comp in v.split(is_sep_byte) {
+ if comp.is_empty() { changed = true }
+ else if comp == b"." { changed = true }
+ else if comp == b".." {
+ if is_abs && comps.is_empty() { changed = true }
+ else if comps.len() == n_up { comps.push(dot_dot_static); n_up += 1 }
+ else { comps.pop().unwrap(); changed = true }
+ } else { comps.push(comp) }
+ }
+ if changed {
+ if comps.is_empty() && !is_abs {
+ if v == b"." {
+ return None;
+ }
+ comps.push(dot_static);
+ }
+ Some(comps)
+ } else {
+ None
+ }
+}
+
+#[allow(non_upper_case_globals)]
+static dot_static: &'static [u8] = b".";
+#[allow(non_upper_case_globals)]
+static dot_dot_static: &'static [u8] = b"..";
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use clone::Clone;
+ use iter::IteratorExt;
+ use option::Option::{self, Some, None};
+ use old_path::GenericPath;
+ use slice::{AsSlice, SliceExt};
+ use str::{self, Str, StrExt};
+ use string::ToString;
+ use vec::Vec;
+
+ macro_rules! t {
+ (s: $path:expr, $exp:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $exp:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.as_vec(), $exp);
+ }
+ )
+ }
+
+ #[test]
+ fn test_paths() {
+ let empty: &[u8] = &[];
+ t!(v: Path::new(empty), b".");
+ t!(v: Path::new(b"/"), b"/");
+ t!(v: Path::new(b"a/b/c"), b"a/b/c");
+ t!(v: Path::new(b"a/b/c\xFF"), b"a/b/c\xFF");
+ t!(v: Path::new(b"\xFF/../foo\x80"), b"foo\x80");
+ let p = Path::new(b"a/b/c\xFF");
+ assert!(p.as_str().is_none());
+
+ t!(s: Path::new(""), ".");
+ t!(s: Path::new("/"), "/");
+ t!(s: Path::new("hi"), "hi");
+ t!(s: Path::new("hi/"), "hi");
+ t!(s: Path::new("/lib"), "/lib");
+ t!(s: Path::new("/lib/"), "/lib");
+ t!(s: Path::new("hi/there"), "hi/there");
+ t!(s: Path::new("hi/there.txt"), "hi/there.txt");
+
+ t!(s: Path::new("hi/there/"), "hi/there");
+ t!(s: Path::new("hi/../there"), "there");
+ t!(s: Path::new("../hi/there"), "../hi/there");
+ t!(s: Path::new("/../hi/there"), "/hi/there");
+ t!(s: Path::new("foo/.."), ".");
+ t!(s: Path::new("/foo/.."), "/");
+ t!(s: Path::new("/foo/../.."), "/");
+ t!(s: Path::new("/foo/../../bar"), "/bar");
+ t!(s: Path::new("/./hi/./there/."), "/hi/there");
+ t!(s: Path::new("/./hi/./there/./.."), "/hi");
+ t!(s: Path::new("foo/../.."), "..");
+ t!(s: Path::new("foo/../../.."), "../..");
+ t!(s: Path::new("foo/../../bar"), "../bar");
+
+ assert_eq!(Path::new(b"foo/bar").into_vec(), b"foo/bar");
+ assert_eq!(Path::new(b"/foo/../../bar").into_vec(),
+ b"/bar");
+
+ let p = Path::new(b"foo/bar\x80");
+ assert!(p.as_str().is_none());
+ }
+
+ #[test]
+ fn test_opt_paths() {
+ assert!(Path::new_opt(b"foo/bar\0").is_none());
+ t!(v: Path::new_opt(b"foo/bar").unwrap(), b"foo/bar");
+ assert!(Path::new_opt("foo/bar\0").is_none());
+ t!(s: Path::new_opt("foo/bar").unwrap(), "foo/bar");
+ }
+
+ #[test]
+ fn test_null_byte() {
+ use thread::Thread;
+ let result = Thread::scoped(move|| {
+ Path::new(b"foo/bar\0")
+ }).join();
+ assert!(result.is_err());
+
+ let result = Thread::scoped(move|| {
+ Path::new("test").set_filename(b"f\0o")
+ }).join();
+ assert!(result.is_err());
+
+ let result = Thread::scoped(move|| {
+ Path::new("test").push(b"f\0o");
+ }).join();
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_display_str() {
+ macro_rules! t {
+ ($path:expr, $disp:ident, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ assert_eq!(path.$disp().to_string(), $exp);
+ }
+ )
+ }
+ t!("foo", display, "foo");
+ t!(b"foo\x80", display, "foo\u{FFFD}");
+ t!(b"foo\xFFbar", display, "foo\u{FFFD}bar");
+ t!(b"foo\xFF/bar", filename_display, "bar");
+ t!(b"foo/\xFFbar", filename_display, "\u{FFFD}bar");
+ t!(b"/", filename_display, "");
+
+ macro_rules! t {
+ ($path:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let mo = path.display().as_cow();
+ assert_eq!(mo.as_slice(), $exp);
+ }
+ );
+ ($path:expr, $exp:expr, filename) => (
+ {
+ let path = Path::new($path);
+ let mo = path.filename_display().as_cow();
+ assert_eq!(mo.as_slice(), $exp);
+ }
+ )
+ }
+
+ t!("foo", "foo");
+ t!(b"foo\x80", "foo\u{FFFD}");
+ t!(b"foo\xFFbar", "foo\u{FFFD}bar");
+ t!(b"foo\xFF/bar", "bar", filename);
+ t!(b"foo/\xFFbar", "\u{FFFD}bar", filename);
+ t!(b"/", "", filename);
+ }
+
+ #[test]
+ fn test_display() {
+ macro_rules! t {
+ ($path:expr, $exp:expr, $expf:expr) => (
+ {
+ let path = Path::new($path);
+ let f = format!("{}", path.display());
+ assert_eq!(f, $exp);
+ let f = format!("{}", path.filename_display());
+ assert_eq!(f, $expf);
+ }
+ )
+ }
+
+ t!(b"foo", "foo", "foo");
+ t!(b"foo/bar", "foo/bar", "bar");
+ t!(b"/", "/", "");
+ t!(b"foo\xFF", "foo\u{FFFD}", "foo\u{FFFD}");
+ t!(b"foo\xFF/bar", "foo\u{FFFD}/bar", "bar");
+ t!(b"foo/\xFFbar", "foo/\u{FFFD}bar", "\u{FFFD}bar");
+ t!(b"\xFFfoo/bar\xFF", "\u{FFFD}foo/bar\u{FFFD}", "bar\u{FFFD}");
+ }
+
+ #[test]
+ fn test_components() {
+ macro_rules! t {
+ (s: $path:expr, $op:ident, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ assert_eq!(path.$op(), ($exp).as_bytes());
+ }
+ );
+ (s: $path:expr, $op:ident, $exp:expr, opt) => (
+ {
+ let path = Path::new($path);
+ let left = path.$op().map(|x| str::from_utf8(x).unwrap());
+ assert_eq!(left, $exp);
+ }
+ );
+ (v: $path:expr, $op:ident, $exp:expr) => (
+ {
+ let arg = $path;
+ let path = Path::new(arg);
+ assert_eq!(path.$op(), $exp);
+ }
+ );
+ }
+
+ t!(v: b"a/b/c", filename, Some(b"c"));
+ t!(v: b"a/b/c\xFF", filename, Some(b"c\xFF"));
+ t!(v: b"a/b\xFF/c", filename, Some(b"c"));
+ t!(s: "a/b/c", filename, Some("c"), opt);
+ t!(s: "/a/b/c", filename, Some("c"), opt);
+ t!(s: "a", filename, Some("a"), opt);
+ t!(s: "/a", filename, Some("a"), opt);
+ t!(s: ".", filename, None, opt);
+ t!(s: "/", filename, None, opt);
+ t!(s: "..", filename, None, opt);
+ t!(s: "../..", filename, None, opt);
+
+ t!(v: b"a/b/c", dirname, b"a/b");
+ t!(v: b"a/b/c\xFF", dirname, b"a/b");
+ t!(v: b"a/b\xFF/c", dirname, b"a/b\xFF");
+ t!(s: "a/b/c", dirname, "a/b");
+ t!(s: "/a/b/c", dirname, "/a/b");
+ t!(s: "a", dirname, ".");
+ t!(s: "/a", dirname, "/");
+ t!(s: ".", dirname, ".");
+ t!(s: "/", dirname, "/");
+ t!(s: "..", dirname, "..");
+ t!(s: "../..", dirname, "../..");
+
+ t!(v: b"hi/there.txt", filestem, Some(b"there"));
+ t!(v: b"hi/there\x80.txt", filestem, Some(b"there\x80"));
+ t!(v: b"hi/there.t\x80xt", filestem, Some(b"there"));
+ t!(s: "hi/there.txt", filestem, Some("there"), opt);
+ t!(s: "hi/there", filestem, Some("there"), opt);
+ t!(s: "there.txt", filestem, Some("there"), opt);
+ t!(s: "there", filestem, Some("there"), opt);
+ t!(s: ".", filestem, None, opt);
+ t!(s: "/", filestem, None, opt);
+ t!(s: "foo/.bar", filestem, Some(".bar"), opt);
+ t!(s: ".bar", filestem, Some(".bar"), opt);
+ t!(s: "..bar", filestem, Some("."), opt);
+ t!(s: "hi/there..txt", filestem, Some("there."), opt);
+ t!(s: "..", filestem, None, opt);
+ t!(s: "../..", filestem, None, opt);
+
+ t!(v: b"hi/there.txt", extension, Some(b"txt"));
+ t!(v: b"hi/there\x80.txt", extension, Some(b"txt"));
+ t!(v: b"hi/there.t\x80xt", extension, Some(b"t\x80xt"));
+ t!(v: b"hi/there", extension, None);
+ t!(v: b"hi/there\x80", extension, None);
+ t!(s: "hi/there.txt", extension, Some("txt"), opt);
+ t!(s: "hi/there", extension, None, opt);
+ t!(s: "there.txt", extension, Some("txt"), opt);
+ t!(s: "there", extension, None, opt);
+ t!(s: ".", extension, None, opt);
+ t!(s: "/", extension, None, opt);
+ t!(s: "foo/.bar", extension, None, opt);
+ t!(s: ".bar", extension, None, opt);
+ t!(s: "..bar", extension, Some("bar"), opt);
+ t!(s: "hi/there..txt", extension, Some("txt"), opt);
+ t!(s: "..", extension, None, opt);
+ t!(s: "../..", extension, None, opt);
+ }
+
+ #[test]
+ fn test_push() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr) => (
+ {
+ let path = $path;
+ let join = $join;
+ let mut p1 = Path::new(path);
+ let p2 = p1.clone();
+ p1.push(join);
+ assert_eq!(p1, p2.join(join));
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "..");
+ t!(s: "/a/b/c", "d");
+ t!(s: "a/b", "c/d");
+ t!(s: "a/b", "/c/d");
+ }
+
+ #[test]
+ fn test_push_path() {
+ macro_rules! t {
+ (s: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ let push = Path::new($push);
+ p.push(&push);
+ assert_eq!(p.as_str(), Some($exp));
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "d", "a/b/c/d");
+ t!(s: "/a/b/c", "d", "/a/b/c/d");
+ t!(s: "a/b", "c/d", "a/b/c/d");
+ t!(s: "a/b", "/c/d", "/c/d");
+ t!(s: "a/b", ".", "a/b");
+ t!(s: "a/b", "../c", "a/c");
+ }
+
+ #[test]
+ fn test_push_many() {
+ macro_rules! t {
+ (s: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ p.push_many(&$push);
+ assert_eq!(p.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ p.push_many(&$push);
+ assert_eq!(p.as_vec(), $exp);
+ }
+ )
+ }
+
+ t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
+ t!(s: "a/b/c", ["d", "/e"], "/e");
+ t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
+ t!(s: "a/b/c", ["d".to_string(), "e".to_string()], "a/b/c/d/e");
+ t!(v: b"a/b/c", [b"d", b"e"], b"a/b/c/d/e");
+ t!(v: b"a/b/c", [b"d", b"/e", b"f"], b"/e/f");
+ t!(v: b"a/b/c", [b"d".to_vec(), b"e".to_vec()], b"a/b/c/d/e");
+ }
+
+ #[test]
+ fn test_pop() {
+ macro_rules! t {
+ (s: $path:expr, $left:expr, $right:expr) => (
+ {
+ let mut p = Path::new($path);
+ let result = p.pop();
+ assert_eq!(p.as_str(), Some($left));
+ assert_eq!(result, $right);
+ }
+ );
+ (b: $path:expr, $left:expr, $right:expr) => (
+ {
+ let mut p = Path::new($path);
+ let result = p.pop();
+ assert_eq!(p.as_vec(), $left);
+ assert_eq!(result, $right);
+ }
+ )
+ }
+
+ t!(b: b"a/b/c", b"a/b", true);
+ t!(b: b"a", b".", true);
+ t!(b: b".", b".", false);
+ t!(b: b"/a", b"/", true);
+ t!(b: b"/", b"/", false);
+ t!(b: b"a/b/c\x80", b"a/b", true);
+ t!(b: b"a/b\x80/c", b"a/b\x80", true);
+ t!(b: b"\xFF", b".", true);
+ t!(b: b"/\xFF", b"/", true);
+ t!(s: "a/b/c", "a/b", true);
+ t!(s: "a", ".", true);
+ t!(s: ".", ".", false);
+ t!(s: "/a", "/", true);
+ t!(s: "/", "/", false);
+ }
+
+ #[test]
+ fn test_root_path() {
+ assert_eq!(Path::new(b"a/b/c").root_path(), None);
+ assert_eq!(Path::new(b"/a/b/c").root_path(), Some(Path::new("/")));
+ }
+
+ #[test]
+ fn test_join() {
+ t!(v: Path::new(b"a/b/c").join(b".."), b"a/b");
+ t!(v: Path::new(b"/a/b/c").join(b"d"), b"/a/b/c/d");
+ t!(v: Path::new(b"a/\x80/c").join(b"\xFF"), b"a/\x80/c/\xFF");
+ t!(s: Path::new("a/b/c").join(".."), "a/b");
+ t!(s: Path::new("/a/b/c").join("d"), "/a/b/c/d");
+ t!(s: Path::new("a/b").join("c/d"), "a/b/c/d");
+ t!(s: Path::new("a/b").join("/c/d"), "/c/d");
+ t!(s: Path::new(".").join("a/b"), "a/b");
+ t!(s: Path::new("/").join("a/b"), "/a/b");
+ }
+
+ #[test]
+ fn test_join_path() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let join = Path::new($join);
+ let res = path.join(&join);
+ assert_eq!(res.as_str(), Some($exp));
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "..", "a/b");
+ t!(s: "/a/b/c", "d", "/a/b/c/d");
+ t!(s: "a/b", "c/d", "a/b/c/d");
+ t!(s: "a/b", "/c/d", "/c/d");
+ t!(s: ".", "a/b", "a/b");
+ t!(s: "/", "a/b", "/a/b");
+ }
+
+ #[test]
+ fn test_join_many() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let res = path.join_many(&$join);
+ assert_eq!(res.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let res = path.join_many(&$join);
+ assert_eq!(res.as_vec(), $exp);
+ }
+ )
+ }
+
+ t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
+ t!(s: "a/b/c", ["..", "d"], "a/b/d");
+ t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
+ t!(s: "a/b/c", ["d".to_string(), "e".to_string()], "a/b/c/d/e");
+ t!(v: b"a/b/c", [b"d", b"e"], b"a/b/c/d/e");
+ t!(v: b"a/b/c", [b"d".to_vec(), b"e".to_vec()], b"a/b/c/d/e");
+ }
+
+ #[test]
+ fn test_with_helpers() {
+ let empty: &[u8] = &[];
+
+ t!(v: Path::new(b"a/b/c").with_filename(b"d"), b"a/b/d");
+ t!(v: Path::new(b"a/b/c\xFF").with_filename(b"\x80"), b"a/b/\x80");
+ t!(v: Path::new(b"/\xFF/foo").with_filename(b"\xCD"),
+ b"/\xFF/\xCD");
+ t!(s: Path::new("a/b/c").with_filename("d"), "a/b/d");
+ t!(s: Path::new(".").with_filename("foo"), "foo");
+ t!(s: Path::new("/a/b/c").with_filename("d"), "/a/b/d");
+ t!(s: Path::new("/").with_filename("foo"), "/foo");
+ t!(s: Path::new("/a").with_filename("foo"), "/foo");
+ t!(s: Path::new("foo").with_filename("bar"), "bar");
+ t!(s: Path::new("/").with_filename("foo/"), "/foo");
+ t!(s: Path::new("/a").with_filename("foo/"), "/foo");
+ t!(s: Path::new("a/b/c").with_filename(""), "a/b");
+ t!(s: Path::new("a/b/c").with_filename("."), "a/b");
+ t!(s: Path::new("a/b/c").with_filename(".."), "a");
+ t!(s: Path::new("/a").with_filename(""), "/");
+ t!(s: Path::new("foo").with_filename(""), ".");
+ t!(s: Path::new("a/b/c").with_filename("d/e"), "a/b/d/e");
+ t!(s: Path::new("a/b/c").with_filename("/d"), "a/b/d");
+ t!(s: Path::new("..").with_filename("foo"), "../foo");
+ t!(s: Path::new("../..").with_filename("foo"), "../../foo");
+ t!(s: Path::new("..").with_filename(""), "..");
+ t!(s: Path::new("../..").with_filename(""), "../..");
+
+ t!(v: Path::new(b"hi/there\x80.txt").with_extension(b"exe"),
+ b"hi/there\x80.exe");
+ t!(v: Path::new(b"hi/there.txt\x80").with_extension(b"\xFF"),
+ b"hi/there.\xFF");
+ t!(v: Path::new(b"hi/there\x80").with_extension(b"\xFF"),
+ b"hi/there\x80.\xFF");
+ t!(v: Path::new(b"hi/there.\xFF").with_extension(empty), b"hi/there");
+ t!(s: Path::new("hi/there.txt").with_extension("exe"), "hi/there.exe");
+ t!(s: Path::new("hi/there.txt").with_extension(""), "hi/there");
+ t!(s: Path::new("hi/there.txt").with_extension("."), "hi/there..");
+ t!(s: Path::new("hi/there.txt").with_extension(".."), "hi/there...");
+ t!(s: Path::new("hi/there").with_extension("txt"), "hi/there.txt");
+ t!(s: Path::new("hi/there").with_extension("."), "hi/there..");
+ t!(s: Path::new("hi/there").with_extension(".."), "hi/there...");
+ t!(s: Path::new("hi/there.").with_extension("txt"), "hi/there.txt");
+ t!(s: Path::new("hi/.foo").with_extension("txt"), "hi/.foo.txt");
+ t!(s: Path::new("hi/there.txt").with_extension(".foo"), "hi/there..foo");
+ t!(s: Path::new("/").with_extension("txt"), "/");
+ t!(s: Path::new("/").with_extension("."), "/");
+ t!(s: Path::new("/").with_extension(".."), "/");
+ t!(s: Path::new(".").with_extension("txt"), ".");
+ }
+
+ #[test]
+ fn test_setters() {
+ macro_rules! t {
+ (s: $path:expr, $set:ident, $with:ident, $arg:expr) => (
+ {
+ let path = $path;
+ let arg = $arg;
+ let mut p1 = Path::new(path);
+ p1.$set(arg);
+ let p2 = Path::new(path);
+ assert_eq!(p1, p2.$with(arg));
+ }
+ );
+ (v: $path:expr, $set:ident, $with:ident, $arg:expr) => (
+ {
+ let path = $path;
+ let arg = $arg;
+ let mut p1 = Path::new(path);
+ p1.$set(arg);
+ let p2 = Path::new(path);
+ assert_eq!(p1, p2.$with(arg));
+ }
+ )
+ }
+
+ t!(v: b"a/b/c", set_filename, with_filename, b"d");
+ t!(v: b"/", set_filename, with_filename, b"foo");
+ t!(v: b"\x80", set_filename, with_filename, b"\xFF");
+ t!(s: "a/b/c", set_filename, with_filename, "d");
+ t!(s: "/", set_filename, with_filename, "foo");
+ t!(s: ".", set_filename, with_filename, "foo");
+ t!(s: "a/b", set_filename, with_filename, "");
+ t!(s: "a", set_filename, with_filename, "");
+
+ t!(v: b"hi/there.txt", set_extension, with_extension, b"exe");
+ t!(v: b"hi/there.t\x80xt", set_extension, with_extension, b"exe\xFF");
+ t!(s: "hi/there.txt", set_extension, with_extension, "exe");
+ t!(s: "hi/there.", set_extension, with_extension, "txt");
+ t!(s: "hi/there", set_extension, with_extension, "txt");
+ t!(s: "hi/there.txt", set_extension, with_extension, "");
+ t!(s: "hi/there", set_extension, with_extension, "");
+ t!(s: ".", set_extension, with_extension, "txt");
+ }
+
+ #[test]
+ fn test_getters() {
+ macro_rules! t {
+ (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.filename_str(), $filename);
+ assert_eq!(path.dirname_str(), $dirname);
+ assert_eq!(path.filestem_str(), $filestem);
+ assert_eq!(path.extension_str(), $ext);
+ }
+ );
+ (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.filename(), $filename);
+ assert_eq!(path.dirname(), $dirname);
+ assert_eq!(path.filestem(), $filestem);
+ assert_eq!(path.extension(), $ext);
+ }
+ )
+ }
+
+ t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), None);
+ t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), None);
+ t!(v: Path::new(b"hi/there.\xFF"), Some(b"there.\xFF"), b"hi",
+ Some(b"there"), Some(b"\xFF"));
+ t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None);
+ t!(s: Path::new("."), None, Some("."), None, None);
+ t!(s: Path::new("/"), None, Some("/"), None, None);
+ t!(s: Path::new(".."), None, Some(".."), None, None);
+ t!(s: Path::new("../.."), None, Some("../.."), None, None);
+ t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"),
+ Some("there"), Some("txt"));
+ t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None);
+ t!(s: Path::new("hi/there."), Some("there."), Some("hi"),
+ Some("there"), Some(""));
+ t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None);
+ t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"),
+ Some("."), Some("there"));
+ t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, None);
+ t!(s: Path::new(b"a/b/\xFF.txt"), None, Some("a/b"), None, Some("txt"));
+ t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), None);
+ t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), None);
+ }
+
+ #[test]
+ fn test_dir_path() {
+ t!(v: Path::new(b"hi/there\x80").dir_path(), b"hi");
+ t!(v: Path::new(b"hi\xFF/there").dir_path(), b"hi\xFF");
+ t!(s: Path::new("hi/there").dir_path(), "hi");
+ t!(s: Path::new("hi").dir_path(), ".");
+ t!(s: Path::new("/hi").dir_path(), "/");
+ t!(s: Path::new("/").dir_path(), "/");
+ t!(s: Path::new("..").dir_path(), "..");
+ t!(s: Path::new("../..").dir_path(), "../..");
+ }
+
+ #[test]
+ fn test_is_absolute() {
+ macro_rules! t {
+ (s: $path:expr, $abs:expr, $rel:expr) => (
+ {
+ let path = Path::new($path);
+ assert_eq!(path.is_absolute(), $abs);
+ assert_eq!(path.is_relative(), $rel);
+ }
+ )
+ }
+ t!(s: "a/b/c", false, true);
+ t!(s: "/a/b/c", true, false);
+ t!(s: "a", false, true);
+ t!(s: "/a", true, false);
+ t!(s: ".", false, true);
+ t!(s: "/", true, false);
+ t!(s: "..", false, true);
+ t!(s: "../..", false, true);
+ }
+
+ #[test]
+ fn test_is_ancestor_of() {
+ macro_rules! t {
+ (s: $path:expr, $dest:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let dest = Path::new($dest);
+ assert_eq!(path.is_ancestor_of(&dest), $exp);
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "a/b/c/d", true);
+ t!(s: "a/b/c", "a/b/c", true);
+ t!(s: "a/b/c", "a/b", false);
+ t!(s: "/a/b/c", "/a/b/c", true);
+ t!(s: "/a/b", "/a/b/c", true);
+ t!(s: "/a/b/c/d", "/a/b/c", false);
+ t!(s: "/a/b", "a/b/c", false);
+ t!(s: "a/b", "/a/b/c", false);
+ t!(s: "a/b/c", "a/b/d", false);
+ t!(s: "../a/b/c", "a/b/c", false);
+ t!(s: "a/b/c", "../a/b/c", false);
+ t!(s: "a/b/c", "a/b/cd", false);
+ t!(s: "a/b/cd", "a/b/c", false);
+ t!(s: "../a/b", "../a/b/c", true);
+ t!(s: ".", "a/b", true);
+ t!(s: ".", ".", true);
+ t!(s: "/", "/", true);
+ t!(s: "/", "/a/b", true);
+ t!(s: "..", "a/b", true);
+ t!(s: "../..", "a/b", true);
+ }
+
+ #[test]
+ fn test_ends_with_path() {
+ macro_rules! t {
+ (s: $path:expr, $child:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let child = Path::new($child);
+ assert_eq!(path.ends_with_path(&child), $exp);
+ }
+ );
+ (v: $path:expr, $child:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let child = Path::new($child);
+ assert_eq!(path.ends_with_path(&child), $exp);
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "c", true);
+ t!(s: "a/b/c", "d", false);
+ t!(s: "foo/bar/quux", "bar", false);
+ t!(s: "foo/bar/quux", "barquux", false);
+ t!(s: "a/b/c", "b/c", true);
+ t!(s: "a/b/c", "a/b/c", true);
+ t!(s: "a/b/c", "foo/a/b/c", false);
+ t!(s: "/a/b/c", "a/b/c", true);
+ t!(s: "/a/b/c", "/a/b/c", false); // child must be relative
+ t!(s: "/a/b/c", "foo/a/b/c", false);
+ t!(s: "a/b/c", "", false);
+ t!(s: "", "", true);
+ t!(s: "/a/b/c", "d/e/f", false);
+ t!(s: "a/b/c", "a/b", false);
+ t!(s: "a/b/c", "b", false);
+ t!(v: b"a/b/c", b"b/c", true);
+ t!(v: b"a/b/\xFF", b"\xFF", true);
+ t!(v: b"a/b/\xFF", b"b/\xFF", true);
+ }
+
+ #[test]
+ fn test_path_relative_from() {
+ macro_rules! t {
+ (s: $path:expr, $other:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let other = Path::new($other);
+ let res = path.path_relative_from(&other);
+ assert_eq!(res.as_ref().and_then(|x| x.as_str()), $exp);
+ }
+ )
+ }
+
+ t!(s: "a/b/c", "a/b", Some("c"));
+ t!(s: "a/b/c", "a/b/d", Some("../c"));
+ t!(s: "a/b/c", "a/b/c/d", Some(".."));
+ t!(s: "a/b/c", "a/b/c", Some("."));
+ t!(s: "a/b/c", "a/b/c/d/e", Some("../.."));
+ t!(s: "a/b/c", "a/d/e", Some("../../b/c"));
+ t!(s: "a/b/c", "d/e/f", Some("../../../a/b/c"));
+ t!(s: "a/b/c", "/a/b/c", None);
+ t!(s: "/a/b/c", "a/b/c", Some("/a/b/c"));
+ t!(s: "/a/b/c", "/a/b/c/d", Some(".."));
+ t!(s: "/a/b/c", "/a/b", Some("c"));
+ t!(s: "/a/b/c", "/a/b/c/d/e", Some("../.."));
+ t!(s: "/a/b/c", "/a/d/e", Some("../../b/c"));
+ t!(s: "/a/b/c", "/d/e/f", Some("../../../a/b/c"));
+ t!(s: "hi/there.txt", "hi/there", Some("../there.txt"));
+ t!(s: ".", "a", Some(".."));
+ t!(s: ".", "a/b", Some("../.."));
+ t!(s: ".", ".", Some("."));
+ t!(s: "a", ".", Some("a"));
+ t!(s: "a/b", ".", Some("a/b"));
+ t!(s: "..", ".", Some(".."));
+ t!(s: "a/b/c", "a/b/c", Some("."));
+ t!(s: "/a/b/c", "/a/b/c", Some("."));
+ t!(s: "/", "/", Some("."));
+ t!(s: "/", ".", Some("/"));
+ t!(s: "../../a", "b", Some("../../../a"));
+ t!(s: "a", "../../b", None);
+ t!(s: "../../a", "../../b", Some("../a"));
+ t!(s: "../../a", "../../a/b", Some(".."));
+ t!(s: "../../a/b", "../../a", Some("b"));
+ }
+
+ #[test]
+ fn test_components_iter() {
+ macro_rules! t {
+ (s: $path:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let comps = path.components().collect::<Vec<&[u8]>>();
+ let exp: &[&str] = &$exp;
+ let exps = exp.iter().map(|x| x.as_bytes()).collect::<Vec<&[u8]>>();
+ assert_eq!(comps, exps);
+ let comps = path.components().rev().collect::<Vec<&[u8]>>();
+ let exps = exps.into_iter().rev().collect::<Vec<&[u8]>>();
+ assert_eq!(comps, exps);
+ }
+ );
+ (b: $arg:expr, [$($exp:expr),*]) => (
+ {
+ let path = Path::new($arg);
+ let comps = path.components().collect::<Vec<&[u8]>>();
+ let exp: &[&[u8]] = &[$($exp),*];
+ assert_eq!(comps, exp);
+ let comps = path.components().rev().collect::<Vec<&[u8]>>();
+ let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>();
+ assert_eq!(comps, exp)
+ }
+ )
+ }
+
+ t!(b: b"a/b/c", [b"a", b"b", b"c"]);
+ t!(b: b"/\xFF/a/\x80", [b"\xFF", b"a", b"\x80"]);
+ t!(b: b"../../foo\xCDbar", [b"..", b"..", b"foo\xCDbar"]);
+ t!(s: "a/b/c", ["a", "b", "c"]);
+ t!(s: "a/b/d", ["a", "b", "d"]);
+ t!(s: "a/b/cd", ["a", "b", "cd"]);
+ t!(s: "/a/b/c", ["a", "b", "c"]);
+ t!(s: "a", ["a"]);
+ t!(s: "/a", ["a"]);
+ t!(s: "/", []);
+ t!(s: ".", ["."]);
+ t!(s: "..", [".."]);
+ t!(s: "../..", ["..", ".."]);
+ t!(s: "../../foo", ["..", "..", "foo"]);
+ }
+
+ #[test]
+ fn test_str_components() {
+ macro_rules! t {
+ (b: $arg:expr, $exp:expr) => (
+ {
+ let path = Path::new($arg);
+ let comps = path.str_components().collect::<Vec<Option<&str>>>();
+ let exp: &[Option<&str>] = &$exp;
+ assert_eq!(comps, exp);
+ let comps = path.str_components().rev().collect::<Vec<Option<&str>>>();
+ let exp = exp.iter().rev().map(|&x|x).collect::<Vec<Option<&str>>>();
+ assert_eq!(comps, exp);
+ }
+ )
+ }
+
+ t!(b: b"a/b/c", [Some("a"), Some("b"), Some("c")]);
+ t!(b: b"/\xFF/a/\x80", [None, Some("a"), None]);
+ t!(b: b"../../foo\xCDbar", [Some(".."), Some(".."), None]);
+ // str_components is a wrapper around components, so no need to do
+ // the full set of tests
+ }
+}
+
+#[cfg(test)]
+mod bench {
+ extern crate test;
+ use self::test::Bencher;
+ use super::*;
+ use prelude::v1::{Clone, GenericPath};
+
+ #[bench]
+ fn join_home_dir(b: &mut Bencher) {
+ let posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.join("home");
+ });
+ }
+
+ #[bench]
+ fn join_abs_path_home_dir(b: &mut Bencher) {
+ let posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.join("/home");
+ });
+ }
+
+ #[bench]
+ fn join_many_home_dir(b: &mut Bencher) {
+ let posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.join_many(&["home"]);
+ });
+ }
+
+ #[bench]
+ fn join_many_abs_path_home_dir(b: &mut Bencher) {
+ let posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.join_many(&["/home"]);
+ });
+ }
+
+ #[bench]
+ fn push_home_dir(b: &mut Bencher) {
+ let mut posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.push("home");
+ });
+ }
+
+ #[bench]
+ fn push_abs_path_home_dir(b: &mut Bencher) {
+ let mut posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.push("/home");
+ });
+ }
+
+ #[bench]
+ fn push_many_home_dir(b: &mut Bencher) {
+ let mut posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.push_many(&["home"]);
+ });
+ }
+
+ #[bench]
+ fn push_many_abs_path_home_dir(b: &mut Bencher) {
+ let mut posix_path = Path::new("/");
+ b.iter(|| {
+ posix_path.push_many(&["/home"]);
+ });
+ }
+
+ #[bench]
+ fn ends_with_path_home_dir(b: &mut Bencher) {
+ let posix_home_path = Path::new("/home");
+ b.iter(|| {
+ posix_home_path.ends_with_path(&Path::new("home"));
+ });
+ }
+
+ #[bench]
+ fn ends_with_path_missmatch_jome_home(b: &mut Bencher) {
+ let posix_home_path = Path::new("/home");
+ b.iter(|| {
+ posix_home_path.ends_with_path(&Path::new("jome"));
+ });
+ }
+
+ #[bench]
+ fn is_ancestor_of_path_with_10_dirs(b: &mut Bencher) {
+ let path = Path::new("/home/1/2/3/4/5/6/7/8/9");
+ let mut sub = path.clone();
+ sub.pop();
+ b.iter(|| {
+ path.is_ancestor_of(&sub);
+ });
+ }
+
+ #[bench]
+ fn path_relative_from_forward(b: &mut Bencher) {
+ let path = Path::new("/a/b/c");
+ let mut other = path.clone();
+ other.pop();
+ b.iter(|| {
+ path.path_relative_from(&other);
+ });
+ }
+
+ #[bench]
+ fn path_relative_from_same_level(b: &mut Bencher) {
+ let path = Path::new("/a/b/c");
+ let mut other = path.clone();
+ other.pop();
+ other.push("d");
+ b.iter(|| {
+ path.path_relative_from(&other);
+ });
+ }
+
+ #[bench]
+ fn path_relative_from_backward(b: &mut Bencher) {
+ let path = Path::new("/a/b");
+ let mut other = path.clone();
+ other.push("c");
+ b.iter(|| {
+ path.path_relative_from(&other);
+ });
+ }
+}
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// ignore-lexer-test FIXME #15883
+
+//! Windows file path handling
+
+use self::PathPrefix::*;
+
+use ascii::AsciiExt;
+use char::CharExt;
+use clone::Clone;
+use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
+use fmt;
+use hash;
+use old_io::Writer;
+use iter::{AdditiveIterator, Extend};
+use iter::{Iterator, IteratorExt, Map, repeat};
+use mem;
+use option::Option::{self, Some, None};
+use result::Result::{self, Ok, Err};
+use slice::{SliceExt, SliceConcatExt};
+use str::{SplitTerminator, FromStr, StrExt};
+use string::{String, ToString};
+use vec::Vec;
+
+use super::{contains_nul, BytesContainer, GenericPath, GenericPathUnsafe};
+
+/// Iterator that yields successive components of a Path as &str
+///
+/// Each component is yielded as Option<&str> for compatibility with PosixPath, but
+/// every component in WindowsPath is guaranteed to be Some.
+pub type StrComponents<'a> =
+ Map<SplitTerminator<'a, char>, fn(&'a str) -> Option<&'a str>>;
+
+/// Iterator that yields successive components of a Path as &[u8]
+pub type Components<'a> =
+ Map<StrComponents<'a>, fn(Option<&str>) -> &[u8]>;
+
+/// Represents a Windows path
+// Notes for Windows path impl:
+// The MAX_PATH is 260, but 253 is the practical limit due to some API bugs
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx for good information
+// about windows paths.
+// That same page puts a bunch of restrictions on allowed characters in a path.
+// `\foo.txt` means "relative to current drive", but will not be considered to be absolute here
+// as `∃P | P.join("\foo.txt") != "\foo.txt"`.
+// `C:` is interesting, that means "the current directory on drive C".
+// Long absolute paths need to have \\?\ prefix (or, for UNC, \\?\UNC\). I think that can be
+// ignored for now, though, and only added in a hypothetical .to_pwstr() function.
+// However, if a path is parsed that has \\?\, this needs to be preserved as it disables the
+// processing of "." and ".." components and / as a separator.
+// Experimentally, \\?\foo is not the same thing as \foo.
+// Also, \\foo is not valid either (certainly not equivalent to \foo).
+// Similarly, C:\\Users is not equivalent to C:\Users, although C:\Users\\foo is equivalent
+// to C:\Users\foo. In fact the command prompt treats C:\\foo\bar as UNC path. But it might be
+// best to just ignore that and normalize it to C:\foo\bar.
+//
+// Based on all this, I think the right approach is to do the following:
+// * Require valid utf-8 paths. Windows API may use WCHARs, but we don't, and utf-8 is convertible
+// to UTF-16 anyway (though does Windows use UTF-16 or UCS-2? Not sure).
+// * Parse the prefixes \\?\UNC\, \\?\, and \\.\ explicitly.
+// * If \\?\UNC\, treat following two path components as server\share. Don't error for missing
+// server\share.
+// * If \\?\, parse disk from following component, if present. Don't error for missing disk.
+// * If \\.\, treat rest of path as just regular components. I don't know how . and .. are handled
+// here, they probably aren't, but I'm not going to worry about that.
+// * Else if starts with \\, treat following two components as server\share. Don't error for missing
+// server\share.
+// * Otherwise, attempt to parse drive from start of path.
+//
+// The only error condition imposed here is valid utf-8. All other invalid paths are simply
+// preserved by the data structure; let the Windows API error out on them.
+#[derive(Clone)]
+pub struct Path {
+ repr: String, // assumed to never be empty
+ prefix: Option<PathPrefix>,
+ sepidx: Option<uint> // index of the final separator in the non-prefix portion of repr
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Debug for Path {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.display(), f)
+ }
+}
+
+impl PartialEq for Path {
+ #[inline]
+ fn eq(&self, other: &Path) -> bool {
+ self.repr == other.repr
+ }
+}
+
+impl Eq for Path {}
+
+impl PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Path {
+ fn cmp(&self, other: &Path) -> Ordering {
+ self.repr.cmp(&other.repr)
+ }
+}
+
+impl FromStr for Path {
+ type Err = ParsePathError;
+ fn from_str(s: &str) -> Result<Path, ParsePathError> {
+ match Path::new_opt(s) {
+ Some(p) => Ok(p),
+ None => Err(ParsePathError),
+ }
+ }
+}
+
+/// Value indicating that a path could not be parsed from a string.
+#[derive(Debug, Clone, PartialEq, Copy)]
+pub struct ParsePathError;
+
+impl<S: hash::Writer + hash::Hasher> hash::Hash<S> for Path {
+ #[cfg(not(test))]
+ #[inline]
+ fn hash(&self, state: &mut S) {
+ self.repr.hash(state)
+ }
+
+ #[cfg(test)]
+ #[inline]
+ fn hash(&self, _: &mut S) {
+ // No-op because the `hash` implementation will be wrong.
+ }
+}
+
+impl BytesContainer for Path {
+ #[inline]
+ fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+ self.as_vec()
+ }
+ #[inline]
+ fn container_as_str<'a>(&'a self) -> Option<&'a str> {
+ self.as_str()
+ }
+ #[inline]
+ fn is_str(_: Option<&Path>) -> bool { true }
+}
+
+impl GenericPathUnsafe for Path {
+ /// See `GenericPathUnsafe::from_vec_unchecked`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if not valid UTF-8.
+ #[inline]
+ unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
+ let (prefix, path) = Path::normalize_(path.container_as_str().unwrap());
+ assert!(!path.is_empty());
+ let mut ret = Path{ repr: path, prefix: prefix, sepidx: None };
+ ret.update_sepidx();
+ ret
+ }
+
+ /// See `GenericPathUnsafe::set_filename_unchecked`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if not valid UTF-8.
+ unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
+ let filename = filename.container_as_str().unwrap();
+ match self.sepidx_or_prefix_len() {
+ None if ".." == self.repr => {
+ let mut s = String::with_capacity(3 + filename.len());
+ s.push_str("..");
+ s.push(SEP);
+ s.push_str(filename);
+ self.update_normalized(&s[]);
+ }
+ None => {
+ self.update_normalized(filename);
+ }
+ Some((_,idxa,end)) if &self.repr[idxa..end] == ".." => {
+ let mut s = String::with_capacity(end + 1 + filename.len());
+ s.push_str(&self.repr[..end]);
+ s.push(SEP);
+ s.push_str(filename);
+ self.update_normalized(&s[]);
+ }
+ Some((idxb,idxa,_)) if self.prefix == Some(DiskPrefix) && idxa == self.prefix_len() => {
+ let mut s = String::with_capacity(idxb + filename.len());
+ s.push_str(&self.repr[..idxb]);
+ s.push_str(filename);
+ self.update_normalized(&s[]);
+ }
+ Some((idxb,_,_)) => {
+ let mut s = String::with_capacity(idxb + 1 + filename.len());
+ s.push_str(&self.repr[..idxb]);
+ s.push(SEP);
+ s.push_str(filename);
+ self.update_normalized(&s[]);
+ }
+ }
+ }
+
+ /// See `GenericPathUnsafe::push_unchecked`.
+ ///
+ /// Concatenating two Windows Paths is rather complicated.
+ /// For the most part, it will behave as expected, except in the case of
+ /// pushing a volume-relative path, e.g. `C:foo.txt`. Because we have no
+ /// concept of per-volume cwds like Windows does, we can't behave exactly
+ /// like Windows will. Instead, if the receiver is an absolute path on
+ /// the same volume as the new path, it will be treated as the cwd that
+ /// the new path is relative to. Otherwise, the new path will be treated
+ /// as if it were absolute and will replace the receiver outright.
+ unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
+ let path = path.container_as_str().unwrap();
+ fn is_vol_abs(path: &str, prefix: Option<PathPrefix>) -> bool {
+ // assume prefix is Some(DiskPrefix)
+ let rest = &path[prefix_len(prefix)..];
+ !rest.is_empty() && rest.as_bytes()[0].is_ascii() && is_sep(rest.as_bytes()[0] as char)
+ }
+ fn shares_volume(me: &Path, path: &str) -> bool {
+ // path is assumed to have a prefix of Some(DiskPrefix)
+ let repr = &me.repr[];
+ match me.prefix {
+ Some(DiskPrefix) => {
+ repr.as_bytes()[0] == path.as_bytes()[0].to_ascii_uppercase()
+ }
+ Some(VerbatimDiskPrefix) => {
+ repr.as_bytes()[4] == path.as_bytes()[0].to_ascii_uppercase()
+ }
+ _ => false
+ }
+ }
+ fn is_sep_(prefix: Option<PathPrefix>, u: u8) -> bool {
+ if prefix_is_verbatim(prefix) { is_sep_verbatim(u as char) }
+ else { is_sep(u as char) }
+ }
+
+ fn replace_path(me: &mut Path, path: &str, prefix: Option<PathPrefix>) {
+ let newpath = Path::normalize__(path, prefix);
+ me.repr = match newpath {
+ Some(p) => p,
+ None => String::from_str(path)
+ };
+ me.prefix = prefix;
+ me.update_sepidx();
+ }
+ fn append_path(me: &mut Path, path: &str) {
+ // appends a path that has no prefix
+ // if me is verbatim, we need to pre-normalize the new path
+ let path_ = if is_verbatim(me) { Path::normalize__(path, None) }
+ else { None };
+ let pathlen = path_.as_ref().map_or(path.len(), |p| p.len());
+ let mut s = String::with_capacity(me.repr.len() + 1 + pathlen);
+ s.push_str(&me.repr[]);
+ let plen = me.prefix_len();
+ // if me is "C:" we don't want to add a path separator
+ match me.prefix {
+ Some(DiskPrefix) if me.repr.len() == plen => (),
+ _ if !(me.repr.len() > plen && me.repr.as_bytes()[me.repr.len()-1] == SEP_BYTE) => {
+ s.push(SEP);
+ }
+ _ => ()
+ }
+ match path_ {
+ None => s.push_str(path),
+ Some(p) => s.push_str(&p[]),
+ };
+ me.update_normalized(&s[])
+ }
+
+ if !path.is_empty() {
+ let prefix = parse_prefix(path);
+ match prefix {
+ Some(DiskPrefix) if !is_vol_abs(path, prefix) && shares_volume(self, path) => {
+ // cwd-relative path, self is on the same volume
+ append_path(self, &path[prefix_len(prefix)..]);
+ }
+ Some(_) => {
+ // absolute path, or cwd-relative and self is not same volume
+ replace_path(self, path, prefix);
+ }
+ None if !path.is_empty() && is_sep_(self.prefix, path.as_bytes()[0]) => {
+ // volume-relative path
+ if self.prefix.is_some() {
+ // truncate self down to the prefix, then append
+ let n = self.prefix_len();
+ self.repr.truncate(n);
+ append_path(self, path);
+ } else {
+ // we have no prefix, so nothing to be relative to
+ replace_path(self, path, prefix);
+ }
+ }
+ None => {
+ // relative path
+ append_path(self, path);
+ }
+ }
+ }
+ }
+}
+
+impl GenericPath for Path {
+ #[inline]
+ fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
+ match path.container_as_str() {
+ None => None,
+ Some(ref s) => {
+ if contains_nul(s) {
+ None
+ } else {
+ Some(unsafe { GenericPathUnsafe::new_unchecked(*s) })
+ }
+ }
+ }
+ }
+
+ /// See `GenericPath::as_str` for info.
+ /// Always returns a `Some` value.
+ #[inline]
+ fn as_str<'a>(&'a self) -> Option<&'a str> {
+ Some(&self.repr[])
+ }
+
+ #[inline]
+ fn as_vec<'a>(&'a self) -> &'a [u8] {
+ self.repr.as_bytes()
+ }
+
+ #[inline]
+ fn into_vec(self) -> Vec<u8> {
+ self.repr.into_bytes()
+ }
+
+ #[inline]
+ fn dirname<'a>(&'a self) -> &'a [u8] {
+ self.dirname_str().unwrap().as_bytes()
+ }
+
+ /// See `GenericPath::dirname_str` for info.
+ /// Always returns a `Some` value.
+ fn dirname_str<'a>(&'a self) -> Option<&'a str> {
+ Some(match self.sepidx_or_prefix_len() {
+ None if ".." == self.repr => &self.repr[],
+ None => ".",
+ Some((_,idxa,end)) if &self.repr[idxa..end] == ".." => {
+ &self.repr[]
+ }
+ Some((idxb,_,end)) if &self.repr[idxb..end] == "\\" => {
+ &self.repr[]
+ }
+ Some((0,idxa,_)) => &self.repr[..idxa],
+ Some((idxb,idxa,_)) => {
+ match self.prefix {
+ Some(DiskPrefix) | Some(VerbatimDiskPrefix) if idxb == self.prefix_len() => {
+ &self.repr[..idxa]
+ }
+ _ => &self.repr[..idxb]
+ }
+ }
+ })
+ }
+
+ #[inline]
+ fn filename<'a>(&'a self) -> Option<&'a [u8]> {
+ self.filename_str().map(|x| x.as_bytes())
+ }
+
+ /// See `GenericPath::filename_str` for info.
+ /// Always returns a `Some` value if `filename` returns a `Some` value.
+ fn filename_str<'a>(&'a self) -> Option<&'a str> {
+ let repr = &self.repr[];
+ match self.sepidx_or_prefix_len() {
+ None if "." == repr || ".." == repr => None,
+ None => Some(repr),
+ Some((_,idxa,end)) if &repr[idxa..end] == ".." => None,
+ Some((_,idxa,end)) if idxa == end => None,
+ Some((_,idxa,end)) => Some(&repr[idxa..end])
+ }
+ }
+
+ /// See `GenericPath::filestem_str` for info.
+ /// Always returns a `Some` value if `filestem` returns a `Some` value.
+ #[inline]
+ fn filestem_str<'a>(&'a self) -> Option<&'a str> {
+ // filestem() returns a byte vector that's guaranteed valid UTF-8
+ self.filestem().map(|t| unsafe { mem::transmute(t) })
+ }
+
+ #[inline]
+ fn extension_str<'a>(&'a self) -> Option<&'a str> {
+ // extension() returns a byte vector that's guaranteed valid UTF-8
+ self.extension().map(|t| unsafe { mem::transmute(t) })
+ }
+
+ fn dir_path(&self) -> Path {
+ unsafe { GenericPathUnsafe::new_unchecked(self.dirname_str().unwrap()) }
+ }
+
+ #[inline]
+ fn pop(&mut self) -> bool {
+ match self.sepidx_or_prefix_len() {
+ None if "." == self.repr => false,
+ None => {
+ self.repr = String::from_str(".");
+ self.sepidx = None;
+ true
+ }
+ Some((idxb,idxa,end)) if idxb == idxa && idxb == end => false,
+ Some((idxb,_,end)) if &self.repr[idxb..end] == "\\" => false,
+ Some((idxb,idxa,_)) => {
+ let trunc = match self.prefix {
+ Some(DiskPrefix) | Some(VerbatimDiskPrefix) | None => {
+ let plen = self.prefix_len();
+ if idxb == plen { idxa } else { idxb }
+ }
+ _ => idxb
+ };
+ self.repr.truncate(trunc);
+ self.update_sepidx();
+ true
+ }
+ }
+ }
+
+ fn root_path(&self) -> Option<Path> {
+ if self.prefix.is_some() {
+ Some(Path::new(match self.prefix {
+ Some(DiskPrefix) if self.is_absolute() => {
+ &self.repr[..self.prefix_len()+1]
+ }
+ Some(VerbatimDiskPrefix) => {
+ &self.repr[..self.prefix_len()+1]
+ }
+ _ => &self.repr[..self.prefix_len()]
+ }))
+ } else if is_vol_relative(self) {
+ Some(Path::new(&self.repr[..1]))
+ } else {
+ None
+ }
+ }
+
+ /// See `GenericPath::is_absolute` for info.
+ ///
+ /// A Windows Path is considered absolute only if it has a non-volume prefix,
+ /// or if it has a volume prefix and the path starts with '\'.
+ /// A path of `\foo` is not considered absolute because it's actually
+ /// relative to the "current volume". A separate method `Path::is_vol_relative`
+ /// is provided to indicate this case. Similarly a path of `C:foo` is not
+ /// considered absolute because it's relative to the cwd on volume C:. A
+ /// separate method `Path::is_cwd_relative` is provided to indicate this case.
+ #[inline]
+ fn is_absolute(&self) -> bool {
+ match self.prefix {
+ Some(DiskPrefix) => {
+ let rest = &self.repr[self.prefix_len()..];
+ rest.len() > 0 && rest.as_bytes()[0] == SEP_BYTE
+ }
+ Some(_) => true,
+ None => false
+ }
+ }
+
+ #[inline]
+ fn is_relative(&self) -> bool {
+ self.prefix.is_none() && !is_vol_relative(self)
+ }
+
+ fn is_ancestor_of(&self, other: &Path) -> bool {
+ if !self.equiv_prefix(other) {
+ false
+ } else if self.is_absolute() != other.is_absolute() ||
+ is_vol_relative(self) != is_vol_relative(other) {
+ false
+ } else {
+ let mut ita = self.str_components().map(|x|x.unwrap());
+ let mut itb = other.str_components().map(|x|x.unwrap());
+ if "." == self.repr {
+ return itb.next() != Some("..");
+ }
+ loop {
+ match (ita.next(), itb.next()) {
+ (None, _) => break,
+ (Some(a), Some(b)) if a == b => { continue },
+ (Some(a), _) if a == ".." => {
+ // if ita contains only .. components, it's an ancestor
+ return ita.all(|x| x == "..");
+ }
+ _ => return false
+ }
+ }
+ true
+ }
+ }
+
+ fn path_relative_from(&self, base: &Path) -> Option<Path> {
+ fn comp_requires_verbatim(s: &str) -> bool {
+ s == "." || s == ".." || s.contains_char(SEP2)
+ }
+
+ if !self.equiv_prefix(base) {
+ // prefixes differ
+ if self.is_absolute() {
+ Some(self.clone())
+ } else if self.prefix == Some(DiskPrefix) && base.prefix == Some(DiskPrefix) {
+ // both drives, drive letters must differ or they'd be equiv
+ Some(self.clone())
+ } else {
+ None
+ }
+ } else if self.is_absolute() != base.is_absolute() {
+ if self.is_absolute() {
+ Some(self.clone())
+ } else {
+ None
+ }
+ } else if is_vol_relative(self) != is_vol_relative(base) {
+ if is_vol_relative(self) {
+ Some(self.clone())
+ } else {
+ None
+ }
+ } else {
+ let mut ita = self.str_components().map(|x|x.unwrap());
+ let mut itb = base.str_components().map(|x|x.unwrap());
+ let mut comps = vec![];
+
+ let a_verb = is_verbatim(self);
+ let b_verb = is_verbatim(base);
+ loop {
+ match (ita.next(), itb.next()) {
+ (None, None) => break,
+ (Some(a), None) if a_verb && comp_requires_verbatim(a) => {
+ return Some(self.clone())
+ }
+ (Some(a), None) => {
+ comps.push(a);
+ if !a_verb {
+ comps.extend(ita.by_ref());
+ break;
+ }
+ }
+ (None, _) => comps.push(".."),
+ (Some(a), Some(b)) if comps.is_empty() && a == b => (),
+ (Some(a), Some(b)) if !b_verb && b == "." => {
+ if a_verb && comp_requires_verbatim(a) {
+ return Some(self.clone())
+ } else { comps.push(a) }
+ }
+ (Some(_), Some(b)) if !b_verb && b == ".." => return None,
+ (Some(a), Some(_)) if a_verb && comp_requires_verbatim(a) => {
+ return Some(self.clone())
+ }
+ (Some(a), Some(_)) => {
+ comps.push("..");
+ for _ in itb.by_ref() {
+ comps.push("..");
+ }
+ comps.push(a);
+ if !a_verb {
+ comps.extend(ita.by_ref());
+ break;
+ }
+ }
+ }
+ }
+ Some(Path::new(comps.connect("\\")))
+ }
+ }
+
+ fn ends_with_path(&self, child: &Path) -> bool {
+ if !child.is_relative() { return false; }
+ let mut selfit = self.str_components().rev();
+ let mut childit = child.str_components().rev();
+ loop {
+ match (selfit.next(), childit.next()) {
+ (Some(a), Some(b)) => if a != b { return false; },
+ (Some(_), None) => break,
+ (None, Some(_)) => return false,
+ (None, None) => break
+ }
+ }
+ true
+ }
+}
+
+impl Path {
+ /// Returns a new `Path` from a `BytesContainer`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the vector contains a `NUL`, or if it contains invalid UTF-8.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// println!("{}", Path::new(r"C:\some\path").display());
+ /// ```
+ #[inline]
+ pub fn new<T: BytesContainer>(path: T) -> Path {
+ GenericPath::new(path)
+ }
+
+ /// Returns a new `Some(Path)` from a `BytesContainer`.
+ ///
+ /// Returns `None` if the vector contains a `NUL`, or if it contains invalid UTF-8.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// let path = Path::new_opt(r"C:\some\path");
+ ///
+ /// match path {
+ /// Some(path) => println!("{}", path.display()),
+ /// None => println!("There was a problem with your path."),
+ /// }
+ /// ```
+ #[inline]
+ pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
+ GenericPath::new_opt(path)
+ }
+
+ /// Returns an iterator that yields each component of the path in turn as a Option<&str>.
+ /// Every component is guaranteed to be Some.
+ /// Does not yield the path prefix (including server/share components in UNC paths).
+ /// Does not distinguish between volume-relative and relative paths, e.g.
+ /// \a\b\c and a\b\c.
+ /// Does not distinguish between absolute and cwd-relative paths, e.g.
+ /// C:\foo and C:foo.
+ pub fn str_components<'a>(&'a self) -> StrComponents<'a> {
+ let repr = &self.repr[];
+ let s = match self.prefix {
+ Some(_) => {
+ let plen = self.prefix_len();
+ if repr.len() > plen && repr.as_bytes()[plen] == SEP_BYTE {
+ &repr[plen+1..]
+ } else { &repr[plen..] }
+ }
+ None if repr.as_bytes()[0] == SEP_BYTE => &repr[1..],
+ None => repr
+ };
+ let some: fn(&'a str) -> Option<&'a str> = Some; // coerce to fn ptr
+ let ret = s.split_terminator(SEP).map(some);
+ ret
+ }
+
+ /// Returns an iterator that yields each component of the path in turn as a &[u8].
+ /// See str_components() for details.
+ pub fn components<'a>(&'a self) -> Components<'a> {
+ fn convert<'a>(x: Option<&'a str>) -> &'a [u8] {
+ #![inline]
+ x.unwrap().as_bytes()
+ }
+ let convert: for<'b> fn(Option<&'b str>) -> &'b [u8] = convert; // coerce to fn ptr
+ self.str_components().map(convert)
+ }
+
+ fn equiv_prefix(&self, other: &Path) -> bool {
+ let s_repr = &self.repr[];
+ let o_repr = &other.repr[];
+ match (self.prefix, other.prefix) {
+ (Some(DiskPrefix), Some(VerbatimDiskPrefix)) => {
+ self.is_absolute() &&
+ s_repr.as_bytes()[0].to_ascii_lowercase() ==
+ o_repr.as_bytes()[4].to_ascii_lowercase()
+ }
+ (Some(VerbatimDiskPrefix), Some(DiskPrefix)) => {
+ other.is_absolute() &&
+ s_repr.as_bytes()[4].to_ascii_lowercase() ==
+ o_repr.as_bytes()[0].to_ascii_lowercase()
+ }
+ (Some(VerbatimDiskPrefix), Some(VerbatimDiskPrefix)) => {
+ s_repr.as_bytes()[4].to_ascii_lowercase() ==
+ o_repr.as_bytes()[4].to_ascii_lowercase()
+ }
+ (Some(UNCPrefix(_,_)), Some(VerbatimUNCPrefix(_,_))) => {
+ &s_repr[2..self.prefix_len()] == &o_repr[8..other.prefix_len()]
+ }
+ (Some(VerbatimUNCPrefix(_,_)), Some(UNCPrefix(_,_))) => {
+ &s_repr[8..self.prefix_len()] == &o_repr[2..other.prefix_len()]
+ }
+ (None, None) => true,
+ (a, b) if a == b => {
+ &s_repr[..self.prefix_len()] == &o_repr[..other.prefix_len()]
+ }
+ _ => false
+ }
+ }
+
+ fn normalize_(s: &str) -> (Option<PathPrefix>, String) {
+ // make borrowck happy
+ let (prefix, val) = {
+ let prefix = parse_prefix(s);
+ let path = Path::normalize__(s, prefix);
+ (prefix, path)
+ };
+ (prefix, match val {
+ None => s.to_string(),
+ Some(val) => val
+ })
+ }
+
+ fn normalize__(s: &str, prefix: Option<PathPrefix>) -> Option<String> {
+ if prefix_is_verbatim(prefix) {
+ // don't do any normalization
+ match prefix {
+ Some(VerbatimUNCPrefix(x, 0)) if s.len() == 8 + x => {
+ // the server component has no trailing '\'
+ let mut s = String::from_str(s);
+ s.push(SEP);
+ Some(s)
+ }
+ _ => None
+ }
+ } else {
+ let (is_abs, comps) = normalize_helper(s, prefix);
+ let mut comps = comps;
+ match (comps.is_some(),prefix) {
+ (false, Some(DiskPrefix)) => {
+ if s.as_bytes()[0] >= b'a' && s.as_bytes()[0] <= b'z' {
+ comps = Some(vec![]);
+ }
+ }
+ (false, Some(VerbatimDiskPrefix)) => {
+ if s.as_bytes()[4] >= b'a' && s.as_bytes()[0] <= b'z' {
+ comps = Some(vec![]);
+ }
+ }
+ _ => ()
+ }
+ match comps {
+ None => None,
+ Some(comps) => {
+ if prefix.is_some() && comps.is_empty() {
+ match prefix.unwrap() {
+ DiskPrefix => {
+ let len = prefix_len(prefix) + is_abs as uint;
+ let mut s = String::from_str(&s[..len]);
+ unsafe {
+ let v = s.as_mut_vec();
+ v[0] = (*v)[0].to_ascii_uppercase();
+ }
+ if is_abs {
+ // normalize C:/ to C:\
+ unsafe {
+ s.as_mut_vec()[2] = SEP_BYTE;
+ }
+ }
+ Some(s)
+ }
+ VerbatimDiskPrefix => {
+ let len = prefix_len(prefix) + is_abs as uint;
+ let mut s = String::from_str(&s[..len]);
+ unsafe {
+ let v = s.as_mut_vec();
+ v[4] = (*v)[4].to_ascii_uppercase();
+ }
+ Some(s)
+ }
+ _ => {
+ let plen = prefix_len(prefix);
+ if s.len() > plen {
+ Some(String::from_str(&s[..plen]))
+ } else { None }
+ }
+ }
+ } else if is_abs && comps.is_empty() {
+ Some(repeat(SEP).take(1).collect())
+ } else {
+ let prefix_ = &s[..prefix_len(prefix)];
+ let n = prefix_.len() +
+ if is_abs { comps.len() } else { comps.len() - 1} +
+ comps.iter().map(|v| v.len()).sum();
+ let mut s = String::with_capacity(n);
+ match prefix {
+ Some(DiskPrefix) => {
+ s.push(prefix_.as_bytes()[0].to_ascii_uppercase() as char);
+ s.push(':');
+ }
+ Some(VerbatimDiskPrefix) => {
+ s.push_str(&prefix_[..4]);
+ s.push(prefix_.as_bytes()[4].to_ascii_uppercase() as char);
+ s.push_str(&prefix_[5..]);
+ }
+ Some(UNCPrefix(a,b)) => {
+ s.push_str("\\\\");
+ s.push_str(&prefix_[2..a+2]);
+ s.push(SEP);
+ s.push_str(&prefix_[3+a..3+a+b]);
+ }
+ Some(_) => s.push_str(prefix_),
+ None => ()
+ }
+ let mut it = comps.into_iter();
+ if !is_abs {
+ match it.next() {
+ None => (),
+ Some(comp) => s.push_str(comp)
+ }
+ }
+ for comp in it {
+ s.push(SEP);
+ s.push_str(comp);
+ }
+ Some(s)
+ }
+ }
+ }
+ }
+ }
+
+ fn update_sepidx(&mut self) {
+ let s = if self.has_nonsemantic_trailing_slash() {
+ &self.repr[..self.repr.len()-1]
+ } else { &self.repr[] };
+ let sep_test: fn(char) -> bool = if !prefix_is_verbatim(self.prefix) {
+ is_sep
+ } else {
+ is_sep_verbatim
+ };
+ let idx = s.rfind(sep_test);
+ let prefixlen = self.prefix_len();
+ self.sepidx = idx.and_then(|x| if x < prefixlen { None } else { Some(x) });
+ }
+
+ fn prefix_len(&self) -> uint {
+ prefix_len(self.prefix)
+ }
+
+ // Returns a tuple (before, after, end) where before is the index of the separator
+ // and after is the index just after the separator.
+ // end is the length of the string, normally, or the index of the final character if it is
+ // a non-semantic trailing separator in a verbatim string.
+ // If the prefix is considered the separator, before and after are the same.
+ fn sepidx_or_prefix_len(&self) -> Option<(uint,uint,uint)> {
+ match self.sepidx {
+ None => match self.prefix_len() { 0 => None, x => Some((x,x,self.repr.len())) },
+ Some(x) => {
+ if self.has_nonsemantic_trailing_slash() {
+ Some((x,x+1,self.repr.len()-1))
+ } else { Some((x,x+1,self.repr.len())) }
+ }
+ }
+ }
+
+ fn has_nonsemantic_trailing_slash(&self) -> bool {
+ is_verbatim(self) && self.repr.len() > self.prefix_len()+1 &&
+ self.repr.as_bytes()[self.repr.len()-1] == SEP_BYTE
+ }
+
+ fn update_normalized(&mut self, s: &str) {
+ let (prefix, path) = Path::normalize_(s);
+ self.repr = path;
+ self.prefix = prefix;
+ self.update_sepidx();
+ }
+}
+
+/// Returns whether the path is considered "volume-relative", which means a path
+/// that looks like "\foo". Paths of this form are relative to the current volume,
+/// but absolute within that volume.
+#[inline]
+pub fn is_vol_relative(path: &Path) -> bool {
+ path.prefix.is_none() && is_sep_byte(&path.repr.as_bytes()[0])
+}
+
+/// Returns whether the path is considered "cwd-relative", which means a path
+/// with a volume prefix that is not absolute. This look like "C:foo.txt". Paths
+/// of this form are relative to the cwd on the given volume.
+#[inline]
+pub fn is_cwd_relative(path: &Path) -> bool {
+ path.prefix == Some(DiskPrefix) && !path.is_absolute()
+}
+
+/// Returns the PathPrefix for this Path
+#[inline]
+pub fn prefix(path: &Path) -> Option<PathPrefix> {
+ path.prefix
+}
+
+/// Returns whether the Path's prefix is a verbatim prefix, i.e. `\\?\`
+#[inline]
+pub fn is_verbatim(path: &Path) -> bool {
+ prefix_is_verbatim(path.prefix)
+}
+
+/// Returns the non-verbatim equivalent of the input path, if possible.
+/// If the input path is a device namespace path, None is returned.
+/// If the input path is not verbatim, it is returned as-is.
+/// If the input path is verbatim, but the same path can be expressed as
+/// non-verbatim, the non-verbatim version is returned.
+/// Otherwise, None is returned.
+pub fn make_non_verbatim(path: &Path) -> Option<Path> {
+ let repr = &path.repr[];
+ let new_path = match path.prefix {
+ Some(VerbatimPrefix(_)) | Some(DeviceNSPrefix(_)) => return None,
+ Some(UNCPrefix(_,_)) | Some(DiskPrefix) | None => return Some(path.clone()),
+ Some(VerbatimDiskPrefix) => {
+ // \\?\D:\
+ Path::new(&repr[4..])
+ }
+ Some(VerbatimUNCPrefix(_,_)) => {
+ // \\?\UNC\server\share
+ Path::new(format!(r"\{}", &repr[7..]))
+ }
+ };
+ if new_path.prefix.is_none() {
+ // \\?\UNC\server is a VerbatimUNCPrefix
+ // but \\server is nothing
+ return None;
+ }
+ // now ensure normalization didn't change anything
+ if &repr[path.prefix_len()..] == &new_path.repr[new_path.prefix_len()..] {
+ Some(new_path)
+ } else {
+ None
+ }
+}
+
+/// The standard path separator character
+pub const SEP: char = '\\';
+/// The standard path separator byte
+pub const SEP_BYTE: u8 = SEP as u8;
+
+/// The alternative path separator character
+pub const SEP2: char = '/';
+/// The alternative path separator character
+pub const SEP2_BYTE: u8 = SEP2 as u8;
+
+/// Returns whether the given char is a path separator.
+/// Allows both the primary separator '\' and the alternative separator '/'.
+#[inline]
+pub fn is_sep(c: char) -> bool {
+ c == SEP || c == SEP2
+}
+
+/// Returns whether the given char is a path separator.
+/// Only allows the primary separator '\'; use is_sep to allow '/'.
+#[inline]
+pub fn is_sep_verbatim(c: char) -> bool {
+ c == SEP
+}
+
+/// Returns whether the given byte is a path separator.
+/// Allows both the primary separator '\' and the alternative separator '/'.
+#[inline]
+pub fn is_sep_byte(u: &u8) -> bool {
+ *u == SEP_BYTE || *u == SEP2_BYTE
+}
+
+/// Returns whether the given byte is a path separator.
+/// Only allows the primary separator '\'; use is_sep_byte to allow '/'.
+#[inline]
+pub fn is_sep_byte_verbatim(u: &u8) -> bool {
+ *u == SEP_BYTE
+}
+
+/// Prefix types for Path
+#[derive(Copy, PartialEq, Clone, Debug)]
+pub enum PathPrefix {
+ /// Prefix `\\?\`, uint is the length of the following component
+ VerbatimPrefix(uint),
+ /// Prefix `\\?\UNC\`, uints are the lengths of the UNC components
+ VerbatimUNCPrefix(uint, uint),
+ /// Prefix `\\?\C:\` (for any alphabetic character)
+ VerbatimDiskPrefix,
+ /// Prefix `\\.\`, uint is the length of the following component
+ DeviceNSPrefix(uint),
+ /// UNC prefix `\\server\share`, uints are the lengths of the server/share
+ UNCPrefix(uint, uint),
+ /// Prefix `C:` for any alphabetic character
+ DiskPrefix
+}
+
+fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> {
+ if path.starts_with("\\\\") {
+ // \\
+ path = &path[2..];
+ if path.starts_with("?\\") {
+ // \\?\
+ path = &path[2..];
+ if path.starts_with("UNC\\") {
+ // \\?\UNC\server\share
+ path = &path[4..];
+ let (idx_a, idx_b) = match parse_two_comps(path, is_sep_verbatim) {
+ Some(x) => x,
+ None => (path.len(), 0)
+ };
+ return Some(VerbatimUNCPrefix(idx_a, idx_b));
+ } else {
+ // \\?\path
+ let idx = path.find('\\');
+ if idx == Some(2) && path.as_bytes()[1] == b':' {
+ let c = path.as_bytes()[0];
+ if c.is_ascii() && (c as char).is_alphabetic() {
+ // \\?\C:\ path
+ return Some(VerbatimDiskPrefix);
+ }
+ }
+ let idx = idx.unwrap_or(path.len());
+ return Some(VerbatimPrefix(idx));
+ }
+ } else if path.starts_with(".\\") {
+ // \\.\path
+ path = &path[2..];
+ let idx = path.find('\\').unwrap_or(path.len());
+ return Some(DeviceNSPrefix(idx));
+ }
+ match parse_two_comps(path, is_sep) {
+ Some((idx_a, idx_b)) if idx_a > 0 && idx_b > 0 => {
+ // \\server\share
+ return Some(UNCPrefix(idx_a, idx_b));
+ }
+ _ => ()
+ }
+ } else if path.len() > 1 && path.as_bytes()[1] == b':' {
+ // C:
+ let c = path.as_bytes()[0];
+ if c.is_ascii() && (c as char).is_alphabetic() {
+ return Some(DiskPrefix);
+ }
+ }
+ return None;
+
+ fn parse_two_comps(mut path: &str, f: fn(char) -> bool) -> Option<(uint, uint)> {
+ let idx_a = match path.find(f) {
+ None => return None,
+ Some(x) => x
+ };
+ path = &path[idx_a+1..];
+ let idx_b = path.find(f).unwrap_or(path.len());
+ Some((idx_a, idx_b))
+ }
+}
+
+// None result means the string didn't need normalizing
+fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool, Option<Vec<&'a str>>) {
+ let f: fn(char) -> bool = if !prefix_is_verbatim(prefix) {
+ is_sep
+ } else {
+ is_sep_verbatim
+ };
+ let is_abs = s.len() > prefix_len(prefix) && f(s.char_at(prefix_len(prefix)));
+ let s_ = &s[prefix_len(prefix)..];
+ let s_ = if is_abs { &s_[1..] } else { s_ };
+
+ if is_abs && s_.is_empty() {
+ return (is_abs, match prefix {
+ Some(DiskPrefix) | None => (if is_sep_verbatim(s.char_at(prefix_len(prefix))) { None }
+ else { Some(vec![]) }),
+ Some(_) => Some(vec![]), // need to trim the trailing separator
+ });
+ }
+ let mut comps: Vec<&'a str> = vec![];
+ let mut n_up = 0u;
+ let mut changed = false;
+ for comp in s_.split(f) {
+ if comp.is_empty() { changed = true }
+ else if comp == "." { changed = true }
+ else if comp == ".." {
+ let has_abs_prefix = match prefix {
+ Some(DiskPrefix) => false,
+ Some(_) => true,
+ None => false
+ };
+ if (is_abs || has_abs_prefix) && comps.is_empty() { changed = true }
+ else if comps.len() == n_up { comps.push(".."); n_up += 1 }
+ else { comps.pop().unwrap(); changed = true }
+ } else { comps.push(comp) }
+ }
+ if !changed && !prefix_is_verbatim(prefix) {
+ changed = s.find(is_sep).is_some();
+ }
+ if changed {
+ if comps.is_empty() && !is_abs && prefix.is_none() {
+ if s == "." {
+ return (is_abs, None);
+ }
+ comps.push(".");
+ }
+ (is_abs, Some(comps))
+ } else {
+ (is_abs, None)
+ }
+}
+
+fn prefix_is_verbatim(p: Option<PathPrefix>) -> bool {
+ match p {
+ Some(VerbatimPrefix(_)) | Some(VerbatimUNCPrefix(_,_)) | Some(VerbatimDiskPrefix) => true,
+ Some(DeviceNSPrefix(_)) => true, // not really sure, but I think so
+ _ => false
+ }
+}
+
+fn prefix_len(p: Option<PathPrefix>) -> uint {
+ match p {
+ None => 0,
+ Some(VerbatimPrefix(x)) => 4 + x,
+ Some(VerbatimUNCPrefix(x,y)) => 8 + x + 1 + y,
+ Some(VerbatimDiskPrefix) => 6,
+ Some(UNCPrefix(x,y)) => 2 + x + 1 + y,
+ Some(DeviceNSPrefix(x)) => 4 + x,
+ Some(DiskPrefix) => 2
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::PathPrefix::*;
+ use super::parse_prefix;
+ use super::*;
+
+ use clone::Clone;
+ use iter::IteratorExt;
+ use option::Option::{self, Some, None};
+ use old_path::GenericPath;
+ use slice::{AsSlice, SliceExt};
+ use str::Str;
+ use string::ToString;
+ use vec::Vec;
+
+ macro_rules! t {
+ (s: $path:expr, $exp:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $exp:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.as_vec(), $exp);
+ }
+ )
+ }
+
+ #[test]
+ fn test_parse_prefix() {
+ macro_rules! t {
+ ($path:expr, $exp:expr) => (
+ {
+ let path = $path;
+ let exp = $exp;
+ let res = parse_prefix(path);
+ assert_eq!(res, exp);
+ }
+ )
+ }
+
+ t!("\\\\SERVER\\share\\foo", Some(UNCPrefix(6,5)));
+ t!("\\\\", None);
+ t!("\\\\SERVER", None);
+ t!("\\\\SERVER\\", None);
+ t!("\\\\SERVER\\\\", None);
+ t!("\\\\SERVER\\\\foo", None);
+ t!("\\\\SERVER\\share", Some(UNCPrefix(6,5)));
+ t!("\\\\SERVER/share/foo", Some(UNCPrefix(6,5)));
+ t!("\\\\SERVER\\share/foo", Some(UNCPrefix(6,5)));
+ t!("//SERVER/share/foo", None);
+ t!("\\\\\\a\\b\\c", None);
+ t!("\\\\?\\a\\b\\c", Some(VerbatimPrefix(1)));
+ t!("\\\\?\\a/b/c", Some(VerbatimPrefix(5)));
+ t!("//?/a/b/c", None);
+ t!("\\\\.\\a\\b", Some(DeviceNSPrefix(1)));
+ t!("\\\\.\\a/b", Some(DeviceNSPrefix(3)));
+ t!("//./a/b", None);
+ t!("\\\\?\\UNC\\server\\share\\foo", Some(VerbatimUNCPrefix(6,5)));
+ t!("\\\\?\\UNC\\\\share\\foo", Some(VerbatimUNCPrefix(0,5)));
+ t!("\\\\?\\UNC\\", Some(VerbatimUNCPrefix(0,0)));
+ t!("\\\\?\\UNC\\server/share/foo", Some(VerbatimUNCPrefix(16,0)));
+ t!("\\\\?\\UNC\\server", Some(VerbatimUNCPrefix(6,0)));
+ t!("\\\\?\\UNC\\server\\", Some(VerbatimUNCPrefix(6,0)));
+ t!("\\\\?\\UNC/server/share", Some(VerbatimPrefix(16)));
+ t!("\\\\?\\UNC", Some(VerbatimPrefix(3)));
+ t!("\\\\?\\C:\\a\\b.txt", Some(VerbatimDiskPrefix));
+ t!("\\\\?\\z:\\", Some(VerbatimDiskPrefix));
+ t!("\\\\?\\C:", Some(VerbatimPrefix(2)));
+ t!("\\\\?\\C:a.txt", Some(VerbatimPrefix(7)));
+ t!("\\\\?\\C:a\\b.txt", Some(VerbatimPrefix(3)));
+ t!("\\\\?\\C:/a", Some(VerbatimPrefix(4)));
+ t!("C:\\foo", Some(DiskPrefix));
+ t!("z:/foo", Some(DiskPrefix));
+ t!("d:", Some(DiskPrefix));
+ t!("ab:", None);
+ t!("ü:\\foo", None);
+ t!("3:\\foo", None);
+ t!(" :\\foo", None);
+ t!("::\\foo", None);
+ t!("\\\\?\\C:", Some(VerbatimPrefix(2)));
+ t!("\\\\?\\z:\\", Some(VerbatimDiskPrefix));
+ t!("\\\\?\\ab:\\", Some(VerbatimPrefix(3)));
+ t!("\\\\?\\C:\\a", Some(VerbatimDiskPrefix));
+ t!("\\\\?\\C:/a", Some(VerbatimPrefix(4)));
+ t!("\\\\?\\C:\\a/b", Some(VerbatimDiskPrefix));
+ }
+
+ #[test]
+ fn test_paths() {
+ let empty: &[u8] = &[];
+ t!(v: Path::new(empty), b".");
+ t!(v: Path::new(b"\\"), b"\\");
+ t!(v: Path::new(b"a\\b\\c"), b"a\\b\\c");
+
+ t!(s: Path::new(""), ".");
+ t!(s: Path::new("\\"), "\\");
+ t!(s: Path::new("hi"), "hi");
+ t!(s: Path::new("hi\\"), "hi");
+ t!(s: Path::new("\\lib"), "\\lib");
+ t!(s: Path::new("\\lib\\"), "\\lib");
+ t!(s: Path::new("hi\\there"), "hi\\there");
+ t!(s: Path::new("hi\\there.txt"), "hi\\there.txt");
+ t!(s: Path::new("/"), "\\");
+ t!(s: Path::new("hi/"), "hi");
+ t!(s: Path::new("/lib"), "\\lib");
+ t!(s: Path::new("/lib/"), "\\lib");
+ t!(s: Path::new("hi/there"), "hi\\there");
+
+ t!(s: Path::new("hi\\there\\"), "hi\\there");
+ t!(s: Path::new("hi\\..\\there"), "there");
+ t!(s: Path::new("hi/../there"), "there");
+ t!(s: Path::new("..\\hi\\there"), "..\\hi\\there");
+ t!(s: Path::new("\\..\\hi\\there"), "\\hi\\there");
+ t!(s: Path::new("/../hi/there"), "\\hi\\there");
+ t!(s: Path::new("foo\\.."), ".");
+ t!(s: Path::new("\\foo\\.."), "\\");
+ t!(s: Path::new("\\foo\\..\\.."), "\\");
+ t!(s: Path::new("\\foo\\..\\..\\bar"), "\\bar");
+ t!(s: Path::new("\\.\\hi\\.\\there\\."), "\\hi\\there");
+ t!(s: Path::new("\\.\\hi\\.\\there\\.\\.."), "\\hi");
+ t!(s: Path::new("foo\\..\\.."), "..");
+ t!(s: Path::new("foo\\..\\..\\.."), "..\\..");
+ t!(s: Path::new("foo\\..\\..\\bar"), "..\\bar");
+
+ assert_eq!(Path::new(b"foo\\bar").into_vec(), b"foo\\bar");
+ assert_eq!(Path::new(b"\\foo\\..\\..\\bar").into_vec(), b"\\bar");
+
+ t!(s: Path::new("\\\\a"), "\\a");
+ t!(s: Path::new("\\\\a\\"), "\\a");
+ t!(s: Path::new("\\\\a\\b"), "\\\\a\\b");
+ t!(s: Path::new("\\\\a\\b\\"), "\\\\a\\b");
+ t!(s: Path::new("\\\\a\\b/"), "\\\\a\\b");
+ t!(s: Path::new("\\\\\\b"), "\\b");
+ t!(s: Path::new("\\\\a\\\\b"), "\\a\\b");
+ t!(s: Path::new("\\\\a\\b\\c"), "\\\\a\\b\\c");
+ t!(s: Path::new("\\\\server\\share/path"), "\\\\server\\share\\path");
+ t!(s: Path::new("\\\\server/share/path"), "\\\\server\\share\\path");
+ t!(s: Path::new("C:a\\b.txt"), "C:a\\b.txt");
+ t!(s: Path::new("C:a/b.txt"), "C:a\\b.txt");
+ t!(s: Path::new("z:\\a\\b.txt"), "Z:\\a\\b.txt");
+ t!(s: Path::new("z:/a/b.txt"), "Z:\\a\\b.txt");
+ t!(s: Path::new("ab:/a/b.txt"), "ab:\\a\\b.txt");
+ t!(s: Path::new("C:\\"), "C:\\");
+ t!(s: Path::new("C:"), "C:");
+ t!(s: Path::new("q:"), "Q:");
+ t!(s: Path::new("C:/"), "C:\\");
+ t!(s: Path::new("C:\\foo\\.."), "C:\\");
+ t!(s: Path::new("C:foo\\.."), "C:");
+ t!(s: Path::new("C:\\a\\"), "C:\\a");
+ t!(s: Path::new("C:\\a/"), "C:\\a");
+ t!(s: Path::new("C:\\a\\b\\"), "C:\\a\\b");
+ t!(s: Path::new("C:\\a\\b/"), "C:\\a\\b");
+ t!(s: Path::new("C:a\\"), "C:a");
+ t!(s: Path::new("C:a/"), "C:a");
+ t!(s: Path::new("C:a\\b\\"), "C:a\\b");
+ t!(s: Path::new("C:a\\b/"), "C:a\\b");
+ t!(s: Path::new("\\\\?\\z:\\a\\b.txt"), "\\\\?\\z:\\a\\b.txt");
+ t!(s: Path::new("\\\\?\\C:/a/b.txt"), "\\\\?\\C:/a/b.txt");
+ t!(s: Path::new("\\\\?\\C:\\a/b.txt"), "\\\\?\\C:\\a/b.txt");
+ t!(s: Path::new("\\\\?\\test\\a\\b.txt"), "\\\\?\\test\\a\\b.txt");
+ t!(s: Path::new("\\\\?\\foo\\bar\\"), "\\\\?\\foo\\bar\\");
+ t!(s: Path::new("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar");
+ t!(s: Path::new("\\\\.\\"), "\\\\.\\");
+ t!(s: Path::new("\\\\?\\UNC\\server\\share\\foo"), "\\\\?\\UNC\\server\\share\\foo");
+ t!(s: Path::new("\\\\?\\UNC\\server/share"), "\\\\?\\UNC\\server/share\\");
+ t!(s: Path::new("\\\\?\\UNC\\server"), "\\\\?\\UNC\\server\\");
+ t!(s: Path::new("\\\\?\\UNC\\"), "\\\\?\\UNC\\\\");
+ t!(s: Path::new("\\\\?\\UNC"), "\\\\?\\UNC");
+
+ // I'm not sure whether \\.\foo/bar should normalize to \\.\foo\bar
+ // as information is sparse and this isn't really googleable.
+ // I'm going to err on the side of not normalizing it, as this skips the filesystem
+ t!(s: Path::new("\\\\.\\foo/bar"), "\\\\.\\foo/bar");
+ t!(s: Path::new("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar");
+ }
+
+ #[test]
+ fn test_opt_paths() {
+ assert!(Path::new_opt(b"foo\\bar\0") == None);
+ assert!(Path::new_opt(b"foo\\bar\x80") == None);
+ t!(v: Path::new_opt(b"foo\\bar").unwrap(), b"foo\\bar");
+ assert!(Path::new_opt("foo\\bar\0") == None);
+ t!(s: Path::new_opt("foo\\bar").unwrap(), "foo\\bar");
+ }
+
+ #[test]
+ fn test_null_byte() {
+ use thread::Thread;
+ let result = Thread::scoped(move|| {
+ Path::new(b"foo/bar\0")
+ }).join();
+ assert!(result.is_err());
+
+ let result = Thread::scoped(move|| {
+ Path::new("test").set_filename(b"f\0o")
+ }).join();
+ assert!(result.is_err());
+
+ let result = Thread::scoped(move || {
+ Path::new("test").push(b"f\0o");
+ }).join();
+ assert!(result.is_err());
+ }
+
+ #[test]
+ #[should_fail]
+ fn test_not_utf8_panics() {
+ Path::new(b"hello\x80.txt");
+ }
+
+ #[test]
+ fn test_display_str() {
+ let path = Path::new("foo");
+ assert_eq!(path.display().to_string(), "foo");
+ let path = Path::new(b"\\");
+ assert_eq!(path.filename_display().to_string(), "");
+
+ let path = Path::new("foo");
+ let mo = path.display().as_cow();
+ assert_eq!(mo.as_slice(), "foo");
+ let path = Path::new(b"\\");
+ let mo = path.filename_display().as_cow();
+ assert_eq!(mo.as_slice(), "");
+ }
+
+ #[test]
+ fn test_display() {
+ macro_rules! t {
+ ($path:expr, $exp:expr, $expf:expr) => (
+ {
+ let path = Path::new($path);
+ let f = format!("{}", path.display());
+ assert_eq!(f, $exp);
+ let f = format!("{}", path.filename_display());
+ assert_eq!(f, $expf);
+ }
+ )
+ }
+
+ t!("foo", "foo", "foo");
+ t!("foo\\bar", "foo\\bar", "bar");
+ t!("\\", "\\", "");
+ }
+
+ #[test]
+ fn test_components() {
+ macro_rules! t {
+ (s: $path:expr, $op:ident, $exp:expr) => (
+ {
+ let path = $path;
+ let path = Path::new(path);
+ assert_eq!(path.$op(), Some($exp));
+ }
+ );
+ (s: $path:expr, $op:ident, $exp:expr, opt) => (
+ {
+ let path = $path;
+ let path = Path::new(path);
+ let left = path.$op();
+ assert_eq!(left, $exp);
+ }
+ );
+ (v: $path:expr, $op:ident, $exp:expr) => (
+ {
+ let path = $path;
+ let path = Path::new(path);
+ assert_eq!(path.$op(), $exp);
+ }
+ )
+ }
+
+ t!(v: b"a\\b\\c", filename, Some(b"c"));
+ t!(s: "a\\b\\c", filename_str, "c");
+ t!(s: "\\a\\b\\c", filename_str, "c");
+ t!(s: "a", filename_str, "a");
+ t!(s: "\\a", filename_str, "a");
+ t!(s: ".", filename_str, None, opt);
+ t!(s: "\\", filename_str, None, opt);
+ t!(s: "..", filename_str, None, opt);
+ t!(s: "..\\..", filename_str, None, opt);
+ t!(s: "c:\\foo.txt", filename_str, "foo.txt");
+ t!(s: "C:\\", filename_str, None, opt);
+ t!(s: "C:", filename_str, None, opt);
+ t!(s: "\\\\server\\share\\foo.txt", filename_str, "foo.txt");
+ t!(s: "\\\\server\\share", filename_str, None, opt);
+ t!(s: "\\\\server", filename_str, "server");
+ t!(s: "\\\\?\\bar\\foo.txt", filename_str, "foo.txt");
+ t!(s: "\\\\?\\bar", filename_str, None, opt);
+ t!(s: "\\\\?\\", filename_str, None, opt);
+ t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", filename_str, "foo.txt");
+ t!(s: "\\\\?\\UNC\\server", filename_str, None, opt);
+ t!(s: "\\\\?\\UNC\\", filename_str, None, opt);
+ t!(s: "\\\\?\\C:\\foo.txt", filename_str, "foo.txt");
+ t!(s: "\\\\?\\C:\\", filename_str, None, opt);
+ t!(s: "\\\\?\\C:", filename_str, None, opt);
+ t!(s: "\\\\?\\foo/bar", filename_str, None, opt);
+ t!(s: "\\\\?\\C:/foo", filename_str, None, opt);
+ t!(s: "\\\\.\\foo\\bar", filename_str, "bar");
+ t!(s: "\\\\.\\foo", filename_str, None, opt);
+ t!(s: "\\\\.\\foo/bar", filename_str, None, opt);
+ t!(s: "\\\\.\\foo\\bar/baz", filename_str, "bar/baz");
+ t!(s: "\\\\.\\", filename_str, None, opt);
+ t!(s: "\\\\?\\a\\b\\", filename_str, "b");
+
+ t!(v: b"a\\b\\c", dirname, b"a\\b");
+ t!(s: "a\\b\\c", dirname_str, "a\\b");
+ t!(s: "\\a\\b\\c", dirname_str, "\\a\\b");
+ t!(s: "a", dirname_str, ".");
+ t!(s: "\\a", dirname_str, "\\");
+ t!(s: ".", dirname_str, ".");
+ t!(s: "\\", dirname_str, "\\");
+ t!(s: "..", dirname_str, "..");
+ t!(s: "..\\..", dirname_str, "..\\..");
+ t!(s: "c:\\foo.txt", dirname_str, "C:\\");
+ t!(s: "C:\\", dirname_str, "C:\\");
+ t!(s: "C:", dirname_str, "C:");
+ t!(s: "C:foo.txt", dirname_str, "C:");
+ t!(s: "\\\\server\\share\\foo.txt", dirname_str, "\\\\server\\share");
+ t!(s: "\\\\server\\share", dirname_str, "\\\\server\\share");
+ t!(s: "\\\\server", dirname_str, "\\");
+ t!(s: "\\\\?\\bar\\foo.txt", dirname_str, "\\\\?\\bar");
+ t!(s: "\\\\?\\bar", dirname_str, "\\\\?\\bar");
+ t!(s: "\\\\?\\", dirname_str, "\\\\?\\");
+ t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", dirname_str, "\\\\?\\UNC\\server\\share");
+ t!(s: "\\\\?\\UNC\\server", dirname_str, "\\\\?\\UNC\\server\\");
+ t!(s: "\\\\?\\UNC\\", dirname_str, "\\\\?\\UNC\\\\");
+ t!(s: "\\\\?\\C:\\foo.txt", dirname_str, "\\\\?\\C:\\");
+ t!(s: "\\\\?\\C:\\", dirname_str, "\\\\?\\C:\\");
+ t!(s: "\\\\?\\C:", dirname_str, "\\\\?\\C:");
+ t!(s: "\\\\?\\C:/foo/bar", dirname_str, "\\\\?\\C:/foo/bar");
+ t!(s: "\\\\?\\foo/bar", dirname_str, "\\\\?\\foo/bar");
+ t!(s: "\\\\.\\foo\\bar", dirname_str, "\\\\.\\foo");
+ t!(s: "\\\\.\\foo", dirname_str, "\\\\.\\foo");
+ t!(s: "\\\\?\\a\\b\\", dirname_str, "\\\\?\\a");
+
+ t!(v: b"hi\\there.txt", filestem, Some(b"there"));
+ t!(s: "hi\\there.txt", filestem_str, "there");
+ t!(s: "hi\\there", filestem_str, "there");
+ t!(s: "there.txt", filestem_str, "there");
+ t!(s: "there", filestem_str, "there");
+ t!(s: ".", filestem_str, None, opt);
+ t!(s: "\\", filestem_str, None, opt);
+ t!(s: "foo\\.bar", filestem_str, ".bar");
+ t!(s: ".bar", filestem_str, ".bar");
+ t!(s: "..bar", filestem_str, ".");
+ t!(s: "hi\\there..txt", filestem_str, "there.");
+ t!(s: "..", filestem_str, None, opt);
+ t!(s: "..\\..", filestem_str, None, opt);
+ // filestem is based on filename, so we don't need the full set of prefix tests
+
+ t!(v: b"hi\\there.txt", extension, Some(b"txt"));
+ t!(v: b"hi\\there", extension, None);
+ t!(s: "hi\\there.txt", extension_str, Some("txt"), opt);
+ t!(s: "hi\\there", extension_str, None, opt);
+ t!(s: "there.txt", extension_str, Some("txt"), opt);
+ t!(s: "there", extension_str, None, opt);
+ t!(s: ".", extension_str, None, opt);
+ t!(s: "\\", extension_str, None, opt);
+ t!(s: "foo\\.bar", extension_str, None, opt);
+ t!(s: ".bar", extension_str, None, opt);
+ t!(s: "..bar", extension_str, Some("bar"), opt);
+ t!(s: "hi\\there..txt", extension_str, Some("txt"), opt);
+ t!(s: "..", extension_str, None, opt);
+ t!(s: "..\\..", extension_str, None, opt);
+ // extension is based on filename, so we don't need the full set of prefix tests
+ }
+
+ #[test]
+ fn test_push() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr) => (
+ {
+ let path = $path;
+ let join = $join;
+ let mut p1 = Path::new(path);
+ let p2 = p1.clone();
+ p1.push(join);
+ assert_eq!(p1, p2.join(join));
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "..");
+ t!(s: "\\a\\b\\c", "d");
+ t!(s: "a\\b", "c\\d");
+ t!(s: "a\\b", "\\c\\d");
+ // this is just a sanity-check test. push and join share an implementation,
+ // so there's no need for the full set of prefix tests
+
+ // we do want to check one odd case though to ensure the prefix is re-parsed
+ let mut p = Path::new("\\\\?\\C:");
+ assert_eq!(prefix(&p), Some(VerbatimPrefix(2)));
+ p.push("foo");
+ assert_eq!(prefix(&p), Some(VerbatimDiskPrefix));
+ assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo"));
+
+ // and another with verbatim non-normalized paths
+ let mut p = Path::new("\\\\?\\C:\\a\\");
+ p.push("foo");
+ assert_eq!(p.as_str(), Some("\\\\?\\C:\\a\\foo"));
+ }
+
+ #[test]
+ fn test_push_path() {
+ macro_rules! t {
+ (s: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ let push = Path::new($push);
+ p.push(&push);
+ assert_eq!(p.as_str(), Some($exp));
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "d", "a\\b\\c\\d");
+ t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d");
+ t!(s: "a\\b", "c\\d", "a\\b\\c\\d");
+ t!(s: "a\\b", "\\c\\d", "\\c\\d");
+ t!(s: "a\\b", ".", "a\\b");
+ t!(s: "a\\b", "..\\c", "a\\c");
+ t!(s: "a\\b", "C:a.txt", "C:a.txt");
+ t!(s: "a\\b", "..\\..\\..\\c", "..\\c");
+ t!(s: "a\\b", "C:\\a.txt", "C:\\a.txt");
+ t!(s: "C:\\a", "C:\\b.txt", "C:\\b.txt");
+ t!(s: "C:\\a\\b\\c", "C:d", "C:\\a\\b\\c\\d");
+ t!(s: "C:a\\b\\c", "C:d", "C:a\\b\\c\\d");
+ t!(s: "C:a\\b", "..\\..\\..\\c", "C:..\\c");
+ t!(s: "C:\\a\\b", "..\\..\\..\\c", "C:\\c");
+ t!(s: "C:", r"a\b\c", r"C:a\b\c");
+ t!(s: "C:", r"..\a", r"C:..\a");
+ t!(s: "\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
+ t!(s: "\\\\server\\share\\foo", "..\\..\\bar", "\\\\server\\share\\bar");
+ t!(s: "\\\\server\\share\\foo", "C:baz", "C:baz");
+ t!(s: "\\\\?\\C:\\a\\b", "C:c\\d", "\\\\?\\C:\\a\\b\\c\\d");
+ t!(s: "\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
+ t!(s: "\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
+ t!(s: "\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
+ t!(s: "\\\\?\\C:\\a\\b", "..\\..\\..\\c", "\\\\?\\C:\\a\\b\\..\\..\\..\\c");
+ t!(s: "\\\\?\\foo\\bar", "..\\..\\c", "\\\\?\\foo\\bar\\..\\..\\c");
+ t!(s: "\\\\?\\", "foo", "\\\\?\\\\foo");
+ t!(s: "\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
+ t!(s: "\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
+ t!(s: "\\\\?\\UNC\\server\\share", "C:a", "C:a");
+ t!(s: "\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\\\foo");
+ t!(s: "C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
+ t!(s: "\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
+ t!(s: "\\\\.\\foo\\bar", "C:a", "C:a");
+ // again, not sure about the following, but I'm assuming \\.\ should be verbatim
+ t!(s: "\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
+
+ t!(s: "\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
+ }
+
+ #[test]
+ fn test_push_many() {
+ macro_rules! t {
+ (s: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ p.push_many(&$push);
+ assert_eq!(p.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $push:expr, $exp:expr) => (
+ {
+ let mut p = Path::new($path);
+ p.push_many(&$push);
+ assert_eq!(p.as_vec(), $exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
+ t!(s: "a\\b\\c", ["d", "\\e"], "\\e");
+ t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
+ t!(s: "a\\b\\c", ["d".to_string(), "e".to_string()], "a\\b\\c\\d\\e");
+ t!(v: b"a\\b\\c", [b"d", b"e"], b"a\\b\\c\\d\\e");
+ t!(v: b"a\\b\\c", [b"d", b"\\e", b"f"], b"\\e\\f");
+ t!(v: b"a\\b\\c", [b"d".to_vec(), b"e".to_vec()],
+ b"a\\b\\c\\d\\e");
+ }
+
+ #[test]
+ fn test_pop() {
+ macro_rules! t {
+ (s: $path:expr, $left:expr, $right:expr) => (
+ {
+ let pstr = $path;
+ let mut p = Path::new(pstr);
+ let result = p.pop();
+ let left = $left;
+ assert_eq!(p.as_str(), Some(left));
+ assert_eq!(result, $right);
+ }
+ );
+ (b: $path:expr, $left:expr, $right:expr) => (
+ {
+ let mut p = Path::new($path);
+ let result = p.pop();
+ assert_eq!(p.as_vec(), $left);
+ assert_eq!(result, $right);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "a\\b", true);
+ t!(s: "a", ".", true);
+ t!(s: ".", ".", false);
+ t!(s: "\\a", "\\", true);
+ t!(s: "\\", "\\", false);
+ t!(b: b"a\\b\\c", b"a\\b", true);
+ t!(b: b"a", b".", true);
+ t!(b: b".", b".", false);
+ t!(b: b"\\a", b"\\", true);
+ t!(b: b"\\", b"\\", false);
+
+ t!(s: "C:\\a\\b", "C:\\a", true);
+ t!(s: "C:\\a", "C:\\", true);
+ t!(s: "C:\\", "C:\\", false);
+ t!(s: "C:a\\b", "C:a", true);
+ t!(s: "C:a", "C:", true);
+ t!(s: "C:", "C:", false);
+ t!(s: "\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
+ t!(s: "\\\\server\\share\\a", "\\\\server\\share", true);
+ t!(s: "\\\\server\\share", "\\\\server\\share", false);
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
+ t!(s: "\\\\?\\a\\b", "\\\\?\\a", true);
+ t!(s: "\\\\?\\a", "\\\\?\\a", false);
+ t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\", true);
+ t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\", false);
+ t!(s: "\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
+ t!(s: "\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share", true);
+ t!(s: "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
+ t!(s: "\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
+ t!(s: "\\\\.\\a\\b", "\\\\.\\a", true);
+ t!(s: "\\\\.\\a", "\\\\.\\a", false);
+
+ t!(s: "\\\\?\\a\\b\\", "\\\\?\\a", true);
+ }
+
+ #[test]
+ fn test_root_path() {
+ assert_eq!(Path::new("a\\b\\c").root_path(), None);
+ assert_eq!(Path::new("\\a\\b\\c").root_path(), Some(Path::new("\\")));
+ assert_eq!(Path::new("C:a").root_path(), Some(Path::new("C:")));
+ assert_eq!(Path::new("C:\\a").root_path(), Some(Path::new("C:\\")));
+ assert_eq!(Path::new("\\\\a\\b\\c").root_path(), Some(Path::new("\\\\a\\b")));
+ assert_eq!(Path::new("\\\\?\\a\\b").root_path(), Some(Path::new("\\\\?\\a")));
+ assert_eq!(Path::new("\\\\?\\C:\\a").root_path(), Some(Path::new("\\\\?\\C:\\")));
+ assert_eq!(Path::new("\\\\?\\UNC\\a\\b\\c").root_path(),
+ Some(Path::new("\\\\?\\UNC\\a\\b")));
+ assert_eq!(Path::new("\\\\.\\a\\b").root_path(), Some(Path::new("\\\\.\\a")));
+ }
+
+ #[test]
+ fn test_join() {
+ t!(s: Path::new("a\\b\\c").join(".."), "a\\b");
+ t!(s: Path::new("\\a\\b\\c").join("d"), "\\a\\b\\c\\d");
+ t!(s: Path::new("a\\b").join("c\\d"), "a\\b\\c\\d");
+ t!(s: Path::new("a\\b").join("\\c\\d"), "\\c\\d");
+ t!(s: Path::new(".").join("a\\b"), "a\\b");
+ t!(s: Path::new("\\").join("a\\b"), "\\a\\b");
+ t!(v: Path::new(b"a\\b\\c").join(b".."), b"a\\b");
+ t!(v: Path::new(b"\\a\\b\\c").join(b"d"), b"\\a\\b\\c\\d");
+ // full join testing is covered under test_push_path, so no need for
+ // the full set of prefix tests
+ }
+
+ #[test]
+ fn test_join_path() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let join = Path::new($join);
+ let res = path.join(&join);
+ assert_eq!(res.as_str(), Some($exp));
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "..", "a\\b");
+ t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d");
+ t!(s: "a\\b", "c\\d", "a\\b\\c\\d");
+ t!(s: "a\\b", "\\c\\d", "\\c\\d");
+ t!(s: ".", "a\\b", "a\\b");
+ t!(s: "\\", "a\\b", "\\a\\b");
+ // join is implemented using push, so there's no need for
+ // the full set of prefix tests
+ }
+
+ #[test]
+ fn test_join_many() {
+ macro_rules! t {
+ (s: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let res = path.join_many(&$join);
+ assert_eq!(res.as_str(), Some($exp));
+ }
+ );
+ (v: $path:expr, $join:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let res = path.join_many(&$join);
+ assert_eq!(res.as_vec(), $exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
+ t!(s: "a\\b\\c", ["..", "d"], "a\\b\\d");
+ t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
+ t!(s: "a\\b\\c", ["d".to_string(), "e".to_string()], "a\\b\\c\\d\\e");
+ t!(v: b"a\\b\\c", [b"d", b"e"], b"a\\b\\c\\d\\e");
+ t!(v: b"a\\b\\c", [b"d".to_vec(), b"e".to_vec()],
+ b"a\\b\\c\\d\\e");
+ }
+
+ #[test]
+ fn test_with_helpers() {
+ macro_rules! t {
+ (s: $path:expr, $op:ident, $arg:expr, $res:expr) => (
+ {
+ let pstr = $path;
+ let path = Path::new(pstr);
+ let arg = $arg;
+ let res = path.$op(arg);
+ let exp = Path::new($res);
+ assert_eq!(res, exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", with_filename, "d", "a\\b\\d");
+ t!(s: ".", with_filename, "foo", "foo");
+ t!(s: "\\a\\b\\c", with_filename, "d", "\\a\\b\\d");
+ t!(s: "\\", with_filename, "foo", "\\foo");
+ t!(s: "\\a", with_filename, "foo", "\\foo");
+ t!(s: "foo", with_filename, "bar", "bar");
+ t!(s: "\\", with_filename, "foo\\", "\\foo");
+ t!(s: "\\a", with_filename, "foo\\", "\\foo");
+ t!(s: "a\\b\\c", with_filename, "", "a\\b");
+ t!(s: "a\\b\\c", with_filename, ".", "a\\b");
+ t!(s: "a\\b\\c", with_filename, "..", "a");
+ t!(s: "\\a", with_filename, "", "\\");
+ t!(s: "foo", with_filename, "", ".");
+ t!(s: "a\\b\\c", with_filename, "d\\e", "a\\b\\d\\e");
+ t!(s: "a\\b\\c", with_filename, "\\d", "a\\b\\d");
+ t!(s: "..", with_filename, "foo", "..\\foo");
+ t!(s: "..\\..", with_filename, "foo", "..\\..\\foo");
+ t!(s: "..", with_filename, "", "..");
+ t!(s: "..\\..", with_filename, "", "..\\..");
+ t!(s: "C:\\foo\\bar", with_filename, "baz", "C:\\foo\\baz");
+ t!(s: "C:\\foo", with_filename, "bar", "C:\\bar");
+ t!(s: "C:\\", with_filename, "foo", "C:\\foo");
+ t!(s: "C:foo\\bar", with_filename, "baz", "C:foo\\baz");
+ t!(s: "C:foo", with_filename, "bar", "C:bar");
+ t!(s: "C:", with_filename, "foo", "C:foo");
+ t!(s: "C:\\foo", with_filename, "", "C:\\");
+ t!(s: "C:foo", with_filename, "", "C:");
+ t!(s: "C:\\foo\\bar", with_filename, "..", "C:\\");
+ t!(s: "C:\\foo", with_filename, "..", "C:\\");
+ t!(s: "C:\\", with_filename, "..", "C:\\");
+ t!(s: "C:foo\\bar", with_filename, "..", "C:");
+ t!(s: "C:foo", with_filename, "..", "C:..");
+ t!(s: "C:", with_filename, "..", "C:..");
+ t!(s: "\\\\server\\share\\foo", with_filename, "bar", "\\\\server\\share\\bar");
+ t!(s: "\\\\server\\share", with_filename, "foo", "\\\\server\\share\\foo");
+ t!(s: "\\\\server\\share\\foo", with_filename, "", "\\\\server\\share");
+ t!(s: "\\\\server\\share", with_filename, "", "\\\\server\\share");
+ t!(s: "\\\\server\\share\\foo", with_filename, "..", "\\\\server\\share");
+ t!(s: "\\\\server\\share", with_filename, "..", "\\\\server\\share");
+ t!(s: "\\\\?\\C:\\foo\\bar", with_filename, "baz", "\\\\?\\C:\\foo\\baz");
+ t!(s: "\\\\?\\C:\\foo", with_filename, "bar", "\\\\?\\C:\\bar");
+ t!(s: "\\\\?\\C:\\", with_filename, "foo", "\\\\?\\C:\\foo");
+ t!(s: "\\\\?\\C:\\foo", with_filename, "..", "\\\\?\\C:\\..");
+ t!(s: "\\\\?\\foo\\bar", with_filename, "baz", "\\\\?\\foo\\baz");
+ t!(s: "\\\\?\\foo", with_filename, "bar", "\\\\?\\foo\\bar");
+ t!(s: "\\\\?\\", with_filename, "foo", "\\\\?\\\\foo");
+ t!(s: "\\\\?\\foo\\bar", with_filename, "..", "\\\\?\\foo\\..");
+ t!(s: "\\\\.\\foo\\bar", with_filename, "baz", "\\\\.\\foo\\baz");
+ t!(s: "\\\\.\\foo", with_filename, "bar", "\\\\.\\foo\\bar");
+ t!(s: "\\\\.\\foo\\bar", with_filename, "..", "\\\\.\\foo\\..");
+
+ t!(s: "hi\\there.txt", with_extension, "exe", "hi\\there.exe");
+ t!(s: "hi\\there.txt", with_extension, "", "hi\\there");
+ t!(s: "hi\\there.txt", with_extension, ".", "hi\\there..");
+ t!(s: "hi\\there.txt", with_extension, "..", "hi\\there...");
+ t!(s: "hi\\there", with_extension, "txt", "hi\\there.txt");
+ t!(s: "hi\\there", with_extension, ".", "hi\\there..");
+ t!(s: "hi\\there", with_extension, "..", "hi\\there...");
+ t!(s: "hi\\there.", with_extension, "txt", "hi\\there.txt");
+ t!(s: "hi\\.foo", with_extension, "txt", "hi\\.foo.txt");
+ t!(s: "hi\\there.txt", with_extension, ".foo", "hi\\there..foo");
+ t!(s: "\\", with_extension, "txt", "\\");
+ t!(s: "\\", with_extension, ".", "\\");
+ t!(s: "\\", with_extension, "..", "\\");
+ t!(s: ".", with_extension, "txt", ".");
+ // extension setter calls filename setter internally, no need for extended tests
+ }
+
+ #[test]
+ fn test_setters() {
+ macro_rules! t {
+ (s: $path:expr, $set:ident, $with:ident, $arg:expr) => (
+ {
+ let path = $path;
+ let arg = $arg;
+ let mut p1 = Path::new(path);
+ p1.$set(arg);
+ let p2 = Path::new(path);
+ assert_eq!(p1, p2.$with(arg));
+ }
+ );
+ (v: $path:expr, $set:ident, $with:ident, $arg:expr) => (
+ {
+ let path = $path;
+ let arg = $arg;
+ let mut p1 = Path::new(path);
+ p1.$set(arg);
+ let p2 = Path::new(path);
+ assert_eq!(p1, p2.$with(arg));
+ }
+ )
+ }
+
+ t!(v: b"a\\b\\c", set_filename, with_filename, b"d");
+ t!(v: b"\\", set_filename, with_filename, b"foo");
+ t!(s: "a\\b\\c", set_filename, with_filename, "d");
+ t!(s: "\\", set_filename, with_filename, "foo");
+ t!(s: ".", set_filename, with_filename, "foo");
+ t!(s: "a\\b", set_filename, with_filename, "");
+ t!(s: "a", set_filename, with_filename, "");
+
+ t!(v: b"hi\\there.txt", set_extension, with_extension, b"exe");
+ t!(s: "hi\\there.txt", set_extension, with_extension, "exe");
+ t!(s: "hi\\there.", set_extension, with_extension, "txt");
+ t!(s: "hi\\there", set_extension, with_extension, "txt");
+ t!(s: "hi\\there.txt", set_extension, with_extension, "");
+ t!(s: "hi\\there", set_extension, with_extension, "");
+ t!(s: ".", set_extension, with_extension, "txt");
+
+ // with_ helpers use the setter internally, so the tests for the with_ helpers
+ // will suffice. No need for the full set of prefix tests.
+ }
+
+ #[test]
+ fn test_getters() {
+ macro_rules! t {
+ (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.filename_str(), $filename);
+ assert_eq!(path.dirname_str(), $dirname);
+ assert_eq!(path.filestem_str(), $filestem);
+ assert_eq!(path.extension_str(), $ext);
+ }
+ );
+ (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
+ {
+ let path = $path;
+ assert_eq!(path.filename(), $filename);
+ assert_eq!(path.dirname(), $dirname);
+ assert_eq!(path.filestem(), $filestem);
+ assert_eq!(path.extension(), $ext);
+ }
+ )
+ }
+
+ t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), None);
+ t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None);
+ t!(s: Path::new("."), None, Some("."), None, None);
+ t!(s: Path::new("\\"), None, Some("\\"), None, None);
+ t!(s: Path::new(".."), None, Some(".."), None, None);
+ t!(s: Path::new("..\\.."), None, Some("..\\.."), None, None);
+ t!(s: Path::new("hi\\there.txt"), Some("there.txt"), Some("hi"),
+ Some("there"), Some("txt"));
+ t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), None);
+ t!(s: Path::new("hi\\there."), Some("there."), Some("hi"),
+ Some("there"), Some(""));
+ t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None);
+ t!(s: Path::new("hi\\..there"), Some("..there"), Some("hi"),
+ Some("."), Some("there"));
+
+ // these are already tested in test_components, so no need for extended tests
+ }
+
+ #[test]
+ fn test_dir_path() {
+ t!(s: Path::new("hi\\there").dir_path(), "hi");
+ t!(s: Path::new("hi").dir_path(), ".");
+ t!(s: Path::new("\\hi").dir_path(), "\\");
+ t!(s: Path::new("\\").dir_path(), "\\");
+ t!(s: Path::new("..").dir_path(), "..");
+ t!(s: Path::new("..\\..").dir_path(), "..\\..");
+
+ // dir_path is just dirname interpreted as a path.
+ // No need for extended tests
+ }
+
+ #[test]
+ fn test_is_absolute() {
+ macro_rules! t {
+ ($path:expr, $abs:expr, $vol:expr, $cwd:expr, $rel:expr) => (
+ {
+ let path = Path::new($path);
+ let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel);
+ assert_eq!(path.is_absolute(), abs);
+ assert_eq!(is_vol_relative(&path), vol);
+ assert_eq!(is_cwd_relative(&path), cwd);
+ assert_eq!(path.is_relative(), rel);
+ }
+ )
+ }
+ t!("a\\b\\c", false, false, false, true);
+ t!("\\a\\b\\c", false, true, false, false);
+ t!("a", false, false, false, true);
+ t!("\\a", false, true, false, false);
+ t!(".", false, false, false, true);
+ t!("\\", false, true, false, false);
+ t!("..", false, false, false, true);
+ t!("..\\..", false, false, false, true);
+ t!("C:a\\b.txt", false, false, true, false);
+ t!("C:\\a\\b.txt", true, false, false, false);
+ t!("\\\\server\\share\\a\\b.txt", true, false, false, false);
+ t!("\\\\?\\a\\b\\c.txt", true, false, false, false);
+ t!("\\\\?\\C:\\a\\b.txt", true, false, false, false);
+ t!("\\\\?\\C:a\\b.txt", true, false, false, false); // NB: not equivalent to C:a\b.txt
+ t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false, false);
+ t!("\\\\.\\a\\b", true, false, false, false);
+ }
+
+ #[test]
+ fn test_is_ancestor_of() {
+ macro_rules! t {
+ (s: $path:expr, $dest:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let dest = Path::new($dest);
+ let exp = $exp;
+ let res = path.is_ancestor_of(&dest);
+ assert_eq!(res, exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "a\\b\\c\\d", true);
+ t!(s: "a\\b\\c", "a\\b\\c", true);
+ t!(s: "a\\b\\c", "a\\b", false);
+ t!(s: "\\a\\b\\c", "\\a\\b\\c", true);
+ t!(s: "\\a\\b", "\\a\\b\\c", true);
+ t!(s: "\\a\\b\\c\\d", "\\a\\b\\c", false);
+ t!(s: "\\a\\b", "a\\b\\c", false);
+ t!(s: "a\\b", "\\a\\b\\c", false);
+ t!(s: "a\\b\\c", "a\\b\\d", false);
+ t!(s: "..\\a\\b\\c", "a\\b\\c", false);
+ t!(s: "a\\b\\c", "..\\a\\b\\c", false);
+ t!(s: "a\\b\\c", "a\\b\\cd", false);
+ t!(s: "a\\b\\cd", "a\\b\\c", false);
+ t!(s: "..\\a\\b", "..\\a\\b\\c", true);
+ t!(s: ".", "a\\b", true);
+ t!(s: ".", ".", true);
+ t!(s: "\\", "\\", true);
+ t!(s: "\\", "\\a\\b", true);
+ t!(s: "..", "a\\b", true);
+ t!(s: "..\\..", "a\\b", true);
+ t!(s: "foo\\bar", "foobar", false);
+ t!(s: "foobar", "foo\\bar", false);
+
+ t!(s: "foo", "C:foo", false);
+ t!(s: "C:foo", "foo", false);
+ t!(s: "C:foo", "C:foo\\bar", true);
+ t!(s: "C:foo\\bar", "C:foo", false);
+ t!(s: "C:\\foo", "C:\\foo\\bar", true);
+ t!(s: "C:", "C:", true);
+ t!(s: "C:", "C:\\", false);
+ t!(s: "C:\\", "C:", false);
+ t!(s: "C:\\", "C:\\", true);
+ t!(s: "C:\\foo\\bar", "C:\\foo", false);
+ t!(s: "C:foo\\bar", "C:foo", false);
+ t!(s: "C:\\foo", "\\foo", false);
+ t!(s: "\\foo", "C:\\foo", false);
+ t!(s: "\\\\server\\share\\foo", "\\\\server\\share\\foo\\bar", true);
+ t!(s: "\\\\server\\share", "\\\\server\\share\\foo", true);
+ t!(s: "\\\\server\\share\\foo", "\\\\server\\share", false);
+ t!(s: "C:\\foo", "\\\\server\\share\\foo", false);
+ t!(s: "\\\\server\\share\\foo", "C:\\foo", false);
+ t!(s: "\\\\?\\foo\\bar", "\\\\?\\foo\\bar\\baz", true);
+ t!(s: "\\\\?\\foo\\bar\\baz", "\\\\?\\foo\\bar", false);
+ t!(s: "\\\\?\\foo\\bar", "\\foo\\bar\\baz", false);
+ t!(s: "\\foo\\bar", "\\\\?\\foo\\bar\\baz", false);
+ t!(s: "\\\\?\\C:\\foo\\bar", "\\\\?\\C:\\foo\\bar\\baz", true);
+ t!(s: "\\\\?\\C:\\foo\\bar\\baz", "\\\\?\\C:\\foo\\bar", false);
+ t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\foo", true);
+ t!(s: "\\\\?\\C:", "\\\\?\\C:\\", false); // this is a weird one
+ t!(s: "\\\\?\\C:\\", "\\\\?\\C:", false);
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\c:\\a\\b", true);
+ t!(s: "\\\\?\\c:\\a", "\\\\?\\C:\\a\\b", true);
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\D:\\a\\b", false);
+ t!(s: "\\\\?\\foo", "\\\\?\\foobar", false);
+ t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\c", true);
+ t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\", true);
+ t!(s: "\\\\?\\a\\b\\", "\\\\?\\a\\b", true);
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", false);
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b\\", false);
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b\\c\\d", true);
+ t!(s: "\\\\?\\UNC\\a\\b\\c\\d", "\\\\?\\UNC\\a\\b\\c", false);
+ t!(s: "\\\\?\\UNC\\a\\b", "\\\\?\\UNC\\a\\b\\c", true);
+ t!(s: "\\\\.\\foo\\bar", "\\\\.\\foo\\bar\\baz", true);
+ t!(s: "\\\\.\\foo\\bar\\baz", "\\\\.\\foo\\bar", false);
+ t!(s: "\\\\.\\foo", "\\\\.\\foo\\bar", true);
+ t!(s: "\\\\.\\foo", "\\\\.\\foobar", false);
+
+ t!(s: "\\a\\b", "\\\\?\\a\\b", false);
+ t!(s: "\\\\?\\a\\b", "\\a\\b", false);
+ t!(s: "\\a\\b", "\\\\?\\C:\\a\\b", false);
+ t!(s: "\\\\?\\C:\\a\\b", "\\a\\b", false);
+ t!(s: "Z:\\a\\b", "\\\\?\\z:\\a\\b", true);
+ t!(s: "C:\\a\\b", "\\\\?\\D:\\a\\b", false);
+ t!(s: "a\\b", "\\\\?\\a\\b", false);
+ t!(s: "\\\\?\\a\\b", "a\\b", false);
+ t!(s: "C:\\a\\b", "\\\\?\\C:\\a\\b", true);
+ t!(s: "\\\\?\\C:\\a\\b", "C:\\a\\b", true);
+ t!(s: "C:a\\b", "\\\\?\\C:\\a\\b", false);
+ t!(s: "C:a\\b", "\\\\?\\C:a\\b", false);
+ t!(s: "\\\\?\\C:\\a\\b", "C:a\\b", false);
+ t!(s: "\\\\?\\C:a\\b", "C:a\\b", false);
+ t!(s: "C:\\a\\b", "\\\\?\\C:\\a\\b\\", true);
+ t!(s: "\\\\?\\C:\\a\\b\\", "C:\\a\\b", true);
+ t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\b\\c", true);
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\b\\c", true);
+ }
+
+ #[test]
+ fn test_ends_with_path() {
+ macro_rules! t {
+ (s: $path:expr, $child:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let child = Path::new($child);
+ assert_eq!(path.ends_with_path(&child), $exp);
+ }
+ );
+ }
+
+ t!(s: "a\\b\\c", "c", true);
+ t!(s: "a\\b\\c", "d", false);
+ t!(s: "foo\\bar\\quux", "bar", false);
+ t!(s: "foo\\bar\\quux", "barquux", false);
+ t!(s: "a\\b\\c", "b\\c", true);
+ t!(s: "a\\b\\c", "a\\b\\c", true);
+ t!(s: "a\\b\\c", "foo\\a\\b\\c", false);
+ t!(s: "\\a\\b\\c", "a\\b\\c", true);
+ t!(s: "\\a\\b\\c", "\\a\\b\\c", false); // child must be relative
+ t!(s: "\\a\\b\\c", "foo\\a\\b\\c", false);
+ t!(s: "a\\b\\c", "", false);
+ t!(s: "", "", true);
+ t!(s: "\\a\\b\\c", "d\\e\\f", false);
+ t!(s: "a\\b\\c", "a\\b", false);
+ t!(s: "a\\b\\c", "b", false);
+ t!(s: "C:\\a\\b", "b", true);
+ t!(s: "C:\\a\\b", "C:b", false);
+ t!(s: "C:\\a\\b", "C:a\\b", false);
+ }
+
+ #[test]
+ fn test_path_relative_from() {
+ macro_rules! t {
+ (s: $path:expr, $other:expr, $exp:expr) => (
+ {
+ assert_eq!(Path::new($path).path_relative_from(&Path::new($other))
+ .as_ref().and_then(|x| x.as_str()), $exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", "a\\b", Some("c"));
+ t!(s: "a\\b\\c", "a\\b\\d", Some("..\\c"));
+ t!(s: "a\\b\\c", "a\\b\\c\\d", Some(".."));
+ t!(s: "a\\b\\c", "a\\b\\c", Some("."));
+ t!(s: "a\\b\\c", "a\\b\\c\\d\\e", Some("..\\.."));
+ t!(s: "a\\b\\c", "a\\d\\e", Some("..\\..\\b\\c"));
+ t!(s: "a\\b\\c", "d\\e\\f", Some("..\\..\\..\\a\\b\\c"));
+ t!(s: "a\\b\\c", "\\a\\b\\c", None);
+ t!(s: "\\a\\b\\c", "a\\b\\c", Some("\\a\\b\\c"));
+ t!(s: "\\a\\b\\c", "\\a\\b\\c\\d", Some(".."));
+ t!(s: "\\a\\b\\c", "\\a\\b", Some("c"));
+ t!(s: "\\a\\b\\c", "\\a\\b\\c\\d\\e", Some("..\\.."));
+ t!(s: "\\a\\b\\c", "\\a\\d\\e", Some("..\\..\\b\\c"));
+ t!(s: "\\a\\b\\c", "\\d\\e\\f", Some("..\\..\\..\\a\\b\\c"));
+ t!(s: "hi\\there.txt", "hi\\there", Some("..\\there.txt"));
+ t!(s: ".", "a", Some(".."));
+ t!(s: ".", "a\\b", Some("..\\.."));
+ t!(s: ".", ".", Some("."));
+ t!(s: "a", ".", Some("a"));
+ t!(s: "a\\b", ".", Some("a\\b"));
+ t!(s: "..", ".", Some(".."));
+ t!(s: "a\\b\\c", "a\\b\\c", Some("."));
+ t!(s: "\\a\\b\\c", "\\a\\b\\c", Some("."));
+ t!(s: "\\", "\\", Some("."));
+ t!(s: "\\", ".", Some("\\"));
+ t!(s: "..\\..\\a", "b", Some("..\\..\\..\\a"));
+ t!(s: "a", "..\\..\\b", None);
+ t!(s: "..\\..\\a", "..\\..\\b", Some("..\\a"));
+ t!(s: "..\\..\\a", "..\\..\\a\\b", Some(".."));
+ t!(s: "..\\..\\a\\b", "..\\..\\a", Some("b"));
+
+ t!(s: "C:a\\b\\c", "C:a\\b", Some("c"));
+ t!(s: "C:a\\b", "C:a\\b\\c", Some(".."));
+ t!(s: "C:" ,"C:a\\b", Some("..\\.."));
+ t!(s: "C:a\\b", "C:c\\d", Some("..\\..\\a\\b"));
+ t!(s: "C:a\\b", "D:c\\d", Some("C:a\\b"));
+ t!(s: "C:a\\b", "C:..\\c", None);
+ t!(s: "C:..\\a", "C:b\\c", Some("..\\..\\..\\a"));
+ t!(s: "C:\\a\\b\\c", "C:\\a\\b", Some("c"));
+ t!(s: "C:\\a\\b", "C:\\a\\b\\c", Some(".."));
+ t!(s: "C:\\", "C:\\a\\b", Some("..\\.."));
+ t!(s: "C:\\a\\b", "C:\\c\\d", Some("..\\..\\a\\b"));
+ t!(s: "C:\\a\\b", "C:a\\b", Some("C:\\a\\b"));
+ t!(s: "C:a\\b", "C:\\a\\b", None);
+ t!(s: "\\a\\b", "C:\\a\\b", None);
+ t!(s: "\\a\\b", "C:a\\b", None);
+ t!(s: "a\\b", "C:\\a\\b", None);
+ t!(s: "a\\b", "C:a\\b", None);
+
+ t!(s: "\\\\a\\b\\c", "\\\\a\\b", Some("c"));
+ t!(s: "\\\\a\\b", "\\\\a\\b\\c", Some(".."));
+ t!(s: "\\\\a\\b\\c\\e", "\\\\a\\b\\c\\d", Some("..\\e"));
+ t!(s: "\\\\a\\c\\d", "\\\\a\\b\\d", Some("\\\\a\\c\\d"));
+ t!(s: "\\\\b\\c\\d", "\\\\a\\c\\d", Some("\\\\b\\c\\d"));
+ t!(s: "\\\\a\\b\\c", "\\d\\e", Some("\\\\a\\b\\c"));
+ t!(s: "\\d\\e", "\\\\a\\b\\c", None);
+ t!(s: "d\\e", "\\\\a\\b\\c", None);
+ t!(s: "C:\\a\\b\\c", "\\\\a\\b\\c", Some("C:\\a\\b\\c"));
+ t!(s: "C:\\c", "\\\\a\\b\\c", Some("C:\\c"));
+
+ t!(s: "\\\\?\\a\\b", "\\a\\b", Some("\\\\?\\a\\b"));
+ t!(s: "\\\\?\\a\\b", "a\\b", Some("\\\\?\\a\\b"));
+ t!(s: "\\\\?\\a\\b", "\\b", Some("\\\\?\\a\\b"));
+ t!(s: "\\\\?\\a\\b", "b", Some("\\\\?\\a\\b"));
+ t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\c", Some(".."));
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", Some("c"));
+ t!(s: "\\\\?\\a\\b", "\\\\?\\c\\d", Some("\\\\?\\a\\b"));
+ t!(s: "\\\\?\\a", "\\\\?\\b", Some("\\\\?\\a"));
+
+ t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", Some("b"));
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\a\\b", Some(".."));
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\b", Some("..\\a"));
+ t!(s: "\\\\?\\C:\\a", "\\\\?\\D:\\a", Some("\\\\?\\C:\\a"));
+ t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\c:\\a", Some("b"));
+ t!(s: "\\\\?\\C:\\a\\b", "C:\\a", Some("b"));
+ t!(s: "\\\\?\\C:\\a", "C:\\a\\b", Some(".."));
+ t!(s: "C:\\a\\b", "\\\\?\\C:\\a", Some("b"));
+ t!(s: "C:\\a", "\\\\?\\C:\\a\\b", Some(".."));
+ t!(s: "\\\\?\\C:\\a", "D:\\a", Some("\\\\?\\C:\\a"));
+ t!(s: "\\\\?\\c:\\a\\b", "C:\\a", Some("b"));
+ t!(s: "\\\\?\\C:\\a\\b", "C:a\\b", Some("\\\\?\\C:\\a\\b"));
+ t!(s: "\\\\?\\C:\\a\\.\\b", "C:\\a", Some("\\\\?\\C:\\a\\.\\b"));
+ t!(s: "\\\\?\\C:\\a\\b/c", "C:\\a", Some("\\\\?\\C:\\a\\b/c"));
+ t!(s: "\\\\?\\C:\\a\\..\\b", "C:\\a", Some("\\\\?\\C:\\a\\..\\b"));
+ t!(s: "C:a\\b", "\\\\?\\C:\\a\\b", None);
+ t!(s: "\\\\?\\C:\\a\\.\\b", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\.\\b"));
+ t!(s: "\\\\?\\C:\\a\\b/c", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\b/c"));
+ t!(s: "\\\\?\\C:\\a\\..\\b", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\..\\b"));
+ t!(s: "\\\\?\\C:\\a\\b\\", "\\\\?\\C:\\a", Some("b"));
+ t!(s: "\\\\?\\C:\\.\\b", "\\\\?\\C:\\.", Some("b"));
+ t!(s: "C:\\b", "\\\\?\\C:\\.", Some("..\\b"));
+ t!(s: "\\\\?\\a\\.\\b\\c", "\\\\?\\a\\.\\b", Some("c"));
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\.\\d", Some("..\\..\\b\\c"));
+ t!(s: "\\\\?\\a\\..\\b", "\\\\?\\a\\..", Some("b"));
+ t!(s: "\\\\?\\a\\b\\..", "\\\\?\\a\\b", Some("\\\\?\\a\\b\\.."));
+ t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\..\\b", Some("..\\..\\b\\c"));
+
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b", Some("c"));
+ t!(s: "\\\\?\\UNC\\a\\b", "\\\\?\\UNC\\a\\b\\c", Some(".."));
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\c\\d", Some("\\\\?\\UNC\\a\\b\\c"));
+ t!(s: "\\\\?\\UNC\\b\\c\\d", "\\\\?\\UNC\\a\\c\\d", Some("\\\\?\\UNC\\b\\c\\d"));
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\a\\b\\c", Some("\\\\?\\UNC\\a\\b\\c"));
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\C:\\a\\b\\c", Some("\\\\?\\UNC\\a\\b\\c"));
+ t!(s: "\\\\?\\UNC\\a\\b\\c/d", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\c/d"));
+ t!(s: "\\\\?\\UNC\\a\\b\\.", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\."));
+ t!(s: "\\\\?\\UNC\\a\\b\\..", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\.."));
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\b", Some("c"));
+ t!(s: "\\\\?\\UNC\\a\\b", "\\\\a\\b\\c", Some(".."));
+ t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\c\\d", Some("\\\\?\\UNC\\a\\b\\c"));
+ t!(s: "\\\\?\\UNC\\b\\c\\d", "\\\\a\\c\\d", Some("\\\\?\\UNC\\b\\c\\d"));
+ t!(s: "\\\\?\\UNC\\a\\b\\.", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\."));
+ t!(s: "\\\\?\\UNC\\a\\b\\c/d", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\c/d"));
+ t!(s: "\\\\?\\UNC\\a\\b\\..", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\.."));
+ t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\b", Some("c"));
+ t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\c\\d", Some("\\\\a\\b\\c"));
+ }
+
+ #[test]
+ fn test_str_components() {
+ macro_rules! t {
+ (s: $path:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let comps = path.str_components().map(|x|x.unwrap())
+ .collect::<Vec<&str>>();
+ let exp: &[&str] = &$exp;
+ assert_eq!(comps, exp);
+ let comps = path.str_components().rev().map(|x|x.unwrap())
+ .collect::<Vec<&str>>();
+ let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&str>>();
+ assert_eq!(comps, exp);
+ }
+ );
+ }
+
+ t!(s: b"a\\b\\c", ["a", "b", "c"]);
+ t!(s: "a\\b\\c", ["a", "b", "c"]);
+ t!(s: "a\\b\\d", ["a", "b", "d"]);
+ t!(s: "a\\b\\cd", ["a", "b", "cd"]);
+ t!(s: "\\a\\b\\c", ["a", "b", "c"]);
+ t!(s: "a", ["a"]);
+ t!(s: "\\a", ["a"]);
+ t!(s: "\\", []);
+ t!(s: ".", ["."]);
+ t!(s: "..", [".."]);
+ t!(s: "..\\..", ["..", ".."]);
+ t!(s: "..\\..\\foo", ["..", "..", "foo"]);
+ t!(s: "C:foo\\bar", ["foo", "bar"]);
+ t!(s: "C:foo", ["foo"]);
+ t!(s: "C:", []);
+ t!(s: "C:\\foo\\bar", ["foo", "bar"]);
+ t!(s: "C:\\foo", ["foo"]);
+ t!(s: "C:\\", []);
+ t!(s: "\\\\server\\share\\foo\\bar", ["foo", "bar"]);
+ t!(s: "\\\\server\\share\\foo", ["foo"]);
+ t!(s: "\\\\server\\share", []);
+ t!(s: "\\\\?\\foo\\bar\\baz", ["bar", "baz"]);
+ t!(s: "\\\\?\\foo\\bar", ["bar"]);
+ t!(s: "\\\\?\\foo", []);
+ t!(s: "\\\\?\\", []);
+ t!(s: "\\\\?\\a\\b", ["b"]);
+ t!(s: "\\\\?\\a\\b\\", ["b"]);
+ t!(s: "\\\\?\\foo\\bar\\\\baz", ["bar", "", "baz"]);
+ t!(s: "\\\\?\\C:\\foo\\bar", ["foo", "bar"]);
+ t!(s: "\\\\?\\C:\\foo", ["foo"]);
+ t!(s: "\\\\?\\C:\\", []);
+ t!(s: "\\\\?\\C:\\foo\\", ["foo"]);
+ t!(s: "\\\\?\\UNC\\server\\share\\foo\\bar", ["foo", "bar"]);
+ t!(s: "\\\\?\\UNC\\server\\share\\foo", ["foo"]);
+ t!(s: "\\\\?\\UNC\\server\\share", []);
+ t!(s: "\\\\.\\foo\\bar\\baz", ["bar", "baz"]);
+ t!(s: "\\\\.\\foo\\bar", ["bar"]);
+ t!(s: "\\\\.\\foo", []);
+ }
+
+ #[test]
+ fn test_components_iter() {
+ macro_rules! t {
+ (s: $path:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let comps = path.components().collect::<Vec<&[u8]>>();
+ let exp: &[&[u8]] = &$exp;
+ assert_eq!(comps, exp);
+ let comps = path.components().rev().collect::<Vec<&[u8]>>();
+ let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>();
+ assert_eq!(comps, exp);
+ }
+ )
+ }
+
+ t!(s: "a\\b\\c", [b"a", b"b", b"c"]);
+ t!(s: ".", [b"."]);
+ // since this is really a wrapper around str_components, those tests suffice
+ }
+
+ #[test]
+ fn test_make_non_verbatim() {
+ macro_rules! t {
+ ($path:expr, $exp:expr) => (
+ {
+ let path = Path::new($path);
+ let exp: Option<&str> = $exp;
+ let exp = exp.map(|s| Path::new(s));
+ assert_eq!(make_non_verbatim(&path), exp);
+ }
+ )
+ }
+
+ t!(r"\a\b\c", Some(r"\a\b\c"));
+ t!(r"a\b\c", Some(r"a\b\c"));
+ t!(r"C:\a\b\c", Some(r"C:\a\b\c"));
+ t!(r"C:a\b\c", Some(r"C:a\b\c"));
+ t!(r"\\server\share\foo", Some(r"\\server\share\foo"));
+ t!(r"\\.\foo", None);
+ t!(r"\\?\foo", None);
+ t!(r"\\?\C:", None);
+ t!(r"\\?\C:foo", None);
+ t!(r"\\?\C:\", Some(r"C:\"));
+ t!(r"\\?\C:\foo", Some(r"C:\foo"));
+ t!(r"\\?\C:\foo\bar\baz", Some(r"C:\foo\bar\baz"));
+ t!(r"\\?\C:\foo\.\bar\baz", None);
+ t!(r"\\?\C:\foo\bar\..\baz", None);
+ t!(r"\\?\C:\foo\bar\..", None);
+ t!(r"\\?\UNC\server\share\foo", Some(r"\\server\share\foo"));
+ t!(r"\\?\UNC\server\share", Some(r"\\server\share"));
+ t!(r"\\?\UNC\server", None);
+ t!(r"\\?\UNC\server\", None);
+ }
+}
use ops::{Drop, FnOnce};
use option::Option::{Some, None};
use option::Option;
-use path::{Path, GenericPath, BytesContainer};
+use old_path::{Path, GenericPath, BytesContainer};
use ptr::PtrExt;
use ptr;
use result::Result::{Err, Ok};
///
/// ```rust
/// use std::os;
-/// use std::path::Path;
+/// use std::old_path::Path;
///
/// let key = "PATH";
/// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
/// # Example
/// ```rust
/// use std::os;
-/// use std::path::Path;
+/// use std::old_path::Path;
///
/// // Assume we're in a path like /home/someuser
/// let rel_path = Path::new("..");
/// # Example
/// ```rust
/// use std::os;
-/// use std::path::Path;
+/// use std::old_path::Path;
///
/// let root = Path::new("/");
/// assert!(os::change_dir(&root).is_ok());
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Cross-platform path manipulation.
+//!
+//! This module provides two types, `PathBuf` and `Path` (akin to `String` and
+//! `str`), for working with paths abstractly. These types are thin wrappers
+//! around `OsString` and `OsStr` respectively, meaning that they work directly
+//! on strings according to the local platform's path syntax.
+//!
+//! ## Simple usage
+//!
+//! Path manipulation involves both parsing components from slices and building
+//! new owned paths.
+//!
+//! To parse a path, you can create a `Path` slice from a `str`
+//! slice and start asking questions:
+//!
+//! ```rust
+//! use std::path::Path;
+//!
+//! let path = Path::new("/tmp/foo/bar.txt");
+//! let file = path.file_name();
+//! let extension = path.extension();
+//! let parent_dir = path.parent();
+//! ```
+//!
+//! To build or modify paths, use `PathBuf`:
+//!
+//! ```rust
+//! use std::path::PathBuf;
+//!
+//! let mut path = PathBuf::new("c:\\");
+//! path.push("windows");
+//! path.push("system32");
+//! path.set_extension("dll");
+//! ```
+//!
+//! ## Path components and normalization
+//!
+//! The path APIs are built around the notion of "components", which roughly
+//! correspond to the substrings between path separators (`/` and, on Windows,
+//! `\`). The APIs for path parsing are largely specified in terms of the path's
+//! components, so it's important to clearly understand how those are determined.
+//!
+//! A path can always be reconstructed into an equivalent path by putting
+//! together its components via `push`. Syntactically, the paths may differ by
+//! the normalization described below.
+//!
+//! ### Component types
+//!
+//! Components come in several types:
+//!
+//! * Normal components are the default: standard references to files or
+//! directories. The path `a/b` has two normal components, `a` and `b`.
+//!
+//! * Current directory components represent the `.` character. For example,
+//! `a/.` has a normal component `a` and a current directory component.
+//!
+//! * The root directory component represents a separator that designates
+//! starting from root. For example, `/a/b` has a root directory component
+//! followed by normal components `a` and `b`.
+//!
+//! On Windows, two additional component types come into play:
+//!
+//! * Prefix components, of which there is a large variety. For example, `C:`
+//! and `\\server\share` are prefixes. The path `C:windows` has a prefix
+//! component `C:` and a normal component `windows`; the path `C:\windows` has a
+//! prefix component `C:`, a root directory component, and a normal component
+//! `windows`.
+//!
+//! * Empty components, a special case for so-called "verbatim" paths where very
+//! little normalization is allowed. For example, `\\?\C:\` has a "verbatim"
+//! prefix `\\?\C:`, a root component, and an empty component (as a way of
+//! representing the trailing `\`. Such a trailing `\` is in fact the only
+//! situation in which an empty component is produced.
+//!
+//! ### Normalization
+//!
+//! Aside from splitting on the separator(s), there is a small amount of
+//! "normalization":
+//!
+//! * Repeated separators are ignored: `a/b` and `a//b` both have components `a`
+//! and `b`.
+//!
+//! * Paths ending in a separator are treated as if they has a current directory
+//! component at the end (or, in verbatim paths, an empty component). For
+//! example, while `a/b` has components `a` and `b`, the paths `a/b/` and
+//! `a/b/.` both have components `a`, `b`, and `.` (current directory). The
+//! reason for this normalization is that `a/b` and `a/b/` are treated
+//! differently in some contexts, but `a/b/` and `a/b/.` are always treated
+//! the same.
+//!
+//! No other normalization takes place by default. In particular, `a/./b/` and
+//! `a/b` are treated distinctly in terms of components, as are `a/c` and
+//! `a/b/../c`. Further normalization is possible to build on top of the
+//! components APIs, and will be included in this library very soon.
+
+#![unstable(feature = "path")]
+
+use core::prelude::*;
+
+use borrow::BorrowFrom;
+use cmp;
+use iter;
+use mem;
+use ops::{self, Deref};
+use string::CowString;
+use vec::Vec;
+use fmt;
+
+use ffi::{OsStr, OsString, AsOsStr};
+
+use self::platform::{is_sep, is_verbatim_sep, MAIN_SEP_STR, parse_prefix, Prefix};
+
+////////////////////////////////////////////////////////////////////////////////
+// GENERAL NOTES
+////////////////////////////////////////////////////////////////////////////////
+//
+// Parsing in this module is done by directly transmuting OsStr to [u8] slices,
+// taking advantage of the fact that OsStr always encodes ASCII characters
+// as-is. Eventually, this transmutation should be replaced by direct uses of
+// OsStr APIs for parsing, but it will take a while for those to become
+// available.
+
+////////////////////////////////////////////////////////////////////////////////
+// Platform-specific definitions
+////////////////////////////////////////////////////////////////////////////////
+
+// The following modules give the most basic tools for parsing paths on various
+// platforms. The bulk of the code is devoted to parsing prefixes on Windows.
+
+#[cfg(unix)]
+mod platform {
+ use core::prelude::*;
+ use ffi::OsStr;
+
+ #[inline]
+ pub fn is_sep(b: u8) -> bool {
+ b == b'/'
+ }
+
+ #[inline]
+ pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'/'
+ }
+
+ pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+ None
+ }
+
+ #[derive(Copy, Clone, Show, Hash, PartialEq, Eq)]
+ pub struct Prefix<'a>;
+
+ impl<'a> Prefix<'a> {
+ #[inline]
+ pub fn len(&self) -> usize { 0 }
+ #[inline]
+ pub fn is_verbatim(&self) -> bool { false }
+ #[inline]
+ pub fn is_drive(&self) -> bool { false }
+ #[inline]
+ pub fn has_implicit_root(&self) -> bool { false }
+ }
+
+ pub const MAIN_SEP_STR: &'static str = "/";
+}
+
+#[cfg(windows)]
+mod platform {
+ use core::prelude::*;
+
+ use super::{Path, os_str_as_u8_slice, u8_slice_as_os_str};
+ use ffi::OsStr;
+ use ascii::*;
+
+ #[inline]
+ pub fn is_sep(b: u8) -> bool {
+ b == b'/' || b == b'\\'
+ }
+
+ #[inline]
+ pub fn is_verbatim_sep(b: u8) -> bool {
+ b == b'\\'
+ }
+
+ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> {
+ use self::Prefix::*;
+ unsafe {
+ // The unsafety here stems from converting between &OsStr and &[u8]
+ // and back. This is safe to do because (1) we only look at ASCII
+ // contents of the encoding and (2) new &OsStr values are produced
+ // only from ASCII-bounded slices of existing &OsStr values.
+ let mut path = os_str_as_u8_slice(path);
+
+ if path.starts_with(br"\\") {
+ // \\
+ path = &path[2..];
+ if path.starts_with(br"?\") {
+ // \\?\
+ path = &path[2..];
+ if path.starts_with(br"UNC\") {
+ // \\?\UNC\server\share
+ path = &path[4..];
+ let (server, share) = match parse_two_comps(path, is_verbatim_sep) {
+ Some((server, share)) => (u8_slice_as_os_str(server),
+ u8_slice_as_os_str(share)),
+ None => (u8_slice_as_os_str(path),
+ u8_slice_as_os_str(&[])),
+ };
+ return Some(VerbatimUNC(server, share));
+ } else {
+ // \\?\path
+ let idx = path.position_elem(&b'\\');
+ if idx == Some(2) && path[1] == b':' {
+ let c = path[0];
+ if c.is_ascii() && (c as char).is_alphabetic() {
+ // \\?\C:\ path
+ let slice = u8_slice_as_os_str(&path[0..1]);
+ return Some(VerbatimDisk(slice));
+ }
+ }
+ let slice = &path[.. idx.unwrap_or(path.len())];
+ return Some(Verbatim(u8_slice_as_os_str(slice)));
+ }
+ } else if path.starts_with(b".\\") {
+ // \\.\path
+ path = &path[2..];
+ let slice = &path[.. path.position_elem(&b'\\').unwrap_or(path.len())];
+ return Some(DeviceNS(u8_slice_as_os_str(slice)));
+ }
+ match parse_two_comps(path, is_sep) {
+ Some((server, share)) if server.len() > 0 && share.len() > 0 => {
+ // \\server\share
+ return Some(UNC(u8_slice_as_os_str(server),
+ u8_slice_as_os_str(share)));
+ }
+ _ => ()
+ }
+ } else if path.len() > 1 && path[1] == b':' {
+ // C:
+ let c = path[0];
+ if c.is_ascii() && (c as char).is_alphabetic() {
+ return Some(Disk(u8_slice_as_os_str(&path[0..1])));
+ }
+ }
+ return None;
+ }
+
+ fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
+ let first = match path.iter().position(|x| f(*x)) {
+ None => return None,
+ Some(x) => &path[.. x]
+ };
+ path = &path[(first.len()+1)..];
+ let idx = path.iter().position(|x| f(*x));
+ let second = &path[.. idx.unwrap_or(path.len())];
+ Some((first, second))
+ }
+ }
+
+ /// Windows path prefixes.
+ ///
+ /// Windows uses a variety of path styles, including references to drive
+ /// volumes (like `C:`), network shared (like `\\server\share`) and
+ /// others. In addition, some path prefixes are "verbatim", in which case
+ /// `/` is *not* treated as a separator and essentially no normalization is
+ /// performed.
+ #[derive(Copy, Clone, Debug, Hash, Eq)]
+ pub enum Prefix<'a> {
+ /// Prefix `\\?\`, together with the given component immediately following it.
+ Verbatim(&'a OsStr),
+
+ /// Prefix `\\?\UNC\`, with the "server" and "share" components following it.
+ VerbatimUNC(&'a OsStr, &'a OsStr),
+
+ /// Prefix like `\\?\C:\`, for the given drive letter
+ VerbatimDisk(&'a OsStr),
+
+ /// Prefix `\\.\`, together with the given component immediately following it.
+ DeviceNS(&'a OsStr),
+
+ /// Prefix `\\server\share`, with the given "server" and "share" components.
+ UNC(&'a OsStr, &'a OsStr),
+
+ /// Prefix `C:` for the given disk drive.
+ Disk(&'a OsStr),
+ }
+
+ impl<'a> Prefix<'a> {
+ #[inline]
+ pub fn len(&self) -> usize {
+ use self::Prefix::*;
+ fn os_str_len(s: &OsStr) -> usize {
+ unsafe { os_str_as_u8_slice(s).len() }
+ }
+ match *self {
+ Verbatim(x) => 4 + os_str_len(x),
+ VerbatimUNC(x,y) => 8 + os_str_len(x) +
+ if os_str_len(y) > 0 { 1 + os_str_len(y) }
+ else { 0 },
+ VerbatimDisk(_) => 6,
+ UNC(x,y) => 2 + os_str_len(x) +
+ if os_str_len(y) > 0 { 1 + os_str_len(y) }
+ else { 0 },
+ DeviceNS(x) => 4 + os_str_len(x),
+ Disk(_) => 2
+ }
+
+ }
+
+ #[inline]
+ pub fn is_verbatim(&self) -> bool {
+ use self::Prefix::*;
+ match *self {
+ Verbatim(_) | VerbatimDisk(_) | VerbatimUNC(_, _) => true,
+ _ => false
+ }
+ }
+
+ #[inline]
+ pub fn is_drive(&self) -> bool {
+ match *self {
+ Prefix::Disk(_) => true,
+ _ => false,
+ }
+ }
+
+ #[inline]
+ pub fn has_implicit_root(&self) -> bool {
+ !self.is_drive()
+ }
+ }
+
+ impl<'a> ops::PartialEq for Prefix<'a> {
+ fn eq(&self, other: &Prefix<'a>) -> bool {
+ use self::Prefix::*;
+ match (*self, *other) {
+ (Verbatim(x), Verbatim(y)) => x == y,
+ (VerbatimUNC(x1, x2), Verbatim(y1, y2)) => x1 == y1 && x2 == y2,
+ (VerbatimDisk(x), VerbatimDisk(y)) =>
+ os_str_as_u8_slice(x).eq_ignore_ascii_case(os_str_as_u8_slice(y)),
+ (DeviceNS(x), DeviceNS(y)) => x == y,
+ (UNC(x1, x2), UNC(y1, y2)) => x1 == y1 && x2 == y2,
+ (Disk(x), Disk(y)) =>
+ os_str_as_u8_slice(x).eq_ignore_ascii_case(os_str_as_u8_slice(y)),
+ _ => false,
+ }
+ }
+ }
+
+ pub const MAIN_SEP_STR: &'static str = "\\";
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Misc helpers
+////////////////////////////////////////////////////////////////////////////////
+
+// Iterate through `iter` while it matches `prefix`; return `None` if `prefix`
+// is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving
+// `iter` after having exhausted `prefix`.
+fn iter_after<A, I, J>(mut iter: I, mut prefix: J) -> Option<I> where
+ I: Iterator<Item=A> + Clone, J: Iterator<Item=A>, A: PartialEq
+{
+ loop {
+ let mut iter_next = iter.clone();
+ match (iter_next.next(), prefix.next()) {
+ (Some(x), Some(y)) => {
+ if x != y { return None }
+ }
+ (Some(_), None) => return Some(iter),
+ (None, None) => return Some(iter),
+ (None, Some(_)) => return None,
+ }
+ iter = iter_next;
+ }
+}
+
+// See note at the top of this module to understand why these are used:
+fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
+ unsafe { mem::transmute(s) }
+}
+unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
+ mem::transmute(s)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Cross-platform parsing
+////////////////////////////////////////////////////////////////////////////////
+
+/// Says whether the path ends in a separator character and therefore needs to
+/// be treated as if it ended with an additional `.`
+fn has_suffix(s: &[u8], prefix: Option<Prefix>) -> bool {
+ let (prefix_len, verbatim) = if let Some(p) = prefix {
+ (p.len(), p.is_verbatim())
+ } else { (0, false) };
+ if prefix_len > 0 && prefix_len == s.len() && !verbatim { return true; }
+ let mut splits = s[prefix_len..].split(|b| is_sep(*b));
+ let last = splits.next_back().unwrap();
+ let more = splits.next_back().is_some();
+ more && last == b""
+}
+
+/// Says whether the first byte after the prefix is a separator.
+fn has_physical_root(s: &[u8], prefix: Option<Prefix>) -> bool {
+ let path = if let Some(p) = prefix { &s[p.len()..] } else { s };
+ path.len() > 0 && is_sep(path[0])
+}
+
+fn parse_single_component(comp: &[u8]) -> Option<Component> {
+ match comp {
+ b"." => Some(Component::CurDir),
+ b".." => Some(Component::ParentDir),
+ b"" => None,
+ _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) }))
+ }
+}
+
+// basic workhorse for splitting stem and extension
+#[allow(unused_unsafe)] // FIXME
+fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
+ unsafe {
+ if os_str_as_u8_slice(file) == b".." { return (Some(file), None) }
+
+ // The unsafety here stems from converting between &OsStr and &[u8]
+ // and back. This is safe to do because (1) we only look at ASCII
+ // contents of the encoding and (2) new &OsStr values are produced
+ // only from ASCII-bounded slices of existing &OsStr values.
+
+ let mut iter = os_str_as_u8_slice(file).rsplitn(1, |b| *b == b'.');
+ let after = iter.next();
+ let before = iter.next();
+ if before == Some(b"") {
+ (Some(file), None)
+ } else {
+ (before.map(|s| u8_slice_as_os_str(s)),
+ after.map(|s| u8_slice_as_os_str(s)))
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// The core iterators
+////////////////////////////////////////////////////////////////////////////////
+
+/// Component parsing works by a double-ended state machine; the cursors at the
+/// front and back of the path each keep track of what parts of the path have
+/// been consumed so far.
+///
+/// Going front to back, a path is made up of a prefix, a root component, a body
+/// (of normal components), and a suffix/emptycomponent (normalized `.` or ``
+/// for a path ending with the separator)
+#[derive(Copy, Clone, PartialEq, PartialOrd, Show)]
+enum State {
+ Prefix = 0, // c:
+ Root = 1, // /
+ Body = 2, // foo/bar/baz
+ Suffix = 3, // .
+ Done = 4,
+}
+
+/// A single component of a path.
+///
+/// See the module documentation for an in-depth explanation of components and
+/// their role in the API.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Show)]
+pub enum Component<'a> {
+ /// A Windows path prefix, e.g. `C:` or `\server\share`
+ Prefix(&'a OsStr),
+
+ /// An empty component. Only used on Windows for the last component of
+ /// verbatim paths ending with a separator (e.g. the last component of
+ /// `\\?\C:\windows\` but not `\\?\C:\windows` or `C:\windows`).
+ Empty,
+
+ /// The root directory component, appears after any prefix and before anything else
+ RootDir,
+
+ /// A reference to the current directory, i.e. `.`
+ CurDir,
+
+ /// A reference to the parent directory, i.e. `..`
+ ParentDir,
+
+ /// A normal component, i.e. `a` and `b` in `a/b`
+ Normal(&'a OsStr),
+}
+
+impl<'a> Component<'a> {
+ /// Extract the underlying `OsStr` slice
+ pub fn as_os_str(self) -> &'a OsStr {
+ match self {
+ Component::Prefix(path) => path,
+ Component::Empty => OsStr::from_str(""),
+ Component::RootDir => OsStr::from_str(MAIN_SEP_STR),
+ Component::CurDir => OsStr::from_str("."),
+ Component::ParentDir => OsStr::from_str(".."),
+ Component::Normal(path) => path,
+ }
+ }
+}
+
+/// The core iterator giving the components of a path.
+///
+/// See the module documentation for an in-depth explanation of components and
+/// their role in the API.
+#[derive(Clone)]
+pub struct Components<'a> {
+ // The path left to parse components from
+ path: &'a [u8],
+
+ // The prefix as it was originally parsed, if any
+ prefix: Option<Prefix<'a>>,
+
+ // true if path *physically* has a root separator; for most Windows
+ // prefixes, it may have a "logical" rootseparator for the purposes of
+ // normalization, e.g. \\server\share == \\server\share\.
+ has_physical_root: bool,
+
+ // The iterator is double-ended, and these two states keep track of what has
+ // been produced from either end
+ front: State,
+ back: State,
+}
+
+/// An iterator over the components of a path, as `OsStr` slices.
+#[derive(Clone)]
+pub struct Iter<'a> {
+ inner: Components<'a>
+}
+
+impl<'a> Components<'a> {
+ // how long is the prefix, if any?
+ #[inline]
+ fn prefix_len(&self) -> usize {
+ self.prefix.as_ref().map(Prefix::len).unwrap_or(0)
+ }
+
+ #[inline]
+ fn prefix_verbatim(&self) -> bool {
+ self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false)
+ }
+
+ /// how much of the prefix is left from the point of view of iteration?
+ #[inline]
+ fn prefix_remaining(&self) -> usize {
+ if self.front == State::Prefix { self.prefix_len() }
+ else { 0 }
+ }
+
+ fn prefix_and_root(&self) -> usize {
+ let root = if self.front <= State::Root && self.has_physical_root { 1 } else { 0 };
+ self.prefix_remaining() + root
+ }
+
+ // is the iteration complete?
+ #[inline]
+ fn finished(&self) -> bool {
+ self.front == State::Done || self.back == State::Done || self.front > self.back
+ }
+
+ #[inline]
+ fn is_sep(&self, b: u8) -> bool {
+ if self.prefix_verbatim() {
+ is_verbatim_sep(b)
+ } else {
+ is_sep(b)
+ }
+ }
+
+ /// Extract a slice corresponding to the portion of the path remaining for iteration.
+ pub fn as_path(&self) -> &'a Path {
+ let mut comps = self.clone();
+ if comps.front == State::Body { comps.trim_left(); }
+ if comps.back == State::Body { comps.trim_right(); }
+ if comps.path.is_empty() && comps.front < comps.back && comps.back == State::Suffix {
+ Path::new(".")
+ } else {
+ unsafe { Path::from_u8_slice(comps.path) }
+ }
+ }
+
+ /// Is the *original* path rooted?
+ fn has_root(&self) -> bool {
+ if self.has_physical_root { return true }
+ if let Some(p) = self.prefix {
+ if p.has_implicit_root() { return true }
+ }
+ false
+ }
+
+ // parse a component from the left, saying how many bytes to consume to
+ // remove the component
+ fn parse_next_component(&self) -> (usize, Option<Component<'a>>) {
+ debug_assert!(self.front == State::Body);
+ let (extra, comp) = match self.path.iter().position(|b| self.is_sep(*b)) {
+ None => (0, self.path),
+ Some(i) => (1, &self.path[.. i]),
+ };
+ (comp.len() + extra, parse_single_component(comp))
+ }
+
+ // parse a component from the right, saying how many bytes to consume to
+ // remove the component
+ fn parse_next_component_back(&self) -> (usize, Option<Component<'a>>) {
+ debug_assert!(self.back == State::Body);
+ let start = self.prefix_and_root();
+ let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep(*b)) {
+ None => (0, &self.path[start ..]),
+ Some(i) => (1, &self.path[start + i + 1 ..]),
+ };
+ (comp.len() + extra, parse_single_component(comp))
+ }
+
+ // trim away repeated separators (i.e. emtpy components) on the left
+ fn trim_left(&mut self) {
+ while !self.path.is_empty() {
+ let (size, comp) = self.parse_next_component();
+ if comp.is_some() {
+ return;
+ } else {
+ self.path = &self.path[size ..];
+ }
+ }
+ }
+
+ // trim away repeated separators (i.e. emtpy components) on the right
+ fn trim_right(&mut self) {
+ while self.path.len() > self.prefix_and_root() {
+ let (size, comp) = self.parse_next_component_back();
+ if comp.is_some() {
+ return;
+ } else {
+ self.path = &self.path[.. self.path.len() - size];
+ }
+ }
+ }
+
+ /// Examine the next component without consuming it.
+ pub fn peek(&self) -> Option<Component<'a>> {
+ self.clone().next()
+ }
+}
+
+impl<'a> Iter<'a> {
+ /// Extract a slice corresponding to the portion of the path remaining for iteration.
+ pub fn as_path(&self) -> &'a Path {
+ self.inner.as_path()
+ }
+}
+
+impl<'a> Iterator for Iter<'a> {
+ type Item = &'a OsStr;
+
+ fn next(&mut self) -> Option<&'a OsStr> {
+ self.inner.next().map(Component::as_os_str)
+ }
+}
+
+impl<'a> DoubleEndedIterator for Iter<'a> {
+ fn next_back(&mut self) -> Option<&'a OsStr> {
+ self.inner.next_back().map(Component::as_os_str)
+ }
+}
+
+impl<'a> Iterator for Components<'a> {
+ type Item = Component<'a>;
+
+ fn next(&mut self) -> Option<Component<'a>> {
+ while !self.finished() {
+ match self.front {
+ State::Prefix if self.prefix_len() > 0 => {
+ self.front = State::Root;
+ debug_assert!(self.prefix_len() <= self.path.len());
+ let prefix = &self.path[.. self.prefix_len()];
+ self.path = &self.path[self.prefix_len() .. ];
+ return Some(Component::Prefix(unsafe { u8_slice_as_os_str(prefix) }))
+ }
+ State::Prefix => {
+ self.front = State::Root;
+ }
+ State::Root => {
+ self.front = State::Body;
+ if self.has_physical_root {
+ debug_assert!(self.path.len() > 0);
+ self.path = &self.path[1..];
+ return Some(Component::RootDir)
+ } else if let Some(p) = self.prefix {
+ if p.has_implicit_root() && !p.is_verbatim() {
+ return Some(Component::RootDir)
+ }
+ }
+ }
+ State::Body if !self.path.is_empty() => {
+ let (size, comp) = self.parse_next_component();
+ self.path = &self.path[size ..];
+ if comp.is_some() { return comp }
+ }
+ State::Body => {
+ self.front = State::Suffix;
+ }
+ State::Suffix => {
+ self.front = State::Done;
+ if self.prefix_verbatim() {
+ return Some(Component::Empty)
+ } else {
+ return Some(Component::CurDir)
+ }
+ }
+ State::Done => unreachable!()
+ }
+ }
+ None
+ }
+}
+
+impl<'a> DoubleEndedIterator for Components<'a> {
+ fn next_back(&mut self) -> Option<Component<'a>> {
+ while !self.finished() {
+ match self.back {
+ State::Suffix => {
+ self.back = State::Body;
+ if self.prefix_verbatim() {
+ return Some(Component::Empty)
+ } else {
+ return Some(Component::CurDir)
+ }
+ }
+ State::Body if self.path.len() > self.prefix_and_root() => {
+ let (size, comp) = self.parse_next_component_back();
+ self.path = &self.path[.. self.path.len() - size];
+ if comp.is_some() { return comp }
+ }
+ State::Body => {
+ self.back = State::Root;
+ }
+ State::Root => {
+ self.back = State::Prefix;
+ if self.has_physical_root {
+ self.path = &self.path[.. self.path.len() - 1];
+ return Some(Component::RootDir)
+ } else if let Some(p) = self.prefix {
+ if p.has_implicit_root() && !p.is_verbatim() {
+ return Some(Component::RootDir)
+ }
+ }
+ }
+ State::Prefix if self.prefix_len() > 0 => {
+ self.back = State::Done;
+ return Some(Component::Prefix(unsafe {
+ u8_slice_as_os_str(self.path)
+ }))
+ }
+ State::Prefix => {
+ self.back = State::Done;
+ return None
+ }
+ State::Done => unreachable!()
+ }
+ }
+ None
+ }
+}
+
+fn optional_path(path: &Path) -> Option<&Path> {
+ if path.as_u8_slice().is_empty() { None } else { Some(path) }
+}
+
+impl<'a> cmp::PartialEq for Components<'a> {
+ fn eq(&self, other: &Components<'a>) -> bool {
+ iter::order::eq(self.clone(), other.clone())
+ }
+}
+
+impl<'a> cmp::Eq for Components<'a> {}
+
+impl<'a> cmp::PartialOrd for Components<'a> {
+ fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> {
+ iter::order::partial_cmp(self.clone(), other.clone())
+ }
+}
+
+impl<'a> cmp::Ord for Components<'a> {
+ fn cmp(&self, other: &Components<'a>) -> cmp::Ordering {
+ iter::order::cmp(self.clone(), other.clone())
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Basic types and traits
+////////////////////////////////////////////////////////////////////////////////
+
+/// An owned, mutable path (akin to `String`).
+///
+/// This type provides methods like `push` and `set_extension` that mutate the
+/// path in place. It also implements `Deref` to `Path`, meaning that all
+/// methods on `Path` slices are available on `PathBuf` values as well.
+///
+/// More details about the overall approach can be found in
+/// the module documentation.
+///
+/// # Example
+///
+/// ```rust
+/// use std::path::PathBuf;
+///
+/// let mut path = PathBuf::new("c:\\");
+/// path.push("windows");
+/// path.push("system32");
+/// path.set_extension("dll");
+/// ```
+#[derive(Clone, Hash)]
+pub struct PathBuf {
+ inner: OsString
+}
+
+impl PathBuf {
+ fn as_mut_vec(&mut self) -> &mut Vec<u8> {
+ unsafe { mem::transmute(self) }
+ }
+
+ /// Allocate a `PathBuf` with initial contents given by the
+ /// argument.
+ pub fn new<S: ?Sized + AsOsStr>(s: &S) -> PathBuf {
+ PathBuf { inner: s.as_os_str().to_os_string() }
+ }
+
+ /// Extend `self` with `path`.
+ ///
+ /// If `path` is absolute, it replaces the current path.
+ ///
+ /// On Windows:
+ ///
+ /// * if `path` has a root but no prefix (e.g. `\windows`), it
+ /// replaces everything except for the prefix (if any) of `self`.
+ /// * if `path` has a prefix but no root, it replaces `self.
+ pub fn push<P: ?Sized>(&mut self, path: &P) where P: AsPath {
+ // in general, a separator is needed if the rightmost byte is not a separator
+ let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep(*c)).unwrap_or(false);
+
+ // in the special case of `C:` on Windows, do *not* add a separator
+ {
+ let comps = self.components();
+ if comps.prefix_len() > 0 &&
+ comps.prefix_len() == comps.path.len() &&
+ comps.prefix.unwrap().is_drive()
+ {
+ need_sep = false
+ }
+ }
+
+ let path = path.as_path();
+
+ // absolute `path` replaces `self`
+ if path.is_absolute() || path.prefix().is_some() {
+ self.as_mut_vec().truncate(0);
+
+ // `path` has a root but no prefix, e.g. `\windows` (Windows only)
+ } else if path.has_root() {
+ let prefix_len = self.components().prefix_remaining();
+ self.as_mut_vec().truncate(prefix_len);
+
+ // `path` is a pure relative path
+ } else if need_sep {
+ self.inner.push_os_str(OsStr::from_str(MAIN_SEP_STR));
+ }
+
+ self.inner.push_os_str(path.as_os_str());
+ }
+
+ /// Truncate `self` to `self.parent()`.
+ ///
+ /// Returns `None` and does nothing if `self.parent()` is `None`.
+ pub fn pop(&mut self) -> bool {
+ match self.parent().map(|p| p.as_u8_slice().len()) {
+ Some(len) => {
+ self.as_mut_vec().truncate(len);
+ true
+ }
+ None => false
+ }
+ }
+
+ /// Updates `self.file_name()` to `file_name`.
+ ///
+ /// If `self.file_name()` was `None`, this is equivalent to pushing
+ /// `file_name`.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use std::path::{Path, PathBuf};
+ ///
+ /// let mut buf = PathBuf::new("/foo/");
+ /// assert!(buf.file_name() == None);
+ /// buf.set_file_name("bar");
+ /// assert!(buf == PathBuf::new("/foo/bar"));
+ /// assert!(buf.file_name().is_some());
+ /// buf.set_file_name("baz.txt");
+ /// assert!(buf == PathBuf::new("/foo/baz.txt"));
+ /// ```
+ pub fn set_file_name<S: ?Sized>(&mut self, file_name: &S) where S: AsOsStr {
+ if self.file_name().is_some() && !self.pop() {
+ // Given that there is a file name, this is reachable only for
+ // Windows paths like c:file or paths like `foo`, but not `c:\` or
+ // `/`.
+ let prefix_len = self.components().prefix_remaining();
+ self.as_mut_vec().truncate(prefix_len);
+ }
+ self.push(file_name.as_os_str());
+ }
+
+ /// Updates `self.extension()` to `extension`.
+ ///
+ /// If `self.file_name()` is `None`, does nothing and returns `false`.
+ ///
+ /// Otherwise, returns `tru`; if `self.exension()` is `None`, the extension
+ /// is added; otherwise it is replaced.
+ pub fn set_extension<S: ?Sized + AsOsStr>(&mut self, extension: &S) -> bool {
+ if self.file_name().is_none() { return false; }
+
+ let mut stem = match self.file_stem() {
+ Some(stem) => stem.to_os_string(),
+ None => OsString::from_str(""),
+ };
+
+ let extension = extension.as_os_str();
+ if os_str_as_u8_slice(extension).len() > 0 {
+ stem.push_os_str(OsStr::from_str("."));
+ stem.push_os_str(extension.as_os_str());
+ }
+ self.set_file_name(&stem);
+
+ true
+ }
+}
+
+impl<'a, P: ?Sized + 'a> iter::FromIterator<&'a P> for PathBuf where P: AsPath {
+ fn from_iter<I: Iterator<Item = &'a P>>(iter: I) -> PathBuf {
+ let mut buf = PathBuf::new("");
+ buf.extend(iter);
+ buf
+ }
+}
+
+impl<'a, P: ?Sized + 'a> iter::Extend<&'a P> for PathBuf where P: AsPath {
+ fn extend<I: Iterator<Item = &'a P>>(&mut self, iter: I) {
+ for p in iter {
+ self.push(p)
+ }
+ }
+}
+
+impl fmt::Debug for PathBuf {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ fmt::Debug::fmt(&**self, formatter)
+ }
+}
+
+impl ops::Deref for PathBuf {
+ type Target = Path;
+
+ fn deref(&self) -> &Path {
+ unsafe { mem::transmute(&self.inner[]) }
+ }
+}
+
+impl BorrowFrom<PathBuf> for Path {
+ fn borrow_from(owned: &PathBuf) -> &Path {
+ owned.deref()
+ }
+}
+
+impl cmp::PartialEq for PathBuf {
+ fn eq(&self, other: &PathBuf) -> bool {
+ self.components() == other.components()
+ }
+}
+
+impl cmp::Eq for PathBuf {}
+
+impl cmp::PartialOrd for PathBuf {
+ fn partial_cmp(&self, other: &PathBuf) -> Option<cmp::Ordering> {
+ self.components().partial_cmp(&other.components())
+ }
+}
+
+impl cmp::Ord for PathBuf {
+ fn cmp(&self, other: &PathBuf) -> cmp::Ordering {
+ self.components().cmp(&other.components())
+ }
+}
+
+/// A slice of a path (akin to `str`).
+///
+/// This type supports a number of operations for inspecting a path, including
+/// breaking the path into its components (separated by `/` or `\`, depending on
+/// the platform), extracting the file name, determining whether the path is
+/// absolute, and so on. More details about the overall approach can be found in
+/// the module documentation.
+///
+/// This is an *unsized* type, meaning that it must always be used with behind a
+/// pointer like `&` or `Box`.
+///
+/// # Example
+///
+/// ```rust
+/// use std::path::Path;
+///
+/// let path = Path::new("/tmp/foo/bar.txt");
+/// let file = path.file_name();
+/// let extension = path.extension();
+/// let parent_dir = path.parent();
+/// ```
+///
+pub struct Path {
+ inner: OsStr
+}
+
+impl Path {
+ // The following (private!) function allows construction of a path from a u8
+ // slice, which is only safe when it is known to follow the OsStr encoding.
+ unsafe fn from_u8_slice(s: &[u8]) -> &Path {
+ mem::transmute(s)
+ }
+ // The following (private!) function reveals the byte encoding used for OsStr.
+ fn as_u8_slice(&self) -> &[u8] {
+ unsafe { mem::transmute(self) }
+ }
+
+ /// Directly wrap a string slice as a `Path` slice.
+ ///
+ /// This is a cost-free conversion.
+ pub fn new<S: ?Sized + AsOsStr>(s: &S) -> &Path {
+ unsafe { mem::transmute(s.as_os_str()) }
+ }
+
+ /// Yield a `&str` slice if the `Path` is valid unicode.
+ ///
+ /// This conversion may entail doing a check for UTF-8 validity.
+ pub fn to_str(&self) -> Option<&str> {
+ self.inner.to_str()
+ }
+
+ /// Convert a `Path` to a `CowString`.
+ ///
+ /// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
+ pub fn to_string_lossy(&self) -> CowString {
+ self.inner.to_string_lossy()
+ }
+
+ /// Convert a `Path` to an owned `PathBuf`.
+ pub fn to_path_buf(&self) -> PathBuf {
+ PathBuf::new(self)
+ }
+
+ /// A path is *absolute* if it is indepedent of the current directory.
+ ///
+ /// * On Unix, a path is absolute if it starts with the root, so
+ /// `is_absolute` and `has_root` are equivalent.
+ ///
+ /// * On Windows, a path is absolute if it has a prefix and starts with the
+ /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. In
+ /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`.
+ pub fn is_absolute(&self) -> bool {
+ self.has_root() &&
+ (cfg!(unix) || self.prefix().is_some())
+ }
+
+ /// A path is *relative* if it is not absolute.
+ pub fn is_relative(&self) -> bool {
+ !self.is_absolute()
+ }
+
+ /// Returns the *prefix* of a path, if any.
+ ///
+ /// Prefixes are relevant only for Windows paths, and consist of volumes
+ /// like `C:`, UNC prefixes like `\\server`, and others described in more
+ /// detail in `std::os::windows::PathExt`.
+ pub fn prefix(&self) -> Option<&Path> {
+ let iter = self.components();
+ optional_path(unsafe {
+ Path::from_u8_slice(
+ &self.as_u8_slice()[.. iter.prefix_remaining()])
+ })
+ }
+
+ /// A path has a root if the body of the path begins with the directory separator.
+ ///
+ /// * On Unix, a path has a root if it begins with `/`.
+ ///
+ /// * On Windows, a path has a root if it:
+ /// * has no prefix and begins with a separator, e.g. `\\windows`
+ /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows`
+ /// * has any non-disk prefix, e.g. `\\server\share`
+ pub fn has_root(&self) -> bool {
+ self.components().has_root()
+ }
+
+ /// The path without its final component.
+ ///
+ /// Does nothing, returning `None` if the path consists of just a prefix
+ /// and/or root directory reference.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use std::path::Path;
+ ///
+ /// let path = Path::new("/foo/bar");
+ /// let foo = path.parent().unwrap();
+ /// assert!(foo == Path::new("/foo"));
+ /// let root = foo.parent().unwrap();
+ /// assert!(root == Path::new("/"));
+ /// assert!(root.parent() == None);
+ /// ```
+ pub fn parent(&self) -> Option<&Path> {
+ let mut comps = self.components();
+ let comp = comps.next_back();
+ let rest = optional_path(comps.as_path());
+
+ match (comp, comps.next_back()) {
+ (Some(Component::CurDir), Some(Component::RootDir)) => None,
+ (Some(Component::CurDir), Some(Component::Prefix(_))) => None,
+ (Some(Component::Empty), Some(Component::RootDir)) => None,
+ (Some(Component::Empty), Some(Component::Prefix(_))) => None,
+ (Some(Component::Prefix(_)), None) => None,
+ (Some(Component::RootDir), Some(Component::Prefix(_))) => None,
+ _ => rest
+ }
+ }
+
+ /// The final component of the path, if it is a normal file.
+ ///
+ /// If the path terminates in `.`, `..`, or consists solely or a root of
+ /// prefix, `file` will return `None`.
+ pub fn file_name(&self) -> Option<&OsStr> {
+ self.components().next_back().and_then(|p| match p {
+ Component::Normal(p) => Some(p.as_os_str()),
+ _ => None
+ })
+ }
+
+ /// Returns a path that, when joined onto `base`, yields `self`.
+ pub fn relative_from<'a, P: ?Sized>(&'a self, base: &'a P) -> Option<&Path> where
+ P: AsPath
+ {
+ iter_after(self.components(), base.as_path().components()).map(|c| c.as_path())
+ }
+
+ /// Determines whether `base` is a prefix of `self`.
+ pub fn starts_with<P: ?Sized>(&self, base: &P) -> bool where P: AsPath {
+ iter_after(self.components(), base.as_path().components()).is_some()
+ }
+
+ /// Determines whether `base` is a suffix of `self`.
+ pub fn ends_with<P: ?Sized>(&self, child: &P) -> bool where P: AsPath {
+ iter_after(self.components().rev(), child.as_path().components().rev()).is_some()
+ }
+
+ /// Extract the stem (non-extension) portion of `self.file()`.
+ ///
+ /// The stem is:
+ ///
+ /// * None, if there is no file name;
+ /// * The entire file name if there is no embedded `.`;
+ /// * The entire file name if the file name begins with `.` and has no other `.`s within;
+ /// * Otherwise, the portion of the file name before the final `.`
+ pub fn file_stem(&self) -> Option<&OsStr> {
+ self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after))
+ }
+
+ /// Extract the extension of `self.file()`, if possible.
+ ///
+ /// The extension is:
+ ///
+ /// * None, if there is no file name;
+ /// * None, if there is no embedded `.`;
+ /// * None, if the file name begins with `.` and has no other `.`s within;
+ /// * Otherwise, the portion of the file name after the final `.`
+ pub fn extension(&self) -> Option<&OsStr> {
+ self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after))
+ }
+
+ /// Creates an owned `PathBuf` with `path` adjoined to `self`.
+ ///
+ /// See `PathBuf::push` for more details on what it means to adjoin a path.
+ pub fn join<P: ?Sized>(&self, path: &P) -> PathBuf where P: AsPath {
+ let mut buf = self.to_path_buf();
+ buf.push(path);
+ buf
+ }
+
+ /// Creates an owned `PathBuf` like `self` but with the given file name.
+ ///
+ /// See `PathBuf::set_file_name` for more details.
+ pub fn with_file_name<S: ?Sized>(&self, file_name: &S) -> PathBuf where S: AsOsStr {
+ let mut buf = self.to_path_buf();
+ buf.set_file_name(file_name);
+ buf
+ }
+
+ /// Creates an owned `PathBuf` like `self` but with the given extension.
+ ///
+ /// See `PathBuf::set_extension` for more details.
+ pub fn with_extension<S: ?Sized>(&self, extension: &S) -> PathBuf where S: AsOsStr {
+ let mut buf = self.to_path_buf();
+ buf.set_extension(extension);
+ buf
+ }
+
+ /// Produce an iterator over the components of the path.
+ pub fn components(&self) -> Components {
+ let prefix = parse_prefix(self.as_os_str());
+ Components {
+ path: self.as_u8_slice(),
+ prefix: prefix,
+ has_physical_root: has_physical_root(self.as_u8_slice(), prefix),
+ front: State::Prefix,
+ back: if has_suffix(self.as_u8_slice(), prefix) { State::Suffix }
+ else { State::Body },
+ }
+ }
+
+ /// Produce an iterator over the path's components viewed as `OsStr` slices.
+ pub fn iter(&self) -> Iter {
+ Iter { inner: self.components() }
+ }
+
+ /// Returns an object that implements `Display` for safely printing paths
+ /// that may contain non-Unicode data.
+ pub fn display(&self) -> Display {
+ Display { path: self }
+ }
+}
+
+impl AsOsStr for Path {
+ fn as_os_str(&self) -> &OsStr {
+ &self.inner
+ }
+}
+
+impl fmt::Debug for Path {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ self.inner.fmt(formatter)
+ }
+}
+
+/// Helper struct for safely printing paths with `format!()` and `{}`
+pub struct Display<'a> {
+ path: &'a Path
+}
+
+impl<'a> fmt::Debug for Display<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.path.to_string_lossy(), f)
+ }
+}
+
+impl<'a> fmt::Display for Display<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.path.to_string_lossy(), f)
+ }
+}
+
+impl cmp::PartialEq for Path {
+ fn eq(&self, other: &Path) -> bool {
+ iter::order::eq(self.components(), other.components())
+ }
+}
+
+impl cmp::Eq for Path {}
+
+impl cmp::PartialOrd for Path {
+ fn partial_cmp(&self, other: &Path) -> Option<cmp::Ordering> {
+ self.components().partial_cmp(&other.components())
+ }
+}
+
+impl cmp::Ord for Path {
+ fn cmp(&self, other: &Path) -> cmp::Ordering {
+ self.components().cmp(&other.components())
+ }
+}
+
+/// Freely convertible to a `Path`.
+pub trait AsPath {
+ /// Convert to a `Path`.
+ fn as_path(&self) -> &Path;
+}
+
+impl<T: AsOsStr + ?Sized> AsPath for T {
+ fn as_path(&self) -> &Path { Path::new(self.as_os_str()) }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use ffi::OsStr;
+ use core::prelude::*;
+ use string::{ToString, String};
+ use vec::Vec;
+
+ macro_rules! t(
+ ($path:expr, iter: $iter:expr) => (
+ {
+ let path = Path::new($path);
+
+ // Forward iteration
+ let comps = path.iter()
+ .map(|p| p.to_string_lossy().into_owned())
+ .collect::<Vec<String>>();
+ let exp: &[&str] = &$iter;
+ let exps = exp.iter().map(|s| s.to_string()).collect::<Vec<String>>();
+ assert!(comps == exps, "iter: Expected {:?}, found {:?}",
+ exps, comps);
+
+ // Reverse iteration
+ let comps = Path::new($path).iter().rev()
+ .map(|p| p.to_string_lossy().into_owned())
+ .collect::<Vec<String>>();
+ let exps = exps.into_iter().rev().collect::<Vec<String>>();
+ assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}",
+ exps, comps);
+ }
+ );
+
+ ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => (
+ {
+ let path = Path::new($path);
+
+ let act_root = path.has_root();
+ assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}",
+ $has_root, act_root);
+
+ let act_abs = path.is_absolute();
+ assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}",
+ $is_absolute, act_abs);
+ }
+ );
+
+ ($path:expr, parent: $parent:expr, file_name: $file:expr) => (
+ {
+ let path = Path::new($path);
+
+ let parent = path.parent().map(|p| p.to_str().unwrap());
+ let exp_parent: Option<&str> = $parent;
+ assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}",
+ exp_parent, parent);
+
+ let file = path.file_name().map(|p| p.to_str().unwrap());
+ let exp_file: Option<&str> = $file;
+ assert!(file == exp_file, "file_name: Expected {:?}, found {:?}",
+ exp_file, file);
+ }
+ );
+
+ ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => (
+ {
+ let path = Path::new($path);
+
+ let stem = path.file_stem().map(|p| p.to_str().unwrap());
+ let exp_stem: Option<&str> = $file_stem;
+ assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}",
+ exp_stem, stem);
+
+ let ext = path.extension().map(|p| p.to_str().unwrap());
+ let exp_ext: Option<&str> = $extension;
+ assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}",
+ exp_ext, ext);
+ }
+ );
+
+ ($path:expr, iter: $iter:expr,
+ has_root: $has_root:expr, is_absolute: $is_absolute:expr,
+ parent: $parent:expr, file_name: $file:expr,
+ file_stem: $file_stem:expr, extension: $extension:expr) => (
+ {
+ t!($path, iter: $iter);
+ t!($path, has_root: $has_root, is_absolute: $is_absolute);
+ t!($path, parent: $parent, file_name: $file);
+ t!($path, file_stem: $file_stem, extension: $extension);
+ }
+ );
+ );
+
+ #[test]
+ #[cfg(unix)]
+ pub fn test_decompositions_unix() {
+ t!("",
+ iter: [],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/",
+ iter: ["/", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo",
+ iter: ["/", "foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/",
+ iter: ["foo", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo/",
+ iter: ["/", "foo", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("/foo/bar",
+ iter: ["/", "foo", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("///foo///",
+ iter: ["/", "foo", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("///foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("///foo///bar",
+ iter: ["/", "foo", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("///foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./.",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./.",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/..",
+ iter: ["/", ".."],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("/"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("../",
+ iter: ["..", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(".."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/.",
+ iter: ["foo", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/..",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./",
+ iter: ["foo", ".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./bar",
+ iter: ["foo", ".", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("foo/../",
+ iter: ["foo", "..", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/../bar",
+ iter: ["foo", "..", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./a",
+ iter: [".", "a"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!(".",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("a/b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a//b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/./b",
+ iter: ["a", ".", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/."),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/b/c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None
+ );
+ }
+
+ #[test]
+ #[cfg(windows)]
+ pub fn test_decompositions_windows() {
+ t!("",
+ iter: [],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo",
+ iter: ["foo"],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("/",
+ iter: ["\\", "."],
+ has_root: true,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\",
+ iter: ["\\", "."],
+ has_root: true,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:",
+ iter: ["c:", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:\\",
+ iter: ["c:", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:\\",
+ iter: ["c:", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("c:/",
+ iter: ["c:", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo",
+ iter: ["\\", "foo"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: Some("foo"),
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo/",
+ iter: ["foo", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/foo/",
+ iter: ["\\", "foo", "."],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/bar",
+ iter: ["foo", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("/foo/bar",
+ iter: ["\\", "foo", "bar"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("///foo///",
+ iter: ["\\", "foo", "."],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("///foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("///foo///bar",
+ iter: ["\\", "foo", "bar"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("///foo"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./.",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./.",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("/..",
+ iter: ["\\", ".."],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("/"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("../",
+ iter: ["..", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some(".."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/.",
+ iter: ["foo", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/..",
+ iter: ["foo", ".."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./",
+ iter: ["foo", ".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/./bar",
+ iter: ["foo", ".", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("foo/../",
+ iter: ["foo", "..", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("foo/../bar",
+ iter: ["foo", "..", "bar"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("foo/.."),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+ t!("./a",
+ iter: [".", "a"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!(".",
+ iter: ["."],
+ has_root: false,
+ is_absolute: false,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("./",
+ iter: [".", "."],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("."),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("a/b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a//b",
+ iter: ["a", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a"),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/./b",
+ iter: ["a", ".", "b"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/."),
+ file_name: Some("b"),
+ file_stem: Some("b"),
+ extension: None
+ );
+
+ t!("a/b/c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a/b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None);
+
+ t!("a\\b\\c",
+ iter: ["a", "b", "c"],
+ has_root: false,
+ is_absolute: false,
+ parent: Some("a\\b"),
+ file_name: Some("c"),
+ file_stem: Some("c"),
+ extension: None
+ );
+
+ t!("\\a",
+ iter: ["\\", "a"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("\\"),
+ file_name: Some("a"),
+ file_stem: Some("a"),
+ extension: None
+ );
+
+ t!("c:\\foo.txt",
+ iter: ["c:", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("c:\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\server\\share\\foo.txt",
+ iter: ["\\\\server\\share", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\server\\share\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\server\\share",
+ iter: ["\\\\server\\share", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\server",
+ iter: ["\\", "server"],
+ has_root: true,
+ is_absolute: false,
+ parent: Some("\\"),
+ file_name: Some("server"),
+ file_stem: Some("server"),
+ extension: None
+ );
+
+ t!("\\\\?\\bar\\foo.txt",
+ iter: ["\\\\?\\bar", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\bar\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\?\\bar",
+ iter: ["\\\\?\\bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\",
+ iter: ["\\\\?\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\UNC\\server\\share\\foo.txt",
+ iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\UNC\\server\\share\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("\\\\?\\UNC\\server",
+ iter: ["\\\\?\\UNC\\server"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\UNC\\",
+ iter: ["\\\\?\\UNC\\"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\C:\\foo.txt",
+ iter: ["\\\\?\\C:", "\\", "foo.txt"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\C:\\"),
+ file_name: Some("foo.txt"),
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+
+ t!("\\\\?\\C:\\",
+ iter: ["\\\\?\\C:", "\\", ""],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\?\\C:",
+ iter: ["\\\\?\\C:"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\?\\foo/bar",
+ iter: ["\\\\?\\foo/bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\?\\C:/foo",
+ iter: ["\\\\?\\C:/foo"],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\.\\foo\\bar",
+ iter: ["\\\\.\\foo", "\\", "bar"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\.\\foo\\"),
+ file_name: Some("bar"),
+ file_stem: Some("bar"),
+ extension: None
+ );
+
+
+ t!("\\\\.\\foo",
+ iter: ["\\\\.\\foo", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\.\\foo/bar",
+ iter: ["\\\\.\\foo/bar", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+
+ t!("\\\\.\\foo\\bar/baz",
+ iter: ["\\\\.\\foo", "\\", "bar", "baz"],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\.\\foo\\bar"),
+ file_name: Some("baz"),
+ file_stem: Some("baz"),
+ extension: None
+ );
+
+
+ t!("\\\\.\\",
+ iter: ["\\\\.\\", "\\", "."],
+ has_root: true,
+ is_absolute: true,
+ parent: None,
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+
+ t!("\\\\?\\a\\b\\",
+ iter: ["\\\\?\\a", "\\", "b", ""],
+ has_root: true,
+ is_absolute: true,
+ parent: Some("\\\\?\\a\\b"),
+ file_name: None,
+ file_stem: None,
+ extension: None
+ );
+ }
+
+ #[test]
+ pub fn test_stem_ext() {
+ t!("foo",
+ file_stem: Some("foo"),
+ extension: None
+ );
+
+ t!("foo.",
+ file_stem: Some("foo"),
+ extension: Some("")
+ );
+
+ t!(".foo",
+ file_stem: Some(".foo"),
+ extension: None
+ );
+
+ t!("foo.txt",
+ file_stem: Some("foo"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.txt",
+ file_stem: Some("foo.bar"),
+ extension: Some("txt")
+ );
+
+ t!("foo.bar.",
+ file_stem: Some("foo.bar"),
+ extension: Some("")
+ );
+
+ t!(".",
+ file_stem: None,
+ extension: None
+ );
+
+ t!("..",
+ file_stem: None,
+ extension: None
+ );
+
+ t!("",
+ file_stem: None,
+ extension: None
+ );
+ }
+
+ #[test]
+ pub fn test_push() {
+ macro_rules! tp(
+ ($path:expr, $push:expr, $expected:expr) => ( {
+ let mut actual = PathBuf::new($path);
+ actual.push($push);
+ assert!(actual.to_str() == Some($expected),
+ "pushing {:?} onto {:?}: Expected {:?}, got {:?}",
+ $push, $path, $expected, actual.to_str().unwrap());
+ });
+ );
+
+ if cfg!(unix) {
+ tp!("", "foo", "foo");
+ tp!("foo", "bar", "foo/bar");
+ tp!("foo/", "bar", "foo/bar");
+ tp!("foo//", "bar", "foo//bar");
+ tp!("foo/.", "bar", "foo/./bar");
+ tp!("foo./.", "bar", "foo././bar");
+ tp!("foo", "", "foo/");
+ tp!("foo", ".", "foo/.");
+ tp!("foo", "..", "foo/..");
+ tp!("foo", "/", "/");
+ tp!("/foo/bar", "/", "/");
+ tp!("/foo/bar", "/baz", "/baz");
+ tp!("/foo/bar", "./baz", "/foo/bar/./baz");
+ } else {
+ tp!("", "foo", "foo");
+ tp!("foo", "bar", r"foo\bar");
+ tp!("foo/", "bar", r"foo/bar");
+ tp!(r"foo\", "bar", r"foo\bar");
+ tp!("foo//", "bar", r"foo//bar");
+ tp!(r"foo\\", "bar", r"foo\\bar");
+ tp!("foo/.", "bar", r"foo/.\bar");
+ tp!("foo./.", "bar", r"foo./.\bar");
+ tp!(r"foo\.", "bar", r"foo\.\bar");
+ tp!(r"foo.\.", "bar", r"foo.\.\bar");
+ tp!("foo", "", "foo\\");
+ tp!("foo", ".", r"foo\.");
+ tp!("foo", "..", r"foo\..");
+ tp!("foo", "/", "/");
+ tp!("foo", r"\", r"\");
+ tp!("/foo/bar", "/", "/");
+ tp!(r"\foo\bar", r"\", r"\");
+ tp!("/foo/bar", "/baz", "/baz");
+ tp!("/foo/bar", r"\baz", r"\baz");
+ tp!("/foo/bar", "./baz", r"/foo/bar\./baz");
+ tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz");
+
+ tp!("c:\\", "windows", "c:\\windows");
+ tp!("c:", "windows", "c:windows");
+
+ tp!("a\\b\\c", "d", "a\\b\\c\\d");
+ tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d");
+ tp!("a\\b", "c\\d", "a\\b\\c\\d");
+ tp!("a\\b", "\\c\\d", "\\c\\d");
+ tp!("a\\b", ".", "a\\b\\.");
+ tp!("a\\b", "..\\c", "a\\b\\..\\c");
+ tp!("a\\b", "C:a.txt", "C:a.txt");
+ tp!("a\\b", "C:\\a.txt", "C:\\a.txt");
+ tp!("C:\\a", "C:\\b.txt", "C:\\b.txt");
+ tp!("C:\\a\\b\\c", "C:d", "C:d");
+ tp!("C:a\\b\\c", "C:d", "C:d");
+ tp!("C:", r"a\b\c", r"C:a\b\c");
+ tp!("C:", r"..\a", r"C:..\a");
+ tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
+ tp!("\\\\server\\share\\foo", "C:baz", "C:baz");
+ tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d");
+ tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
+ tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
+ tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
+ tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
+ tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
+ tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a");
+
+ // Note: modified from old path API
+ tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo");
+
+ tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
+ tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
+ tp!("\\\\.\\foo\\bar", "C:a", "C:a");
+ // again, not sure about the following, but I'm assuming \\.\ should be verbatim
+ tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
+
+ tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
+ }
+ }
+
+ #[test]
+ pub fn test_pop() {
+ macro_rules! tp(
+ ($path:expr, $expected:expr, $output:expr) => ( {
+ let mut actual = PathBuf::new($path);
+ let output = actual.pop();
+ assert!(actual.to_str() == Some($expected) && output == $output,
+ "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
+ $path, $expected, $output,
+ actual.to_str().unwrap(), output);
+ });
+ );
+
+ tp!("", "", false);
+ tp!("/", "/", false);
+ tp!("foo", "foo", false);
+ tp!(".", ".", false);
+ tp!("/foo", "/", true);
+ tp!("/foo/bar", "/foo", true);
+ tp!("foo/bar", "foo", true);
+ tp!("foo/.", "foo", true);
+ tp!("foo//bar", "foo", true);
+
+ if cfg!(windows) {
+ tp!("a\\b\\c", "a\\b", true);
+ tp!("\\a", "\\", true);
+ tp!("\\", "\\", false);
+
+ tp!("C:\\a\\b", "C:\\a", true);
+ tp!("C:\\a", "C:\\", true);
+ tp!("C:\\", "C:\\", false);
+ tp!("C:a\\b", "C:a", true);
+ tp!("C:a", "C:", true);
+ tp!("C:", "C:", false);
+ tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
+ tp!("\\\\server\\share\\a", "\\\\server\\share\\", true);
+ tp!("\\\\server\\share", "\\\\server\\share", false);
+ tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
+ tp!("\\\\?\\a\\b", "\\\\?\\a\\", true);
+ tp!("\\\\?\\a", "\\\\?\\a", false);
+ tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
+ tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true);
+ tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false);
+ tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
+ tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true);
+ tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
+ tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
+ tp!("\\\\.\\a\\b", "\\\\.\\a\\", true);
+ tp!("\\\\.\\a", "\\\\.\\a", false);
+
+ tp!("\\\\?\\a\\b\\", "\\\\?\\a\\b", true);
+ }
+ }
+
+ #[test]
+ pub fn test_set_file_name() {
+ macro_rules! tfn(
+ ($path:expr, $file:expr, $expected:expr) => ( {
+ let mut p = PathBuf::new($path);
+ p.set_file_name($file);
+ assert!(p.to_str() == Some($expected),
+ "setting file name of {:?} to {:?}: Expected {:?}, got {:?}",
+ $path, $file, $expected,
+ p.to_str().unwrap());
+ });
+ );
+
+ tfn!("foo", "foo", "foo");
+ tfn!("foo", "bar", "bar");
+ tfn!("foo", "", "");
+ tfn!("", "foo", "foo");
+ tfn!(".", "foo", "./foo");
+ tfn!("foo/", "bar", "foo/bar");
+ tfn!("foo/.", "bar", "foo/./bar");
+ tfn!("..", "foo", "../foo");
+ tfn!("foo/..", "bar", "foo/../bar");
+ tfn!("/", "foo", "/foo");
+ }
+
+ #[test]
+ pub fn test_set_extension() {
+ macro_rules! tfe(
+ ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( {
+ let mut p = PathBuf::new($path);
+ let output = p.set_extension($ext);
+ assert!(p.to_str() == Some($expected) && output == $output,
+ "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}",
+ $path, $ext, $expected, $output,
+ p.to_str().unwrap(), output);
+ });
+ );
+
+ tfe!("foo", "txt", "foo.txt", true);
+ tfe!("foo.bar", "txt", "foo.txt", true);
+ tfe!("foo.bar.baz", "txt", "foo.bar.txt", true);
+ tfe!(".test", "txt", ".test.txt", true);
+ tfe!("foo.txt", "", "foo", true);
+ tfe!("foo", "", "foo", true);
+ tfe!("", "foo", "", false);
+ tfe!(".", "foo", ".", false);
+ tfe!("foo/", "bar", "foo/", false);
+ tfe!("foo/.", "bar", "foo/.", false);
+ tfe!("..", "foo", "..", false);
+ tfe!("foo/..", "bar", "foo/..", false);
+ tfe!("/", "foo", "/", false);
+ }
+
+ #[test]
+ pub fn test_compare() {
+ macro_rules! tc(
+ ($path1:expr, $path2:expr, eq: $eq:expr,
+ starts_with: $starts_with:expr, ends_with: $ends_with:expr,
+ relative_from: $relative_from:expr) => ({
+ let path1 = Path::new($path1);
+ let path2 = Path::new($path2);
+
+ let eq = path1 == path2;
+ assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
+ $path1, $path2, $eq, eq);
+
+ let starts_with = path1.starts_with(path2);
+ assert!(starts_with == $starts_with,
+ "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2,
+ $starts_with, starts_with);
+
+ let ends_with = path1.ends_with(path2);
+ assert!(ends_with == $ends_with,
+ "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2,
+ $ends_with, ends_with);
+
+ let relative_from = path1.relative_from(path2).map(|p| p.to_str().unwrap());
+ let exp: Option<&str> = $relative_from;
+ assert!(relative_from == exp,
+ "{:?}.relative_from({:?}), expected {:?}, got {:?}", $path1, $path2,
+ exp, relative_from);
+ });
+ );
+
+ tc!("", "",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo", "",
+ eq: false,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("foo")
+ );
+
+ tc!("", "foo",
+ eq: false,
+ starts_with: false,
+ ends_with: false,
+ relative_from: None
+ );
+
+ tc!("foo", "foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo/", "foo",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some(".")
+ );
+
+ tc!("foo/bar", "foo",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("bar")
+ );
+
+ tc!("foo/bar/baz", "foo/bar",
+ eq: false,
+ starts_with: true,
+ ends_with: false,
+ relative_from: Some("baz")
+ );
+
+ tc!("foo/bar", "foo/bar/baz",
+ eq: false,
+ starts_with: false,
+ ends_with: false,
+ relative_from: None
+ );
+
+ tc!("./foo/bar/", ".",
+ eq: false,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("foo/bar/")
+ );
+ }
+}
+++ /dev/null
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Cross-platform path support
-//!
-//! This module implements support for two flavors of paths. `PosixPath` represents a path on any
-//! unix-like system, whereas `WindowsPath` represents a path on Windows. This module also exposes
-//! a typedef `Path` which is equal to the appropriate platform-specific path variant.
-//!
-//! Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which contains the set of
-//! methods that behave the same for both paths. They each also implement some methods that could
-//! not be expressed in `GenericPath`, yet behave identically for both path flavors, such as
-//! `.components()`.
-//!
-//! The three main design goals of this module are 1) to avoid unnecessary allocation, 2) to behave
-//! the same regardless of which flavor of path is being used, and 3) to support paths that cannot
-//! be represented in UTF-8 (as Linux has no restriction on paths beyond disallowing NUL).
-//!
-//! ## Usage
-//!
-//! Usage of this module is fairly straightforward. Unless writing platform-specific code, `Path`
-//! should be used to refer to the platform-native path.
-//!
-//! Creation of a path is typically done with either `Path::new(some_str)` or
-//! `Path::new(some_vec)`. This path can be modified with `.push()` and `.pop()` (and other
-//! setters). The resulting Path can either be passed to another API that expects a path, or can be
-//! turned into a `&[u8]` with `.as_vec()` or a `Option<&str>` with `.as_str()`. Similarly,
-//! attributes of the path can be queried with methods such as `.filename()`. There are also
-//! methods that return a new path instead of modifying the receiver, such as `.join()` or
-//! `.dir_path()`.
-//!
-//! Paths are always kept in normalized form. This means that creating the path
-//! `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt to mutate the path
-//! will always leave it in normalized form.
-//!
-//! When rendering a path to some form of output, there is a method `.display()` which is
-//! compatible with the `format!()` parameter `{}`. This will render the path as a string,
-//! replacing all non-utf8 sequences with the Replacement Character (U+FFFD). As such it is not
-//! suitable for passing to any API that actually operates on the path; it is only intended for
-//! display.
-//!
-//! ## Example
-//!
-//! ```rust
-//! use std::old_io::fs::PathExtensions;
-//!
-//! let mut path = Path::new("/tmp/path");
-//! println!("path: {}", path.display());
-//! path.set_filename("foo");
-//! path.push("bar");
-//! println!("new path: {}", path.display());
-//! println!("path exists: {}", path.exists());
-//! ```
-
-#![unstable(feature = "path")]
-
-use core::marker::Sized;
-use ffi::CString;
-use clone::Clone;
-use fmt;
-use iter::IteratorExt;
-use option::Option;
-use option::Option::{None, Some};
-use str;
-use str::StrExt;
-use string::{String, CowString};
-use slice::SliceExt;
-use vec::Vec;
-
-/// Typedef for POSIX file paths.
-/// See `posix::Path` for more info.
-pub use self::posix::Path as PosixPath;
-
-/// Typedef for Windows file paths.
-/// See `windows::Path` for more info.
-pub use self::windows::Path as WindowsPath;
-
-/// Typedef for the platform-native path type
-#[cfg(unix)]
-pub use self::posix::Path as Path;
-/// Typedef for the platform-native path type
-#[cfg(windows)]
-pub use self::windows::Path as Path;
-
-/// Typedef for the platform-native component iterator
-#[cfg(unix)]
-pub use self::posix::Components as Components;
-/// Typedef for the platform-native component iterator
-#[cfg(windows)]
-pub use self::windows::Components as Components;
-
-/// Typedef for the platform-native str component iterator
-#[cfg(unix)]
-pub use self::posix::StrComponents as StrComponents;
-/// Typedef for the platform-native str component iterator
-#[cfg(windows)]
-pub use self::windows::StrComponents as StrComponents;
-
-/// Alias for the platform-native separator character.
-#[cfg(unix)]
-pub use self::posix::SEP as SEP;
-/// Alias for the platform-native separator character.
-#[cfg(windows)]
-pub use self::windows::SEP as SEP;
-
-/// Alias for the platform-native separator byte.
-#[cfg(unix)]
-pub use self::posix::SEP_BYTE as SEP_BYTE;
-/// Alias for the platform-native separator byte.
-#[cfg(windows)]
-pub use self::windows::SEP_BYTE as SEP_BYTE;
-
-/// Typedef for the platform-native separator char func
-#[cfg(unix)]
-pub use self::posix::is_sep as is_sep;
-/// Typedef for the platform-native separator char func
-#[cfg(windows)]
-pub use self::windows::is_sep as is_sep;
-/// Typedef for the platform-native separator byte func
-#[cfg(unix)]
-pub use self::posix::is_sep_byte as is_sep_byte;
-/// Typedef for the platform-native separator byte func
-#[cfg(windows)]
-pub use self::windows::is_sep_byte as is_sep_byte;
-
-pub mod posix;
-pub mod windows;
-
-/// A trait that represents the generic operations available on paths
-pub trait GenericPath: Clone + GenericPathUnsafe {
- /// Creates a new Path from a byte vector or string.
- /// The resulting Path will always be normalized.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let path = Path::new("foo/bar");
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the path contains a NUL.
- ///
- /// See individual Path impls for additional restrictions.
- #[inline]
- fn new<T: BytesContainer>(path: T) -> Self {
- assert!(!contains_nul(&path));
- unsafe { GenericPathUnsafe::new_unchecked(path) }
- }
-
- /// Creates a new Path from a byte vector or string, if possible.
- /// The resulting Path will always be normalized.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let x: &[u8] = b"foo\0";
- /// assert!(Path::new_opt(x).is_none());
- /// # }
- /// ```
- #[inline]
- fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
- if contains_nul(&path) {
- None
- } else {
- Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
- }
- }
-
- /// Returns the path as a string, if possible.
- /// If the path is not representable in utf-8, this returns None.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("/abc/def");
- /// assert_eq!(p.as_str(), Some("/abc/def"));
- /// # }
- /// ```
- #[inline]
- fn as_str<'a>(&'a self) -> Option<&'a str> {
- str::from_utf8(self.as_vec()).ok()
- }
-
- /// Returns the path as a byte vector
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def");
- /// assert_eq!(p.as_vec(), b"abc/def");
- /// # }
- /// ```
- fn as_vec<'a>(&'a self) -> &'a [u8];
-
- /// Converts the Path into an owned byte vector
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def");
- /// assert_eq!(p.into_vec(), b"abc/def".to_vec());
- /// // attempting to use p now results in "error: use of moved value"
- /// # }
- /// ```
- fn into_vec(self) -> Vec<u8>;
-
- /// Returns an object that implements `Show` for printing paths
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def");
- /// println!("{}", p.display()); // prints "abc/def"
- /// # }
- /// ```
- fn display<'a>(&'a self) -> Display<'a, Self> {
- Display{ path: self, filename: false }
- }
-
- /// Returns an object that implements `Show` for printing filenames
- ///
- /// If there is no filename, nothing will be printed.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def");
- /// println!("{}", p.filename_display()); // prints "def"
- /// # }
- /// ```
- fn filename_display<'a>(&'a self) -> Display<'a, Self> {
- Display{ path: self, filename: true }
- }
-
- /// Returns the directory component of `self`, as a byte vector (with no trailing separator).
- /// If `self` has no directory component, returns ['.'].
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def/ghi");
- /// assert_eq!(p.dirname(), b"abc/def");
- /// # }
- /// ```
- fn dirname<'a>(&'a self) -> &'a [u8];
-
- /// Returns the directory component of `self`, as a string, if possible.
- /// See `dirname` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def/ghi");
- /// assert_eq!(p.dirname_str(), Some("abc/def"));
- /// # }
- /// ```
- #[inline]
- fn dirname_str<'a>(&'a self) -> Option<&'a str> {
- str::from_utf8(self.dirname()).ok()
- }
-
- /// Returns the file component of `self`, as a byte vector.
- /// If `self` represents the root of the file hierarchy, returns None.
- /// If `self` is "." or "..", returns None.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def/ghi");
- /// assert_eq!(p.filename(), Some(b"ghi"));
- /// # }
- /// ```
- fn filename<'a>(&'a self) -> Option<&'a [u8]>;
-
- /// Returns the file component of `self`, as a string, if possible.
- /// See `filename` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def/ghi");
- /// assert_eq!(p.filename_str(), Some("ghi"));
- /// # }
- /// ```
- #[inline]
- fn filename_str<'a>(&'a self) -> Option<&'a str> {
- self.filename().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Returns the stem of the filename of `self`, as a byte vector.
- /// The stem is the portion of the filename just before the last '.'.
- /// If there is no '.', the entire filename is returned.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("/abc/def.txt");
- /// assert_eq!(p.filestem(), Some(b"def"));
- /// # }
- /// ```
- fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
- match self.filename() {
- None => None,
- Some(name) => Some({
- let dot = b'.';
- match name.rposition_elem(&dot) {
- None | Some(0) => name,
- Some(1) if name == b".." => name,
- Some(pos) => &name[..pos]
- }
- })
- }
- }
-
- /// Returns the stem of the filename of `self`, as a string, if possible.
- /// See `filestem` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("/abc/def.txt");
- /// assert_eq!(p.filestem_str(), Some("def"));
- /// # }
- /// ```
- #[inline]
- fn filestem_str<'a>(&'a self) -> Option<&'a str> {
- self.filestem().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Returns the extension of the filename of `self`, as an optional byte vector.
- /// The extension is the portion of the filename just after the last '.'.
- /// If there is no extension, None is returned.
- /// If the filename ends in '.', the empty vector is returned.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def.txt");
- /// assert_eq!(p.extension(), Some(b"txt"));
- /// # }
- /// ```
- fn extension<'a>(&'a self) -> Option<&'a [u8]> {
- match self.filename() {
- None => None,
- Some(name) => {
- let dot = b'.';
- match name.rposition_elem(&dot) {
- None | Some(0) => None,
- Some(1) if name == b".." => None,
- Some(pos) => Some(&name[pos+1..])
- }
- }
- }
- }
-
- /// Returns the extension of the filename of `self`, as a string, if possible.
- /// See `extension` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def.txt");
- /// assert_eq!(p.extension_str(), Some("txt"));
- /// # }
- /// ```
- #[inline]
- fn extension_str<'a>(&'a self) -> Option<&'a str> {
- self.extension().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Replaces the filename portion of the path with the given byte vector or string.
- /// If the replacement name is [], this is equivalent to popping the path.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("abc/def.txt");
- /// p.set_filename("foo.dat");
- /// assert!(p == Path::new("abc/foo.dat"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the filename contains a NUL.
- #[inline]
- fn set_filename<T: BytesContainer>(&mut self, filename: T) {
- assert!(!contains_nul(&filename));
- unsafe { self.set_filename_unchecked(filename) }
- }
-
- /// Replaces the extension with the given byte vector or string.
- /// If there is no extension in `self`, this adds one.
- /// If the argument is [] or "", this removes the extension.
- /// If `self` has no filename, this is a no-op.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("abc/def.txt");
- /// p.set_extension("csv");
- /// assert_eq!(p, Path::new("abc/def.csv"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the extension contains a NUL.
- fn set_extension<T: BytesContainer>(&mut self, extension: T) {
- assert!(!contains_nul(&extension));
-
- let val = self.filename().and_then(|name| {
- let dot = b'.';
- let extlen = extension.container_as_bytes().len();
- match (name.rposition_elem(&dot), extlen) {
- (None, 0) | (Some(0), 0) => None,
- (Some(idx), 0) => Some(name[..idx].to_vec()),
- (idx, extlen) => {
- let idx = match idx {
- None | Some(0) => name.len(),
- Some(val) => val
- };
-
- let mut v;
- v = Vec::with_capacity(idx + extlen + 1);
- v.push_all(&name[..idx]);
- v.push(dot);
- v.push_all(extension.container_as_bytes());
- Some(v)
- }
- }
- });
-
- match val {
- None => (),
- Some(v) => unsafe { self.set_filename_unchecked(v) }
- }
- }
-
- /// Returns a new Path constructed by replacing the filename with the given
- /// byte vector or string.
- /// See `set_filename` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("abc/def.txt");
- /// assert_eq!(p.with_filename("foo.dat"), Path::new("abc/foo.dat"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the filename contains a NUL.
- #[inline]
- fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
- let mut p = self.clone();
- p.set_filename(filename);
- p
- }
-
- /// Returns a new Path constructed by setting the extension to the given
- /// byte vector or string.
- /// See `set_extension` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("abc/def.txt");
- /// assert_eq!(p.with_extension("csv"), Path::new("abc/def.csv"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the extension contains a NUL.
- #[inline]
- fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
- let mut p = self.clone();
- p.set_extension(extension);
- p
- }
-
- /// Returns the directory component of `self`, as a Path.
- /// If `self` represents the root of the filesystem hierarchy, returns `self`.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def/ghi");
- /// assert_eq!(p.dir_path(), Path::new("abc/def"));
- /// # }
- /// ```
- fn dir_path(&self) -> Self {
- // self.dirname() returns a NUL-free vector
- unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
- }
-
- /// Returns a Path that represents the filesystem root that `self` is rooted in.
- ///
- /// If `self` is not absolute, or vol/cwd-relative in the case of Windows, this returns None.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// assert_eq!(Path::new("abc/def").root_path(), None);
- /// assert_eq!(Path::new("/abc/def").root_path(), Some(Path::new("/")));
- /// # }
- /// ```
- fn root_path(&self) -> Option<Self>;
-
- /// Pushes a path (as a byte vector or string) onto `self`.
- /// If the argument represents an absolute path, it replaces `self`.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("foo/bar");
- /// p.push("baz.txt");
- /// assert_eq!(p, Path::new("foo/bar/baz.txt"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the path contains a NUL.
- #[inline]
- fn push<T: BytesContainer>(&mut self, path: T) {
- assert!(!contains_nul(&path));
- unsafe { self.push_unchecked(path) }
- }
-
- /// Pushes multiple paths (as byte vectors or strings) onto `self`.
- /// See `push` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("foo");
- /// p.push_many(&["bar", "baz.txt"]);
- /// assert_eq!(p, Path::new("foo/bar/baz.txt"));
- /// # }
- /// ```
- #[inline]
- fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
- let t: Option<&T> = None;
- if BytesContainer::is_str(t) {
- for p in paths {
- self.push(p.container_as_str().unwrap())
- }
- } else {
- for p in paths {
- self.push(p.container_as_bytes())
- }
- }
- }
-
- /// Removes the last path component from the receiver.
- /// Returns `true` if the receiver was modified, or `false` if it already
- /// represented the root of the file hierarchy.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let mut p = Path::new("foo/bar/baz.txt");
- /// p.pop();
- /// assert_eq!(p, Path::new("foo/bar"));
- /// # }
- /// ```
- fn pop(&mut self) -> bool;
-
- /// Returns a new Path constructed by joining `self` with the given path
- /// (as a byte vector or string).
- /// If the given path is absolute, the new Path will represent just that.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("/foo");
- /// assert_eq!(p.join("bar.txt"), Path::new("/foo/bar.txt"));
- /// # }
- /// ```
- ///
- /// # Panics
- ///
- /// Panics the task if the path contains a NUL.
- #[inline]
- fn join<T: BytesContainer>(&self, path: T) -> Self {
- let mut p = self.clone();
- p.push(path);
- p
- }
-
- /// Returns a new Path constructed by joining `self` with the given paths
- /// (as byte vectors or strings).
- /// See `join` for details.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("foo");
- /// let fbbq = Path::new("foo/bar/baz/quux.txt");
- /// assert_eq!(p.join_many(&["bar", "baz", "quux.txt"]), fbbq);
- /// # }
- /// ```
- #[inline]
- fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
- let mut p = self.clone();
- p.push_many(paths);
- p
- }
-
- /// Returns whether `self` represents an absolute path.
- /// An absolute path is defined as one that, when joined to another path, will
- /// yield back the same absolute path.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("/abc/def");
- /// assert!(p.is_absolute());
- /// # }
- /// ```
- fn is_absolute(&self) -> bool;
-
- /// Returns whether `self` represents a relative path.
- /// Typically this is the inverse of `is_absolute`.
- /// But for Windows paths, it also means the path is not volume-relative or
- /// relative to the current working directory.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("abc/def");
- /// assert!(p.is_relative());
- /// # }
- /// ```
- fn is_relative(&self) -> bool {
- !self.is_absolute()
- }
-
- /// Returns whether `self` is equal to, or is an ancestor of, the given path.
- /// If both paths are relative, they are compared as though they are relative
- /// to the same parent path.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("foo/bar/baz/quux.txt");
- /// let fb = Path::new("foo/bar");
- /// let bq = Path::new("baz/quux.txt");
- /// assert!(fb.is_ancestor_of(&p));
- /// # }
- /// ```
- fn is_ancestor_of(&self, other: &Self) -> bool;
-
- /// Returns the Path that, were it joined to `base`, would yield `self`.
- /// If no such path exists, None is returned.
- /// If `self` is absolute and `base` is relative, or on Windows if both
- /// paths refer to separate drives, an absolute path is returned.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("foo/bar/baz/quux.txt");
- /// let fb = Path::new("foo/bar");
- /// let bq = Path::new("baz/quux.txt");
- /// assert_eq!(p.path_relative_from(&fb), Some(bq));
- /// # }
- /// ```
- fn path_relative_from(&self, base: &Self) -> Option<Self>;
-
- /// Returns whether the relative path `child` is a suffix of `self`.
- ///
- /// # Example
- ///
- /// ```
- /// # foo();
- /// # #[cfg(windows)] fn foo() {}
- /// # #[cfg(unix)] fn foo() {
- /// let p = Path::new("foo/bar/baz/quux.txt");
- /// let bq = Path::new("baz/quux.txt");
- /// assert!(p.ends_with_path(&bq));
- /// # }
- /// ```
- fn ends_with_path(&self, child: &Self) -> bool;
-}
-
-/// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
-pub trait BytesContainer {
- /// Returns a &[u8] representing the receiver
- fn container_as_bytes<'a>(&'a self) -> &'a [u8];
- /// Returns the receiver interpreted as a utf-8 string, if possible
- #[inline]
- fn container_as_str<'a>(&'a self) -> Option<&'a str> {
- str::from_utf8(self.container_as_bytes()).ok()
- }
- /// Returns whether .container_as_str() is guaranteed to not fail
- // FIXME (#8888): Remove unused arg once ::<for T> works
- #[inline]
- fn is_str(_: Option<&Self>) -> bool { false }
-}
-
-/// A trait that represents the unsafe operations on GenericPaths
-pub trait GenericPathUnsafe {
- /// Creates a new Path without checking for null bytes.
- /// The resulting Path will always be normalized.
- unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
-
- /// Replaces the filename portion of the path without checking for null
- /// bytes.
- /// See `set_filename` for details.
- unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
-
- /// Pushes a path onto `self` without checking for null bytes.
- /// See `push` for details.
- unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
-}
-
-/// Helper struct for printing paths with format!()
-pub struct Display<'a, P:'a> {
- path: &'a P,
- filename: bool
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, P: GenericPath> fmt::Debug for Display<'a, P> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&self.as_cow(), f)
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, P: GenericPath> fmt::Display for Display<'a, P> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.as_cow().fmt(f)
- }
-}
-
-impl<'a, P: GenericPath> Display<'a, P> {
- /// Returns the path as a possibly-owned string.
- ///
- /// If the path is not UTF-8, invalid sequences will be replaced with the
- /// Unicode replacement char. This involves allocation.
- #[inline]
- pub fn as_cow(&self) -> CowString<'a> {
- String::from_utf8_lossy(if self.filename {
- match self.path.filename() {
- None => {
- let result: &[u8] = &[];
- result
- }
- Some(v) => v
- }
- } else {
- self.path.as_vec()
- })
- }
-}
-
-impl BytesContainer for str {
- #[inline]
- fn container_as_bytes(&self) -> &[u8] {
- self.as_bytes()
- }
- #[inline]
- fn container_as_str(&self) -> Option<&str> {
- Some(self)
- }
- #[inline]
- fn is_str(_: Option<&str>) -> bool { true }
-}
-
-impl BytesContainer for String {
- #[inline]
- fn container_as_bytes(&self) -> &[u8] {
- self.as_bytes()
- }
- #[inline]
- fn container_as_str(&self) -> Option<&str> {
- Some(&self[])
- }
- #[inline]
- fn is_str(_: Option<&String>) -> bool { true }
-}
-
-impl BytesContainer for [u8] {
- #[inline]
- fn container_as_bytes(&self) -> &[u8] {
- self
- }
-}
-
-impl BytesContainer for Vec<u8> {
- #[inline]
- fn container_as_bytes(&self) -> &[u8] {
- &self[]
- }
-}
-
-impl BytesContainer for CString {
- #[inline]
- fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
- self.as_bytes()
- }
-}
-
-impl<'a, T: ?Sized + BytesContainer> BytesContainer for &'a T {
- #[inline]
- fn container_as_bytes(&self) -> &[u8] {
- (**self).container_as_bytes()
- }
- #[inline]
- fn container_as_str(&self) -> Option<&str> {
- (**self).container_as_str()
- }
- #[inline]
- fn is_str(_: Option<& &'a T>) -> bool { BytesContainer::is_str(None::<&T>) }
-}
-
-#[inline(always)]
-fn contains_nul<T: BytesContainer>(v: &T) -> bool {
- v.container_as_bytes().iter().any(|&x| x == 0)
-}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! POSIX file path handling
-
-use clone::Clone;
-use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
-use fmt;
-use hash;
-use old_io::Writer;
-use iter::{AdditiveIterator, Extend};
-use iter::{Iterator, IteratorExt, Map};
-use marker::Sized;
-use option::Option::{self, Some, None};
-use result::Result::{self, Ok, Err};
-use slice::{AsSlice, Split, SliceExt, SliceConcatExt};
-use str::{self, FromStr, StrExt};
-use vec::Vec;
-
-use super::{BytesContainer, GenericPath, GenericPathUnsafe};
-
-/// Iterator that yields successive components of a Path as &[u8]
-pub type Components<'a> = Split<'a, u8, fn(&u8) -> bool>;
-
-/// Iterator that yields successive components of a Path as Option<&str>
-pub type StrComponents<'a> =
- Map<Components<'a>, fn(&[u8]) -> Option<&str>>;
-
-/// Represents a POSIX file path
-#[derive(Clone)]
-pub struct Path {
- repr: Vec<u8>, // assumed to never be empty or contain NULs
- sepidx: Option<uint> // index of the final separator in repr
-}
-
-/// The standard path separator character
-pub const SEP: char = '/';
-
-/// The standard path separator byte
-pub const SEP_BYTE: u8 = SEP as u8;
-
-/// Returns whether the given byte is a path separator
-#[inline]
-pub fn is_sep_byte(u: &u8) -> bool {
- *u as char == SEP
-}
-
-/// Returns whether the given char is a path separator
-#[inline]
-pub fn is_sep(c: char) -> bool {
- c == SEP
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for Path {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&self.display(), f)
- }
-}
-
-impl PartialEq for Path {
- #[inline]
- fn eq(&self, other: &Path) -> bool {
- self.repr == other.repr
- }
-}
-
-impl Eq for Path {}
-
-impl PartialOrd for Path {
- fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Path {
- fn cmp(&self, other: &Path) -> Ordering {
- self.repr.cmp(&other.repr)
- }
-}
-
-impl FromStr for Path {
- type Err = ParsePathError;
- fn from_str(s: &str) -> Result<Path, ParsePathError> {
- match Path::new_opt(s) {
- Some(p) => Ok(p),
- None => Err(ParsePathError),
- }
- }
-}
-
-/// Valuelue indicating that a path could not be parsed from a string.
-#[derive(Debug, Clone, PartialEq, Copy)]
-pub struct ParsePathError;
-
-impl<S: hash::Writer + hash::Hasher> hash::Hash<S> for Path {
- #[inline]
- fn hash(&self, state: &mut S) {
- self.repr.hash(state)
- }
-}
-
-impl BytesContainer for Path {
- #[inline]
- fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
- self.as_vec()
- }
-}
-
-impl GenericPathUnsafe for Path {
- unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
- let path = Path::normalize(path.container_as_bytes());
- assert!(!path.is_empty());
- let idx = path.rposition_elem(&SEP_BYTE);
- Path{ repr: path, sepidx: idx }
- }
-
- unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
- let filename = filename.container_as_bytes();
- match self.sepidx {
- None if b".." == self.repr => {
- let mut v = Vec::with_capacity(3 + filename.len());
- v.push_all(dot_dot_static);
- v.push(SEP_BYTE);
- v.push_all(filename);
- // FIXME: this is slow
- self.repr = Path::normalize(v.as_slice());
- }
- None => {
- self.repr = Path::normalize(filename);
- }
- Some(idx) if &self.repr[idx+1..] == b".." => {
- let mut v = Vec::with_capacity(self.repr.len() + 1 + filename.len());
- v.push_all(self.repr.as_slice());
- v.push(SEP_BYTE);
- v.push_all(filename);
- // FIXME: this is slow
- self.repr = Path::normalize(v.as_slice());
- }
- Some(idx) => {
- let mut v = Vec::with_capacity(idx + 1 + filename.len());
- v.push_all(&self.repr[..idx+1]);
- v.push_all(filename);
- // FIXME: this is slow
- self.repr = Path::normalize(v.as_slice());
- }
- }
- self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
- }
-
- unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
- let path = path.container_as_bytes();
- if !path.is_empty() {
- if path[0] == SEP_BYTE {
- self.repr = Path::normalize(path);
- } else {
- let mut v = Vec::with_capacity(self.repr.len() + path.len() + 1);
- v.push_all(self.repr.as_slice());
- v.push(SEP_BYTE);
- v.push_all(path);
- // FIXME: this is slow
- self.repr = Path::normalize(v.as_slice());
- }
- self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
- }
- }
-}
-
-impl GenericPath for Path {
- #[inline]
- fn as_vec<'a>(&'a self) -> &'a [u8] {
- self.repr.as_slice()
- }
-
- fn into_vec(self) -> Vec<u8> {
- self.repr
- }
-
- fn dirname<'a>(&'a self) -> &'a [u8] {
- match self.sepidx {
- None if b".." == self.repr => self.repr.as_slice(),
- None => dot_static,
- Some(0) => &self.repr[..1],
- Some(idx) if &self.repr[idx+1..] == b".." => self.repr.as_slice(),
- Some(idx) => &self.repr[..idx]
- }
- }
-
- fn filename<'a>(&'a self) -> Option<&'a [u8]> {
- match self.sepidx {
- None if b"." == self.repr ||
- b".." == self.repr => None,
- None => Some(self.repr.as_slice()),
- Some(idx) if &self.repr[idx+1..] == b".." => None,
- Some(0) if self.repr[1..].is_empty() => None,
- Some(idx) => Some(&self.repr[idx+1..])
- }
- }
-
- fn pop(&mut self) -> bool {
- match self.sepidx {
- None if b"." == self.repr => false,
- None => {
- self.repr = vec![b'.'];
- self.sepidx = None;
- true
- }
- Some(0) if b"/" == self.repr => false,
- Some(idx) => {
- if idx == 0 {
- self.repr.truncate(idx+1);
- } else {
- self.repr.truncate(idx);
- }
- self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
- true
- }
- }
- }
-
- fn root_path(&self) -> Option<Path> {
- if self.is_absolute() {
- Some(Path::new("/"))
- } else {
- None
- }
- }
-
- #[inline]
- fn is_absolute(&self) -> bool {
- self.repr[0] == SEP_BYTE
- }
-
- fn is_ancestor_of(&self, other: &Path) -> bool {
- if self.is_absolute() != other.is_absolute() {
- false
- } else {
- let mut ita = self.components();
- let mut itb = other.components();
- if b"." == self.repr {
- return match itb.next() {
- None => true,
- Some(b) => b != b".."
- };
- }
- loop {
- match (ita.next(), itb.next()) {
- (None, _) => break,
- (Some(a), Some(b)) if a == b => { continue },
- (Some(a), _) if a == b".." => {
- // if ita contains only .. components, it's an ancestor
- return ita.all(|x| x == b"..");
- }
- _ => return false
- }
- }
- true
- }
- }
-
- fn path_relative_from(&self, base: &Path) -> Option<Path> {
- if self.is_absolute() != base.is_absolute() {
- if self.is_absolute() {
- Some(self.clone())
- } else {
- None
- }
- } else {
- let mut ita = self.components();
- let mut itb = base.components();
- let mut comps = vec![];
- loop {
- match (ita.next(), itb.next()) {
- (None, None) => break,
- (Some(a), None) => {
- comps.push(a);
- comps.extend(ita.by_ref());
- break;
- }
- (None, _) => comps.push(dot_dot_static),
- (Some(a), Some(b)) if comps.is_empty() && a == b => (),
- (Some(a), Some(b)) if b == b"." => comps.push(a),
- (Some(_), Some(b)) if b == b".." => return None,
- (Some(a), Some(_)) => {
- comps.push(dot_dot_static);
- for _ in itb {
- comps.push(dot_dot_static);
- }
- comps.push(a);
- comps.extend(ita.by_ref());
- break;
- }
- }
- }
- Some(Path::new(comps.connect(&SEP_BYTE)))
- }
- }
-
- fn ends_with_path(&self, child: &Path) -> bool {
- if !child.is_relative() { return false; }
- let mut selfit = self.components().rev();
- let mut childit = child.components().rev();
- loop {
- match (selfit.next(), childit.next()) {
- (Some(a), Some(b)) => if a != b { return false; },
- (Some(_), None) => break,
- (None, Some(_)) => return false,
- (None, None) => break
- }
- }
- true
- }
-}
-
-impl Path {
- /// Returns a new Path from a byte vector or string
- ///
- /// # Panics
- ///
- /// Panics the task if the vector contains a NUL.
- #[inline]
- pub fn new<T: BytesContainer>(path: T) -> Path {
- GenericPath::new(path)
- }
-
- /// Returns a new Path from a byte vector or string, if possible
- #[inline]
- pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
- GenericPath::new_opt(path)
- }
-
- /// Returns a normalized byte vector representation of a path, by removing all empty
- /// components, and unnecessary . and .. components.
- fn normalize<V: ?Sized + AsSlice<u8>>(v: &V) -> Vec<u8> {
- // borrowck is being very picky
- let val = {
- let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == SEP_BYTE;
- let v_ = if is_abs { &v.as_slice()[1..] } else { v.as_slice() };
- let comps = normalize_helper(v_, is_abs);
- match comps {
- None => None,
- Some(comps) => {
- if is_abs && comps.is_empty() {
- Some(vec![SEP_BYTE])
- } else {
- let n = if is_abs { comps.len() } else { comps.len() - 1} +
- comps.iter().map(|v| v.len()).sum();
- let mut v = Vec::with_capacity(n);
- let mut it = comps.into_iter();
- if !is_abs {
- match it.next() {
- None => (),
- Some(comp) => v.push_all(comp)
- }
- }
- for comp in it {
- v.push(SEP_BYTE);
- v.push_all(comp);
- }
- Some(v)
- }
- }
- }
- };
- match val {
- None => v.as_slice().to_vec(),
- Some(val) => val
- }
- }
-
- /// Returns an iterator that yields each component of the path in turn.
- /// Does not distinguish between absolute and relative paths, e.g.
- /// /a/b/c and a/b/c yield the same set of components.
- /// A path of "/" yields no components. A path of "." yields one component.
- pub fn components<'a>(&'a self) -> Components<'a> {
- let v = if self.repr[0] == SEP_BYTE {
- &self.repr[1..]
- } else { self.repr.as_slice() };
- let is_sep_byte: fn(&u8) -> bool = is_sep_byte; // coerce to fn ptr
- let mut ret = v.split(is_sep_byte);
- if v.is_empty() {
- // consume the empty "" component
- ret.next();
- }
- ret
- }
-
- /// Returns an iterator that yields each component of the path as Option<&str>.
- /// See components() for details.
- pub fn str_components<'a>(&'a self) -> StrComponents<'a> {
- fn from_utf8(s: &[u8]) -> Option<&str> {
- str::from_utf8(s).ok()
- }
- let f: fn(&[u8]) -> Option<&str> = from_utf8; // coerce to fn ptr
- self.components().map(f)
- }
-}
-
-// None result means the byte vector didn't need normalizing
-fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<Vec<&'a [u8]>> {
- if is_abs && v.is_empty() {
- return None;
- }
- let mut comps: Vec<&'a [u8]> = vec![];
- let mut n_up = 0u;
- let mut changed = false;
- for comp in v.split(is_sep_byte) {
- if comp.is_empty() { changed = true }
- else if comp == b"." { changed = true }
- else if comp == b".." {
- if is_abs && comps.is_empty() { changed = true }
- else if comps.len() == n_up { comps.push(dot_dot_static); n_up += 1 }
- else { comps.pop().unwrap(); changed = true }
- } else { comps.push(comp) }
- }
- if changed {
- if comps.is_empty() && !is_abs {
- if v == b"." {
- return None;
- }
- comps.push(dot_static);
- }
- Some(comps)
- } else {
- None
- }
-}
-
-#[allow(non_upper_case_globals)]
-static dot_static: &'static [u8] = b".";
-#[allow(non_upper_case_globals)]
-static dot_dot_static: &'static [u8] = b"..";
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use clone::Clone;
- use iter::IteratorExt;
- use option::Option::{self, Some, None};
- use path::GenericPath;
- use slice::{AsSlice, SliceExt};
- use str::{self, Str, StrExt};
- use string::ToString;
- use vec::Vec;
-
- macro_rules! t {
- (s: $path:expr, $exp:expr) => (
- {
- let path = $path;
- assert_eq!(path.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $exp:expr) => (
- {
- let path = $path;
- assert_eq!(path.as_vec(), $exp);
- }
- )
- }
-
- #[test]
- fn test_paths() {
- let empty: &[u8] = &[];
- t!(v: Path::new(empty), b".");
- t!(v: Path::new(b"/"), b"/");
- t!(v: Path::new(b"a/b/c"), b"a/b/c");
- t!(v: Path::new(b"a/b/c\xFF"), b"a/b/c\xFF");
- t!(v: Path::new(b"\xFF/../foo\x80"), b"foo\x80");
- let p = Path::new(b"a/b/c\xFF");
- assert!(p.as_str().is_none());
-
- t!(s: Path::new(""), ".");
- t!(s: Path::new("/"), "/");
- t!(s: Path::new("hi"), "hi");
- t!(s: Path::new("hi/"), "hi");
- t!(s: Path::new("/lib"), "/lib");
- t!(s: Path::new("/lib/"), "/lib");
- t!(s: Path::new("hi/there"), "hi/there");
- t!(s: Path::new("hi/there.txt"), "hi/there.txt");
-
- t!(s: Path::new("hi/there/"), "hi/there");
- t!(s: Path::new("hi/../there"), "there");
- t!(s: Path::new("../hi/there"), "../hi/there");
- t!(s: Path::new("/../hi/there"), "/hi/there");
- t!(s: Path::new("foo/.."), ".");
- t!(s: Path::new("/foo/.."), "/");
- t!(s: Path::new("/foo/../.."), "/");
- t!(s: Path::new("/foo/../../bar"), "/bar");
- t!(s: Path::new("/./hi/./there/."), "/hi/there");
- t!(s: Path::new("/./hi/./there/./.."), "/hi");
- t!(s: Path::new("foo/../.."), "..");
- t!(s: Path::new("foo/../../.."), "../..");
- t!(s: Path::new("foo/../../bar"), "../bar");
-
- assert_eq!(Path::new(b"foo/bar").into_vec(), b"foo/bar");
- assert_eq!(Path::new(b"/foo/../../bar").into_vec(),
- b"/bar");
-
- let p = Path::new(b"foo/bar\x80");
- assert!(p.as_str().is_none());
- }
-
- #[test]
- fn test_opt_paths() {
- assert!(Path::new_opt(b"foo/bar\0").is_none());
- t!(v: Path::new_opt(b"foo/bar").unwrap(), b"foo/bar");
- assert!(Path::new_opt("foo/bar\0").is_none());
- t!(s: Path::new_opt("foo/bar").unwrap(), "foo/bar");
- }
-
- #[test]
- fn test_null_byte() {
- use thread::Thread;
- let result = Thread::scoped(move|| {
- Path::new(b"foo/bar\0")
- }).join();
- assert!(result.is_err());
-
- let result = Thread::scoped(move|| {
- Path::new("test").set_filename(b"f\0o")
- }).join();
- assert!(result.is_err());
-
- let result = Thread::scoped(move|| {
- Path::new("test").push(b"f\0o");
- }).join();
- assert!(result.is_err());
- }
-
- #[test]
- fn test_display_str() {
- macro_rules! t {
- ($path:expr, $disp:ident, $exp:expr) => (
- {
- let path = Path::new($path);
- assert_eq!(path.$disp().to_string(), $exp);
- }
- )
- }
- t!("foo", display, "foo");
- t!(b"foo\x80", display, "foo\u{FFFD}");
- t!(b"foo\xFFbar", display, "foo\u{FFFD}bar");
- t!(b"foo\xFF/bar", filename_display, "bar");
- t!(b"foo/\xFFbar", filename_display, "\u{FFFD}bar");
- t!(b"/", filename_display, "");
-
- macro_rules! t {
- ($path:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let mo = path.display().as_cow();
- assert_eq!(mo.as_slice(), $exp);
- }
- );
- ($path:expr, $exp:expr, filename) => (
- {
- let path = Path::new($path);
- let mo = path.filename_display().as_cow();
- assert_eq!(mo.as_slice(), $exp);
- }
- )
- }
-
- t!("foo", "foo");
- t!(b"foo\x80", "foo\u{FFFD}");
- t!(b"foo\xFFbar", "foo\u{FFFD}bar");
- t!(b"foo\xFF/bar", "bar", filename);
- t!(b"foo/\xFFbar", "\u{FFFD}bar", filename);
- t!(b"/", "", filename);
- }
-
- #[test]
- fn test_display() {
- macro_rules! t {
- ($path:expr, $exp:expr, $expf:expr) => (
- {
- let path = Path::new($path);
- let f = format!("{}", path.display());
- assert_eq!(f, $exp);
- let f = format!("{}", path.filename_display());
- assert_eq!(f, $expf);
- }
- )
- }
-
- t!(b"foo", "foo", "foo");
- t!(b"foo/bar", "foo/bar", "bar");
- t!(b"/", "/", "");
- t!(b"foo\xFF", "foo\u{FFFD}", "foo\u{FFFD}");
- t!(b"foo\xFF/bar", "foo\u{FFFD}/bar", "bar");
- t!(b"foo/\xFFbar", "foo/\u{FFFD}bar", "\u{FFFD}bar");
- t!(b"\xFFfoo/bar\xFF", "\u{FFFD}foo/bar\u{FFFD}", "bar\u{FFFD}");
- }
-
- #[test]
- fn test_components() {
- macro_rules! t {
- (s: $path:expr, $op:ident, $exp:expr) => (
- {
- let path = Path::new($path);
- assert_eq!(path.$op(), ($exp).as_bytes());
- }
- );
- (s: $path:expr, $op:ident, $exp:expr, opt) => (
- {
- let path = Path::new($path);
- let left = path.$op().map(|x| str::from_utf8(x).unwrap());
- assert_eq!(left, $exp);
- }
- );
- (v: $path:expr, $op:ident, $exp:expr) => (
- {
- let arg = $path;
- let path = Path::new(arg);
- assert_eq!(path.$op(), $exp);
- }
- );
- }
-
- t!(v: b"a/b/c", filename, Some(b"c"));
- t!(v: b"a/b/c\xFF", filename, Some(b"c\xFF"));
- t!(v: b"a/b\xFF/c", filename, Some(b"c"));
- t!(s: "a/b/c", filename, Some("c"), opt);
- t!(s: "/a/b/c", filename, Some("c"), opt);
- t!(s: "a", filename, Some("a"), opt);
- t!(s: "/a", filename, Some("a"), opt);
- t!(s: ".", filename, None, opt);
- t!(s: "/", filename, None, opt);
- t!(s: "..", filename, None, opt);
- t!(s: "../..", filename, None, opt);
-
- t!(v: b"a/b/c", dirname, b"a/b");
- t!(v: b"a/b/c\xFF", dirname, b"a/b");
- t!(v: b"a/b\xFF/c", dirname, b"a/b\xFF");
- t!(s: "a/b/c", dirname, "a/b");
- t!(s: "/a/b/c", dirname, "/a/b");
- t!(s: "a", dirname, ".");
- t!(s: "/a", dirname, "/");
- t!(s: ".", dirname, ".");
- t!(s: "/", dirname, "/");
- t!(s: "..", dirname, "..");
- t!(s: "../..", dirname, "../..");
-
- t!(v: b"hi/there.txt", filestem, Some(b"there"));
- t!(v: b"hi/there\x80.txt", filestem, Some(b"there\x80"));
- t!(v: b"hi/there.t\x80xt", filestem, Some(b"there"));
- t!(s: "hi/there.txt", filestem, Some("there"), opt);
- t!(s: "hi/there", filestem, Some("there"), opt);
- t!(s: "there.txt", filestem, Some("there"), opt);
- t!(s: "there", filestem, Some("there"), opt);
- t!(s: ".", filestem, None, opt);
- t!(s: "/", filestem, None, opt);
- t!(s: "foo/.bar", filestem, Some(".bar"), opt);
- t!(s: ".bar", filestem, Some(".bar"), opt);
- t!(s: "..bar", filestem, Some("."), opt);
- t!(s: "hi/there..txt", filestem, Some("there."), opt);
- t!(s: "..", filestem, None, opt);
- t!(s: "../..", filestem, None, opt);
-
- t!(v: b"hi/there.txt", extension, Some(b"txt"));
- t!(v: b"hi/there\x80.txt", extension, Some(b"txt"));
- t!(v: b"hi/there.t\x80xt", extension, Some(b"t\x80xt"));
- t!(v: b"hi/there", extension, None);
- t!(v: b"hi/there\x80", extension, None);
- t!(s: "hi/there.txt", extension, Some("txt"), opt);
- t!(s: "hi/there", extension, None, opt);
- t!(s: "there.txt", extension, Some("txt"), opt);
- t!(s: "there", extension, None, opt);
- t!(s: ".", extension, None, opt);
- t!(s: "/", extension, None, opt);
- t!(s: "foo/.bar", extension, None, opt);
- t!(s: ".bar", extension, None, opt);
- t!(s: "..bar", extension, Some("bar"), opt);
- t!(s: "hi/there..txt", extension, Some("txt"), opt);
- t!(s: "..", extension, None, opt);
- t!(s: "../..", extension, None, opt);
- }
-
- #[test]
- fn test_push() {
- macro_rules! t {
- (s: $path:expr, $join:expr) => (
- {
- let path = $path;
- let join = $join;
- let mut p1 = Path::new(path);
- let p2 = p1.clone();
- p1.push(join);
- assert_eq!(p1, p2.join(join));
- }
- )
- }
-
- t!(s: "a/b/c", "..");
- t!(s: "/a/b/c", "d");
- t!(s: "a/b", "c/d");
- t!(s: "a/b", "/c/d");
- }
-
- #[test]
- fn test_push_path() {
- macro_rules! t {
- (s: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- let push = Path::new($push);
- p.push(&push);
- assert_eq!(p.as_str(), Some($exp));
- }
- )
- }
-
- t!(s: "a/b/c", "d", "a/b/c/d");
- t!(s: "/a/b/c", "d", "/a/b/c/d");
- t!(s: "a/b", "c/d", "a/b/c/d");
- t!(s: "a/b", "/c/d", "/c/d");
- t!(s: "a/b", ".", "a/b");
- t!(s: "a/b", "../c", "a/c");
- }
-
- #[test]
- fn test_push_many() {
- macro_rules! t {
- (s: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- p.push_many(&$push);
- assert_eq!(p.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- p.push_many(&$push);
- assert_eq!(p.as_vec(), $exp);
- }
- )
- }
-
- t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
- t!(s: "a/b/c", ["d", "/e"], "/e");
- t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
- t!(s: "a/b/c", ["d".to_string(), "e".to_string()], "a/b/c/d/e");
- t!(v: b"a/b/c", [b"d", b"e"], b"a/b/c/d/e");
- t!(v: b"a/b/c", [b"d", b"/e", b"f"], b"/e/f");
- t!(v: b"a/b/c", [b"d".to_vec(), b"e".to_vec()], b"a/b/c/d/e");
- }
-
- #[test]
- fn test_pop() {
- macro_rules! t {
- (s: $path:expr, $left:expr, $right:expr) => (
- {
- let mut p = Path::new($path);
- let result = p.pop();
- assert_eq!(p.as_str(), Some($left));
- assert_eq!(result, $right);
- }
- );
- (b: $path:expr, $left:expr, $right:expr) => (
- {
- let mut p = Path::new($path);
- let result = p.pop();
- assert_eq!(p.as_vec(), $left);
- assert_eq!(result, $right);
- }
- )
- }
-
- t!(b: b"a/b/c", b"a/b", true);
- t!(b: b"a", b".", true);
- t!(b: b".", b".", false);
- t!(b: b"/a", b"/", true);
- t!(b: b"/", b"/", false);
- t!(b: b"a/b/c\x80", b"a/b", true);
- t!(b: b"a/b\x80/c", b"a/b\x80", true);
- t!(b: b"\xFF", b".", true);
- t!(b: b"/\xFF", b"/", true);
- t!(s: "a/b/c", "a/b", true);
- t!(s: "a", ".", true);
- t!(s: ".", ".", false);
- t!(s: "/a", "/", true);
- t!(s: "/", "/", false);
- }
-
- #[test]
- fn test_root_path() {
- assert_eq!(Path::new(b"a/b/c").root_path(), None);
- assert_eq!(Path::new(b"/a/b/c").root_path(), Some(Path::new("/")));
- }
-
- #[test]
- fn test_join() {
- t!(v: Path::new(b"a/b/c").join(b".."), b"a/b");
- t!(v: Path::new(b"/a/b/c").join(b"d"), b"/a/b/c/d");
- t!(v: Path::new(b"a/\x80/c").join(b"\xFF"), b"a/\x80/c/\xFF");
- t!(s: Path::new("a/b/c").join(".."), "a/b");
- t!(s: Path::new("/a/b/c").join("d"), "/a/b/c/d");
- t!(s: Path::new("a/b").join("c/d"), "a/b/c/d");
- t!(s: Path::new("a/b").join("/c/d"), "/c/d");
- t!(s: Path::new(".").join("a/b"), "a/b");
- t!(s: Path::new("/").join("a/b"), "/a/b");
- }
-
- #[test]
- fn test_join_path() {
- macro_rules! t {
- (s: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let join = Path::new($join);
- let res = path.join(&join);
- assert_eq!(res.as_str(), Some($exp));
- }
- )
- }
-
- t!(s: "a/b/c", "..", "a/b");
- t!(s: "/a/b/c", "d", "/a/b/c/d");
- t!(s: "a/b", "c/d", "a/b/c/d");
- t!(s: "a/b", "/c/d", "/c/d");
- t!(s: ".", "a/b", "a/b");
- t!(s: "/", "a/b", "/a/b");
- }
-
- #[test]
- fn test_join_many() {
- macro_rules! t {
- (s: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let res = path.join_many(&$join);
- assert_eq!(res.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let res = path.join_many(&$join);
- assert_eq!(res.as_vec(), $exp);
- }
- )
- }
-
- t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e");
- t!(s: "a/b/c", ["..", "d"], "a/b/d");
- t!(s: "a/b/c", ["d", "/e", "f"], "/e/f");
- t!(s: "a/b/c", ["d".to_string(), "e".to_string()], "a/b/c/d/e");
- t!(v: b"a/b/c", [b"d", b"e"], b"a/b/c/d/e");
- t!(v: b"a/b/c", [b"d".to_vec(), b"e".to_vec()], b"a/b/c/d/e");
- }
-
- #[test]
- fn test_with_helpers() {
- let empty: &[u8] = &[];
-
- t!(v: Path::new(b"a/b/c").with_filename(b"d"), b"a/b/d");
- t!(v: Path::new(b"a/b/c\xFF").with_filename(b"\x80"), b"a/b/\x80");
- t!(v: Path::new(b"/\xFF/foo").with_filename(b"\xCD"),
- b"/\xFF/\xCD");
- t!(s: Path::new("a/b/c").with_filename("d"), "a/b/d");
- t!(s: Path::new(".").with_filename("foo"), "foo");
- t!(s: Path::new("/a/b/c").with_filename("d"), "/a/b/d");
- t!(s: Path::new("/").with_filename("foo"), "/foo");
- t!(s: Path::new("/a").with_filename("foo"), "/foo");
- t!(s: Path::new("foo").with_filename("bar"), "bar");
- t!(s: Path::new("/").with_filename("foo/"), "/foo");
- t!(s: Path::new("/a").with_filename("foo/"), "/foo");
- t!(s: Path::new("a/b/c").with_filename(""), "a/b");
- t!(s: Path::new("a/b/c").with_filename("."), "a/b");
- t!(s: Path::new("a/b/c").with_filename(".."), "a");
- t!(s: Path::new("/a").with_filename(""), "/");
- t!(s: Path::new("foo").with_filename(""), ".");
- t!(s: Path::new("a/b/c").with_filename("d/e"), "a/b/d/e");
- t!(s: Path::new("a/b/c").with_filename("/d"), "a/b/d");
- t!(s: Path::new("..").with_filename("foo"), "../foo");
- t!(s: Path::new("../..").with_filename("foo"), "../../foo");
- t!(s: Path::new("..").with_filename(""), "..");
- t!(s: Path::new("../..").with_filename(""), "../..");
-
- t!(v: Path::new(b"hi/there\x80.txt").with_extension(b"exe"),
- b"hi/there\x80.exe");
- t!(v: Path::new(b"hi/there.txt\x80").with_extension(b"\xFF"),
- b"hi/there.\xFF");
- t!(v: Path::new(b"hi/there\x80").with_extension(b"\xFF"),
- b"hi/there\x80.\xFF");
- t!(v: Path::new(b"hi/there.\xFF").with_extension(empty), b"hi/there");
- t!(s: Path::new("hi/there.txt").with_extension("exe"), "hi/there.exe");
- t!(s: Path::new("hi/there.txt").with_extension(""), "hi/there");
- t!(s: Path::new("hi/there.txt").with_extension("."), "hi/there..");
- t!(s: Path::new("hi/there.txt").with_extension(".."), "hi/there...");
- t!(s: Path::new("hi/there").with_extension("txt"), "hi/there.txt");
- t!(s: Path::new("hi/there").with_extension("."), "hi/there..");
- t!(s: Path::new("hi/there").with_extension(".."), "hi/there...");
- t!(s: Path::new("hi/there.").with_extension("txt"), "hi/there.txt");
- t!(s: Path::new("hi/.foo").with_extension("txt"), "hi/.foo.txt");
- t!(s: Path::new("hi/there.txt").with_extension(".foo"), "hi/there..foo");
- t!(s: Path::new("/").with_extension("txt"), "/");
- t!(s: Path::new("/").with_extension("."), "/");
- t!(s: Path::new("/").with_extension(".."), "/");
- t!(s: Path::new(".").with_extension("txt"), ".");
- }
-
- #[test]
- fn test_setters() {
- macro_rules! t {
- (s: $path:expr, $set:ident, $with:ident, $arg:expr) => (
- {
- let path = $path;
- let arg = $arg;
- let mut p1 = Path::new(path);
- p1.$set(arg);
- let p2 = Path::new(path);
- assert_eq!(p1, p2.$with(arg));
- }
- );
- (v: $path:expr, $set:ident, $with:ident, $arg:expr) => (
- {
- let path = $path;
- let arg = $arg;
- let mut p1 = Path::new(path);
- p1.$set(arg);
- let p2 = Path::new(path);
- assert_eq!(p1, p2.$with(arg));
- }
- )
- }
-
- t!(v: b"a/b/c", set_filename, with_filename, b"d");
- t!(v: b"/", set_filename, with_filename, b"foo");
- t!(v: b"\x80", set_filename, with_filename, b"\xFF");
- t!(s: "a/b/c", set_filename, with_filename, "d");
- t!(s: "/", set_filename, with_filename, "foo");
- t!(s: ".", set_filename, with_filename, "foo");
- t!(s: "a/b", set_filename, with_filename, "");
- t!(s: "a", set_filename, with_filename, "");
-
- t!(v: b"hi/there.txt", set_extension, with_extension, b"exe");
- t!(v: b"hi/there.t\x80xt", set_extension, with_extension, b"exe\xFF");
- t!(s: "hi/there.txt", set_extension, with_extension, "exe");
- t!(s: "hi/there.", set_extension, with_extension, "txt");
- t!(s: "hi/there", set_extension, with_extension, "txt");
- t!(s: "hi/there.txt", set_extension, with_extension, "");
- t!(s: "hi/there", set_extension, with_extension, "");
- t!(s: ".", set_extension, with_extension, "txt");
- }
-
- #[test]
- fn test_getters() {
- macro_rules! t {
- (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
- {
- let path = $path;
- assert_eq!(path.filename_str(), $filename);
- assert_eq!(path.dirname_str(), $dirname);
- assert_eq!(path.filestem_str(), $filestem);
- assert_eq!(path.extension_str(), $ext);
- }
- );
- (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
- {
- let path = $path;
- assert_eq!(path.filename(), $filename);
- assert_eq!(path.dirname(), $dirname);
- assert_eq!(path.filestem(), $filestem);
- assert_eq!(path.extension(), $ext);
- }
- )
- }
-
- t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), None);
- t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), None);
- t!(v: Path::new(b"hi/there.\xFF"), Some(b"there.\xFF"), b"hi",
- Some(b"there"), Some(b"\xFF"));
- t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None);
- t!(s: Path::new("."), None, Some("."), None, None);
- t!(s: Path::new("/"), None, Some("/"), None, None);
- t!(s: Path::new(".."), None, Some(".."), None, None);
- t!(s: Path::new("../.."), None, Some("../.."), None, None);
- t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"),
- Some("there"), Some("txt"));
- t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None);
- t!(s: Path::new("hi/there."), Some("there."), Some("hi"),
- Some("there"), Some(""));
- t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None);
- t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"),
- Some("."), Some("there"));
- t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, None);
- t!(s: Path::new(b"a/b/\xFF.txt"), None, Some("a/b"), None, Some("txt"));
- t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), None);
- t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), None);
- }
-
- #[test]
- fn test_dir_path() {
- t!(v: Path::new(b"hi/there\x80").dir_path(), b"hi");
- t!(v: Path::new(b"hi\xFF/there").dir_path(), b"hi\xFF");
- t!(s: Path::new("hi/there").dir_path(), "hi");
- t!(s: Path::new("hi").dir_path(), ".");
- t!(s: Path::new("/hi").dir_path(), "/");
- t!(s: Path::new("/").dir_path(), "/");
- t!(s: Path::new("..").dir_path(), "..");
- t!(s: Path::new("../..").dir_path(), "../..");
- }
-
- #[test]
- fn test_is_absolute() {
- macro_rules! t {
- (s: $path:expr, $abs:expr, $rel:expr) => (
- {
- let path = Path::new($path);
- assert_eq!(path.is_absolute(), $abs);
- assert_eq!(path.is_relative(), $rel);
- }
- )
- }
- t!(s: "a/b/c", false, true);
- t!(s: "/a/b/c", true, false);
- t!(s: "a", false, true);
- t!(s: "/a", true, false);
- t!(s: ".", false, true);
- t!(s: "/", true, false);
- t!(s: "..", false, true);
- t!(s: "../..", false, true);
- }
-
- #[test]
- fn test_is_ancestor_of() {
- macro_rules! t {
- (s: $path:expr, $dest:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let dest = Path::new($dest);
- assert_eq!(path.is_ancestor_of(&dest), $exp);
- }
- )
- }
-
- t!(s: "a/b/c", "a/b/c/d", true);
- t!(s: "a/b/c", "a/b/c", true);
- t!(s: "a/b/c", "a/b", false);
- t!(s: "/a/b/c", "/a/b/c", true);
- t!(s: "/a/b", "/a/b/c", true);
- t!(s: "/a/b/c/d", "/a/b/c", false);
- t!(s: "/a/b", "a/b/c", false);
- t!(s: "a/b", "/a/b/c", false);
- t!(s: "a/b/c", "a/b/d", false);
- t!(s: "../a/b/c", "a/b/c", false);
- t!(s: "a/b/c", "../a/b/c", false);
- t!(s: "a/b/c", "a/b/cd", false);
- t!(s: "a/b/cd", "a/b/c", false);
- t!(s: "../a/b", "../a/b/c", true);
- t!(s: ".", "a/b", true);
- t!(s: ".", ".", true);
- t!(s: "/", "/", true);
- t!(s: "/", "/a/b", true);
- t!(s: "..", "a/b", true);
- t!(s: "../..", "a/b", true);
- }
-
- #[test]
- fn test_ends_with_path() {
- macro_rules! t {
- (s: $path:expr, $child:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let child = Path::new($child);
- assert_eq!(path.ends_with_path(&child), $exp);
- }
- );
- (v: $path:expr, $child:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let child = Path::new($child);
- assert_eq!(path.ends_with_path(&child), $exp);
- }
- )
- }
-
- t!(s: "a/b/c", "c", true);
- t!(s: "a/b/c", "d", false);
- t!(s: "foo/bar/quux", "bar", false);
- t!(s: "foo/bar/quux", "barquux", false);
- t!(s: "a/b/c", "b/c", true);
- t!(s: "a/b/c", "a/b/c", true);
- t!(s: "a/b/c", "foo/a/b/c", false);
- t!(s: "/a/b/c", "a/b/c", true);
- t!(s: "/a/b/c", "/a/b/c", false); // child must be relative
- t!(s: "/a/b/c", "foo/a/b/c", false);
- t!(s: "a/b/c", "", false);
- t!(s: "", "", true);
- t!(s: "/a/b/c", "d/e/f", false);
- t!(s: "a/b/c", "a/b", false);
- t!(s: "a/b/c", "b", false);
- t!(v: b"a/b/c", b"b/c", true);
- t!(v: b"a/b/\xFF", b"\xFF", true);
- t!(v: b"a/b/\xFF", b"b/\xFF", true);
- }
-
- #[test]
- fn test_path_relative_from() {
- macro_rules! t {
- (s: $path:expr, $other:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let other = Path::new($other);
- let res = path.path_relative_from(&other);
- assert_eq!(res.as_ref().and_then(|x| x.as_str()), $exp);
- }
- )
- }
-
- t!(s: "a/b/c", "a/b", Some("c"));
- t!(s: "a/b/c", "a/b/d", Some("../c"));
- t!(s: "a/b/c", "a/b/c/d", Some(".."));
- t!(s: "a/b/c", "a/b/c", Some("."));
- t!(s: "a/b/c", "a/b/c/d/e", Some("../.."));
- t!(s: "a/b/c", "a/d/e", Some("../../b/c"));
- t!(s: "a/b/c", "d/e/f", Some("../../../a/b/c"));
- t!(s: "a/b/c", "/a/b/c", None);
- t!(s: "/a/b/c", "a/b/c", Some("/a/b/c"));
- t!(s: "/a/b/c", "/a/b/c/d", Some(".."));
- t!(s: "/a/b/c", "/a/b", Some("c"));
- t!(s: "/a/b/c", "/a/b/c/d/e", Some("../.."));
- t!(s: "/a/b/c", "/a/d/e", Some("../../b/c"));
- t!(s: "/a/b/c", "/d/e/f", Some("../../../a/b/c"));
- t!(s: "hi/there.txt", "hi/there", Some("../there.txt"));
- t!(s: ".", "a", Some(".."));
- t!(s: ".", "a/b", Some("../.."));
- t!(s: ".", ".", Some("."));
- t!(s: "a", ".", Some("a"));
- t!(s: "a/b", ".", Some("a/b"));
- t!(s: "..", ".", Some(".."));
- t!(s: "a/b/c", "a/b/c", Some("."));
- t!(s: "/a/b/c", "/a/b/c", Some("."));
- t!(s: "/", "/", Some("."));
- t!(s: "/", ".", Some("/"));
- t!(s: "../../a", "b", Some("../../../a"));
- t!(s: "a", "../../b", None);
- t!(s: "../../a", "../../b", Some("../a"));
- t!(s: "../../a", "../../a/b", Some(".."));
- t!(s: "../../a/b", "../../a", Some("b"));
- }
-
- #[test]
- fn test_components_iter() {
- macro_rules! t {
- (s: $path:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let comps = path.components().collect::<Vec<&[u8]>>();
- let exp: &[&str] = &$exp;
- let exps = exp.iter().map(|x| x.as_bytes()).collect::<Vec<&[u8]>>();
- assert_eq!(comps, exps);
- let comps = path.components().rev().collect::<Vec<&[u8]>>();
- let exps = exps.into_iter().rev().collect::<Vec<&[u8]>>();
- assert_eq!(comps, exps);
- }
- );
- (b: $arg:expr, [$($exp:expr),*]) => (
- {
- let path = Path::new($arg);
- let comps = path.components().collect::<Vec<&[u8]>>();
- let exp: &[&[u8]] = &[$($exp),*];
- assert_eq!(comps, exp);
- let comps = path.components().rev().collect::<Vec<&[u8]>>();
- let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>();
- assert_eq!(comps, exp)
- }
- )
- }
-
- t!(b: b"a/b/c", [b"a", b"b", b"c"]);
- t!(b: b"/\xFF/a/\x80", [b"\xFF", b"a", b"\x80"]);
- t!(b: b"../../foo\xCDbar", [b"..", b"..", b"foo\xCDbar"]);
- t!(s: "a/b/c", ["a", "b", "c"]);
- t!(s: "a/b/d", ["a", "b", "d"]);
- t!(s: "a/b/cd", ["a", "b", "cd"]);
- t!(s: "/a/b/c", ["a", "b", "c"]);
- t!(s: "a", ["a"]);
- t!(s: "/a", ["a"]);
- t!(s: "/", []);
- t!(s: ".", ["."]);
- t!(s: "..", [".."]);
- t!(s: "../..", ["..", ".."]);
- t!(s: "../../foo", ["..", "..", "foo"]);
- }
-
- #[test]
- fn test_str_components() {
- macro_rules! t {
- (b: $arg:expr, $exp:expr) => (
- {
- let path = Path::new($arg);
- let comps = path.str_components().collect::<Vec<Option<&str>>>();
- let exp: &[Option<&str>] = &$exp;
- assert_eq!(comps, exp);
- let comps = path.str_components().rev().collect::<Vec<Option<&str>>>();
- let exp = exp.iter().rev().map(|&x|x).collect::<Vec<Option<&str>>>();
- assert_eq!(comps, exp);
- }
- )
- }
-
- t!(b: b"a/b/c", [Some("a"), Some("b"), Some("c")]);
- t!(b: b"/\xFF/a/\x80", [None, Some("a"), None]);
- t!(b: b"../../foo\xCDbar", [Some(".."), Some(".."), None]);
- // str_components is a wrapper around components, so no need to do
- // the full set of tests
- }
-}
-
-#[cfg(test)]
-mod bench {
- extern crate test;
- use self::test::Bencher;
- use super::*;
- use prelude::v1::{Clone, GenericPath};
-
- #[bench]
- fn join_home_dir(b: &mut Bencher) {
- let posix_path = Path::new("/");
- b.iter(|| {
- posix_path.join("home");
- });
- }
-
- #[bench]
- fn join_abs_path_home_dir(b: &mut Bencher) {
- let posix_path = Path::new("/");
- b.iter(|| {
- posix_path.join("/home");
- });
- }
-
- #[bench]
- fn join_many_home_dir(b: &mut Bencher) {
- let posix_path = Path::new("/");
- b.iter(|| {
- posix_path.join_many(&["home"]);
- });
- }
-
- #[bench]
- fn join_many_abs_path_home_dir(b: &mut Bencher) {
- let posix_path = Path::new("/");
- b.iter(|| {
- posix_path.join_many(&["/home"]);
- });
- }
-
- #[bench]
- fn push_home_dir(b: &mut Bencher) {
- let mut posix_path = Path::new("/");
- b.iter(|| {
- posix_path.push("home");
- });
- }
-
- #[bench]
- fn push_abs_path_home_dir(b: &mut Bencher) {
- let mut posix_path = Path::new("/");
- b.iter(|| {
- posix_path.push("/home");
- });
- }
-
- #[bench]
- fn push_many_home_dir(b: &mut Bencher) {
- let mut posix_path = Path::new("/");
- b.iter(|| {
- posix_path.push_many(&["home"]);
- });
- }
-
- #[bench]
- fn push_many_abs_path_home_dir(b: &mut Bencher) {
- let mut posix_path = Path::new("/");
- b.iter(|| {
- posix_path.push_many(&["/home"]);
- });
- }
-
- #[bench]
- fn ends_with_path_home_dir(b: &mut Bencher) {
- let posix_home_path = Path::new("/home");
- b.iter(|| {
- posix_home_path.ends_with_path(&Path::new("home"));
- });
- }
-
- #[bench]
- fn ends_with_path_missmatch_jome_home(b: &mut Bencher) {
- let posix_home_path = Path::new("/home");
- b.iter(|| {
- posix_home_path.ends_with_path(&Path::new("jome"));
- });
- }
-
- #[bench]
- fn is_ancestor_of_path_with_10_dirs(b: &mut Bencher) {
- let path = Path::new("/home/1/2/3/4/5/6/7/8/9");
- let mut sub = path.clone();
- sub.pop();
- b.iter(|| {
- path.is_ancestor_of(&sub);
- });
- }
-
- #[bench]
- fn path_relative_from_forward(b: &mut Bencher) {
- let path = Path::new("/a/b/c");
- let mut other = path.clone();
- other.pop();
- b.iter(|| {
- path.path_relative_from(&other);
- });
- }
-
- #[bench]
- fn path_relative_from_same_level(b: &mut Bencher) {
- let path = Path::new("/a/b/c");
- let mut other = path.clone();
- other.pop();
- other.push("d");
- b.iter(|| {
- path.path_relative_from(&other);
- });
- }
-
- #[bench]
- fn path_relative_from_backward(b: &mut Bencher) {
- let path = Path::new("/a/b");
- let mut other = path.clone();
- other.push("c");
- b.iter(|| {
- path.path_relative_from(&other);
- });
- }
-}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-//
-// ignore-lexer-test FIXME #15883
-
-//! Windows file path handling
-
-use self::PathPrefix::*;
-
-use ascii::AsciiExt;
-use char::CharExt;
-use clone::Clone;
-use cmp::{Ordering, Eq, Ord, PartialEq, PartialOrd};
-use fmt;
-use hash;
-use old_io::Writer;
-use iter::{AdditiveIterator, Extend};
-use iter::{Iterator, IteratorExt, Map, repeat};
-use mem;
-use option::Option::{self, Some, None};
-use result::Result::{self, Ok, Err};
-use slice::{SliceExt, SliceConcatExt};
-use str::{SplitTerminator, FromStr, StrExt};
-use string::{String, ToString};
-use vec::Vec;
-
-use super::{contains_nul, BytesContainer, GenericPath, GenericPathUnsafe};
-
-/// Iterator that yields successive components of a Path as &str
-///
-/// Each component is yielded as Option<&str> for compatibility with PosixPath, but
-/// every component in WindowsPath is guaranteed to be Some.
-pub type StrComponents<'a> =
- Map<SplitTerminator<'a, char>, fn(&'a str) -> Option<&'a str>>;
-
-/// Iterator that yields successive components of a Path as &[u8]
-pub type Components<'a> =
- Map<StrComponents<'a>, fn(Option<&str>) -> &[u8]>;
-
-/// Represents a Windows path
-// Notes for Windows path impl:
-// The MAX_PATH is 260, but 253 is the practical limit due to some API bugs
-// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx for good information
-// about windows paths.
-// That same page puts a bunch of restrictions on allowed characters in a path.
-// `\foo.txt` means "relative to current drive", but will not be considered to be absolute here
-// as `∃P | P.join("\foo.txt") != "\foo.txt"`.
-// `C:` is interesting, that means "the current directory on drive C".
-// Long absolute paths need to have \\?\ prefix (or, for UNC, \\?\UNC\). I think that can be
-// ignored for now, though, and only added in a hypothetical .to_pwstr() function.
-// However, if a path is parsed that has \\?\, this needs to be preserved as it disables the
-// processing of "." and ".." components and / as a separator.
-// Experimentally, \\?\foo is not the same thing as \foo.
-// Also, \\foo is not valid either (certainly not equivalent to \foo).
-// Similarly, C:\\Users is not equivalent to C:\Users, although C:\Users\\foo is equivalent
-// to C:\Users\foo. In fact the command prompt treats C:\\foo\bar as UNC path. But it might be
-// best to just ignore that and normalize it to C:\foo\bar.
-//
-// Based on all this, I think the right approach is to do the following:
-// * Require valid utf-8 paths. Windows API may use WCHARs, but we don't, and utf-8 is convertible
-// to UTF-16 anyway (though does Windows use UTF-16 or UCS-2? Not sure).
-// * Parse the prefixes \\?\UNC\, \\?\, and \\.\ explicitly.
-// * If \\?\UNC\, treat following two path components as server\share. Don't error for missing
-// server\share.
-// * If \\?\, parse disk from following component, if present. Don't error for missing disk.
-// * If \\.\, treat rest of path as just regular components. I don't know how . and .. are handled
-// here, they probably aren't, but I'm not going to worry about that.
-// * Else if starts with \\, treat following two components as server\share. Don't error for missing
-// server\share.
-// * Otherwise, attempt to parse drive from start of path.
-//
-// The only error condition imposed here is valid utf-8. All other invalid paths are simply
-// preserved by the data structure; let the Windows API error out on them.
-#[derive(Clone)]
-pub struct Path {
- repr: String, // assumed to never be empty
- prefix: Option<PathPrefix>,
- sepidx: Option<uint> // index of the final separator in the non-prefix portion of repr
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl fmt::Debug for Path {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&self.display(), f)
- }
-}
-
-impl PartialEq for Path {
- #[inline]
- fn eq(&self, other: &Path) -> bool {
- self.repr == other.repr
- }
-}
-
-impl Eq for Path {}
-
-impl PartialOrd for Path {
- fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Path {
- fn cmp(&self, other: &Path) -> Ordering {
- self.repr.cmp(&other.repr)
- }
-}
-
-impl FromStr for Path {
- type Err = ParsePathError;
- fn from_str(s: &str) -> Result<Path, ParsePathError> {
- match Path::new_opt(s) {
- Some(p) => Ok(p),
- None => Err(ParsePathError),
- }
- }
-}
-
-/// Value indicating that a path could not be parsed from a string.
-#[derive(Debug, Clone, PartialEq, Copy)]
-pub struct ParsePathError;
-
-impl<S: hash::Writer + hash::Hasher> hash::Hash<S> for Path {
- #[cfg(not(test))]
- #[inline]
- fn hash(&self, state: &mut S) {
- self.repr.hash(state)
- }
-
- #[cfg(test)]
- #[inline]
- fn hash(&self, _: &mut S) {
- // No-op because the `hash` implementation will be wrong.
- }
-}
-
-impl BytesContainer for Path {
- #[inline]
- fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
- self.as_vec()
- }
- #[inline]
- fn container_as_str<'a>(&'a self) -> Option<&'a str> {
- self.as_str()
- }
- #[inline]
- fn is_str(_: Option<&Path>) -> bool { true }
-}
-
-impl GenericPathUnsafe for Path {
- /// See `GenericPathUnsafe::from_vec_unchecked`.
- ///
- /// # Panics
- ///
- /// Panics if not valid UTF-8.
- #[inline]
- unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
- let (prefix, path) = Path::normalize_(path.container_as_str().unwrap());
- assert!(!path.is_empty());
- let mut ret = Path{ repr: path, prefix: prefix, sepidx: None };
- ret.update_sepidx();
- ret
- }
-
- /// See `GenericPathUnsafe::set_filename_unchecked`.
- ///
- /// # Panics
- ///
- /// Panics if not valid UTF-8.
- unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
- let filename = filename.container_as_str().unwrap();
- match self.sepidx_or_prefix_len() {
- None if ".." == self.repr => {
- let mut s = String::with_capacity(3 + filename.len());
- s.push_str("..");
- s.push(SEP);
- s.push_str(filename);
- self.update_normalized(&s[]);
- }
- None => {
- self.update_normalized(filename);
- }
- Some((_,idxa,end)) if &self.repr[idxa..end] == ".." => {
- let mut s = String::with_capacity(end + 1 + filename.len());
- s.push_str(&self.repr[..end]);
- s.push(SEP);
- s.push_str(filename);
- self.update_normalized(&s[]);
- }
- Some((idxb,idxa,_)) if self.prefix == Some(DiskPrefix) && idxa == self.prefix_len() => {
- let mut s = String::with_capacity(idxb + filename.len());
- s.push_str(&self.repr[..idxb]);
- s.push_str(filename);
- self.update_normalized(&s[]);
- }
- Some((idxb,_,_)) => {
- let mut s = String::with_capacity(idxb + 1 + filename.len());
- s.push_str(&self.repr[..idxb]);
- s.push(SEP);
- s.push_str(filename);
- self.update_normalized(&s[]);
- }
- }
- }
-
- /// See `GenericPathUnsafe::push_unchecked`.
- ///
- /// Concatenating two Windows Paths is rather complicated.
- /// For the most part, it will behave as expected, except in the case of
- /// pushing a volume-relative path, e.g. `C:foo.txt`. Because we have no
- /// concept of per-volume cwds like Windows does, we can't behave exactly
- /// like Windows will. Instead, if the receiver is an absolute path on
- /// the same volume as the new path, it will be treated as the cwd that
- /// the new path is relative to. Otherwise, the new path will be treated
- /// as if it were absolute and will replace the receiver outright.
- unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
- let path = path.container_as_str().unwrap();
- fn is_vol_abs(path: &str, prefix: Option<PathPrefix>) -> bool {
- // assume prefix is Some(DiskPrefix)
- let rest = &path[prefix_len(prefix)..];
- !rest.is_empty() && rest.as_bytes()[0].is_ascii() && is_sep(rest.as_bytes()[0] as char)
- }
- fn shares_volume(me: &Path, path: &str) -> bool {
- // path is assumed to have a prefix of Some(DiskPrefix)
- let repr = &me.repr[];
- match me.prefix {
- Some(DiskPrefix) => {
- repr.as_bytes()[0] == path.as_bytes()[0].to_ascii_uppercase()
- }
- Some(VerbatimDiskPrefix) => {
- repr.as_bytes()[4] == path.as_bytes()[0].to_ascii_uppercase()
- }
- _ => false
- }
- }
- fn is_sep_(prefix: Option<PathPrefix>, u: u8) -> bool {
- if prefix_is_verbatim(prefix) { is_sep_verbatim(u as char) }
- else { is_sep(u as char) }
- }
-
- fn replace_path(me: &mut Path, path: &str, prefix: Option<PathPrefix>) {
- let newpath = Path::normalize__(path, prefix);
- me.repr = match newpath {
- Some(p) => p,
- None => String::from_str(path)
- };
- me.prefix = prefix;
- me.update_sepidx();
- }
- fn append_path(me: &mut Path, path: &str) {
- // appends a path that has no prefix
- // if me is verbatim, we need to pre-normalize the new path
- let path_ = if is_verbatim(me) { Path::normalize__(path, None) }
- else { None };
- let pathlen = path_.as_ref().map_or(path.len(), |p| p.len());
- let mut s = String::with_capacity(me.repr.len() + 1 + pathlen);
- s.push_str(&me.repr[]);
- let plen = me.prefix_len();
- // if me is "C:" we don't want to add a path separator
- match me.prefix {
- Some(DiskPrefix) if me.repr.len() == plen => (),
- _ if !(me.repr.len() > plen && me.repr.as_bytes()[me.repr.len()-1] == SEP_BYTE) => {
- s.push(SEP);
- }
- _ => ()
- }
- match path_ {
- None => s.push_str(path),
- Some(p) => s.push_str(&p[]),
- };
- me.update_normalized(&s[])
- }
-
- if !path.is_empty() {
- let prefix = parse_prefix(path);
- match prefix {
- Some(DiskPrefix) if !is_vol_abs(path, prefix) && shares_volume(self, path) => {
- // cwd-relative path, self is on the same volume
- append_path(self, &path[prefix_len(prefix)..]);
- }
- Some(_) => {
- // absolute path, or cwd-relative and self is not same volume
- replace_path(self, path, prefix);
- }
- None if !path.is_empty() && is_sep_(self.prefix, path.as_bytes()[0]) => {
- // volume-relative path
- if self.prefix.is_some() {
- // truncate self down to the prefix, then append
- let n = self.prefix_len();
- self.repr.truncate(n);
- append_path(self, path);
- } else {
- // we have no prefix, so nothing to be relative to
- replace_path(self, path, prefix);
- }
- }
- None => {
- // relative path
- append_path(self, path);
- }
- }
- }
- }
-}
-
-impl GenericPath for Path {
- #[inline]
- fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
- match path.container_as_str() {
- None => None,
- Some(ref s) => {
- if contains_nul(s) {
- None
- } else {
- Some(unsafe { GenericPathUnsafe::new_unchecked(*s) })
- }
- }
- }
- }
-
- /// See `GenericPath::as_str` for info.
- /// Always returns a `Some` value.
- #[inline]
- fn as_str<'a>(&'a self) -> Option<&'a str> {
- Some(&self.repr[])
- }
-
- #[inline]
- fn as_vec<'a>(&'a self) -> &'a [u8] {
- self.repr.as_bytes()
- }
-
- #[inline]
- fn into_vec(self) -> Vec<u8> {
- self.repr.into_bytes()
- }
-
- #[inline]
- fn dirname<'a>(&'a self) -> &'a [u8] {
- self.dirname_str().unwrap().as_bytes()
- }
-
- /// See `GenericPath::dirname_str` for info.
- /// Always returns a `Some` value.
- fn dirname_str<'a>(&'a self) -> Option<&'a str> {
- Some(match self.sepidx_or_prefix_len() {
- None if ".." == self.repr => &self.repr[],
- None => ".",
- Some((_,idxa,end)) if &self.repr[idxa..end] == ".." => {
- &self.repr[]
- }
- Some((idxb,_,end)) if &self.repr[idxb..end] == "\\" => {
- &self.repr[]
- }
- Some((0,idxa,_)) => &self.repr[..idxa],
- Some((idxb,idxa,_)) => {
- match self.prefix {
- Some(DiskPrefix) | Some(VerbatimDiskPrefix) if idxb == self.prefix_len() => {
- &self.repr[..idxa]
- }
- _ => &self.repr[..idxb]
- }
- }
- })
- }
-
- #[inline]
- fn filename<'a>(&'a self) -> Option<&'a [u8]> {
- self.filename_str().map(|x| x.as_bytes())
- }
-
- /// See `GenericPath::filename_str` for info.
- /// Always returns a `Some` value if `filename` returns a `Some` value.
- fn filename_str<'a>(&'a self) -> Option<&'a str> {
- let repr = &self.repr[];
- match self.sepidx_or_prefix_len() {
- None if "." == repr || ".." == repr => None,
- None => Some(repr),
- Some((_,idxa,end)) if &repr[idxa..end] == ".." => None,
- Some((_,idxa,end)) if idxa == end => None,
- Some((_,idxa,end)) => Some(&repr[idxa..end])
- }
- }
-
- /// See `GenericPath::filestem_str` for info.
- /// Always returns a `Some` value if `filestem` returns a `Some` value.
- #[inline]
- fn filestem_str<'a>(&'a self) -> Option<&'a str> {
- // filestem() returns a byte vector that's guaranteed valid UTF-8
- self.filestem().map(|t| unsafe { mem::transmute(t) })
- }
-
- #[inline]
- fn extension_str<'a>(&'a self) -> Option<&'a str> {
- // extension() returns a byte vector that's guaranteed valid UTF-8
- self.extension().map(|t| unsafe { mem::transmute(t) })
- }
-
- fn dir_path(&self) -> Path {
- unsafe { GenericPathUnsafe::new_unchecked(self.dirname_str().unwrap()) }
- }
-
- #[inline]
- fn pop(&mut self) -> bool {
- match self.sepidx_or_prefix_len() {
- None if "." == self.repr => false,
- None => {
- self.repr = String::from_str(".");
- self.sepidx = None;
- true
- }
- Some((idxb,idxa,end)) if idxb == idxa && idxb == end => false,
- Some((idxb,_,end)) if &self.repr[idxb..end] == "\\" => false,
- Some((idxb,idxa,_)) => {
- let trunc = match self.prefix {
- Some(DiskPrefix) | Some(VerbatimDiskPrefix) | None => {
- let plen = self.prefix_len();
- if idxb == plen { idxa } else { idxb }
- }
- _ => idxb
- };
- self.repr.truncate(trunc);
- self.update_sepidx();
- true
- }
- }
- }
-
- fn root_path(&self) -> Option<Path> {
- if self.prefix.is_some() {
- Some(Path::new(match self.prefix {
- Some(DiskPrefix) if self.is_absolute() => {
- &self.repr[..self.prefix_len()+1]
- }
- Some(VerbatimDiskPrefix) => {
- &self.repr[..self.prefix_len()+1]
- }
- _ => &self.repr[..self.prefix_len()]
- }))
- } else if is_vol_relative(self) {
- Some(Path::new(&self.repr[..1]))
- } else {
- None
- }
- }
-
- /// See `GenericPath::is_absolute` for info.
- ///
- /// A Windows Path is considered absolute only if it has a non-volume prefix,
- /// or if it has a volume prefix and the path starts with '\'.
- /// A path of `\foo` is not considered absolute because it's actually
- /// relative to the "current volume". A separate method `Path::is_vol_relative`
- /// is provided to indicate this case. Similarly a path of `C:foo` is not
- /// considered absolute because it's relative to the cwd on volume C:. A
- /// separate method `Path::is_cwd_relative` is provided to indicate this case.
- #[inline]
- fn is_absolute(&self) -> bool {
- match self.prefix {
- Some(DiskPrefix) => {
- let rest = &self.repr[self.prefix_len()..];
- rest.len() > 0 && rest.as_bytes()[0] == SEP_BYTE
- }
- Some(_) => true,
- None => false
- }
- }
-
- #[inline]
- fn is_relative(&self) -> bool {
- self.prefix.is_none() && !is_vol_relative(self)
- }
-
- fn is_ancestor_of(&self, other: &Path) -> bool {
- if !self.equiv_prefix(other) {
- false
- } else if self.is_absolute() != other.is_absolute() ||
- is_vol_relative(self) != is_vol_relative(other) {
- false
- } else {
- let mut ita = self.str_components().map(|x|x.unwrap());
- let mut itb = other.str_components().map(|x|x.unwrap());
- if "." == self.repr {
- return itb.next() != Some("..");
- }
- loop {
- match (ita.next(), itb.next()) {
- (None, _) => break,
- (Some(a), Some(b)) if a == b => { continue },
- (Some(a), _) if a == ".." => {
- // if ita contains only .. components, it's an ancestor
- return ita.all(|x| x == "..");
- }
- _ => return false
- }
- }
- true
- }
- }
-
- fn path_relative_from(&self, base: &Path) -> Option<Path> {
- fn comp_requires_verbatim(s: &str) -> bool {
- s == "." || s == ".." || s.contains_char(SEP2)
- }
-
- if !self.equiv_prefix(base) {
- // prefixes differ
- if self.is_absolute() {
- Some(self.clone())
- } else if self.prefix == Some(DiskPrefix) && base.prefix == Some(DiskPrefix) {
- // both drives, drive letters must differ or they'd be equiv
- Some(self.clone())
- } else {
- None
- }
- } else if self.is_absolute() != base.is_absolute() {
- if self.is_absolute() {
- Some(self.clone())
- } else {
- None
- }
- } else if is_vol_relative(self) != is_vol_relative(base) {
- if is_vol_relative(self) {
- Some(self.clone())
- } else {
- None
- }
- } else {
- let mut ita = self.str_components().map(|x|x.unwrap());
- let mut itb = base.str_components().map(|x|x.unwrap());
- let mut comps = vec![];
-
- let a_verb = is_verbatim(self);
- let b_verb = is_verbatim(base);
- loop {
- match (ita.next(), itb.next()) {
- (None, None) => break,
- (Some(a), None) if a_verb && comp_requires_verbatim(a) => {
- return Some(self.clone())
- }
- (Some(a), None) => {
- comps.push(a);
- if !a_verb {
- comps.extend(ita.by_ref());
- break;
- }
- }
- (None, _) => comps.push(".."),
- (Some(a), Some(b)) if comps.is_empty() && a == b => (),
- (Some(a), Some(b)) if !b_verb && b == "." => {
- if a_verb && comp_requires_verbatim(a) {
- return Some(self.clone())
- } else { comps.push(a) }
- }
- (Some(_), Some(b)) if !b_verb && b == ".." => return None,
- (Some(a), Some(_)) if a_verb && comp_requires_verbatim(a) => {
- return Some(self.clone())
- }
- (Some(a), Some(_)) => {
- comps.push("..");
- for _ in itb.by_ref() {
- comps.push("..");
- }
- comps.push(a);
- if !a_verb {
- comps.extend(ita.by_ref());
- break;
- }
- }
- }
- }
- Some(Path::new(comps.connect("\\")))
- }
- }
-
- fn ends_with_path(&self, child: &Path) -> bool {
- if !child.is_relative() { return false; }
- let mut selfit = self.str_components().rev();
- let mut childit = child.str_components().rev();
- loop {
- match (selfit.next(), childit.next()) {
- (Some(a), Some(b)) => if a != b { return false; },
- (Some(_), None) => break,
- (None, Some(_)) => return false,
- (None, None) => break
- }
- }
- true
- }
-}
-
-impl Path {
- /// Returns a new `Path` from a `BytesContainer`.
- ///
- /// # Panics
- ///
- /// Panics if the vector contains a `NUL`, or if it contains invalid UTF-8.
- ///
- /// # Example
- ///
- /// ```
- /// println!("{}", Path::new(r"C:\some\path").display());
- /// ```
- #[inline]
- pub fn new<T: BytesContainer>(path: T) -> Path {
- GenericPath::new(path)
- }
-
- /// Returns a new `Some(Path)` from a `BytesContainer`.
- ///
- /// Returns `None` if the vector contains a `NUL`, or if it contains invalid UTF-8.
- ///
- /// # Example
- ///
- /// ```
- /// let path = Path::new_opt(r"C:\some\path");
- ///
- /// match path {
- /// Some(path) => println!("{}", path.display()),
- /// None => println!("There was a problem with your path."),
- /// }
- /// ```
- #[inline]
- pub fn new_opt<T: BytesContainer>(path: T) -> Option<Path> {
- GenericPath::new_opt(path)
- }
-
- /// Returns an iterator that yields each component of the path in turn as a Option<&str>.
- /// Every component is guaranteed to be Some.
- /// Does not yield the path prefix (including server/share components in UNC paths).
- /// Does not distinguish between volume-relative and relative paths, e.g.
- /// \a\b\c and a\b\c.
- /// Does not distinguish between absolute and cwd-relative paths, e.g.
- /// C:\foo and C:foo.
- pub fn str_components<'a>(&'a self) -> StrComponents<'a> {
- let repr = &self.repr[];
- let s = match self.prefix {
- Some(_) => {
- let plen = self.prefix_len();
- if repr.len() > plen && repr.as_bytes()[plen] == SEP_BYTE {
- &repr[plen+1..]
- } else { &repr[plen..] }
- }
- None if repr.as_bytes()[0] == SEP_BYTE => &repr[1..],
- None => repr
- };
- let some: fn(&'a str) -> Option<&'a str> = Some; // coerce to fn ptr
- let ret = s.split_terminator(SEP).map(some);
- ret
- }
-
- /// Returns an iterator that yields each component of the path in turn as a &[u8].
- /// See str_components() for details.
- pub fn components<'a>(&'a self) -> Components<'a> {
- fn convert<'a>(x: Option<&'a str>) -> &'a [u8] {
- #![inline]
- x.unwrap().as_bytes()
- }
- let convert: for<'b> fn(Option<&'b str>) -> &'b [u8] = convert; // coerce to fn ptr
- self.str_components().map(convert)
- }
-
- fn equiv_prefix(&self, other: &Path) -> bool {
- let s_repr = &self.repr[];
- let o_repr = &other.repr[];
- match (self.prefix, other.prefix) {
- (Some(DiskPrefix), Some(VerbatimDiskPrefix)) => {
- self.is_absolute() &&
- s_repr.as_bytes()[0].to_ascii_lowercase() ==
- o_repr.as_bytes()[4].to_ascii_lowercase()
- }
- (Some(VerbatimDiskPrefix), Some(DiskPrefix)) => {
- other.is_absolute() &&
- s_repr.as_bytes()[4].to_ascii_lowercase() ==
- o_repr.as_bytes()[0].to_ascii_lowercase()
- }
- (Some(VerbatimDiskPrefix), Some(VerbatimDiskPrefix)) => {
- s_repr.as_bytes()[4].to_ascii_lowercase() ==
- o_repr.as_bytes()[4].to_ascii_lowercase()
- }
- (Some(UNCPrefix(_,_)), Some(VerbatimUNCPrefix(_,_))) => {
- &s_repr[2..self.prefix_len()] == &o_repr[8..other.prefix_len()]
- }
- (Some(VerbatimUNCPrefix(_,_)), Some(UNCPrefix(_,_))) => {
- &s_repr[8..self.prefix_len()] == &o_repr[2..other.prefix_len()]
- }
- (None, None) => true,
- (a, b) if a == b => {
- &s_repr[..self.prefix_len()] == &o_repr[..other.prefix_len()]
- }
- _ => false
- }
- }
-
- fn normalize_(s: &str) -> (Option<PathPrefix>, String) {
- // make borrowck happy
- let (prefix, val) = {
- let prefix = parse_prefix(s);
- let path = Path::normalize__(s, prefix);
- (prefix, path)
- };
- (prefix, match val {
- None => s.to_string(),
- Some(val) => val
- })
- }
-
- fn normalize__(s: &str, prefix: Option<PathPrefix>) -> Option<String> {
- if prefix_is_verbatim(prefix) {
- // don't do any normalization
- match prefix {
- Some(VerbatimUNCPrefix(x, 0)) if s.len() == 8 + x => {
- // the server component has no trailing '\'
- let mut s = String::from_str(s);
- s.push(SEP);
- Some(s)
- }
- _ => None
- }
- } else {
- let (is_abs, comps) = normalize_helper(s, prefix);
- let mut comps = comps;
- match (comps.is_some(),prefix) {
- (false, Some(DiskPrefix)) => {
- if s.as_bytes()[0] >= b'a' && s.as_bytes()[0] <= b'z' {
- comps = Some(vec![]);
- }
- }
- (false, Some(VerbatimDiskPrefix)) => {
- if s.as_bytes()[4] >= b'a' && s.as_bytes()[0] <= b'z' {
- comps = Some(vec![]);
- }
- }
- _ => ()
- }
- match comps {
- None => None,
- Some(comps) => {
- if prefix.is_some() && comps.is_empty() {
- match prefix.unwrap() {
- DiskPrefix => {
- let len = prefix_len(prefix) + is_abs as uint;
- let mut s = String::from_str(&s[..len]);
- unsafe {
- let v = s.as_mut_vec();
- v[0] = (*v)[0].to_ascii_uppercase();
- }
- if is_abs {
- // normalize C:/ to C:\
- unsafe {
- s.as_mut_vec()[2] = SEP_BYTE;
- }
- }
- Some(s)
- }
- VerbatimDiskPrefix => {
- let len = prefix_len(prefix) + is_abs as uint;
- let mut s = String::from_str(&s[..len]);
- unsafe {
- let v = s.as_mut_vec();
- v[4] = (*v)[4].to_ascii_uppercase();
- }
- Some(s)
- }
- _ => {
- let plen = prefix_len(prefix);
- if s.len() > plen {
- Some(String::from_str(&s[..plen]))
- } else { None }
- }
- }
- } else if is_abs && comps.is_empty() {
- Some(repeat(SEP).take(1).collect())
- } else {
- let prefix_ = &s[..prefix_len(prefix)];
- let n = prefix_.len() +
- if is_abs { comps.len() } else { comps.len() - 1} +
- comps.iter().map(|v| v.len()).sum();
- let mut s = String::with_capacity(n);
- match prefix {
- Some(DiskPrefix) => {
- s.push(prefix_.as_bytes()[0].to_ascii_uppercase() as char);
- s.push(':');
- }
- Some(VerbatimDiskPrefix) => {
- s.push_str(&prefix_[..4]);
- s.push(prefix_.as_bytes()[4].to_ascii_uppercase() as char);
- s.push_str(&prefix_[5..]);
- }
- Some(UNCPrefix(a,b)) => {
- s.push_str("\\\\");
- s.push_str(&prefix_[2..a+2]);
- s.push(SEP);
- s.push_str(&prefix_[3+a..3+a+b]);
- }
- Some(_) => s.push_str(prefix_),
- None => ()
- }
- let mut it = comps.into_iter();
- if !is_abs {
- match it.next() {
- None => (),
- Some(comp) => s.push_str(comp)
- }
- }
- for comp in it {
- s.push(SEP);
- s.push_str(comp);
- }
- Some(s)
- }
- }
- }
- }
- }
-
- fn update_sepidx(&mut self) {
- let s = if self.has_nonsemantic_trailing_slash() {
- &self.repr[..self.repr.len()-1]
- } else { &self.repr[] };
- let sep_test: fn(char) -> bool = if !prefix_is_verbatim(self.prefix) {
- is_sep
- } else {
- is_sep_verbatim
- };
- let idx = s.rfind(sep_test);
- let prefixlen = self.prefix_len();
- self.sepidx = idx.and_then(|x| if x < prefixlen { None } else { Some(x) });
- }
-
- fn prefix_len(&self) -> uint {
- prefix_len(self.prefix)
- }
-
- // Returns a tuple (before, after, end) where before is the index of the separator
- // and after is the index just after the separator.
- // end is the length of the string, normally, or the index of the final character if it is
- // a non-semantic trailing separator in a verbatim string.
- // If the prefix is considered the separator, before and after are the same.
- fn sepidx_or_prefix_len(&self) -> Option<(uint,uint,uint)> {
- match self.sepidx {
- None => match self.prefix_len() { 0 => None, x => Some((x,x,self.repr.len())) },
- Some(x) => {
- if self.has_nonsemantic_trailing_slash() {
- Some((x,x+1,self.repr.len()-1))
- } else { Some((x,x+1,self.repr.len())) }
- }
- }
- }
-
- fn has_nonsemantic_trailing_slash(&self) -> bool {
- is_verbatim(self) && self.repr.len() > self.prefix_len()+1 &&
- self.repr.as_bytes()[self.repr.len()-1] == SEP_BYTE
- }
-
- fn update_normalized(&mut self, s: &str) {
- let (prefix, path) = Path::normalize_(s);
- self.repr = path;
- self.prefix = prefix;
- self.update_sepidx();
- }
-}
-
-/// Returns whether the path is considered "volume-relative", which means a path
-/// that looks like "\foo". Paths of this form are relative to the current volume,
-/// but absolute within that volume.
-#[inline]
-pub fn is_vol_relative(path: &Path) -> bool {
- path.prefix.is_none() && is_sep_byte(&path.repr.as_bytes()[0])
-}
-
-/// Returns whether the path is considered "cwd-relative", which means a path
-/// with a volume prefix that is not absolute. This look like "C:foo.txt". Paths
-/// of this form are relative to the cwd on the given volume.
-#[inline]
-pub fn is_cwd_relative(path: &Path) -> bool {
- path.prefix == Some(DiskPrefix) && !path.is_absolute()
-}
-
-/// Returns the PathPrefix for this Path
-#[inline]
-pub fn prefix(path: &Path) -> Option<PathPrefix> {
- path.prefix
-}
-
-/// Returns whether the Path's prefix is a verbatim prefix, i.e. `\\?\`
-#[inline]
-pub fn is_verbatim(path: &Path) -> bool {
- prefix_is_verbatim(path.prefix)
-}
-
-/// Returns the non-verbatim equivalent of the input path, if possible.
-/// If the input path is a device namespace path, None is returned.
-/// If the input path is not verbatim, it is returned as-is.
-/// If the input path is verbatim, but the same path can be expressed as
-/// non-verbatim, the non-verbatim version is returned.
-/// Otherwise, None is returned.
-pub fn make_non_verbatim(path: &Path) -> Option<Path> {
- let repr = &path.repr[];
- let new_path = match path.prefix {
- Some(VerbatimPrefix(_)) | Some(DeviceNSPrefix(_)) => return None,
- Some(UNCPrefix(_,_)) | Some(DiskPrefix) | None => return Some(path.clone()),
- Some(VerbatimDiskPrefix) => {
- // \\?\D:\
- Path::new(&repr[4..])
- }
- Some(VerbatimUNCPrefix(_,_)) => {
- // \\?\UNC\server\share
- Path::new(format!(r"\{}", &repr[7..]))
- }
- };
- if new_path.prefix.is_none() {
- // \\?\UNC\server is a VerbatimUNCPrefix
- // but \\server is nothing
- return None;
- }
- // now ensure normalization didn't change anything
- if &repr[path.prefix_len()..] == &new_path.repr[new_path.prefix_len()..] {
- Some(new_path)
- } else {
- None
- }
-}
-
-/// The standard path separator character
-pub const SEP: char = '\\';
-/// The standard path separator byte
-pub const SEP_BYTE: u8 = SEP as u8;
-
-/// The alternative path separator character
-pub const SEP2: char = '/';
-/// The alternative path separator character
-pub const SEP2_BYTE: u8 = SEP2 as u8;
-
-/// Returns whether the given char is a path separator.
-/// Allows both the primary separator '\' and the alternative separator '/'.
-#[inline]
-pub fn is_sep(c: char) -> bool {
- c == SEP || c == SEP2
-}
-
-/// Returns whether the given char is a path separator.
-/// Only allows the primary separator '\'; use is_sep to allow '/'.
-#[inline]
-pub fn is_sep_verbatim(c: char) -> bool {
- c == SEP
-}
-
-/// Returns whether the given byte is a path separator.
-/// Allows both the primary separator '\' and the alternative separator '/'.
-#[inline]
-pub fn is_sep_byte(u: &u8) -> bool {
- *u == SEP_BYTE || *u == SEP2_BYTE
-}
-
-/// Returns whether the given byte is a path separator.
-/// Only allows the primary separator '\'; use is_sep_byte to allow '/'.
-#[inline]
-pub fn is_sep_byte_verbatim(u: &u8) -> bool {
- *u == SEP_BYTE
-}
-
-/// Prefix types for Path
-#[derive(Copy, PartialEq, Clone, Debug)]
-pub enum PathPrefix {
- /// Prefix `\\?\`, uint is the length of the following component
- VerbatimPrefix(uint),
- /// Prefix `\\?\UNC\`, uints are the lengths of the UNC components
- VerbatimUNCPrefix(uint, uint),
- /// Prefix `\\?\C:\` (for any alphabetic character)
- VerbatimDiskPrefix,
- /// Prefix `\\.\`, uint is the length of the following component
- DeviceNSPrefix(uint),
- /// UNC prefix `\\server\share`, uints are the lengths of the server/share
- UNCPrefix(uint, uint),
- /// Prefix `C:` for any alphabetic character
- DiskPrefix
-}
-
-fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> {
- if path.starts_with("\\\\") {
- // \\
- path = &path[2..];
- if path.starts_with("?\\") {
- // \\?\
- path = &path[2..];
- if path.starts_with("UNC\\") {
- // \\?\UNC\server\share
- path = &path[4..];
- let (idx_a, idx_b) = match parse_two_comps(path, is_sep_verbatim) {
- Some(x) => x,
- None => (path.len(), 0)
- };
- return Some(VerbatimUNCPrefix(idx_a, idx_b));
- } else {
- // \\?\path
- let idx = path.find('\\');
- if idx == Some(2) && path.as_bytes()[1] == b':' {
- let c = path.as_bytes()[0];
- if c.is_ascii() && (c as char).is_alphabetic() {
- // \\?\C:\ path
- return Some(VerbatimDiskPrefix);
- }
- }
- let idx = idx.unwrap_or(path.len());
- return Some(VerbatimPrefix(idx));
- }
- } else if path.starts_with(".\\") {
- // \\.\path
- path = &path[2..];
- let idx = path.find('\\').unwrap_or(path.len());
- return Some(DeviceNSPrefix(idx));
- }
- match parse_two_comps(path, is_sep) {
- Some((idx_a, idx_b)) if idx_a > 0 && idx_b > 0 => {
- // \\server\share
- return Some(UNCPrefix(idx_a, idx_b));
- }
- _ => ()
- }
- } else if path.len() > 1 && path.as_bytes()[1] == b':' {
- // C:
- let c = path.as_bytes()[0];
- if c.is_ascii() && (c as char).is_alphabetic() {
- return Some(DiskPrefix);
- }
- }
- return None;
-
- fn parse_two_comps(mut path: &str, f: fn(char) -> bool) -> Option<(uint, uint)> {
- let idx_a = match path.find(f) {
- None => return None,
- Some(x) => x
- };
- path = &path[idx_a+1..];
- let idx_b = path.find(f).unwrap_or(path.len());
- Some((idx_a, idx_b))
- }
-}
-
-// None result means the string didn't need normalizing
-fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool, Option<Vec<&'a str>>) {
- let f: fn(char) -> bool = if !prefix_is_verbatim(prefix) {
- is_sep
- } else {
- is_sep_verbatim
- };
- let is_abs = s.len() > prefix_len(prefix) && f(s.char_at(prefix_len(prefix)));
- let s_ = &s[prefix_len(prefix)..];
- let s_ = if is_abs { &s_[1..] } else { s_ };
-
- if is_abs && s_.is_empty() {
- return (is_abs, match prefix {
- Some(DiskPrefix) | None => (if is_sep_verbatim(s.char_at(prefix_len(prefix))) { None }
- else { Some(vec![]) }),
- Some(_) => Some(vec![]), // need to trim the trailing separator
- });
- }
- let mut comps: Vec<&'a str> = vec![];
- let mut n_up = 0u;
- let mut changed = false;
- for comp in s_.split(f) {
- if comp.is_empty() { changed = true }
- else if comp == "." { changed = true }
- else if comp == ".." {
- let has_abs_prefix = match prefix {
- Some(DiskPrefix) => false,
- Some(_) => true,
- None => false
- };
- if (is_abs || has_abs_prefix) && comps.is_empty() { changed = true }
- else if comps.len() == n_up { comps.push(".."); n_up += 1 }
- else { comps.pop().unwrap(); changed = true }
- } else { comps.push(comp) }
- }
- if !changed && !prefix_is_verbatim(prefix) {
- changed = s.find(is_sep).is_some();
- }
- if changed {
- if comps.is_empty() && !is_abs && prefix.is_none() {
- if s == "." {
- return (is_abs, None);
- }
- comps.push(".");
- }
- (is_abs, Some(comps))
- } else {
- (is_abs, None)
- }
-}
-
-fn prefix_is_verbatim(p: Option<PathPrefix>) -> bool {
- match p {
- Some(VerbatimPrefix(_)) | Some(VerbatimUNCPrefix(_,_)) | Some(VerbatimDiskPrefix) => true,
- Some(DeviceNSPrefix(_)) => true, // not really sure, but I think so
- _ => false
- }
-}
-
-fn prefix_len(p: Option<PathPrefix>) -> uint {
- match p {
- None => 0,
- Some(VerbatimPrefix(x)) => 4 + x,
- Some(VerbatimUNCPrefix(x,y)) => 8 + x + 1 + y,
- Some(VerbatimDiskPrefix) => 6,
- Some(UNCPrefix(x,y)) => 2 + x + 1 + y,
- Some(DeviceNSPrefix(x)) => 4 + x,
- Some(DiskPrefix) => 2
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::PathPrefix::*;
- use super::parse_prefix;
- use super::*;
-
- use clone::Clone;
- use iter::IteratorExt;
- use option::Option::{self, Some, None};
- use path::GenericPath;
- use slice::{AsSlice, SliceExt};
- use str::Str;
- use string::ToString;
- use vec::Vec;
-
- macro_rules! t {
- (s: $path:expr, $exp:expr) => (
- {
- let path = $path;
- assert_eq!(path.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $exp:expr) => (
- {
- let path = $path;
- assert_eq!(path.as_vec(), $exp);
- }
- )
- }
-
- #[test]
- fn test_parse_prefix() {
- macro_rules! t {
- ($path:expr, $exp:expr) => (
- {
- let path = $path;
- let exp = $exp;
- let res = parse_prefix(path);
- assert_eq!(res, exp);
- }
- )
- }
-
- t!("\\\\SERVER\\share\\foo", Some(UNCPrefix(6,5)));
- t!("\\\\", None);
- t!("\\\\SERVER", None);
- t!("\\\\SERVER\\", None);
- t!("\\\\SERVER\\\\", None);
- t!("\\\\SERVER\\\\foo", None);
- t!("\\\\SERVER\\share", Some(UNCPrefix(6,5)));
- t!("\\\\SERVER/share/foo", Some(UNCPrefix(6,5)));
- t!("\\\\SERVER\\share/foo", Some(UNCPrefix(6,5)));
- t!("//SERVER/share/foo", None);
- t!("\\\\\\a\\b\\c", None);
- t!("\\\\?\\a\\b\\c", Some(VerbatimPrefix(1)));
- t!("\\\\?\\a/b/c", Some(VerbatimPrefix(5)));
- t!("//?/a/b/c", None);
- t!("\\\\.\\a\\b", Some(DeviceNSPrefix(1)));
- t!("\\\\.\\a/b", Some(DeviceNSPrefix(3)));
- t!("//./a/b", None);
- t!("\\\\?\\UNC\\server\\share\\foo", Some(VerbatimUNCPrefix(6,5)));
- t!("\\\\?\\UNC\\\\share\\foo", Some(VerbatimUNCPrefix(0,5)));
- t!("\\\\?\\UNC\\", Some(VerbatimUNCPrefix(0,0)));
- t!("\\\\?\\UNC\\server/share/foo", Some(VerbatimUNCPrefix(16,0)));
- t!("\\\\?\\UNC\\server", Some(VerbatimUNCPrefix(6,0)));
- t!("\\\\?\\UNC\\server\\", Some(VerbatimUNCPrefix(6,0)));
- t!("\\\\?\\UNC/server/share", Some(VerbatimPrefix(16)));
- t!("\\\\?\\UNC", Some(VerbatimPrefix(3)));
- t!("\\\\?\\C:\\a\\b.txt", Some(VerbatimDiskPrefix));
- t!("\\\\?\\z:\\", Some(VerbatimDiskPrefix));
- t!("\\\\?\\C:", Some(VerbatimPrefix(2)));
- t!("\\\\?\\C:a.txt", Some(VerbatimPrefix(7)));
- t!("\\\\?\\C:a\\b.txt", Some(VerbatimPrefix(3)));
- t!("\\\\?\\C:/a", Some(VerbatimPrefix(4)));
- t!("C:\\foo", Some(DiskPrefix));
- t!("z:/foo", Some(DiskPrefix));
- t!("d:", Some(DiskPrefix));
- t!("ab:", None);
- t!("ü:\\foo", None);
- t!("3:\\foo", None);
- t!(" :\\foo", None);
- t!("::\\foo", None);
- t!("\\\\?\\C:", Some(VerbatimPrefix(2)));
- t!("\\\\?\\z:\\", Some(VerbatimDiskPrefix));
- t!("\\\\?\\ab:\\", Some(VerbatimPrefix(3)));
- t!("\\\\?\\C:\\a", Some(VerbatimDiskPrefix));
- t!("\\\\?\\C:/a", Some(VerbatimPrefix(4)));
- t!("\\\\?\\C:\\a/b", Some(VerbatimDiskPrefix));
- }
-
- #[test]
- fn test_paths() {
- let empty: &[u8] = &[];
- t!(v: Path::new(empty), b".");
- t!(v: Path::new(b"\\"), b"\\");
- t!(v: Path::new(b"a\\b\\c"), b"a\\b\\c");
-
- t!(s: Path::new(""), ".");
- t!(s: Path::new("\\"), "\\");
- t!(s: Path::new("hi"), "hi");
- t!(s: Path::new("hi\\"), "hi");
- t!(s: Path::new("\\lib"), "\\lib");
- t!(s: Path::new("\\lib\\"), "\\lib");
- t!(s: Path::new("hi\\there"), "hi\\there");
- t!(s: Path::new("hi\\there.txt"), "hi\\there.txt");
- t!(s: Path::new("/"), "\\");
- t!(s: Path::new("hi/"), "hi");
- t!(s: Path::new("/lib"), "\\lib");
- t!(s: Path::new("/lib/"), "\\lib");
- t!(s: Path::new("hi/there"), "hi\\there");
-
- t!(s: Path::new("hi\\there\\"), "hi\\there");
- t!(s: Path::new("hi\\..\\there"), "there");
- t!(s: Path::new("hi/../there"), "there");
- t!(s: Path::new("..\\hi\\there"), "..\\hi\\there");
- t!(s: Path::new("\\..\\hi\\there"), "\\hi\\there");
- t!(s: Path::new("/../hi/there"), "\\hi\\there");
- t!(s: Path::new("foo\\.."), ".");
- t!(s: Path::new("\\foo\\.."), "\\");
- t!(s: Path::new("\\foo\\..\\.."), "\\");
- t!(s: Path::new("\\foo\\..\\..\\bar"), "\\bar");
- t!(s: Path::new("\\.\\hi\\.\\there\\."), "\\hi\\there");
- t!(s: Path::new("\\.\\hi\\.\\there\\.\\.."), "\\hi");
- t!(s: Path::new("foo\\..\\.."), "..");
- t!(s: Path::new("foo\\..\\..\\.."), "..\\..");
- t!(s: Path::new("foo\\..\\..\\bar"), "..\\bar");
-
- assert_eq!(Path::new(b"foo\\bar").into_vec(), b"foo\\bar");
- assert_eq!(Path::new(b"\\foo\\..\\..\\bar").into_vec(), b"\\bar");
-
- t!(s: Path::new("\\\\a"), "\\a");
- t!(s: Path::new("\\\\a\\"), "\\a");
- t!(s: Path::new("\\\\a\\b"), "\\\\a\\b");
- t!(s: Path::new("\\\\a\\b\\"), "\\\\a\\b");
- t!(s: Path::new("\\\\a\\b/"), "\\\\a\\b");
- t!(s: Path::new("\\\\\\b"), "\\b");
- t!(s: Path::new("\\\\a\\\\b"), "\\a\\b");
- t!(s: Path::new("\\\\a\\b\\c"), "\\\\a\\b\\c");
- t!(s: Path::new("\\\\server\\share/path"), "\\\\server\\share\\path");
- t!(s: Path::new("\\\\server/share/path"), "\\\\server\\share\\path");
- t!(s: Path::new("C:a\\b.txt"), "C:a\\b.txt");
- t!(s: Path::new("C:a/b.txt"), "C:a\\b.txt");
- t!(s: Path::new("z:\\a\\b.txt"), "Z:\\a\\b.txt");
- t!(s: Path::new("z:/a/b.txt"), "Z:\\a\\b.txt");
- t!(s: Path::new("ab:/a/b.txt"), "ab:\\a\\b.txt");
- t!(s: Path::new("C:\\"), "C:\\");
- t!(s: Path::new("C:"), "C:");
- t!(s: Path::new("q:"), "Q:");
- t!(s: Path::new("C:/"), "C:\\");
- t!(s: Path::new("C:\\foo\\.."), "C:\\");
- t!(s: Path::new("C:foo\\.."), "C:");
- t!(s: Path::new("C:\\a\\"), "C:\\a");
- t!(s: Path::new("C:\\a/"), "C:\\a");
- t!(s: Path::new("C:\\a\\b\\"), "C:\\a\\b");
- t!(s: Path::new("C:\\a\\b/"), "C:\\a\\b");
- t!(s: Path::new("C:a\\"), "C:a");
- t!(s: Path::new("C:a/"), "C:a");
- t!(s: Path::new("C:a\\b\\"), "C:a\\b");
- t!(s: Path::new("C:a\\b/"), "C:a\\b");
- t!(s: Path::new("\\\\?\\z:\\a\\b.txt"), "\\\\?\\z:\\a\\b.txt");
- t!(s: Path::new("\\\\?\\C:/a/b.txt"), "\\\\?\\C:/a/b.txt");
- t!(s: Path::new("\\\\?\\C:\\a/b.txt"), "\\\\?\\C:\\a/b.txt");
- t!(s: Path::new("\\\\?\\test\\a\\b.txt"), "\\\\?\\test\\a\\b.txt");
- t!(s: Path::new("\\\\?\\foo\\bar\\"), "\\\\?\\foo\\bar\\");
- t!(s: Path::new("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar");
- t!(s: Path::new("\\\\.\\"), "\\\\.\\");
- t!(s: Path::new("\\\\?\\UNC\\server\\share\\foo"), "\\\\?\\UNC\\server\\share\\foo");
- t!(s: Path::new("\\\\?\\UNC\\server/share"), "\\\\?\\UNC\\server/share\\");
- t!(s: Path::new("\\\\?\\UNC\\server"), "\\\\?\\UNC\\server\\");
- t!(s: Path::new("\\\\?\\UNC\\"), "\\\\?\\UNC\\\\");
- t!(s: Path::new("\\\\?\\UNC"), "\\\\?\\UNC");
-
- // I'm not sure whether \\.\foo/bar should normalize to \\.\foo\bar
- // as information is sparse and this isn't really googleable.
- // I'm going to err on the side of not normalizing it, as this skips the filesystem
- t!(s: Path::new("\\\\.\\foo/bar"), "\\\\.\\foo/bar");
- t!(s: Path::new("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar");
- }
-
- #[test]
- fn test_opt_paths() {
- assert!(Path::new_opt(b"foo\\bar\0") == None);
- assert!(Path::new_opt(b"foo\\bar\x80") == None);
- t!(v: Path::new_opt(b"foo\\bar").unwrap(), b"foo\\bar");
- assert!(Path::new_opt("foo\\bar\0") == None);
- t!(s: Path::new_opt("foo\\bar").unwrap(), "foo\\bar");
- }
-
- #[test]
- fn test_null_byte() {
- use thread::Thread;
- let result = Thread::scoped(move|| {
- Path::new(b"foo/bar\0")
- }).join();
- assert!(result.is_err());
-
- let result = Thread::scoped(move|| {
- Path::new("test").set_filename(b"f\0o")
- }).join();
- assert!(result.is_err());
-
- let result = Thread::scoped(move || {
- Path::new("test").push(b"f\0o");
- }).join();
- assert!(result.is_err());
- }
-
- #[test]
- #[should_fail]
- fn test_not_utf8_panics() {
- Path::new(b"hello\x80.txt");
- }
-
- #[test]
- fn test_display_str() {
- let path = Path::new("foo");
- assert_eq!(path.display().to_string(), "foo");
- let path = Path::new(b"\\");
- assert_eq!(path.filename_display().to_string(), "");
-
- let path = Path::new("foo");
- let mo = path.display().as_cow();
- assert_eq!(mo.as_slice(), "foo");
- let path = Path::new(b"\\");
- let mo = path.filename_display().as_cow();
- assert_eq!(mo.as_slice(), "");
- }
-
- #[test]
- fn test_display() {
- macro_rules! t {
- ($path:expr, $exp:expr, $expf:expr) => (
- {
- let path = Path::new($path);
- let f = format!("{}", path.display());
- assert_eq!(f, $exp);
- let f = format!("{}", path.filename_display());
- assert_eq!(f, $expf);
- }
- )
- }
-
- t!("foo", "foo", "foo");
- t!("foo\\bar", "foo\\bar", "bar");
- t!("\\", "\\", "");
- }
-
- #[test]
- fn test_components() {
- macro_rules! t {
- (s: $path:expr, $op:ident, $exp:expr) => (
- {
- let path = $path;
- let path = Path::new(path);
- assert_eq!(path.$op(), Some($exp));
- }
- );
- (s: $path:expr, $op:ident, $exp:expr, opt) => (
- {
- let path = $path;
- let path = Path::new(path);
- let left = path.$op();
- assert_eq!(left, $exp);
- }
- );
- (v: $path:expr, $op:ident, $exp:expr) => (
- {
- let path = $path;
- let path = Path::new(path);
- assert_eq!(path.$op(), $exp);
- }
- )
- }
-
- t!(v: b"a\\b\\c", filename, Some(b"c"));
- t!(s: "a\\b\\c", filename_str, "c");
- t!(s: "\\a\\b\\c", filename_str, "c");
- t!(s: "a", filename_str, "a");
- t!(s: "\\a", filename_str, "a");
- t!(s: ".", filename_str, None, opt);
- t!(s: "\\", filename_str, None, opt);
- t!(s: "..", filename_str, None, opt);
- t!(s: "..\\..", filename_str, None, opt);
- t!(s: "c:\\foo.txt", filename_str, "foo.txt");
- t!(s: "C:\\", filename_str, None, opt);
- t!(s: "C:", filename_str, None, opt);
- t!(s: "\\\\server\\share\\foo.txt", filename_str, "foo.txt");
- t!(s: "\\\\server\\share", filename_str, None, opt);
- t!(s: "\\\\server", filename_str, "server");
- t!(s: "\\\\?\\bar\\foo.txt", filename_str, "foo.txt");
- t!(s: "\\\\?\\bar", filename_str, None, opt);
- t!(s: "\\\\?\\", filename_str, None, opt);
- t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", filename_str, "foo.txt");
- t!(s: "\\\\?\\UNC\\server", filename_str, None, opt);
- t!(s: "\\\\?\\UNC\\", filename_str, None, opt);
- t!(s: "\\\\?\\C:\\foo.txt", filename_str, "foo.txt");
- t!(s: "\\\\?\\C:\\", filename_str, None, opt);
- t!(s: "\\\\?\\C:", filename_str, None, opt);
- t!(s: "\\\\?\\foo/bar", filename_str, None, opt);
- t!(s: "\\\\?\\C:/foo", filename_str, None, opt);
- t!(s: "\\\\.\\foo\\bar", filename_str, "bar");
- t!(s: "\\\\.\\foo", filename_str, None, opt);
- t!(s: "\\\\.\\foo/bar", filename_str, None, opt);
- t!(s: "\\\\.\\foo\\bar/baz", filename_str, "bar/baz");
- t!(s: "\\\\.\\", filename_str, None, opt);
- t!(s: "\\\\?\\a\\b\\", filename_str, "b");
-
- t!(v: b"a\\b\\c", dirname, b"a\\b");
- t!(s: "a\\b\\c", dirname_str, "a\\b");
- t!(s: "\\a\\b\\c", dirname_str, "\\a\\b");
- t!(s: "a", dirname_str, ".");
- t!(s: "\\a", dirname_str, "\\");
- t!(s: ".", dirname_str, ".");
- t!(s: "\\", dirname_str, "\\");
- t!(s: "..", dirname_str, "..");
- t!(s: "..\\..", dirname_str, "..\\..");
- t!(s: "c:\\foo.txt", dirname_str, "C:\\");
- t!(s: "C:\\", dirname_str, "C:\\");
- t!(s: "C:", dirname_str, "C:");
- t!(s: "C:foo.txt", dirname_str, "C:");
- t!(s: "\\\\server\\share\\foo.txt", dirname_str, "\\\\server\\share");
- t!(s: "\\\\server\\share", dirname_str, "\\\\server\\share");
- t!(s: "\\\\server", dirname_str, "\\");
- t!(s: "\\\\?\\bar\\foo.txt", dirname_str, "\\\\?\\bar");
- t!(s: "\\\\?\\bar", dirname_str, "\\\\?\\bar");
- t!(s: "\\\\?\\", dirname_str, "\\\\?\\");
- t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", dirname_str, "\\\\?\\UNC\\server\\share");
- t!(s: "\\\\?\\UNC\\server", dirname_str, "\\\\?\\UNC\\server\\");
- t!(s: "\\\\?\\UNC\\", dirname_str, "\\\\?\\UNC\\\\");
- t!(s: "\\\\?\\C:\\foo.txt", dirname_str, "\\\\?\\C:\\");
- t!(s: "\\\\?\\C:\\", dirname_str, "\\\\?\\C:\\");
- t!(s: "\\\\?\\C:", dirname_str, "\\\\?\\C:");
- t!(s: "\\\\?\\C:/foo/bar", dirname_str, "\\\\?\\C:/foo/bar");
- t!(s: "\\\\?\\foo/bar", dirname_str, "\\\\?\\foo/bar");
- t!(s: "\\\\.\\foo\\bar", dirname_str, "\\\\.\\foo");
- t!(s: "\\\\.\\foo", dirname_str, "\\\\.\\foo");
- t!(s: "\\\\?\\a\\b\\", dirname_str, "\\\\?\\a");
-
- t!(v: b"hi\\there.txt", filestem, Some(b"there"));
- t!(s: "hi\\there.txt", filestem_str, "there");
- t!(s: "hi\\there", filestem_str, "there");
- t!(s: "there.txt", filestem_str, "there");
- t!(s: "there", filestem_str, "there");
- t!(s: ".", filestem_str, None, opt);
- t!(s: "\\", filestem_str, None, opt);
- t!(s: "foo\\.bar", filestem_str, ".bar");
- t!(s: ".bar", filestem_str, ".bar");
- t!(s: "..bar", filestem_str, ".");
- t!(s: "hi\\there..txt", filestem_str, "there.");
- t!(s: "..", filestem_str, None, opt);
- t!(s: "..\\..", filestem_str, None, opt);
- // filestem is based on filename, so we don't need the full set of prefix tests
-
- t!(v: b"hi\\there.txt", extension, Some(b"txt"));
- t!(v: b"hi\\there", extension, None);
- t!(s: "hi\\there.txt", extension_str, Some("txt"), opt);
- t!(s: "hi\\there", extension_str, None, opt);
- t!(s: "there.txt", extension_str, Some("txt"), opt);
- t!(s: "there", extension_str, None, opt);
- t!(s: ".", extension_str, None, opt);
- t!(s: "\\", extension_str, None, opt);
- t!(s: "foo\\.bar", extension_str, None, opt);
- t!(s: ".bar", extension_str, None, opt);
- t!(s: "..bar", extension_str, Some("bar"), opt);
- t!(s: "hi\\there..txt", extension_str, Some("txt"), opt);
- t!(s: "..", extension_str, None, opt);
- t!(s: "..\\..", extension_str, None, opt);
- // extension is based on filename, so we don't need the full set of prefix tests
- }
-
- #[test]
- fn test_push() {
- macro_rules! t {
- (s: $path:expr, $join:expr) => (
- {
- let path = $path;
- let join = $join;
- let mut p1 = Path::new(path);
- let p2 = p1.clone();
- p1.push(join);
- assert_eq!(p1, p2.join(join));
- }
- )
- }
-
- t!(s: "a\\b\\c", "..");
- t!(s: "\\a\\b\\c", "d");
- t!(s: "a\\b", "c\\d");
- t!(s: "a\\b", "\\c\\d");
- // this is just a sanity-check test. push and join share an implementation,
- // so there's no need for the full set of prefix tests
-
- // we do want to check one odd case though to ensure the prefix is re-parsed
- let mut p = Path::new("\\\\?\\C:");
- assert_eq!(prefix(&p), Some(VerbatimPrefix(2)));
- p.push("foo");
- assert_eq!(prefix(&p), Some(VerbatimDiskPrefix));
- assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo"));
-
- // and another with verbatim non-normalized paths
- let mut p = Path::new("\\\\?\\C:\\a\\");
- p.push("foo");
- assert_eq!(p.as_str(), Some("\\\\?\\C:\\a\\foo"));
- }
-
- #[test]
- fn test_push_path() {
- macro_rules! t {
- (s: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- let push = Path::new($push);
- p.push(&push);
- assert_eq!(p.as_str(), Some($exp));
- }
- )
- }
-
- t!(s: "a\\b\\c", "d", "a\\b\\c\\d");
- t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d");
- t!(s: "a\\b", "c\\d", "a\\b\\c\\d");
- t!(s: "a\\b", "\\c\\d", "\\c\\d");
- t!(s: "a\\b", ".", "a\\b");
- t!(s: "a\\b", "..\\c", "a\\c");
- t!(s: "a\\b", "C:a.txt", "C:a.txt");
- t!(s: "a\\b", "..\\..\\..\\c", "..\\c");
- t!(s: "a\\b", "C:\\a.txt", "C:\\a.txt");
- t!(s: "C:\\a", "C:\\b.txt", "C:\\b.txt");
- t!(s: "C:\\a\\b\\c", "C:d", "C:\\a\\b\\c\\d");
- t!(s: "C:a\\b\\c", "C:d", "C:a\\b\\c\\d");
- t!(s: "C:a\\b", "..\\..\\..\\c", "C:..\\c");
- t!(s: "C:\\a\\b", "..\\..\\..\\c", "C:\\c");
- t!(s: "C:", r"a\b\c", r"C:a\b\c");
- t!(s: "C:", r"..\a", r"C:..\a");
- t!(s: "\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar");
- t!(s: "\\\\server\\share\\foo", "..\\..\\bar", "\\\\server\\share\\bar");
- t!(s: "\\\\server\\share\\foo", "C:baz", "C:baz");
- t!(s: "\\\\?\\C:\\a\\b", "C:c\\d", "\\\\?\\C:\\a\\b\\c\\d");
- t!(s: "\\\\?\\C:a\\b", "C:c\\d", "C:c\\d");
- t!(s: "\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d");
- t!(s: "\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz");
- t!(s: "\\\\?\\C:\\a\\b", "..\\..\\..\\c", "\\\\?\\C:\\a\\b\\..\\..\\..\\c");
- t!(s: "\\\\?\\foo\\bar", "..\\..\\c", "\\\\?\\foo\\bar\\..\\..\\c");
- t!(s: "\\\\?\\", "foo", "\\\\?\\\\foo");
- t!(s: "\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar");
- t!(s: "\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a");
- t!(s: "\\\\?\\UNC\\server\\share", "C:a", "C:a");
- t!(s: "\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\\\foo");
- t!(s: "C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share");
- t!(s: "\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz");
- t!(s: "\\\\.\\foo\\bar", "C:a", "C:a");
- // again, not sure about the following, but I'm assuming \\.\ should be verbatim
- t!(s: "\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar");
-
- t!(s: "\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one
- }
-
- #[test]
- fn test_push_many() {
- macro_rules! t {
- (s: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- p.push_many(&$push);
- assert_eq!(p.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $push:expr, $exp:expr) => (
- {
- let mut p = Path::new($path);
- p.push_many(&$push);
- assert_eq!(p.as_vec(), $exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
- t!(s: "a\\b\\c", ["d", "\\e"], "\\e");
- t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
- t!(s: "a\\b\\c", ["d".to_string(), "e".to_string()], "a\\b\\c\\d\\e");
- t!(v: b"a\\b\\c", [b"d", b"e"], b"a\\b\\c\\d\\e");
- t!(v: b"a\\b\\c", [b"d", b"\\e", b"f"], b"\\e\\f");
- t!(v: b"a\\b\\c", [b"d".to_vec(), b"e".to_vec()],
- b"a\\b\\c\\d\\e");
- }
-
- #[test]
- fn test_pop() {
- macro_rules! t {
- (s: $path:expr, $left:expr, $right:expr) => (
- {
- let pstr = $path;
- let mut p = Path::new(pstr);
- let result = p.pop();
- let left = $left;
- assert_eq!(p.as_str(), Some(left));
- assert_eq!(result, $right);
- }
- );
- (b: $path:expr, $left:expr, $right:expr) => (
- {
- let mut p = Path::new($path);
- let result = p.pop();
- assert_eq!(p.as_vec(), $left);
- assert_eq!(result, $right);
- }
- )
- }
-
- t!(s: "a\\b\\c", "a\\b", true);
- t!(s: "a", ".", true);
- t!(s: ".", ".", false);
- t!(s: "\\a", "\\", true);
- t!(s: "\\", "\\", false);
- t!(b: b"a\\b\\c", b"a\\b", true);
- t!(b: b"a", b".", true);
- t!(b: b".", b".", false);
- t!(b: b"\\a", b"\\", true);
- t!(b: b"\\", b"\\", false);
-
- t!(s: "C:\\a\\b", "C:\\a", true);
- t!(s: "C:\\a", "C:\\", true);
- t!(s: "C:\\", "C:\\", false);
- t!(s: "C:a\\b", "C:a", true);
- t!(s: "C:a", "C:", true);
- t!(s: "C:", "C:", false);
- t!(s: "\\\\server\\share\\a\\b", "\\\\server\\share\\a", true);
- t!(s: "\\\\server\\share\\a", "\\\\server\\share", true);
- t!(s: "\\\\server\\share", "\\\\server\\share", false);
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", true);
- t!(s: "\\\\?\\a\\b", "\\\\?\\a", true);
- t!(s: "\\\\?\\a", "\\\\?\\a", false);
- t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true);
- t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\", true);
- t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\", false);
- t!(s: "\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true);
- t!(s: "\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share", true);
- t!(s: "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false);
- t!(s: "\\\\.\\a\\b\\c", "\\\\.\\a\\b", true);
- t!(s: "\\\\.\\a\\b", "\\\\.\\a", true);
- t!(s: "\\\\.\\a", "\\\\.\\a", false);
-
- t!(s: "\\\\?\\a\\b\\", "\\\\?\\a", true);
- }
-
- #[test]
- fn test_root_path() {
- assert_eq!(Path::new("a\\b\\c").root_path(), None);
- assert_eq!(Path::new("\\a\\b\\c").root_path(), Some(Path::new("\\")));
- assert_eq!(Path::new("C:a").root_path(), Some(Path::new("C:")));
- assert_eq!(Path::new("C:\\a").root_path(), Some(Path::new("C:\\")));
- assert_eq!(Path::new("\\\\a\\b\\c").root_path(), Some(Path::new("\\\\a\\b")));
- assert_eq!(Path::new("\\\\?\\a\\b").root_path(), Some(Path::new("\\\\?\\a")));
- assert_eq!(Path::new("\\\\?\\C:\\a").root_path(), Some(Path::new("\\\\?\\C:\\")));
- assert_eq!(Path::new("\\\\?\\UNC\\a\\b\\c").root_path(),
- Some(Path::new("\\\\?\\UNC\\a\\b")));
- assert_eq!(Path::new("\\\\.\\a\\b").root_path(), Some(Path::new("\\\\.\\a")));
- }
-
- #[test]
- fn test_join() {
- t!(s: Path::new("a\\b\\c").join(".."), "a\\b");
- t!(s: Path::new("\\a\\b\\c").join("d"), "\\a\\b\\c\\d");
- t!(s: Path::new("a\\b").join("c\\d"), "a\\b\\c\\d");
- t!(s: Path::new("a\\b").join("\\c\\d"), "\\c\\d");
- t!(s: Path::new(".").join("a\\b"), "a\\b");
- t!(s: Path::new("\\").join("a\\b"), "\\a\\b");
- t!(v: Path::new(b"a\\b\\c").join(b".."), b"a\\b");
- t!(v: Path::new(b"\\a\\b\\c").join(b"d"), b"\\a\\b\\c\\d");
- // full join testing is covered under test_push_path, so no need for
- // the full set of prefix tests
- }
-
- #[test]
- fn test_join_path() {
- macro_rules! t {
- (s: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let join = Path::new($join);
- let res = path.join(&join);
- assert_eq!(res.as_str(), Some($exp));
- }
- )
- }
-
- t!(s: "a\\b\\c", "..", "a\\b");
- t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d");
- t!(s: "a\\b", "c\\d", "a\\b\\c\\d");
- t!(s: "a\\b", "\\c\\d", "\\c\\d");
- t!(s: ".", "a\\b", "a\\b");
- t!(s: "\\", "a\\b", "\\a\\b");
- // join is implemented using push, so there's no need for
- // the full set of prefix tests
- }
-
- #[test]
- fn test_join_many() {
- macro_rules! t {
- (s: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let res = path.join_many(&$join);
- assert_eq!(res.as_str(), Some($exp));
- }
- );
- (v: $path:expr, $join:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let res = path.join_many(&$join);
- assert_eq!(res.as_vec(), $exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e");
- t!(s: "a\\b\\c", ["..", "d"], "a\\b\\d");
- t!(s: "a\\b\\c", ["d", "\\e", "f"], "\\e\\f");
- t!(s: "a\\b\\c", ["d".to_string(), "e".to_string()], "a\\b\\c\\d\\e");
- t!(v: b"a\\b\\c", [b"d", b"e"], b"a\\b\\c\\d\\e");
- t!(v: b"a\\b\\c", [b"d".to_vec(), b"e".to_vec()],
- b"a\\b\\c\\d\\e");
- }
-
- #[test]
- fn test_with_helpers() {
- macro_rules! t {
- (s: $path:expr, $op:ident, $arg:expr, $res:expr) => (
- {
- let pstr = $path;
- let path = Path::new(pstr);
- let arg = $arg;
- let res = path.$op(arg);
- let exp = Path::new($res);
- assert_eq!(res, exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", with_filename, "d", "a\\b\\d");
- t!(s: ".", with_filename, "foo", "foo");
- t!(s: "\\a\\b\\c", with_filename, "d", "\\a\\b\\d");
- t!(s: "\\", with_filename, "foo", "\\foo");
- t!(s: "\\a", with_filename, "foo", "\\foo");
- t!(s: "foo", with_filename, "bar", "bar");
- t!(s: "\\", with_filename, "foo\\", "\\foo");
- t!(s: "\\a", with_filename, "foo\\", "\\foo");
- t!(s: "a\\b\\c", with_filename, "", "a\\b");
- t!(s: "a\\b\\c", with_filename, ".", "a\\b");
- t!(s: "a\\b\\c", with_filename, "..", "a");
- t!(s: "\\a", with_filename, "", "\\");
- t!(s: "foo", with_filename, "", ".");
- t!(s: "a\\b\\c", with_filename, "d\\e", "a\\b\\d\\e");
- t!(s: "a\\b\\c", with_filename, "\\d", "a\\b\\d");
- t!(s: "..", with_filename, "foo", "..\\foo");
- t!(s: "..\\..", with_filename, "foo", "..\\..\\foo");
- t!(s: "..", with_filename, "", "..");
- t!(s: "..\\..", with_filename, "", "..\\..");
- t!(s: "C:\\foo\\bar", with_filename, "baz", "C:\\foo\\baz");
- t!(s: "C:\\foo", with_filename, "bar", "C:\\bar");
- t!(s: "C:\\", with_filename, "foo", "C:\\foo");
- t!(s: "C:foo\\bar", with_filename, "baz", "C:foo\\baz");
- t!(s: "C:foo", with_filename, "bar", "C:bar");
- t!(s: "C:", with_filename, "foo", "C:foo");
- t!(s: "C:\\foo", with_filename, "", "C:\\");
- t!(s: "C:foo", with_filename, "", "C:");
- t!(s: "C:\\foo\\bar", with_filename, "..", "C:\\");
- t!(s: "C:\\foo", with_filename, "..", "C:\\");
- t!(s: "C:\\", with_filename, "..", "C:\\");
- t!(s: "C:foo\\bar", with_filename, "..", "C:");
- t!(s: "C:foo", with_filename, "..", "C:..");
- t!(s: "C:", with_filename, "..", "C:..");
- t!(s: "\\\\server\\share\\foo", with_filename, "bar", "\\\\server\\share\\bar");
- t!(s: "\\\\server\\share", with_filename, "foo", "\\\\server\\share\\foo");
- t!(s: "\\\\server\\share\\foo", with_filename, "", "\\\\server\\share");
- t!(s: "\\\\server\\share", with_filename, "", "\\\\server\\share");
- t!(s: "\\\\server\\share\\foo", with_filename, "..", "\\\\server\\share");
- t!(s: "\\\\server\\share", with_filename, "..", "\\\\server\\share");
- t!(s: "\\\\?\\C:\\foo\\bar", with_filename, "baz", "\\\\?\\C:\\foo\\baz");
- t!(s: "\\\\?\\C:\\foo", with_filename, "bar", "\\\\?\\C:\\bar");
- t!(s: "\\\\?\\C:\\", with_filename, "foo", "\\\\?\\C:\\foo");
- t!(s: "\\\\?\\C:\\foo", with_filename, "..", "\\\\?\\C:\\..");
- t!(s: "\\\\?\\foo\\bar", with_filename, "baz", "\\\\?\\foo\\baz");
- t!(s: "\\\\?\\foo", with_filename, "bar", "\\\\?\\foo\\bar");
- t!(s: "\\\\?\\", with_filename, "foo", "\\\\?\\\\foo");
- t!(s: "\\\\?\\foo\\bar", with_filename, "..", "\\\\?\\foo\\..");
- t!(s: "\\\\.\\foo\\bar", with_filename, "baz", "\\\\.\\foo\\baz");
- t!(s: "\\\\.\\foo", with_filename, "bar", "\\\\.\\foo\\bar");
- t!(s: "\\\\.\\foo\\bar", with_filename, "..", "\\\\.\\foo\\..");
-
- t!(s: "hi\\there.txt", with_extension, "exe", "hi\\there.exe");
- t!(s: "hi\\there.txt", with_extension, "", "hi\\there");
- t!(s: "hi\\there.txt", with_extension, ".", "hi\\there..");
- t!(s: "hi\\there.txt", with_extension, "..", "hi\\there...");
- t!(s: "hi\\there", with_extension, "txt", "hi\\there.txt");
- t!(s: "hi\\there", with_extension, ".", "hi\\there..");
- t!(s: "hi\\there", with_extension, "..", "hi\\there...");
- t!(s: "hi\\there.", with_extension, "txt", "hi\\there.txt");
- t!(s: "hi\\.foo", with_extension, "txt", "hi\\.foo.txt");
- t!(s: "hi\\there.txt", with_extension, ".foo", "hi\\there..foo");
- t!(s: "\\", with_extension, "txt", "\\");
- t!(s: "\\", with_extension, ".", "\\");
- t!(s: "\\", with_extension, "..", "\\");
- t!(s: ".", with_extension, "txt", ".");
- // extension setter calls filename setter internally, no need for extended tests
- }
-
- #[test]
- fn test_setters() {
- macro_rules! t {
- (s: $path:expr, $set:ident, $with:ident, $arg:expr) => (
- {
- let path = $path;
- let arg = $arg;
- let mut p1 = Path::new(path);
- p1.$set(arg);
- let p2 = Path::new(path);
- assert_eq!(p1, p2.$with(arg));
- }
- );
- (v: $path:expr, $set:ident, $with:ident, $arg:expr) => (
- {
- let path = $path;
- let arg = $arg;
- let mut p1 = Path::new(path);
- p1.$set(arg);
- let p2 = Path::new(path);
- assert_eq!(p1, p2.$with(arg));
- }
- )
- }
-
- t!(v: b"a\\b\\c", set_filename, with_filename, b"d");
- t!(v: b"\\", set_filename, with_filename, b"foo");
- t!(s: "a\\b\\c", set_filename, with_filename, "d");
- t!(s: "\\", set_filename, with_filename, "foo");
- t!(s: ".", set_filename, with_filename, "foo");
- t!(s: "a\\b", set_filename, with_filename, "");
- t!(s: "a", set_filename, with_filename, "");
-
- t!(v: b"hi\\there.txt", set_extension, with_extension, b"exe");
- t!(s: "hi\\there.txt", set_extension, with_extension, "exe");
- t!(s: "hi\\there.", set_extension, with_extension, "txt");
- t!(s: "hi\\there", set_extension, with_extension, "txt");
- t!(s: "hi\\there.txt", set_extension, with_extension, "");
- t!(s: "hi\\there", set_extension, with_extension, "");
- t!(s: ".", set_extension, with_extension, "txt");
-
- // with_ helpers use the setter internally, so the tests for the with_ helpers
- // will suffice. No need for the full set of prefix tests.
- }
-
- #[test]
- fn test_getters() {
- macro_rules! t {
- (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
- {
- let path = $path;
- assert_eq!(path.filename_str(), $filename);
- assert_eq!(path.dirname_str(), $dirname);
- assert_eq!(path.filestem_str(), $filestem);
- assert_eq!(path.extension_str(), $ext);
- }
- );
- (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => (
- {
- let path = $path;
- assert_eq!(path.filename(), $filename);
- assert_eq!(path.dirname(), $dirname);
- assert_eq!(path.filestem(), $filestem);
- assert_eq!(path.extension(), $ext);
- }
- )
- }
-
- t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), None);
- t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None);
- t!(s: Path::new("."), None, Some("."), None, None);
- t!(s: Path::new("\\"), None, Some("\\"), None, None);
- t!(s: Path::new(".."), None, Some(".."), None, None);
- t!(s: Path::new("..\\.."), None, Some("..\\.."), None, None);
- t!(s: Path::new("hi\\there.txt"), Some("there.txt"), Some("hi"),
- Some("there"), Some("txt"));
- t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), None);
- t!(s: Path::new("hi\\there."), Some("there."), Some("hi"),
- Some("there"), Some(""));
- t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None);
- t!(s: Path::new("hi\\..there"), Some("..there"), Some("hi"),
- Some("."), Some("there"));
-
- // these are already tested in test_components, so no need for extended tests
- }
-
- #[test]
- fn test_dir_path() {
- t!(s: Path::new("hi\\there").dir_path(), "hi");
- t!(s: Path::new("hi").dir_path(), ".");
- t!(s: Path::new("\\hi").dir_path(), "\\");
- t!(s: Path::new("\\").dir_path(), "\\");
- t!(s: Path::new("..").dir_path(), "..");
- t!(s: Path::new("..\\..").dir_path(), "..\\..");
-
- // dir_path is just dirname interpreted as a path.
- // No need for extended tests
- }
-
- #[test]
- fn test_is_absolute() {
- macro_rules! t {
- ($path:expr, $abs:expr, $vol:expr, $cwd:expr, $rel:expr) => (
- {
- let path = Path::new($path);
- let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel);
- assert_eq!(path.is_absolute(), abs);
- assert_eq!(is_vol_relative(&path), vol);
- assert_eq!(is_cwd_relative(&path), cwd);
- assert_eq!(path.is_relative(), rel);
- }
- )
- }
- t!("a\\b\\c", false, false, false, true);
- t!("\\a\\b\\c", false, true, false, false);
- t!("a", false, false, false, true);
- t!("\\a", false, true, false, false);
- t!(".", false, false, false, true);
- t!("\\", false, true, false, false);
- t!("..", false, false, false, true);
- t!("..\\..", false, false, false, true);
- t!("C:a\\b.txt", false, false, true, false);
- t!("C:\\a\\b.txt", true, false, false, false);
- t!("\\\\server\\share\\a\\b.txt", true, false, false, false);
- t!("\\\\?\\a\\b\\c.txt", true, false, false, false);
- t!("\\\\?\\C:\\a\\b.txt", true, false, false, false);
- t!("\\\\?\\C:a\\b.txt", true, false, false, false); // NB: not equivalent to C:a\b.txt
- t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false, false);
- t!("\\\\.\\a\\b", true, false, false, false);
- }
-
- #[test]
- fn test_is_ancestor_of() {
- macro_rules! t {
- (s: $path:expr, $dest:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let dest = Path::new($dest);
- let exp = $exp;
- let res = path.is_ancestor_of(&dest);
- assert_eq!(res, exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", "a\\b\\c\\d", true);
- t!(s: "a\\b\\c", "a\\b\\c", true);
- t!(s: "a\\b\\c", "a\\b", false);
- t!(s: "\\a\\b\\c", "\\a\\b\\c", true);
- t!(s: "\\a\\b", "\\a\\b\\c", true);
- t!(s: "\\a\\b\\c\\d", "\\a\\b\\c", false);
- t!(s: "\\a\\b", "a\\b\\c", false);
- t!(s: "a\\b", "\\a\\b\\c", false);
- t!(s: "a\\b\\c", "a\\b\\d", false);
- t!(s: "..\\a\\b\\c", "a\\b\\c", false);
- t!(s: "a\\b\\c", "..\\a\\b\\c", false);
- t!(s: "a\\b\\c", "a\\b\\cd", false);
- t!(s: "a\\b\\cd", "a\\b\\c", false);
- t!(s: "..\\a\\b", "..\\a\\b\\c", true);
- t!(s: ".", "a\\b", true);
- t!(s: ".", ".", true);
- t!(s: "\\", "\\", true);
- t!(s: "\\", "\\a\\b", true);
- t!(s: "..", "a\\b", true);
- t!(s: "..\\..", "a\\b", true);
- t!(s: "foo\\bar", "foobar", false);
- t!(s: "foobar", "foo\\bar", false);
-
- t!(s: "foo", "C:foo", false);
- t!(s: "C:foo", "foo", false);
- t!(s: "C:foo", "C:foo\\bar", true);
- t!(s: "C:foo\\bar", "C:foo", false);
- t!(s: "C:\\foo", "C:\\foo\\bar", true);
- t!(s: "C:", "C:", true);
- t!(s: "C:", "C:\\", false);
- t!(s: "C:\\", "C:", false);
- t!(s: "C:\\", "C:\\", true);
- t!(s: "C:\\foo\\bar", "C:\\foo", false);
- t!(s: "C:foo\\bar", "C:foo", false);
- t!(s: "C:\\foo", "\\foo", false);
- t!(s: "\\foo", "C:\\foo", false);
- t!(s: "\\\\server\\share\\foo", "\\\\server\\share\\foo\\bar", true);
- t!(s: "\\\\server\\share", "\\\\server\\share\\foo", true);
- t!(s: "\\\\server\\share\\foo", "\\\\server\\share", false);
- t!(s: "C:\\foo", "\\\\server\\share\\foo", false);
- t!(s: "\\\\server\\share\\foo", "C:\\foo", false);
- t!(s: "\\\\?\\foo\\bar", "\\\\?\\foo\\bar\\baz", true);
- t!(s: "\\\\?\\foo\\bar\\baz", "\\\\?\\foo\\bar", false);
- t!(s: "\\\\?\\foo\\bar", "\\foo\\bar\\baz", false);
- t!(s: "\\foo\\bar", "\\\\?\\foo\\bar\\baz", false);
- t!(s: "\\\\?\\C:\\foo\\bar", "\\\\?\\C:\\foo\\bar\\baz", true);
- t!(s: "\\\\?\\C:\\foo\\bar\\baz", "\\\\?\\C:\\foo\\bar", false);
- t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\foo", true);
- t!(s: "\\\\?\\C:", "\\\\?\\C:\\", false); // this is a weird one
- t!(s: "\\\\?\\C:\\", "\\\\?\\C:", false);
- t!(s: "\\\\?\\C:\\a", "\\\\?\\c:\\a\\b", true);
- t!(s: "\\\\?\\c:\\a", "\\\\?\\C:\\a\\b", true);
- t!(s: "\\\\?\\C:\\a", "\\\\?\\D:\\a\\b", false);
- t!(s: "\\\\?\\foo", "\\\\?\\foobar", false);
- t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\c", true);
- t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\", true);
- t!(s: "\\\\?\\a\\b\\", "\\\\?\\a\\b", true);
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", false);
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b\\", false);
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b\\c\\d", true);
- t!(s: "\\\\?\\UNC\\a\\b\\c\\d", "\\\\?\\UNC\\a\\b\\c", false);
- t!(s: "\\\\?\\UNC\\a\\b", "\\\\?\\UNC\\a\\b\\c", true);
- t!(s: "\\\\.\\foo\\bar", "\\\\.\\foo\\bar\\baz", true);
- t!(s: "\\\\.\\foo\\bar\\baz", "\\\\.\\foo\\bar", false);
- t!(s: "\\\\.\\foo", "\\\\.\\foo\\bar", true);
- t!(s: "\\\\.\\foo", "\\\\.\\foobar", false);
-
- t!(s: "\\a\\b", "\\\\?\\a\\b", false);
- t!(s: "\\\\?\\a\\b", "\\a\\b", false);
- t!(s: "\\a\\b", "\\\\?\\C:\\a\\b", false);
- t!(s: "\\\\?\\C:\\a\\b", "\\a\\b", false);
- t!(s: "Z:\\a\\b", "\\\\?\\z:\\a\\b", true);
- t!(s: "C:\\a\\b", "\\\\?\\D:\\a\\b", false);
- t!(s: "a\\b", "\\\\?\\a\\b", false);
- t!(s: "\\\\?\\a\\b", "a\\b", false);
- t!(s: "C:\\a\\b", "\\\\?\\C:\\a\\b", true);
- t!(s: "\\\\?\\C:\\a\\b", "C:\\a\\b", true);
- t!(s: "C:a\\b", "\\\\?\\C:\\a\\b", false);
- t!(s: "C:a\\b", "\\\\?\\C:a\\b", false);
- t!(s: "\\\\?\\C:\\a\\b", "C:a\\b", false);
- t!(s: "\\\\?\\C:a\\b", "C:a\\b", false);
- t!(s: "C:\\a\\b", "\\\\?\\C:\\a\\b\\", true);
- t!(s: "\\\\?\\C:\\a\\b\\", "C:\\a\\b", true);
- t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\b\\c", true);
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\b\\c", true);
- }
-
- #[test]
- fn test_ends_with_path() {
- macro_rules! t {
- (s: $path:expr, $child:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let child = Path::new($child);
- assert_eq!(path.ends_with_path(&child), $exp);
- }
- );
- }
-
- t!(s: "a\\b\\c", "c", true);
- t!(s: "a\\b\\c", "d", false);
- t!(s: "foo\\bar\\quux", "bar", false);
- t!(s: "foo\\bar\\quux", "barquux", false);
- t!(s: "a\\b\\c", "b\\c", true);
- t!(s: "a\\b\\c", "a\\b\\c", true);
- t!(s: "a\\b\\c", "foo\\a\\b\\c", false);
- t!(s: "\\a\\b\\c", "a\\b\\c", true);
- t!(s: "\\a\\b\\c", "\\a\\b\\c", false); // child must be relative
- t!(s: "\\a\\b\\c", "foo\\a\\b\\c", false);
- t!(s: "a\\b\\c", "", false);
- t!(s: "", "", true);
- t!(s: "\\a\\b\\c", "d\\e\\f", false);
- t!(s: "a\\b\\c", "a\\b", false);
- t!(s: "a\\b\\c", "b", false);
- t!(s: "C:\\a\\b", "b", true);
- t!(s: "C:\\a\\b", "C:b", false);
- t!(s: "C:\\a\\b", "C:a\\b", false);
- }
-
- #[test]
- fn test_path_relative_from() {
- macro_rules! t {
- (s: $path:expr, $other:expr, $exp:expr) => (
- {
- assert_eq!(Path::new($path).path_relative_from(&Path::new($other))
- .as_ref().and_then(|x| x.as_str()), $exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", "a\\b", Some("c"));
- t!(s: "a\\b\\c", "a\\b\\d", Some("..\\c"));
- t!(s: "a\\b\\c", "a\\b\\c\\d", Some(".."));
- t!(s: "a\\b\\c", "a\\b\\c", Some("."));
- t!(s: "a\\b\\c", "a\\b\\c\\d\\e", Some("..\\.."));
- t!(s: "a\\b\\c", "a\\d\\e", Some("..\\..\\b\\c"));
- t!(s: "a\\b\\c", "d\\e\\f", Some("..\\..\\..\\a\\b\\c"));
- t!(s: "a\\b\\c", "\\a\\b\\c", None);
- t!(s: "\\a\\b\\c", "a\\b\\c", Some("\\a\\b\\c"));
- t!(s: "\\a\\b\\c", "\\a\\b\\c\\d", Some(".."));
- t!(s: "\\a\\b\\c", "\\a\\b", Some("c"));
- t!(s: "\\a\\b\\c", "\\a\\b\\c\\d\\e", Some("..\\.."));
- t!(s: "\\a\\b\\c", "\\a\\d\\e", Some("..\\..\\b\\c"));
- t!(s: "\\a\\b\\c", "\\d\\e\\f", Some("..\\..\\..\\a\\b\\c"));
- t!(s: "hi\\there.txt", "hi\\there", Some("..\\there.txt"));
- t!(s: ".", "a", Some(".."));
- t!(s: ".", "a\\b", Some("..\\.."));
- t!(s: ".", ".", Some("."));
- t!(s: "a", ".", Some("a"));
- t!(s: "a\\b", ".", Some("a\\b"));
- t!(s: "..", ".", Some(".."));
- t!(s: "a\\b\\c", "a\\b\\c", Some("."));
- t!(s: "\\a\\b\\c", "\\a\\b\\c", Some("."));
- t!(s: "\\", "\\", Some("."));
- t!(s: "\\", ".", Some("\\"));
- t!(s: "..\\..\\a", "b", Some("..\\..\\..\\a"));
- t!(s: "a", "..\\..\\b", None);
- t!(s: "..\\..\\a", "..\\..\\b", Some("..\\a"));
- t!(s: "..\\..\\a", "..\\..\\a\\b", Some(".."));
- t!(s: "..\\..\\a\\b", "..\\..\\a", Some("b"));
-
- t!(s: "C:a\\b\\c", "C:a\\b", Some("c"));
- t!(s: "C:a\\b", "C:a\\b\\c", Some(".."));
- t!(s: "C:" ,"C:a\\b", Some("..\\.."));
- t!(s: "C:a\\b", "C:c\\d", Some("..\\..\\a\\b"));
- t!(s: "C:a\\b", "D:c\\d", Some("C:a\\b"));
- t!(s: "C:a\\b", "C:..\\c", None);
- t!(s: "C:..\\a", "C:b\\c", Some("..\\..\\..\\a"));
- t!(s: "C:\\a\\b\\c", "C:\\a\\b", Some("c"));
- t!(s: "C:\\a\\b", "C:\\a\\b\\c", Some(".."));
- t!(s: "C:\\", "C:\\a\\b", Some("..\\.."));
- t!(s: "C:\\a\\b", "C:\\c\\d", Some("..\\..\\a\\b"));
- t!(s: "C:\\a\\b", "C:a\\b", Some("C:\\a\\b"));
- t!(s: "C:a\\b", "C:\\a\\b", None);
- t!(s: "\\a\\b", "C:\\a\\b", None);
- t!(s: "\\a\\b", "C:a\\b", None);
- t!(s: "a\\b", "C:\\a\\b", None);
- t!(s: "a\\b", "C:a\\b", None);
-
- t!(s: "\\\\a\\b\\c", "\\\\a\\b", Some("c"));
- t!(s: "\\\\a\\b", "\\\\a\\b\\c", Some(".."));
- t!(s: "\\\\a\\b\\c\\e", "\\\\a\\b\\c\\d", Some("..\\e"));
- t!(s: "\\\\a\\c\\d", "\\\\a\\b\\d", Some("\\\\a\\c\\d"));
- t!(s: "\\\\b\\c\\d", "\\\\a\\c\\d", Some("\\\\b\\c\\d"));
- t!(s: "\\\\a\\b\\c", "\\d\\e", Some("\\\\a\\b\\c"));
- t!(s: "\\d\\e", "\\\\a\\b\\c", None);
- t!(s: "d\\e", "\\\\a\\b\\c", None);
- t!(s: "C:\\a\\b\\c", "\\\\a\\b\\c", Some("C:\\a\\b\\c"));
- t!(s: "C:\\c", "\\\\a\\b\\c", Some("C:\\c"));
-
- t!(s: "\\\\?\\a\\b", "\\a\\b", Some("\\\\?\\a\\b"));
- t!(s: "\\\\?\\a\\b", "a\\b", Some("\\\\?\\a\\b"));
- t!(s: "\\\\?\\a\\b", "\\b", Some("\\\\?\\a\\b"));
- t!(s: "\\\\?\\a\\b", "b", Some("\\\\?\\a\\b"));
- t!(s: "\\\\?\\a\\b", "\\\\?\\a\\b\\c", Some(".."));
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", Some("c"));
- t!(s: "\\\\?\\a\\b", "\\\\?\\c\\d", Some("\\\\?\\a\\b"));
- t!(s: "\\\\?\\a", "\\\\?\\b", Some("\\\\?\\a"));
-
- t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", Some("b"));
- t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\a\\b", Some(".."));
- t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\b", Some("..\\a"));
- t!(s: "\\\\?\\C:\\a", "\\\\?\\D:\\a", Some("\\\\?\\C:\\a"));
- t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\c:\\a", Some("b"));
- t!(s: "\\\\?\\C:\\a\\b", "C:\\a", Some("b"));
- t!(s: "\\\\?\\C:\\a", "C:\\a\\b", Some(".."));
- t!(s: "C:\\a\\b", "\\\\?\\C:\\a", Some("b"));
- t!(s: "C:\\a", "\\\\?\\C:\\a\\b", Some(".."));
- t!(s: "\\\\?\\C:\\a", "D:\\a", Some("\\\\?\\C:\\a"));
- t!(s: "\\\\?\\c:\\a\\b", "C:\\a", Some("b"));
- t!(s: "\\\\?\\C:\\a\\b", "C:a\\b", Some("\\\\?\\C:\\a\\b"));
- t!(s: "\\\\?\\C:\\a\\.\\b", "C:\\a", Some("\\\\?\\C:\\a\\.\\b"));
- t!(s: "\\\\?\\C:\\a\\b/c", "C:\\a", Some("\\\\?\\C:\\a\\b/c"));
- t!(s: "\\\\?\\C:\\a\\..\\b", "C:\\a", Some("\\\\?\\C:\\a\\..\\b"));
- t!(s: "C:a\\b", "\\\\?\\C:\\a\\b", None);
- t!(s: "\\\\?\\C:\\a\\.\\b", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\.\\b"));
- t!(s: "\\\\?\\C:\\a\\b/c", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\b/c"));
- t!(s: "\\\\?\\C:\\a\\..\\b", "\\\\?\\C:\\a", Some("\\\\?\\C:\\a\\..\\b"));
- t!(s: "\\\\?\\C:\\a\\b\\", "\\\\?\\C:\\a", Some("b"));
- t!(s: "\\\\?\\C:\\.\\b", "\\\\?\\C:\\.", Some("b"));
- t!(s: "C:\\b", "\\\\?\\C:\\.", Some("..\\b"));
- t!(s: "\\\\?\\a\\.\\b\\c", "\\\\?\\a\\.\\b", Some("c"));
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\.\\d", Some("..\\..\\b\\c"));
- t!(s: "\\\\?\\a\\..\\b", "\\\\?\\a\\..", Some("b"));
- t!(s: "\\\\?\\a\\b\\..", "\\\\?\\a\\b", Some("\\\\?\\a\\b\\.."));
- t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\..\\b", Some("..\\..\\b\\c"));
-
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b", Some("c"));
- t!(s: "\\\\?\\UNC\\a\\b", "\\\\?\\UNC\\a\\b\\c", Some(".."));
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\c\\d", Some("\\\\?\\UNC\\a\\b\\c"));
- t!(s: "\\\\?\\UNC\\b\\c\\d", "\\\\?\\UNC\\a\\c\\d", Some("\\\\?\\UNC\\b\\c\\d"));
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\a\\b\\c", Some("\\\\?\\UNC\\a\\b\\c"));
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\?\\C:\\a\\b\\c", Some("\\\\?\\UNC\\a\\b\\c"));
- t!(s: "\\\\?\\UNC\\a\\b\\c/d", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\c/d"));
- t!(s: "\\\\?\\UNC\\a\\b\\.", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\."));
- t!(s: "\\\\?\\UNC\\a\\b\\..", "\\\\?\\UNC\\a\\b", Some("\\\\?\\UNC\\a\\b\\.."));
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\b", Some("c"));
- t!(s: "\\\\?\\UNC\\a\\b", "\\\\a\\b\\c", Some(".."));
- t!(s: "\\\\?\\UNC\\a\\b\\c", "\\\\a\\c\\d", Some("\\\\?\\UNC\\a\\b\\c"));
- t!(s: "\\\\?\\UNC\\b\\c\\d", "\\\\a\\c\\d", Some("\\\\?\\UNC\\b\\c\\d"));
- t!(s: "\\\\?\\UNC\\a\\b\\.", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\."));
- t!(s: "\\\\?\\UNC\\a\\b\\c/d", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\c/d"));
- t!(s: "\\\\?\\UNC\\a\\b\\..", "\\\\a\\b", Some("\\\\?\\UNC\\a\\b\\.."));
- t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\b", Some("c"));
- t!(s: "\\\\a\\b\\c", "\\\\?\\UNC\\a\\c\\d", Some("\\\\a\\b\\c"));
- }
-
- #[test]
- fn test_str_components() {
- macro_rules! t {
- (s: $path:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let comps = path.str_components().map(|x|x.unwrap())
- .collect::<Vec<&str>>();
- let exp: &[&str] = &$exp;
- assert_eq!(comps, exp);
- let comps = path.str_components().rev().map(|x|x.unwrap())
- .collect::<Vec<&str>>();
- let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&str>>();
- assert_eq!(comps, exp);
- }
- );
- }
-
- t!(s: b"a\\b\\c", ["a", "b", "c"]);
- t!(s: "a\\b\\c", ["a", "b", "c"]);
- t!(s: "a\\b\\d", ["a", "b", "d"]);
- t!(s: "a\\b\\cd", ["a", "b", "cd"]);
- t!(s: "\\a\\b\\c", ["a", "b", "c"]);
- t!(s: "a", ["a"]);
- t!(s: "\\a", ["a"]);
- t!(s: "\\", []);
- t!(s: ".", ["."]);
- t!(s: "..", [".."]);
- t!(s: "..\\..", ["..", ".."]);
- t!(s: "..\\..\\foo", ["..", "..", "foo"]);
- t!(s: "C:foo\\bar", ["foo", "bar"]);
- t!(s: "C:foo", ["foo"]);
- t!(s: "C:", []);
- t!(s: "C:\\foo\\bar", ["foo", "bar"]);
- t!(s: "C:\\foo", ["foo"]);
- t!(s: "C:\\", []);
- t!(s: "\\\\server\\share\\foo\\bar", ["foo", "bar"]);
- t!(s: "\\\\server\\share\\foo", ["foo"]);
- t!(s: "\\\\server\\share", []);
- t!(s: "\\\\?\\foo\\bar\\baz", ["bar", "baz"]);
- t!(s: "\\\\?\\foo\\bar", ["bar"]);
- t!(s: "\\\\?\\foo", []);
- t!(s: "\\\\?\\", []);
- t!(s: "\\\\?\\a\\b", ["b"]);
- t!(s: "\\\\?\\a\\b\\", ["b"]);
- t!(s: "\\\\?\\foo\\bar\\\\baz", ["bar", "", "baz"]);
- t!(s: "\\\\?\\C:\\foo\\bar", ["foo", "bar"]);
- t!(s: "\\\\?\\C:\\foo", ["foo"]);
- t!(s: "\\\\?\\C:\\", []);
- t!(s: "\\\\?\\C:\\foo\\", ["foo"]);
- t!(s: "\\\\?\\UNC\\server\\share\\foo\\bar", ["foo", "bar"]);
- t!(s: "\\\\?\\UNC\\server\\share\\foo", ["foo"]);
- t!(s: "\\\\?\\UNC\\server\\share", []);
- t!(s: "\\\\.\\foo\\bar\\baz", ["bar", "baz"]);
- t!(s: "\\\\.\\foo\\bar", ["bar"]);
- t!(s: "\\\\.\\foo", []);
- }
-
- #[test]
- fn test_components_iter() {
- macro_rules! t {
- (s: $path:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let comps = path.components().collect::<Vec<&[u8]>>();
- let exp: &[&[u8]] = &$exp;
- assert_eq!(comps, exp);
- let comps = path.components().rev().collect::<Vec<&[u8]>>();
- let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>();
- assert_eq!(comps, exp);
- }
- )
- }
-
- t!(s: "a\\b\\c", [b"a", b"b", b"c"]);
- t!(s: ".", [b"."]);
- // since this is really a wrapper around str_components, those tests suffice
- }
-
- #[test]
- fn test_make_non_verbatim() {
- macro_rules! t {
- ($path:expr, $exp:expr) => (
- {
- let path = Path::new($path);
- let exp: Option<&str> = $exp;
- let exp = exp.map(|s| Path::new(s));
- assert_eq!(make_non_verbatim(&path), exp);
- }
- )
- }
-
- t!(r"\a\b\c", Some(r"\a\b\c"));
- t!(r"a\b\c", Some(r"a\b\c"));
- t!(r"C:\a\b\c", Some(r"C:\a\b\c"));
- t!(r"C:a\b\c", Some(r"C:a\b\c"));
- t!(r"\\server\share\foo", Some(r"\\server\share\foo"));
- t!(r"\\.\foo", None);
- t!(r"\\?\foo", None);
- t!(r"\\?\C:", None);
- t!(r"\\?\C:foo", None);
- t!(r"\\?\C:\", Some(r"C:\"));
- t!(r"\\?\C:\foo", Some(r"C:\foo"));
- t!(r"\\?\C:\foo\bar\baz", Some(r"C:\foo\bar\baz"));
- t!(r"\\?\C:\foo\.\bar\baz", None);
- t!(r"\\?\C:\foo\bar\..\baz", None);
- t!(r"\\?\C:\foo\bar\..", None);
- t!(r"\\?\UNC\server\share\foo", Some(r"\\server\share\foo"));
- t!(r"\\?\UNC\server\share", Some(r"\\server\share"));
- t!(r"\\?\UNC\server", None);
- t!(r"\\?\UNC\server\", None);
- }
-}
#[doc(no_inline)] pub use vec::Vec;
// NB: remove when path reform lands
-#[doc(no_inline)] pub use path::{Path, GenericPath};
+#[doc(no_inline)] pub use old_path::{Path, GenericPath};
// NB: remove when I/O reform lands
#[doc(no_inline)] pub use old_io::{Buffer, Writer, Reader, Seek, BufferPrelude};
// NB: remove when range syntax lands
use self::OsRngInner::*;
use old_io::{IoResult, File};
- use path::Path;
+ use old_path::Path;
use rand::Rng;
use rand::reader::ReaderRng;
use result::Result::Ok;
use sys::{last_error, retry};
use ffi::CString;
use num::Int;
-use path::BytesContainer;
+use old_path::BytesContainer;
use collections;
pub mod backtrace;
use string::{String, CowString};
use mem;
-#[derive(Clone)]
+#[derive(Clone, Hash)]
pub struct Buf {
pub inner: Vec<u8>
}
use libc::{self, pid_t, c_void, c_int};
use mem;
use os;
-use path::BytesContainer;
+use old_path::BytesContainer;
use ptr;
use sync::mpsc::{channel, Sender, Receiver};
use sys::fs::FileDesc;
use mem;
use ops::Drop;
use option::Option::{Some};
-use path::Path;
+use old_path::Path;
use ptr;
use result::Result::{Ok, Err};
use slice::SliceExt;
use option::Option;
use mem;
-#[derive(Clone)]
+#[derive(Clone, Hash)]
pub struct Buf {
pub inner: Wtf8Buf
}
use old_io::{IoResult, IoError};
use old_io;
use os;
-use path::BytesContainer;
+use old_path::BytesContainer;
use ptr;
use str;
use sync::{StaticMutex, MUTEX_INIT};
/// ISO 8601 time duration with nanosecond precision.
/// This also allows for the negative duration; see individual methods for details.
+#[unstable(feature = "std_misc")]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Duration {
secs: i64,
}
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
+#[unstable(feature = "std_misc")]
pub const MIN: Duration = Duration {
secs: i64::MIN / MILLIS_PER_SEC - 1,
nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
+#[unstable(feature = "std_misc")]
pub const MAX: Duration = Duration {
secs: i64::MAX / MILLIS_PER_SEC,
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60), with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn weeks(weeks: i64) -> Duration {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
Duration::seconds(secs)
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn days(days: i64) -> Duration {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
Duration::seconds(secs)
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn hours(hours: i64) -> Duration {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
Duration::seconds(secs)
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn minutes(minutes: i64) -> Duration {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
Duration::seconds(secs)
/// Panics when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn seconds(seconds: i64) -> Duration {
let d = Duration { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
/// Makes a new `Duration` with given number of milliseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn milliseconds(milliseconds: i64) -> Duration {
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
let nanos = millis as i32 * NANOS_PER_MILLI;
/// Makes a new `Duration` with given number of microseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn microseconds(microseconds: i64) -> Duration {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
let nanos = micros as i32 * NANOS_PER_MICRO;
/// Makes a new `Duration` with given number of nanoseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn nanoseconds(nanos: i64) -> Duration {
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
Duration { secs: secs, nanos: nanos as i32 }
/// Runs a closure, returning the duration of time it took to run the
/// closure.
+ #[unstable(feature = "std_misc")]
pub fn span<F>(f: F) -> Duration where F: FnOnce() {
let before = super::precise_time_ns();
f();
/// Returns the total number of whole weeks in the duration.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn num_weeks(&self) -> i64 {
self.num_days() / 7
}
/// Returns the total number of whole days in the duration.
+ #[unstable(feature = "std_misc")]
pub fn num_days(&self) -> i64 {
self.num_seconds() / SECS_PER_DAY
}
/// Returns the total number of whole hours in the duration.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn num_hours(&self) -> i64 {
self.num_seconds() / SECS_PER_HOUR
}
/// Returns the total number of whole minutes in the duration.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn num_minutes(&self) -> i64 {
self.num_seconds() / SECS_PER_MINUTE
}
/// Returns the total number of whole seconds in the duration.
+ #[unstable(feature = "std_misc")]
pub fn num_seconds(&self) -> i64 {
// If secs is negative, nanos should be subtracted from the duration.
if self.secs < 0 && self.nanos > 0 {
}
/// Returns the total number of whole milliseconds in the duration,
+ #[unstable(feature = "std_misc")]
pub fn num_milliseconds(&self) -> i64 {
// A proper Duration will not overflow, because MIN and MAX are defined
// such that the range is exactly i64 milliseconds.
/// Returns the total number of whole microseconds in the duration,
/// or `None` on overflow (exceeding 2^63 microseconds in either direction).
+ #[unstable(feature = "std_misc")]
pub fn num_microseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC));
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
/// Returns the total number of whole nanoseconds in the duration,
/// or `None` on overflow (exceeding 2^63 nanoseconds in either direction).
+ #[unstable(feature = "std_misc")]
pub fn num_nanoseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64));
let nanos_part = self.nanos_mod_sec();
}
/// Add two durations, returning `None` if overflow occured.
+ #[unstable(feature = "std_misc")]
pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
let mut nanos = self.nanos + rhs.nanos;
}
/// Subtract two durations, returning `None` if overflow occured.
+ #[unstable(feature = "std_misc")]
pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
let mut nanos = self.nanos - rhs.nanos;
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn min_value() -> Duration { MIN }
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn max_value() -> Duration { MAX }
/// A duration where the stored seconds and nanoseconds are equal to zero.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn zero() -> Duration {
Duration { secs: 0, nanos: 0 }
}
/// Returns `true` if the duration equals `Duration::zero()`.
#[inline]
+ #[unstable(feature = "std_misc")]
pub fn is_zero(&self) -> bool {
self.secs == 0 && self.nanos == 0
}
}
+#[unstable(feature = "std_misc")]
impl Neg for Duration {
type Output = Duration;
}
}
+#[unstable(feature = "std_misc")]
impl Add for Duration {
type Output = Duration;
}
}
+#[unstable(feature = "std_misc")]
impl Sub for Duration {
type Output = Duration;
}
}
+#[unstable(feature = "std_misc")]
impl Mul<i32> for Duration {
type Output = Duration;
}
}
+#[unstable(feature = "std_misc")]
impl Div<i32> for Duration {
type Output = Duration;
//! Temporal quantification.
+#![unstable(feature = "std_misc")]
+
use sys::time::SteadyTime;
pub use self::duration::Duration;
use std::fmt;
use std::mem;
use std::ops::Deref;
-use std::path::BytesContainer;
+use std::old_path::BytesContainer;
use std::rc::Rc;
#[allow(non_camel_case_types)]
out: out,
log_out: log_out,
use_color: use_color(opts),
- total: 0u,
- passed: 0u,
- failed: 0u,
- ignored: 0u,
- measured: 0u,
+ total: 0,
+ passed: 0,
+ failed: 0,
+ ignored: 0,
+ measured: 0,
metrics: MetricMap::new(),
failures: Vec::new(),
- max_name_len: 0u,
+ max_name_len: 0,
})
}
pub fn write_run_finish(&mut self) -> old_io::IoResult<bool> {
assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
- let success = self.failed == 0u;
+ let success = self.failed == 0;
if !success {
try!(self.write_failures());
}
let mut st = try!(ConsoleTestState::new(opts, None::<StdWriter>));
fn len_if_padded(t: &TestDescAndFn) -> uint {
match t.testfn.padding() {
- PadNone => 0u,
+ PadNone => 0,
PadOnLeft | PadOnRight => t.desc.name.as_slice().len(),
}
}
log_out: None,
out: Raw(Vec::new()),
use_color: false,
- total: 0u,
- passed: 0u,
- failed: 0u,
- ignored: 0u,
- measured: 0u,
- max_name_len: 10u,
+ total: 0,
+ passed: 0,
+ failed: 0,
+ ignored: 0,
+ measured: 0,
+ max_name_len: 10,
metrics: MetricMap::new(),
failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
};
#[inline]
fn size_hint(&self) -> (uint, Option<uint>) {
let slen = self.string.len();
- (cmp::min(slen, 1u), Some(slen))
+ (cmp::min(slen, 1), Some(slen))
}
#[inline]
pub use reexport::Reexported;
+pub struct Foo;
+pub enum Bar { X }
+
pub mod foo {
pub trait PubPub {
fn method(&self) {}
// issue #21405
-fn foo<F>(f: F) where F: FnMut(usize) {}
+struct Foo;
+
+fn foo<F>(f: F) where F: FnMut(Foo) {}
fn main() {
foo(|s| s.is_empty());
extern crate no_method_suggested_traits;
+struct Foo;
+enum Bar { X }
+
mod foo {
trait Bar {
fn method(&self) {}
}
fn main() {
+ // test the values themselves, and autoderef.
+
+
1u32.method();
//~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them
//~^^ ERROR does not implement
//~^^^ HELP `foo::Bar`
//~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ std::rc::Rc::new(&mut Box::new(&1u32)).method();
+ //~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them
+ //~^^ ERROR does not implement
+ //~^^^ HELP `foo::Bar`
+ //~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
'a'.method();
//~^ ERROR does not implement
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
//~^^^ HELP `foo::Bar`
+ std::rc::Rc::new(&mut Box::new(&'a')).method();
+ //~^ ERROR does not implement
+ //~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
+ //~^^^ HELP `foo::Bar`
1i32.method();
//~^ ERROR does not implement
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ std::rc::Rc::new(&mut Box::new(&1i32)).method();
+ //~^ ERROR does not implement
+ //~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
+ //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
- 1u64.method();
+ Foo.method();
+ //~^ ERROR does not implement
+ //~^^ HELP following traits define a method `method`, perhaps you need to implement one of them
+ //~^^^ HELP `foo::Bar`
+ //~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ //~^^^^^ HELP `no_method_suggested_traits::reexport::Reexported`
+ //~^^^^^^ HELP `no_method_suggested_traits::bar::PubPriv`
+ //~^^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub`
+ //~^^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv`
+ std::rc::Rc::new(&mut Box::new(&Foo)).method();
//~^ ERROR does not implement
//~^^ HELP following traits define a method `method`, perhaps you need to implement one of them
//~^^^ HELP `foo::Bar`
//~^ ERROR does not implement
//~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it
//~^^^ HELP `foo::Bar`
- 1u64.method3();
+ std::rc::Rc::new(&mut Box::new(&1u64)).method2();
//~^ ERROR does not implement
- //~^^ HELP the following trait defines a method `method3`, perhaps you need to implement it
+ //~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it
+ //~^^^ HELP `foo::Bar`
+
+ no_method_suggested_traits::Foo.method2();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
+ //~^^^ HELP `foo::Bar`
+ std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
+ //~^^^ HELP `foo::Bar`
+ no_method_suggested_traits::Bar::X.method2();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
+ //~^^^ HELP `foo::Bar`
+ std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
+ //~^^^ HELP `foo::Bar`
+
+ Foo.method3();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
+ //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ std::rc::Rc::new(&mut Box::new(&Foo)).method3();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
+ //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ Bar::X.method3();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+ std::rc::Rc::new(&mut Box::new(&Bar::X)).method3();
+ //~^ ERROR does not implement
+ //~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
+ //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+
+ // should have no help:
+ 1us.method3(); //~ ERROR does not implement
+ std::rc::Rc::new(&mut Box::new(&1us)).method3(); //~ ERROR does not implement
+ no_method_suggested_traits::Foo.method3(); //~ ERROR does not implement
+ std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3();
+ //~^ ERROR does not implement
+ no_method_suggested_traits::Bar::X.method3(); //~ ERROR does not implement
+ std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3();
+ //~^ ERROR does not implement
}
pub fn main() {
let r = 1..2..3;
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
-}
\ No newline at end of file
+}
pub fn main() {
let r = ..1..2;
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
-}
\ No newline at end of file
+}
assoc_enum(Enum::Variant2(8i64, 9i32));
}
-fn zzz() { () }
\ No newline at end of file
+fn zzz() { () }
use std::slice::SliceExt;
use std::old_io::{Command, fs, USER_RWX};
use std::os;
-use std::path::BytesContainer;
+use std::old_path::BytesContainer;
use std::rand::random;
fn main() {
-
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
#![feature(box_syntax)]
#![feature(unboxed_closures)]
-use std::path::{Path};
-use std::path;
+use std::old_path::{Path};
+use std::old_path;
use std::result;
use std::thunk::Thunk;
result::Result::Ok("more blah".to_string())
};
- let path = path::Path::new("blah");
+ let path = old_path::Path::new("blah");
assert!(loader(&path).is_ok());
}
use std::old_io::fs;
use std::old_io::Command;
use std::os;
-use std::path::Path;
+use std::old_path::Path;
fn main() {
let my_args = os::args();