Closes #8706.
.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;
* `simd` - Allows use of the `#[simd]` attribute, which is overly simple and
not the SIMD interface we want to expose in the long term.
+* `staged_api` - Allows usage of stability markers and `#![staged_api]` in a crate
+
* `struct_inherit` - Allows using struct inheritance, which is barely
implemented and will probably be removed. Don't use this.
which is considered wildly unsafe and will be
obsoleted by language improvements.
+* `unmarked_api` - Allows use of items within a `#![staged_api]` crate
+ which have not been marked with a stability marker.
+ Such items should not be allowed by the compiler to exist,
+ so if you need this there probably is a compiler bug.
+
* `associated_types` - Allows type aliases in traits. Experimental.
If a feature is promoted to a language feature, then all existing programs will
'Encodable': (0, [], 0), # FIXME: quoting gives horrible spans
}
-for (trait, supers, errs) in [('Rand', [], 1),
- ('Clone', [], 1),
+for (trait, supers, errs) in [('Clone', [], 1),
('PartialEq', [], 2),
('PartialOrd', ['PartialEq'], 8),
('Eq', ['PartialEq'], 1),
#![stable(feature = "rust1", since = "1.0.0")]
+use core::prelude::*;
+
use core::any::Any;
-use core::clone::Clone;
-use core::cmp::{PartialEq, PartialOrd, Eq, Ord, Ordering};
+use core::cmp::Ordering;
use core::default::Default;
use core::error::{Error, FromError};
use core::fmt;
use core::hash::{self, Hash};
-use core::iter::Iterator;
-use core::marker::Sized;
use core::mem;
use core::ops::{Deref, DerefMut};
-use core::option::Option;
use core::ptr::Unique;
use core::raw::TraitObject;
-use core::result::Result::{Ok, Err};
-use core::result::Result;
/// A value that represents the heap. This is the default place that the `box` keyword allocates
/// into when no place is supplied.
fn deref_mut(&mut self) -> &mut T { &mut **self }
}
-impl<'a, T> Iterator for Box<Iterator<Item=T> + 'a> {
- type Item = T;
-
- fn next(&mut self) -> Option<T> {
- (**self).next()
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (**self).size_hint()
- }
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<I: Iterator + ?Sized> Iterator for Box<I> {
+ type Item = I::Item;
+ fn next(&mut self) -> Option<I::Item> { (**self).next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { (**self).size_hint() }
+}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for Box<I> {
+ fn next_back(&mut self) -> Option<I::Item> { (**self).next_back() }
}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Box<I> {}
+#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + 'a> FromError<E> for Box<Error + 'a> {
fn from_error(err: E) -> Box<Error + 'a> {
Box::new(err)
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 ),
#![feature(unicode)]
#![feature(unsafe_destructor, slicing_syntax)]
#![cfg_attr(test, feature(test))]
+#![cfg_attr(test, allow(deprecated))] // rand
#![no_std]
#![allow(missing_docs)]
+pub use self::Entry::*;
+
use core::prelude::*;
use core::cmp::Ordering;
v: Vec<Option<V>>,
}
+/// A view into a single entry in a map, which may either be vacant or occupied.
+#[unstable(feature = "collections",
+ reason = "precise API still under development")]
+pub enum Entry<'a, V:'a> {
+ /// A vacant Entry
+ Vacant(VacantEntry<'a, V>),
+ /// An occupied Entry
+ Occupied(OccupiedEntry<'a, V>),
+}
+
+/// A vacant Entry.
+#[unstable(feature = "collections",
+ reason = "precise API still under development")]
+pub struct VacantEntry<'a, V:'a> {
+ map: &'a mut VecMap<V>,
+ index: usize,
+}
+
+/// An occupied Entry.
+#[unstable(feature = "collections",
+ reason = "precise API still under development")]
+pub struct OccupiedEntry<'a, V:'a> {
+ map: &'a mut VecMap<V>,
+ index: usize,
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<V> Default for VecMap<V> {
#[stable(feature = "rust1", since = "1.0.0")]
let result = &mut self.v[*key];
result.take()
}
+
+ /// Gets the given key's corresponding entry in the map for in-place manipulation.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::VecMap;
+ /// use std::collections::vec_map::Entry;
+ ///
+ /// let mut count: VecMap<u32> = VecMap::new();
+ ///
+ /// // count the number of occurrences of numbers in the vec
+ /// for x in vec![1, 2, 1, 2, 3, 4, 1, 2, 4].iter() {
+ /// match count.entry(*x) {
+ /// Entry::Vacant(view) => {
+ /// view.insert(1);
+ /// },
+ /// Entry::Occupied(mut view) => {
+ /// let v = view.get_mut();
+ /// *v += 1;
+ /// },
+ /// }
+ /// }
+ ///
+ /// assert_eq!(count[1], 3);
+ /// ```
+ #[unstable(feature = "collections",
+ reason = "precise API still under development")]
+ pub fn entry(&mut self, key: usize) -> Entry<V> {
+ // FIXME(Gankro): this is basically the dumbest implementation of
+ // entry possible, because weird non-lexical borrows issues make it
+ // completely insane to do any other way. That said, Entry is a border-line
+ // useless construct on VecMap, so it's hardly a big loss.
+ if self.contains_key(&key) {
+ Occupied(OccupiedEntry {
+ map: self,
+ index: key,
+ })
+ } else {
+ Vacant(VacantEntry {
+ map: self,
+ index: key,
+ })
+ }
+ }
+}
+
+
+impl<'a, V> Entry<'a, V> {
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ /// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant
+ pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, V>> {
+ match self {
+ Occupied(entry) => Ok(entry.into_mut()),
+ Vacant(entry) => Err(entry),
+ }
+ }
+}
+
+impl<'a, V> VacantEntry<'a, V> {
+ /// Sets the value of the entry with the VacantEntry's key,
+ /// and returns a mutable reference to it.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn insert(self, value: V) -> &'a mut V {
+ let index = self.index;
+ self.map.insert(index, value);
+ &mut self.map[index]
+ }
+}
+
+impl<'a, V> OccupiedEntry<'a, V> {
+ /// Gets a reference to the value in the entry.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn get(&self) -> &V {
+ let index = self.index;
+ &self.map[index]
+ }
+
+ /// Gets a mutable reference to the value in the entry.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn get_mut(&mut self) -> &mut V {
+ let index = self.index;
+ &mut self.map[index]
+ }
+
+ /// Converts the entry into a mutable reference to its value.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn into_mut(self) -> &'a mut V {
+ let index = self.index;
+ &mut self.map[index]
+ }
+
+ /// Sets the value of the entry with the OccupiedEntry's key,
+ /// and returns the entry's old value.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn insert(&mut self, value: V) -> V {
+ let index = self.index;
+ self.map.insert(index, value).unwrap()
+ }
+
+ /// Takes the value of the entry out of the map, and returns it.
+ #[unstable(feature = "collections",
+ reason = "matches collection reform v2 specification, waiting for dust to settle")]
+ pub fn remove(self) -> V {
+ let index = self.index;
+ self.map.remove(&index).unwrap()
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
use prelude::*;
use core::hash::{hash, SipHasher};
- use super::VecMap;
+ use super::{VecMap, Occupied, Vacant};
#[test]
fn test_get_mut() {
map[4];
}
+
+ #[test]
+ fn test_entry(){
+ let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
+
+ let mut map: VecMap<i32> = xs.iter().map(|&x| x).collect();
+
+ // Existing key (insert)
+ match map.entry(1) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ assert_eq!(view.get(), &10);
+ assert_eq!(view.insert(100), 10);
+ }
+ }
+ assert_eq!(map.get(&1).unwrap(), &100);
+ assert_eq!(map.len(), 6);
+
+
+ // Existing key (update)
+ match map.entry(2) {
+ Vacant(_) => unreachable!(),
+ Occupied(mut view) => {
+ let v = view.get_mut();
+ *v *= 10;
+ }
+ }
+ assert_eq!(map.get(&2).unwrap(), &200);
+ assert_eq!(map.len(), 6);
+
+ // Existing key (take)
+ match map.entry(3) {
+ Vacant(_) => unreachable!(),
+ Occupied(view) => {
+ assert_eq!(view.remove(), 30);
+ }
+ }
+ assert_eq!(map.get(&3), None);
+ assert_eq!(map.len(), 5);
+
+
+ // Inexistent key (insert)
+ match map.entry(10) {
+ Occupied(_) => unreachable!(),
+ Vacant(view) => {
+ assert_eq!(*view.insert(1000), 1000);
+ }
+ }
+ assert_eq!(map.get(&10).unwrap(), &1000);
+ assert_eq!(map.len(), 6);
+ }
}
#[cfg(test)]
//! 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),
#[lang = "debug_trait"]
pub trait Debug {
/// Formats the value using the given formatter.
+ #[stable(feature = "rust1", since = "1.0.0")]
fn fmt(&self, &mut Formatter) -> Result;
}
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Display {
/// Formats the value using the given formatter.
+ #[stable(feature = "rust1", since = "1.0.0")]
fn fmt(&self, &mut Formatter) -> Result;
}
fn size_hint(&self) -> (usize, Option<usize>) { (0, None) }
}
-impl<'a, T> Iterator for &'a mut (Iterator<Item=T> + 'a) {
- type Item = T;
-
- fn next(&mut self) -> Option<T> {
- (**self).next()
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (**self).size_hint()
- }
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, I: Iterator + ?Sized> Iterator for &'a mut I {
+ type Item = I::Item;
+ fn next(&mut self) -> Option<I::Item> { (**self).next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { (**self).size_hint() }
}
/// Conversion from an `Iterator`
built from an iterator over elements of type `{A}`"]
pub trait FromIterator<A> {
/// Build a container with elements from an external iterator.
+ #[stable(feature = "rust1", since = "1.0.0")]
fn from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
}
/// assert!(it.next() == Some(5));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
- fn by_ref<'r>(&'r mut self) -> ByRef<'r, Self> {
- ByRef{iter: self}
- }
+ fn by_ref(&mut self) -> &mut Self { self }
/// Loops through the entire iterator, collecting all of the elements into
/// a container implementing `FromIterator`.
P: FnMut(Self::Item) -> bool,
Self: ExactSizeIterator + DoubleEndedIterator
{
- let len = self.len();
- for i in (0..len).rev() {
- if predicate(self.next_back().expect("rposition: incorrect ExactSizeIterator")) {
+ let mut i = self.len() - 1;
+ while let Some(v) = self.next_back() {
+ if predicate(v) {
return Some(i);
}
+ i -= 1;
}
None
}
/// A range iterator able to yield elements from both ends
///
-/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and `next_back()` exhaust
-/// elements from the *same* range, and do not work independently of each other.
+/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and
+/// `next_back()` exhaust elements from the *same* range, and do not work
+/// independently of each other.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait DoubleEndedIterator: Iterator {
- /// Yield an element from the end of the range, returning `None` if the range is empty.
+ /// Yield an element from the end of the range, returning `None` if the
+ /// range is empty.
#[stable(feature = "rust1", since = "1.0.0")]
fn next_back(&mut self) -> Option<Self::Item>;
}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I {
+ fn next_back(&mut self) -> Option<I::Item> { (**self).next_back() }
+}
+
/// An object implementing random access indexing by `usize`
///
/// A `RandomAccessIterator` should be either infinite or a `DoubleEndedIterator`.
}
}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, I: ExactSizeIterator + ?Sized> ExactSizeIterator for &'a mut I {}
+
// All adaptors that preserve the size of the wrapped iterator are fine
// Adaptors that may overflow in `size_hint` are not, i.e. `Chain`.
#[stable(feature = "rust1", since = "1.0.0")]
}
}
-/// A mutable reference to an iterator
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct ByRef<'a, I:'a> {
- iter: &'a mut I,
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, I> Iterator for ByRef<'a, I> where I: 'a + Iterator {
- type Item = <I as Iterator>::Item;
-
- #[inline]
- fn next(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() }
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, I> DoubleEndedIterator for ByRef<'a, I> where I: 'a + DoubleEndedIterator {
- #[inline]
- fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next_back() }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, I> ExactSizeIterator for ByRef<'a, I> where I: 'a + ExactSizeIterator {}
-
/// A trait for iterators over elements which can be added together
#[unstable(feature = "core",
reason = "needs to be re-evaluated as part of numerics reform")]
/// Return a reference to the next element of the iterator with out
/// advancing it, or None if the iterator is exhausted.
#[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
pub fn peek(&mut self) -> Option<&I::Item> {
if self.peeked.is_none() {
self.peeked = self.iter.next();
#![feature(int_uint)]
#![feature(unboxed_closures)]
#![feature(unsafe_destructor, slicing_syntax)]
+#![allow(deprecated)] // rand
extern crate core;
extern crate test;
#[cfg(test)]
mod tests {
+ #![allow(deprecated)]
use super::{inflate_bytes, deflate_bytes};
use std::rand;
use std::rand::Rng;
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());
}
}
#![feature(staged_api)]
#![staged_api]
#![feature(core)]
+#![deprecated(reason = "use the crates.io `rand` library instead",
+ since = "1.0.0-alpha")]
+
+#![allow(deprecated)]
#[macro_use]
extern crate core;
/// 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() {
ast::ExprLoop(ref b, _) => {
self.with_context(Loop, |v| v.visit_block(&**b));
}
- ast::ExprClosure(_, _, _, ref b) => {
+ ast::ExprClosure(_, _, ref b) => {
self.with_context(Closure, |v| v.visit_block(&**b));
}
ast::ExprBreak(_) => self.require_loop("break", e.span),
self.propagate_through_expr(&**e, succ)
}
- ast::ExprClosure(_, _, _, ref blk) => {
+ ast::ExprClosure(_, _, ref blk) => {
debug!("{} is an ExprClosure",
expr_to_string(expr));
};
match fn_expr.node {
- ast::ExprClosure(_, _, _, ref body) => body.id,
+ ast::ExprClosure(_, _, ref body) => body.id,
_ => unreachable!()
}
};
use session::Session;
use lint;
use middle::ty;
+use middle::privacy::PublicItems;
use metadata::csearch;
use syntax::parse::token::InternedString;
use syntax::codemap::{Span, DUMMY_SP};
// A private tree-walker for producing an Index.
struct Annotator<'a> {
sess: &'a Session,
- index: Index,
- parent: Option<Stability>
+ index: &'a mut Index,
+ parent: Option<Stability>,
+ export_map: &'a PublicItems,
}
impl<'a> Annotator<'a> {
// Determine the stability for a node based on its attributes and inherited
// stability. The stability is recorded in the index and used as the parent.
fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
- attrs: &Vec<Attribute>, item_sp: Span, f: F) where
+ attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
F: FnOnce(&mut Annotator),
{
match attr::find_stability(self.sess.diagnostic(), attrs.as_slice(), item_sp) {
}
None => {
if use_parent {
- self.parent.clone().map(|stab| self.index.local.insert(id, stab));
+ if let Some(stab) = self.parent.clone() {
+ self.index.local.insert(id, stab);
+ } else if self.index.staged_api && required
+ && self.export_map.contains(&id)
+ && !self.sess.opts.test {
+ self.sess.span_err(item_sp,
+ "This node does not have a stability attribute");
+ }
}
f(self);
}
_ => true,
};
- self.annotate(i.id, use_parent, &i.attrs, i.span, |v| visit::walk_item(v, i));
+ // In case of a `pub use <mod>;`, we should not error since the stability
+ // is inherited from the module itself
+ let required = match i.node {
+ ast::ItemUse(_) => i.vis != ast::Public,
+ _ => true
+ };
+
+ self.annotate(i.id, use_parent, &i.attrs, i.span,
+ |v| visit::walk_item(v, i), required);
if let ast::ItemStruct(ref sd, _) = i.node {
sd.ctor_id.map(|id| {
- self.annotate(id, true, &i.attrs, i.span, |_| {})
+ self.annotate(id, true, &i.attrs, i.span, |_| {}, true)
});
}
}
_: &'v Block, sp: Span, _: NodeId) {
if let FkMethod(_, _, meth) = fk {
// Methods are not already annotated, so we annotate it
- self.annotate(meth.id, true, &meth.attrs, sp, |_| {});
+ self.annotate(meth.id, true, &meth.attrs, sp, |_| {}, true);
}
// Items defined in a function body have no reason to have
// a stability attribute, so we don't recurse.
TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs,
typedef.ty_param.span),
};
- self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t));
+ self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t), true);
}
fn visit_variant(&mut self, var: &Variant, g: &'v Generics) {
self.annotate(var.node.id, true, &var.node.attrs, var.span,
- |v| visit::walk_variant(v, var, g))
+ |v| visit::walk_variant(v, var, g), true)
}
fn visit_struct_field(&mut self, s: &StructField) {
self.annotate(s.node.id, true, &s.node.attrs, s.span,
- |v| visit::walk_struct_field(v, s));
+ |v| visit::walk_struct_field(v, s), true);
}
fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
- self.annotate(i.id, true, &i.attrs, i.span, |_| {});
+ self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
}
}
impl Index {
/// Construct the stability index for a crate being compiled.
- pub fn build(sess: &Session, krate: &Crate) -> Index {
+ pub fn build(&mut self, sess: &Session, krate: &Crate, export_map: &PublicItems) {
+ if !self.staged_api {
+ return;
+ }
+ let mut annotator = Annotator {
+ sess: sess,
+ index: self,
+ parent: None,
+ export_map: export_map,
+ };
+ annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
+ |v| visit::walk_crate(v, krate), true);
+ }
+
+ pub fn new(krate: &Crate) -> Index {
let mut staged_api = false;
for attr in &krate.attrs {
if attr.name().get() == "staged_api" {
}
}
}
- let index = Index {
+ Index {
staged_api: staged_api,
local: NodeMap(),
extern_cache: DefIdMap()
- };
- if !staged_api {
- return index;
}
- let mut annotator = Annotator {
- sess: sess,
- index: index,
- parent: None
- };
- annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
- |v| visit::walk_crate(v, krate));
- annotator.index
}
}
None => {
// This is an 'unmarked' API, which should not exist
// in the standard library.
- self.tcx.sess.span_err(span, "use of unmarked library feature");
- self.tcx.sess.span_note(span, "this is either a bug in the library you are \
- using or a bug in the compiler - there is \
- no way to use this feature");
+ if self.tcx.sess.features.borrow().unmarked_api {
+ self.tcx.sess.span_warn(span, "use of unmarked library feature");
+ self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+ using and a bug in the compiler - please \
+ report it in both places");
+ } else {
+ self.tcx.sess.span_err(span, "use of unmarked library feature");
+ self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+ using and a bug in the compiler - please \
+ report it in both places");
+ self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \
+ crate attributes to override this");
+ }
}
}
}
obligation.repr(selcx.tcx()));
let infcx = selcx.infcx();
- let result = infcx.try(|snapshot| {
+ infcx.try(|snapshot| {
let (skol_predicate, skol_map) =
infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
let skol_obligation = obligation.with(skol_predicate);
match project_and_unify_type(selcx, &skol_obligation) {
- Ok(Some(obligations)) => {
+ Ok(result) => {
match infcx.leak_check(&skol_map, snapshot) {
- Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)),
- Err(e) => Err(Some(MismatchedProjectionTypes { err: e })),
+ Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)),
+ Err(e) => Err(MismatchedProjectionTypes { err: e }),
}
}
- Ok(None) => {
- // Signal ambiguity using Err just so that infcx.try()
- // rolls back the snapshot. We adapt below.
- Err(None)
- }
Err(e) => {
- Err(Some(e))
+ Err(e)
}
}
- });
-
- // Above, we use Err(None) to signal ambiguity so that the
- // snapshot will be rolled back. But here, we want to translate to
- // Ok(None). Kind of weird.
- match result {
- Ok(obligations) => Ok(Some(obligations)),
- Err(None) => Ok(None),
- Err(Some(e)) => Err(e),
- }
+ })
}
/// Evaluates constraints of the form:
obligation.cause.clone(),
obligation.recursion_depth) {
Some(n) => n,
- None => { return Ok(None); }
+ None => {
+ consider_unification_despite_ambiguity(selcx, obligation);
+ return Ok(None);
+ }
};
debug!("project_and_unify_type: normalized_ty={} obligations={}",
}
}
+fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext<'cx,'tcx>,
+ obligation: &ProjectionObligation<'tcx>) {
+ debug!("consider_unification_despite_ambiguity(obligation={})",
+ obligation.repr(selcx.tcx()));
+
+ let def_id = obligation.predicate.projection_ty.trait_ref.def_id;
+ match selcx.tcx().lang_items.fn_trait_kind(def_id) {
+ Some(_) => { }
+ None => { return; }
+ }
+
+ let infcx = selcx.infcx();
+ let self_ty = obligation.predicate.projection_ty.trait_ref.self_ty();
+ let self_ty = infcx.shallow_resolve(self_ty);
+ debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}",
+ self_ty.sty);
+ match self_ty.sty {
+ ty::ty_closure(closure_def_id, _, substs) => {
+ let closure_typer = selcx.closure_typer();
+ let closure_type = closure_typer.closure_type(closure_def_id, substs);
+ let ty::Binder((_, ret_type)) =
+ util::closure_trait_ref_and_return_type(infcx.tcx,
+ def_id,
+ self_ty,
+ &closure_type.sig,
+ util::TupleArgumentsFlag::No);
+ let (ret_type, _) =
+ infcx.replace_late_bound_regions_with_fresh_var(
+ obligation.cause.span,
+ infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name),
+ &ty::Binder(ret_type));
+ debug!("consider_unification_despite_ambiguity: ret_type={:?}",
+ ret_type.repr(selcx.tcx()));
+ let origin = infer::RelateOutputImplTypes(obligation.cause.span);
+ let obligation_ty = obligation.predicate.ty;
+ match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) {
+ Ok(()) => { }
+ Err(_) => { /* ignore errors */ }
+ }
+ }
+ _ => { }
+ }
+}
+
/// Normalizes any associated type projections in `value`, replacing
/// them with a fully resolved type where possible. The return value
/// combines the normalized result and any additional obligations that
// is `Vec<Foo>:Iterable<Bar>`, but the impl specifies
// `impl<T> Iterable<T> for Vec<T>`, than an error would result.
- /// Evaluates whether the obligation can be satisfied. Returns an indication of whether the
- /// obligation can be satisfied and, if so, by what means. Never affects surrounding typing
- /// environment.
+ /// Attempts to satisfy the obligation. If successful, this will affect the surrounding
+ /// type environment by performing unification.
pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
-> SelectionResult<'tcx, Selection<'tcx>> {
debug!("select({})", obligation.repr(self.tcx()));
let stack = self.push_stack(None, obligation);
match try!(self.candidate_from_obligation(&stack)) {
- None => Ok(None),
+ None => {
+ self.consider_unification_despite_ambiguity(obligation);
+ Ok(None)
+ }
Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))),
}
}
+ /// In the particular case of unboxed closure obligations, we can
+ /// sometimes do some amount of unification for the
+ /// argument/return types even though we can't yet fully match obligation.
+ /// The particular case we are interesting in is an obligation of the form:
+ ///
+ /// C : FnFoo<A>
+ ///
+ /// where `C` is an unboxed closure type and `FnFoo` is one of the
+ /// `Fn` traits. Because we know that users cannot write impls for closure types
+ /// themselves, the only way that `C : FnFoo` can fail to match is under two
+ /// conditions:
+ ///
+ /// 1. The closure kind for `C` is not yet known, because inference isn't complete.
+ /// 2. The closure kind for `C` *is* known, but doesn't match what is needed.
+ /// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed.
+ ///
+ /// In either case, we always know what argument types are
+ /// expected by `C`, no matter what kind of `Fn` trait it
+ /// eventually matches. So we can go ahead and unify the argument
+ /// types, even though the end result is ambiguous.
+ ///
+ /// Note that this is safe *even if* the trait would never be
+ /// matched (case 2 above). After all, in that case, an error will
+ /// result, so it kind of doesn't matter what we do --- unifying
+ /// the argument types can only be helpful to the user, because
+ /// once they patch up the kind of closure that is expected, the
+ /// argment types won't really change.
+ fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) {
+ // Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`?
+ match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
+ Some(_) => { }
+ None => { return; }
+ }
+
+ // Is the self-type a closure type? We ignore bindings here
+ // because if it is a closure type, it must be a closure type from
+ // within this current fn, and hence none of the higher-ranked
+ // lifetimes can appear inside the self-type.
+ let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
+ let (closure_def_id, substs) = match self_ty.sty {
+ ty::ty_closure(id, _, ref substs) => (id, substs.clone()),
+ _ => { return; }
+ };
+ assert!(!substs.has_escaping_regions());
+
+ let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs);
+ match self.confirm_poly_trait_refs(obligation.cause.clone(),
+ obligation.predicate.to_poly_trait_ref(),
+ closure_trait_ref) {
+ Ok(()) => { }
+ Err(_) => { /* Silently ignore errors. */ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// EVALUATION
//
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
- let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) {
+ let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
Some(k) => k,
None => { return Ok(()); }
};
impl_obligations
}
- fn fn_family_trait_kind(&self,
- trait_def_id: ast::DefId)
- -> Option<ty::ClosureKind>
- {
- let tcx = self.tcx();
- if Some(trait_def_id) == tcx.lang_items.fn_trait() {
- Some(ty::FnClosureKind)
- } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() {
- Some(ty::FnMutClosureKind)
- } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
- Some(ty::FnOnceClosureKind)
- } else {
- None
- }
- }
-
#[allow(unused_comparisons)]
fn derived_cause(&self,
obligation: &TraitObligation<'tcx>,
// 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);
#[cfg(test)]
mod tests {
+ #![allow(deprecated)]
extern crate rand;
use self::rand::Rng;
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> {
tcx: &ty::ctxt) -> ast::NodeId {
match tcx.map.get(closure_id) {
ast_map::NodeExpr(expr) => match expr.node {
- ast::ExprClosure(_, _, _, ref block) => {
+ ast::ExprClosure(_, _, ref block) => {
block.id
}
_ => {
time(time_passes, "loop checking", (), |_|
middle::check_loop::check_crate(&sess, krate));
- let stability_index = time(time_passes, "stability index", (), |_|
- stability::Index::build(&sess, krate));
-
time(time_passes, "static item recursion checking", (), |_|
middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map));
freevars,
region_map,
lang_items,
- stability_index);
+ stability::Index::new(krate));
// passes are timed inside typeck
typeck::check_crate(&ty_cx, trait_map);
time(time_passes, "privacy checking", maps, |(a, b)|
rustc_privacy::check_crate(&ty_cx, &export_map, a, b));
+ // Do not move this check past lint
+ time(time_passes, "stability index", (), |_|
+ ty_cx.stability.borrow_mut().build(&ty_cx.sess, krate, &public_items));
+
time(time_passes, "intrinsic checking", (), |_|
middle::intrinsicck::check_crate(&ty_cx));
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;
resolve::resolve_crate(&sess, &ast_map, &lang_items, krate, resolve::MakeGlobMap::No);
let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map);
let region_map = region::resolve_crate(&sess, krate);
- let stability_index = stability::Index::build(&sess, krate);
let tcx = ty::mk_ctxt(sess,
&arenas,
def_map,
freevars,
region_map,
lang_items,
- stability_index);
+ stability::Index::new(krate));
let infcx = infer::new_infer_ctxt(&tcx);
body(Env { infcx: &infcx });
infcx.resolve_regions_and_report_errors(ast::CRATE_NODE_ID);
/// 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",
visit::walk_expr(self, expr);
}
- ExprClosure(_, _, ref fn_decl, ref block) => {
+ ExprClosure(_, ref fn_decl, ref block) => {
self.resolve_function(ClosureRibKind(expr.id),
Some(&**fn_decl), NoTypeParameters,
&**block);
type, found {:?}", ty)[]),
}
},
- ast::ExprClosure(_, _, ref decl, ref body) => {
+ ast::ExprClosure(_, ref decl, ref body) => {
if generated_code(body.span) {
return
}
}
Some(ast_map::NodeExpr(e)) => {
match e.node {
- ast::ExprClosure(_, _, _, ref blk) => {
+ ast::ExprClosure(_, _, ref blk) => {
blk
}
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
}
ast_map::NodeExpr(ref expr) => {
match expr.node {
- ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => {
+ ast::ExprClosure(_, ref fn_decl, ref top_level_block) => {
let name = format!("fn{}", token::gensym("fn"));
let name = token::str_to_ident(&name[]);
(name, &**fn_decl,
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 &&
})
}
- ast::ExprClosure(_, _, ref decl, ref block) => {
+ ast::ExprClosure(_, ref decl, ref block) => {
with_new_scope(cx,
block.span,
scope_stack,
// 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(..) => {
ast::ExprVec(..) | ast::ExprRepeat(..) => {
tvec::trans_fixed_vstore(bcx, expr, dest)
}
- ast::ExprClosure(_, _, ref decl, ref body) => {
+ ast::ExprClosure(_, ref decl, ref body) => {
closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest)
}
ast::ExprCall(ref f, ref args) => {
pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
_capture: ast::CaptureClause,
- opt_kind: Option<ast::ClosureKind>,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
expected: Expectation<'tcx>) {
expr.repr(fcx.tcx()),
expected.repr(fcx.tcx()));
- let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| {
- deduce_expectations_from_expected_type(fcx, ty)
- });
-
- match opt_kind {
- None => {
- // If users didn't specify what sort of closure they want,
- // examine the expected type. For now, if we see explicit
- // evidence than an unboxed closure is desired, we'll use
- // that. Otherwise, we leave it unspecified, to be filled
- // in by upvar inference.
- match expected_sig_and_kind {
- None => { // don't have information about the kind, request explicit annotation
- check_closure(fcx, expr, None, decl, body, None);
- },
- Some((sig, kind)) => {
- check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
- }
- }
- }
-
- Some(kind) => {
- let kind = match kind {
- ast::FnClosureKind => ty::FnClosureKind,
- ast::FnMutClosureKind => ty::FnMutClosureKind,
- ast::FnOnceClosureKind => ty::FnOnceClosureKind,
- };
-
- let expected_sig = expected_sig_and_kind.map(|t| t.0);
- check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
- }
- }
+ // It's always helpful for inference if we know the kind of
+ // closure sooner rather than later, so first examine the expected
+ // type, and see if can glean a closure kind from there.
+ let (expected_sig,expected_kind) = match expected.to_option(fcx) {
+ Some(ty) => deduce_expectations_from_expected_type(fcx, ty),
+ None => (None, None)
+ };
+ check_closure(fcx, expr, expected_kind, decl, body, expected_sig)
}
fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
fn deduce_expectations_from_expected_type<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
expected_ty: Ty<'tcx>)
- -> Option<(ty::FnSig<'tcx>,ty::ClosureKind)>
+ -> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
{
+ debug!("deduce_expectations_from_expected_type(expected_ty={})",
+ expected_ty.repr(fcx.tcx()));
+
match expected_ty.sty {
ty::ty_trait(ref object_type) => {
let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(),
fcx.tcx().types.err);
- proj_bounds.iter()
- .filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
- .next()
+ let expectations =
+ proj_bounds.iter()
+ .filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
+ .next();
+
+ match expectations {
+ Some((sig, kind)) => (Some(sig), Some(kind)),
+ None => (None, None)
+ }
}
ty::ty_infer(ty::TyVar(vid)) => {
deduce_expectations_from_obligations(fcx, vid)
}
_ => {
- None
+ (None, None)
}
}
}
fn deduce_expectations_from_obligations<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
expected_vid: ty::TyVid)
- -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
+ -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
{
let fulfillment_cx = fcx.inh.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
- fulfillment_cx.pending_obligations()
- .iter()
- .filter_map(|obligation| {
- match obligation.predicate {
- ty::Predicate::Projection(ref proj_predicate) => {
- let trait_ref = proj_predicate.to_poly_trait_ref();
- let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
- match self_ty.sty {
- ty::ty_infer(ty::TyVar(v)) if expected_vid == v => {
- deduce_expectations_from_projection(fcx, proj_predicate)
- }
- _ => {
- None
- }
- }
- }
- _ => {
- None
- }
- }
- })
- .next()
+ let expected_sig_and_kind =
+ fulfillment_cx
+ .pending_obligations()
+ .iter()
+ .filter_map(|obligation| {
+ debug!("deduce_expectations_from_obligations: obligation.predicate={}",
+ obligation.predicate.repr(fcx.tcx()));
+
+ match obligation.predicate {
+ // Given a Projection predicate, we can potentially infer
+ // the complete signature.
+ ty::Predicate::Projection(ref proj_predicate) => {
+ let trait_ref = proj_predicate.to_poly_trait_ref();
+ self_type_matches_expected_vid(fcx, trait_ref, expected_vid)
+ .and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate))
+ }
+ _ => {
+ None
+ }
+ }
+ })
+ .next();
+
+ match expected_sig_and_kind {
+ Some((sig, kind)) => { return (Some(sig), Some(kind)); }
+ None => { }
+ }
+
+ // Even if we can't infer the full signature, we may be able to
+ // infer the kind. This can occur if there is a trait-reference
+ // like `F : Fn<A>`.
+ let expected_kind =
+ fulfillment_cx
+ .pending_obligations()
+ .iter()
+ .filter_map(|obligation| {
+ let opt_trait_ref = match obligation.predicate {
+ ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
+ ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
+ ty::Predicate::Equate(..) => None,
+ ty::Predicate::RegionOutlives(..) => None,
+ ty::Predicate::TypeOutlives(..) => None,
+ };
+ opt_trait_ref
+ .and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))
+ .and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id()))
+ })
+ .next();
+
+ (None, expected_kind)
}
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
return Some((fn_sig, kind));
}
+fn self_type_matches_expected_vid<'a,'tcx>(
+ fcx: &FnCtxt<'a,'tcx>,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ expected_vid: ty::TyVid)
+ -> Option<ty::PolyTraitRef<'tcx>>
+{
+ let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
+ debug!("self_type_matches_expected_vid(trait_ref={}, self_ty={})",
+ trait_ref.repr(fcx.tcx()),
+ self_ty.repr(fcx.tcx()));
+ match self_ty.sty {
+ ty::ty_infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
+ _ => None,
+ }
+}
+
+
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,
ast::ExprMatch(ref discrim, ref arms, match_src) => {
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
}
- ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
- closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
+ ast::ExprClosure(capture, ref decl, ref body) => {
+ closure::check_expr_closure(fcx, expr, capture, &**decl, &**body, expected);
}
ast::ExprBlock(ref b) => {
check_block_with_expected(fcx, &**b, expected);
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!(
visit::walk_expr(rcx, expr);
}
- ast::ExprClosure(_, _, _, ref body) => {
+ ast::ExprClosure(_, _, ref body) => {
check_expr_fn_block(rcx, expr, &**body);
}
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);
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
fn visit_expr(&mut self, expr: &ast::Expr) {
match expr.node {
- ast::ExprClosure(cc, _, _, ref body) => {
+ ast::ExprClosure(cc, _, ref body) => {
self.check_closure(expr, cc, &**body);
}
MethodCall::expr(e.id));
match e.node {
- ast::ExprClosure(_, _, ref decl, _) => {
+ ast::ExprClosure(_, ref decl, _) => {
for input in &decl.inputs {
let _ = self.visit_node_id(ResolvingExpr(e.span),
input.id);
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%.
impl RandomState {
/// Construct a new `RandomState` that is initialized with random keys.
#[inline]
+ #[allow(deprecated)]
pub fn new() -> RandomState {
let mut r = rand::thread_rng();
RandomState { k0: r.gen(), k1: r.gen() }
/// 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};
--- /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.
+//
+// ignore-lexer-test FIXME #15883
+
+//! Buffering wrappers for I/O traits
+
+use prelude::v1::*;
+use io::prelude::*;
+
+use cmp;
+use error::Error as StdError;
+use error::FromError;
+use fmt;
+use io::{self, Cursor, DEFAULT_BUF_SIZE, Error, ErrorKind};
+use ptr;
+
+/// Wraps a `Read` and buffers input from it
+///
+/// It can be excessively inefficient to work directly with a `Read` instance.
+/// For example, every call to `read` on `TcpStream` results in a system call.
+/// A `BufReader` performs large, infrequent reads on the underlying `Read`
+/// and maintains an in-memory buffer of the results.
+pub struct BufReader<R> {
+ inner: R,
+ buf: Cursor<Vec<u8>>,
+}
+
+impl<R: Read> BufReader<R> {
+ /// Creates a new `BufReader` with a default buffer capacity
+ pub fn new(inner: R) -> BufReader<R> {
+ BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
+ }
+
+ /// Creates a new `BufReader` with the specified buffer capacity
+ pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
+ BufReader {
+ inner: inner,
+ buf: Cursor::new(Vec::with_capacity(cap)),
+ }
+ }
+
+ /// Gets a reference to the underlying reader.
+ pub fn get_ref<'a>(&self) -> &R { &self.inner }
+
+ /// Gets a mutable reference to the underlying reader.
+ ///
+ /// # Warning
+ ///
+ /// It is inadvisable to directly read from the underlying reader.
+ pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
+
+ /// Unwraps this `BufReader`, returning the underlying reader.
+ ///
+ /// Note that any leftover data in the internal buffer is lost.
+ pub fn into_inner(self) -> R { self.inner }
+}
+
+impl<R: Read> Read for BufReader<R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ // If we don't have any buffered data and we're doing a massive read
+ // (larger than our internal buffer), bypass our internal buffer
+ // entirely.
+ if self.buf.get_ref().len() == self.buf.position() as usize &&
+ buf.len() >= self.buf.get_ref().capacity() {
+ return self.inner.read(buf);
+ }
+ try!(self.fill_buf());
+ self.buf.read(buf)
+ }
+}
+
+impl<R: Read> BufRead for BufReader<R> {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ // If we've reached the end of our internal buffer then we need to fetch
+ // some more data from the underlying reader.
+ if self.buf.position() as usize == self.buf.get_ref().len() {
+ self.buf.set_position(0);
+ let v = self.buf.get_mut();
+ v.truncate(0);
+ let inner = &mut self.inner;
+ try!(super::with_end_to_cap(v, |b| inner.read(b)));
+ }
+ self.buf.fill_buf()
+ }
+
+ fn consume(&mut self, amt: uint) {
+ self.buf.consume(amt)
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<R> fmt::Debug for BufReader<R> where R: fmt::Debug {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "BufReader {{ reader: {:?}, buffer: {}/{} }}",
+ self.inner, self.buf.position(), self.buf.get_ref().len())
+ }
+}
+
+/// Wraps a Writer and buffers output to it
+///
+/// It can be excessively inefficient to work directly with a `Write`. For
+/// example, every call to `write` on `TcpStream` results in a system call. A
+/// `BufWriter` keeps an in memory buffer of data and writes it to the
+/// underlying `Write` in large, infrequent batches.
+///
+/// This writer will be flushed when it is dropped.
+pub struct BufWriter<W> {
+ inner: Option<W>,
+ buf: Vec<u8>,
+}
+
+/// An error returned by `into_inner` which indicates whether a flush error
+/// happened or not.
+#[derive(Debug)]
+pub struct IntoInnerError<W>(W, Error);
+
+impl<W: Write> BufWriter<W> {
+ /// Creates a new `BufWriter` with a default buffer capacity
+ pub fn new(inner: W) -> BufWriter<W> {
+ BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
+ }
+
+ /// Creates a new `BufWriter` with the specified buffer capacity
+ pub fn with_capacity(cap: usize, inner: W) -> BufWriter<W> {
+ BufWriter {
+ inner: Some(inner),
+ buf: Vec::with_capacity(cap),
+ }
+ }
+
+ fn flush_buf(&mut self) -> io::Result<()> {
+ let mut written = 0;
+ let len = self.buf.len();
+ let mut ret = Ok(());
+ while written < len {
+ match self.inner.as_mut().unwrap().write(&self.buf[written..]) {
+ Ok(0) => {
+ ret = Err(Error::new(ErrorKind::WriteZero,
+ "failed to flush", None));
+ break;
+ }
+ Ok(n) => written += n,
+ Err(e) => { ret = Err(e); break }
+
+ }
+ }
+ if written > 0 {
+ // NB: would be better expressed as .remove(0..n) if it existed
+ unsafe {
+ ptr::copy_memory(self.buf.as_mut_ptr(),
+ self.buf.as_ptr().offset(written as isize),
+ len - written);
+ }
+ }
+ self.buf.truncate(len - written);
+ ret
+ }
+
+ /// Gets a reference to the underlying writer.
+ pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() }
+
+ /// Gets a mutable reference to the underlying write.
+ ///
+ /// # Warning
+ ///
+ /// It is inadvisable to directly read from the underlying writer.
+ pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() }
+
+ /// Unwraps this `BufWriter`, returning the underlying writer.
+ ///
+ /// The buffer is flushed before returning the writer.
+ pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
+ match self.flush_buf() {
+ Err(e) => Err(IntoInnerError(self, e)),
+ Ok(()) => Ok(self.inner.take().unwrap())
+ }
+ }
+}
+
+impl<W: Write> Write for BufWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ if self.buf.len() + buf.len() > self.buf.capacity() {
+ try!(self.flush_buf());
+ }
+ if buf.len() >= self.buf.capacity() {
+ self.inner.as_mut().unwrap().write(buf)
+ } else {
+ let amt = cmp::min(buf.len(), self.buf.capacity());
+ Write::write(&mut self.buf, &buf[..amt])
+ }
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ self.flush_buf().and_then(|()| self.get_mut().flush())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<W> fmt::Debug for BufWriter<W> where W: fmt::Debug {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "BufWriter {{ writer: {:?}, buffer: {}/{} }}",
+ self.inner.as_ref().unwrap(), self.buf.len(), self.buf.capacity())
+ }
+}
+
+#[unsafe_destructor]
+impl<W: Write> Drop for BufWriter<W> {
+ fn drop(&mut self) {
+ if self.inner.is_some() {
+ // dtors should not panic, so we ignore a failed flush
+ let _r = self.flush_buf();
+ }
+ }
+}
+
+impl<W> IntoInnerError<W> {
+ /// Returns the error which caused the call to `into_inner` to fail.
+ ///
+ /// This error was returned when attempting to flush the internal buffer.
+ pub fn error(&self) -> &Error { &self.1 }
+
+ /// Returns the underlying `BufWriter` instance which generated the error.
+ ///
+ /// The returned object can be used to retry a flush or re-inspect the
+ /// buffer.
+ pub fn into_inner(self) -> W { self.0 }
+}
+
+impl<W> FromError<IntoInnerError<W>> for Error {
+ fn from_error(iie: IntoInnerError<W>) -> Error { iie.1 }
+}
+
+impl<W> StdError for IntoInnerError<W> {
+ fn description(&self) -> &str { self.error().description() }
+}
+
+impl<W> fmt::Display for IntoInnerError<W> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.error().fmt(f)
+ }
+}
+
+/// Wraps a Writer and buffers output to it, flushing whenever a newline
+/// (`0x0a`, `'\n'`) is detected.
+///
+/// This writer will be flushed when it is dropped.
+pub struct LineWriter<W> {
+ inner: BufWriter<W>,
+}
+
+impl<W: Write> LineWriter<W> {
+ /// Creates a new `LineWriter`
+ pub fn new(inner: W) -> LineWriter<W> {
+ // Lines typically aren't that long, don't use a giant buffer
+ LineWriter { inner: BufWriter::with_capacity(1024, inner) }
+ }
+
+ /// Gets a reference to the underlying writer.
+ ///
+ /// This type does not expose the ability to get a mutable reference to the
+ /// underlying reader because that could possibly corrupt the buffer.
+ pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() }
+
+ /// Unwraps this `LineWriter`, returning the underlying writer.
+ ///
+ /// The internal buffer is flushed before returning the writer.
+ pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
+ self.inner.into_inner().map_err(|IntoInnerError(buf, e)| {
+ IntoInnerError(LineWriter { inner: buf }, e)
+ })
+ }
+}
+
+impl<W: Write> Write for LineWriter<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match buf.rposition_elem(&b'\n') {
+ Some(i) => {
+ let n = try!(self.inner.write(&buf[..i + 1]));
+ if n != i + 1 { return Ok(n) }
+ try!(self.inner.flush());
+ self.inner.write(&buf[i + 1..]).map(|i| n + i)
+ }
+ None => self.inner.write(buf),
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<W> fmt::Debug for LineWriter<W> where W: fmt::Debug {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "LineWriter {{ writer: {:?}, buffer: {}/{} }}",
+ self.inner.inner, self.inner.buf.len(),
+ self.inner.buf.capacity())
+ }
+}
+
+struct InternalBufWriter<W>(BufWriter<W>);
+
+impl<W> InternalBufWriter<W> {
+ fn get_mut(&mut self) -> &mut BufWriter<W> {
+ let InternalBufWriter(ref mut w) = *self;
+ return w;
+ }
+}
+
+impl<W: Read> Read for InternalBufWriter<W> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.get_mut().inner.as_mut().unwrap().read(buf)
+ }
+}
+
+/// Wraps a Stream and buffers input and output to and from it.
+///
+/// It can be excessively inefficient to work directly with a `Stream`. For
+/// example, every call to `read` or `write` on `TcpStream` results in a system
+/// call. A `BufStream` keeps in memory buffers of data, making large,
+/// infrequent calls to `read` and `write` on the underlying `Stream`.
+///
+/// The output half will be flushed when this stream is dropped.
+pub struct BufStream<S> {
+ inner: BufReader<InternalBufWriter<S>>
+}
+
+impl<S: Read + Write> BufStream<S> {
+ /// Creates a new buffered stream with explicitly listed capacities for the
+ /// reader/writer buffer.
+ pub fn with_capacities(reader_cap: usize, writer_cap: usize, inner: S)
+ -> BufStream<S> {
+ let writer = BufWriter::with_capacity(writer_cap, inner);
+ let internal_writer = InternalBufWriter(writer);
+ let reader = BufReader::with_capacity(reader_cap, internal_writer);
+ BufStream { inner: reader }
+ }
+
+ /// Creates a new buffered stream with the default reader/writer buffer
+ /// capacities.
+ pub fn new(inner: S) -> BufStream<S> {
+ BufStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE, inner)
+ }
+
+ /// Gets a reference to the underlying stream.
+ pub fn get_ref(&self) -> &S {
+ let InternalBufWriter(ref w) = self.inner.inner;
+ w.get_ref()
+ }
+
+ /// Gets a mutable reference to the underlying stream.
+ ///
+ /// # Warning
+ ///
+ /// It is inadvisable to read directly from or write directly to the
+ /// underlying stream.
+ pub fn get_mut(&mut self) -> &mut S {
+ let InternalBufWriter(ref mut w) = self.inner.inner;
+ w.get_mut()
+ }
+
+ /// Unwraps this `BufStream`, returning the underlying stream.
+ ///
+ /// The internal buffer is flushed before returning the stream. Any leftover
+ /// data in the read buffer is lost.
+ pub fn into_inner(self) -> Result<S, IntoInnerError<BufStream<S>>> {
+ let BufReader { inner: InternalBufWriter(w), buf } = self.inner;
+ w.into_inner().map_err(|IntoInnerError(w, e)| {
+ IntoInnerError(BufStream {
+ inner: BufReader { inner: InternalBufWriter(w), buf: buf },
+ }, e)
+ })
+ }
+}
+
+impl<S: Read + Write> BufRead for BufStream<S> {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
+ fn consume(&mut self, amt: uint) { self.inner.consume(amt) }
+}
+
+impl<S: Read + Write> Read for BufStream<S> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(buf)
+ }
+}
+
+impl<S: Read + Write> Write for BufStream<S> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.inner.inner.get_mut().write(buf)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ self.inner.inner.get_mut().flush()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<S> fmt::Debug for BufStream<S> where S: fmt::Debug {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let reader = &self.inner;
+ let writer = &self.inner.inner.0;
+ write!(fmt, "BufStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}",
+ writer.inner,
+ writer.buf.len(), writer.buf.capacity(),
+ reader.buf.position(), reader.buf.get_ref().len())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use prelude::v1::*;
+ use io::prelude::*;
+ use io::{self, BufReader, BufWriter, BufStream, Cursor, LineWriter};
+ use test;
+
+ /// A dummy reader intended at testing short-reads propagation.
+ pub struct ShortReader {
+ lengths: Vec<usize>,
+ }
+
+ impl Read for ShortReader {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
+ if self.lengths.is_empty() {
+ Ok(0)
+ } else {
+ Ok(self.lengths.remove(0))
+ }
+ }
+ }
+
+ #[test]
+ fn test_buffered_reader() {
+ let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+ let mut reader = BufReader::with_capacity(2, inner);
+
+ let mut buf = [0, 0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(Ok(3), nread);
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(buf, b);
+
+ let mut buf = [0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(Ok(2), nread);
+ let b: &[_] = &[0, 1];
+ assert_eq!(buf, b);
+
+ let mut buf = [0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(Ok(1), nread);
+ let b: &[_] = &[2];
+ assert_eq!(buf, b);
+
+ let mut buf = [0, 0, 0];
+ let nread = reader.read(&mut buf);
+ assert_eq!(Ok(1), nread);
+ let b: &[_] = &[3, 0, 0];
+ assert_eq!(buf, b);
+
+ let nread = reader.read(&mut buf);
+ assert_eq!(Ok(1), nread);
+ let b: &[_] = &[4, 0, 0];
+ assert_eq!(buf, b);
+
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ }
+
+ #[test]
+ fn test_buffered_writer() {
+ let inner = Vec::new();
+ let mut writer = BufWriter::with_capacity(2, inner);
+
+ writer.write(&[0, 1]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.write(&[2]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.write(&[3]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1]);
+
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+ writer.write(&[4]).unwrap();
+ writer.write(&[5]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+ writer.write(&[6]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
+
+ writer.write(&[7, 8]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+ writer.write(&[9, 10, 11]).unwrap();
+ let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+ }
+
+ #[test]
+ fn test_buffered_writer_inner_flushes() {
+ let mut w = BufWriter::with_capacity(3, Vec::new());
+ w.write(&[0, 1]).unwrap();
+ assert_eq!(*w.get_ref(), []);
+ let w = w.into_inner().unwrap();
+ assert_eq!(w, [0, 1]);
+ }
+
+ // This is just here to make sure that we don't infinite loop in the
+ // newtype struct autoderef weirdness
+ #[test]
+ fn test_buffered_stream() {
+ struct S;
+
+ impl Write for S {
+ fn write(&mut self, b: &[u8]) -> io::Result<usize> { Ok(b.len()) }
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+ }
+
+ impl Read for S {
+ fn read(&mut self, _: &mut [u8]) -> io::Result<usize> { Ok(0) }
+ }
+
+ let mut stream = BufStream::new(S);
+ assert_eq!(stream.read(&mut [0; 10]), Ok(0));
+ stream.write(&[0; 10]).unwrap();
+ stream.flush().unwrap();
+ }
+
+ #[test]
+ fn test_read_until() {
+ let inner: &[u8] = &[0, 1, 2, 1, 0];
+ let mut reader = BufReader::with_capacity(2, inner);
+ let mut v = Vec::new();
+ reader.read_until(0, &mut v).unwrap();
+ assert_eq!(v, [0]);
+ v.truncate(0);
+ reader.read_until(2, &mut v).unwrap();
+ assert_eq!(v, [1, 2]);
+ v.truncate(0);
+ reader.read_until(1, &mut v).unwrap();
+ assert_eq!(v, [1]);
+ v.truncate(0);
+ reader.read_until(8, &mut v).unwrap();
+ assert_eq!(v, [0]);
+ v.truncate(0);
+ reader.read_until(9, &mut v).unwrap();
+ assert_eq!(v, []);
+ }
+
+ #[test]
+ fn test_line_buffer() {
+ let mut writer = LineWriter::new(Vec::new());
+ writer.write(&[0]).unwrap();
+ assert_eq!(*writer.get_ref(), []);
+ writer.write(&[1]).unwrap();
+ assert_eq!(*writer.get_ref(), []);
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1]);
+ writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']);
+ writer.flush().unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]);
+ writer.write(&[3, b'\n']).unwrap();
+ assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']);
+ }
+
+ #[test]
+ fn test_read_line() {
+ let in_buf = b"a\nb\nc";
+ let mut reader = BufReader::with_capacity(2, in_buf);
+ let mut s = String::new();
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "a\n");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "b\n");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "c");
+ s.truncate(0);
+ reader.read_line(&mut s).unwrap();
+ assert_eq!(s, "");
+ }
+
+ #[test]
+ fn test_lines() {
+ let in_buf = b"a\nb\nc";
+ let mut reader = BufReader::with_capacity(2, in_buf);
+ let mut it = reader.lines();
+ assert_eq!(it.next(), Some(Ok("a".to_string())));
+ assert_eq!(it.next(), Some(Ok("b".to_string())));
+ assert_eq!(it.next(), Some(Ok("c".to_string())));
+ assert_eq!(it.next(), None);
+ }
+
+ #[test]
+ fn test_short_reads() {
+ let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]};
+ let mut reader = BufReader::new(inner);
+ let mut buf = [0, 0];
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ assert_eq!(reader.read(&mut buf), Ok(1));
+ assert_eq!(reader.read(&mut buf), Ok(2));
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ assert_eq!(reader.read(&mut buf), Ok(1));
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ }
+
+ #[test]
+ fn read_char_buffered() {
+ let buf = [195u8, 159u8];
+ let mut reader = BufReader::with_capacity(1, &buf[]);
+ assert_eq!(reader.chars().next(), Some(Ok('ß')));
+ }
+
+ #[test]
+ fn test_chars() {
+ let buf = [195u8, 159u8, b'a'];
+ let mut reader = BufReader::with_capacity(1, &buf[]);
+ let mut it = reader.chars();
+ assert_eq!(it.next(), Some(Ok('ß')));
+ assert_eq!(it.next(), Some(Ok('a')));
+ assert_eq!(it.next(), None);
+ }
+
+ #[test]
+ #[should_fail]
+ fn dont_panic_in_drop_on_panicked_flush() {
+ struct FailFlushWriter;
+
+ impl Write for FailFlushWriter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
+ fn flush(&mut self) -> io::Result<()> {
+ Err(io::Error::last_os_error())
+ }
+ }
+
+ let writer = FailFlushWriter;
+ let _writer = BufWriter::new(writer);
+
+ // If writer panics *again* due to the flush error then the process will
+ // abort.
+ panic!();
+ }
+
+ #[bench]
+ fn bench_buffered_reader(b: &mut test::Bencher) {
+ b.iter(|| {
+ BufReader::new(io::empty())
+ });
+ }
+
+ #[bench]
+ fn bench_buffered_writer(b: &mut test::Bencher) {
+ b.iter(|| {
+ BufWriter::new(io::sink())
+ });
+ }
+
+ #[bench]
+ fn bench_buffered_stream(b: &mut test::Bencher) {
+ let mut buf = Cursor::new(Vec::new());
+ b.iter(|| {
+ BufStream::new(&mut buf);
+ });
+ }
+}
--- /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.
+
+#![allow(missing_copy_implementations)]
+
+use prelude::v1::*;
+use io::prelude::*;
+
+use cmp;
+use io::{self, SeekFrom, Error, ErrorKind};
+use iter::repeat;
+use num::Int;
+use slice;
+
+/// A `Cursor` is a type which wraps another I/O object to provide a `Seek`
+/// implementation.
+///
+/// Cursors are currently typically used with memory buffer objects in order to
+/// allow `Seek` plus `Read` and `Write` implementations. For example, common
+/// cursor types include:
+///
+/// * `Cursor<Vec<u8>>`
+/// * `Cursor<&[u8]>`
+///
+/// Implementations of the I/O traits for `Cursor<T>` are not currently generic
+/// over `T` itself. Instead, specific implementations are provided for various
+/// in-memory buffer types like `Vec<u8>` and `&[u8]`.
+pub struct Cursor<T> {
+ inner: T,
+ pos: u64,
+}
+
+impl<T> Cursor<T> {
+ /// Create a new cursor wrapping the provided underlying I/O object.
+ pub fn new(inner: T) -> Cursor<T> {
+ Cursor { pos: 0, inner: inner }
+ }
+
+ /// Consume this cursor, returning the underlying value.
+ pub fn into_inner(self) -> T { self.inner }
+
+ /// Get a reference to the underlying value in this cursor.
+ pub fn get_ref(&self) -> &T { &self.inner }
+
+ /// Get a mutable reference to the underlying value in this cursor.
+ ///
+ /// Care should be taken to avoid modifying the internal I/O state of the
+ /// underlying value as it may corrupt this cursor's position.
+ pub fn get_mut(&mut self) -> &mut T { &mut self.inner }
+
+ /// Returns the current value of this cursor
+ pub fn position(&self) -> u64 { self.pos }
+
+ /// Sets the value of this cursor
+ pub fn set_position(&mut self, pos: u64) { self.pos = pos; }
+}
+
+macro_rules! seek {
+ () => {
+ fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
+ let pos = match style {
+ SeekFrom::Start(n) => { self.pos = n; return Ok(n) }
+ SeekFrom::End(n) => self.inner.len() as i64 + n,
+ SeekFrom::Current(n) => self.pos as i64 + n,
+ };
+
+ if pos < 0 {
+ Err(Error::new(ErrorKind::InvalidInput,
+ "invalid seek to a negative position",
+ None))
+ } else {
+ self.pos = pos as u64;
+ Ok(self.pos)
+ }
+ }
+ }
+}
+
+impl<'a> io::Seek for Cursor<&'a [u8]> { seek!(); }
+impl<'a> io::Seek for Cursor<&'a mut [u8]> { seek!(); }
+impl io::Seek for Cursor<Vec<u8>> { seek!(); }
+
+macro_rules! read {
+ () => {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let n = try!(Read::read(&mut try!(self.fill_buf()), buf));
+ self.pos += n as u64;
+ Ok(n)
+ }
+ }
+}
+
+impl<'a> Read for Cursor<&'a [u8]> { read!(); }
+impl<'a> Read for Cursor<&'a mut [u8]> { read!(); }
+impl Read for Cursor<Vec<u8>> { read!(); }
+
+macro_rules! buffer {
+ () => {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ let amt = cmp::min(self.pos, self.inner.len() as u64);
+ Ok(&self.inner[(amt as usize)..])
+ }
+ fn consume(&mut self, amt: usize) { self.pos += amt as u64; }
+ }
+}
+
+impl<'a> BufRead for Cursor<&'a [u8]> { buffer!(); }
+impl<'a> BufRead for Cursor<&'a mut [u8]> { buffer!(); }
+impl<'a> BufRead for Cursor<Vec<u8>> { buffer!(); }
+
+impl<'a> Write for Cursor<&'a mut [u8]> {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let pos = cmp::min(self.pos, self.inner.len() as u64);
+ let amt = try!((&mut self.inner[(pos as usize)..]).write(data));
+ self.pos += amt as u64;
+ Ok(amt)
+ }
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+impl Write for Cursor<Vec<u8>> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ // Make sure the internal buffer is as least as big as where we
+ // currently are
+ let pos = self.position();
+ let amt = pos.saturating_sub(self.inner.len() as u64);
+ self.inner.extend(repeat(0).take(amt as usize));
+
+ // Figure out what bytes will be used to overwrite what's currently
+ // there (left), and what will be appended on the end (right)
+ let space = self.inner.len() - pos as usize;
+ let (left, right) = buf.split_at(cmp::min(space, buf.len()));
+ slice::bytes::copy_memory(&mut self.inner[(pos as usize)..], left);
+ self.inner.push_all(right);
+
+ // Bump us forward
+ self.set_position(pos + buf.len() as u64);
+ Ok(buf.len())
+ }
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use core::prelude::*;
+
+ use io::prelude::*;
+ use io::{Cursor, SeekFrom};
+ use vec::Vec;
+
+ #[test]
+ fn test_vec_writer() {
+ let mut writer = Vec::new();
+ assert_eq!(writer.write(&[0]), Ok(1));
+ assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
+ assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+ assert_eq!(writer, b);
+ }
+
+ #[test]
+ fn test_mem_writer() {
+ let mut writer = Cursor::new(Vec::new());
+ assert_eq!(writer.write(&[0]), Ok(1));
+ assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
+ assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[], b);
+ }
+
+ #[test]
+ fn test_buf_writer() {
+ let mut buf = [0 as u8; 9];
+ {
+ let mut writer = Cursor::new(&mut buf[]);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[0]), Ok(1));
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
+ assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
+ assert_eq!(writer.position(), 8);
+ assert_eq!(writer.write(&[]), Ok(0));
+ assert_eq!(writer.position(), 8);
+
+ assert_eq!(writer.write(&[8, 9]), Ok(1));
+ assert_eq!(writer.write(&[10]), Ok(0));
+ }
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+ assert_eq!(buf, b);
+ }
+
+ #[test]
+ fn test_buf_writer_seek() {
+ let mut buf = [0 as u8; 8];
+ {
+ let mut writer = Cursor::new(&mut buf[]);
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[1]), Ok(1));
+ assert_eq!(writer.position(), 1);
+
+ assert_eq!(writer.seek(SeekFrom::Start(2)), Ok(2));
+ assert_eq!(writer.position(), 2);
+ assert_eq!(writer.write(&[2]), Ok(1));
+ assert_eq!(writer.position(), 3);
+
+ assert_eq!(writer.seek(SeekFrom::Current(-2)), Ok(1));
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[3]), Ok(1));
+ assert_eq!(writer.position(), 2);
+
+ assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7));
+ assert_eq!(writer.position(), 7);
+ assert_eq!(writer.write(&[4]), Ok(1));
+ assert_eq!(writer.position(), 8);
+
+ }
+ let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
+ assert_eq!(buf, b);
+ }
+
+ #[test]
+ fn test_buf_writer_error() {
+ let mut buf = [0 as u8; 2];
+ let mut writer = Cursor::new(&mut buf[]);
+ assert_eq!(writer.write(&[0]), Ok(1));
+ assert_eq!(writer.write(&[0, 0]), Ok(1));
+ assert_eq!(writer.write(&[0, 0]), Ok(0));
+ }
+
+ #[test]
+ fn test_mem_reader() {
+ let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7));
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf), Ok(1));
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf), Ok(4));
+ assert_eq!(reader.position(), 5);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf, b);
+ assert_eq!(reader.read(&mut buf), Ok(3));
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ }
+
+ #[test]
+ fn read_to_end() {
+ let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7));
+ let mut v = Vec::new();
+ reader.read_to_end(&mut v).ok().unwrap();
+ assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
+ }
+
+ #[test]
+ fn test_slice_reader() {
+ let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
+ let mut reader = &mut in_buf.as_slice();
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf), Ok(1));
+ assert_eq!(reader.len(), 7);
+ let b: &[_] = &[0];
+ assert_eq!(buf.as_slice(), b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf), Ok(4));
+ assert_eq!(reader.len(), 3);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf.as_slice(), b);
+ assert_eq!(reader.read(&mut buf), Ok(3));
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ }
+
+ #[test]
+ fn test_buf_reader() {
+ let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
+ let mut reader = Cursor::new(in_buf.as_slice());
+ let mut buf = [];
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ assert_eq!(reader.position(), 0);
+ let mut buf = [0];
+ assert_eq!(reader.read(&mut buf), Ok(1));
+ assert_eq!(reader.position(), 1);
+ let b: &[_] = &[0];
+ assert_eq!(buf, b);
+ let mut buf = [0; 4];
+ assert_eq!(reader.read(&mut buf), Ok(4));
+ assert_eq!(reader.position(), 5);
+ let b: &[_] = &[1, 2, 3, 4];
+ assert_eq!(buf, b);
+ assert_eq!(reader.read(&mut buf), Ok(3));
+ let b: &[_] = &[5, 6, 7];
+ assert_eq!(&buf[..3], b);
+ assert_eq!(reader.read(&mut buf), Ok(0));
+ }
+
+ #[test]
+ fn test_read_char() {
+ let b = b"Vi\xE1\xBB\x87t";
+ let mut c = Cursor::new(b).chars();
+ assert_eq!(c.next(), Some(Ok('V')));
+ assert_eq!(c.next(), Some(Ok('i')));
+ assert_eq!(c.next(), Some(Ok('ệ')));
+ assert_eq!(c.next(), Some(Ok('t')));
+ assert_eq!(c.next(), None);
+ }
+
+ #[test]
+ fn test_read_bad_char() {
+ let b = b"\x80";
+ let mut c = Cursor::new(b).chars();
+ assert!(c.next().unwrap().is_err());
+ }
+
+ #[test]
+ fn seek_past_end() {
+ let buf = [0xff];
+ let mut r = Cursor::new(&buf[]);
+ assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
+ assert_eq!(r.read(&mut [0]), Ok(0));
+
+ let mut r = Cursor::new(vec!(10u8));
+ assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
+ assert_eq!(r.read(&mut [0]), Ok(0));
+
+ let mut buf = [0];
+ let mut r = Cursor::new(&mut buf[]);
+ assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
+ assert_eq!(r.write(&[3]), Ok(0));
+ }
+
+ #[test]
+ fn seek_before_0() {
+ let buf = [0xff_u8];
+ let mut r = Cursor::new(&buf[]);
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+
+ let mut r = Cursor::new(vec!(10u8));
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+
+ let mut buf = [0];
+ let mut r = Cursor::new(&mut buf[]);
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+ }
+
+ #[test]
+ fn test_seekable_mem_writer() {
+ let mut writer = Cursor::new(Vec::<u8>::new());
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[0]), Ok(1));
+ assert_eq!(writer.position(), 1);
+ assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
+ assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
+ assert_eq!(writer.position(), 8);
+ let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[], b);
+
+ assert_eq!(writer.seek(SeekFrom::Start(0)), Ok(0));
+ assert_eq!(writer.position(), 0);
+ assert_eq!(writer.write(&[3, 4]), Ok(2));
+ let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[], b);
+
+ assert_eq!(writer.seek(SeekFrom::Current(1)), Ok(3));
+ assert_eq!(writer.write(&[0, 1]), Ok(2));
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
+ assert_eq!(&writer.get_ref()[], b);
+
+ assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7));
+ assert_eq!(writer.write(&[1, 2]), Ok(2));
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
+ assert_eq!(&writer.get_ref()[], b);
+
+ assert_eq!(writer.seek(SeekFrom::End(1)), Ok(10));
+ assert_eq!(writer.write(&[1]), Ok(1));
+ let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
+ assert_eq!(&writer.get_ref()[], b);
+ }
+
+ #[test]
+ fn vec_seek_past_end() {
+ let mut r = Cursor::new(Vec::new());
+ assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
+ assert_eq!(r.write(&[3]), Ok(1));
+ }
+
+ #[test]
+ fn vec_seek_before_0() {
+ let mut r = Cursor::new(Vec::new());
+ assert!(r.seek(SeekFrom::End(-2)).is_err());
+ }
+}
--- /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.
+
+use boxed::Box;
+use clone::Clone;
+use error::Error as StdError;
+use fmt;
+use option::Option::{self, Some, None};
+use result;
+use string::String;
+use sys;
+
+/// A type for results generated by I/O related functions where the `Err` type
+/// is hard-wired to `io::Error`.
+///
+/// This typedef is generally used to avoid writing out `io::Error` directly and
+/// is otherwise a direct mapping to `std::result::Result`.
+pub type Result<T> = result::Result<T, Error>;
+
+/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and
+/// associated traits.
+///
+/// Errors mostly originate from the underlying OS, but custom instances of
+/// `Error` can be created with crafted error messages and a particular value of
+/// `ErrorKind`.
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct Error {
+ repr: Repr,
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+enum Repr {
+ Os(i32),
+ Custom(Box<Custom>),
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+struct Custom {
+ kind: ErrorKind,
+ desc: &'static str,
+ detail: Option<String>
+}
+
+/// A list specifying general categories of I/O error.
+#[derive(Copy, PartialEq, Eq, Clone, Debug)]
+pub enum ErrorKind {
+ /// The file was not found.
+ FileNotFound,
+ /// The file permissions disallowed access to this file.
+ PermissionDenied,
+ /// The connection was refused by the remote server.
+ ConnectionRefused,
+ /// The connection was reset by the remote server.
+ ConnectionReset,
+ /// The connection was aborted (terminated) by the remote server.
+ ConnectionAborted,
+ /// The network operation failed because it was not connected yet.
+ NotConnected,
+ /// The operation failed because a pipe was closed.
+ BrokenPipe,
+ /// A file already existed with that name.
+ PathAlreadyExists,
+ /// No file exists at that location.
+ PathDoesntExist,
+ /// The path did not specify the type of file that this operation required.
+ /// For example, attempting to copy a directory with the `fs::copy()`
+ /// operation will fail with this error.
+ MismatchedFileTypeForOperation,
+ /// The operation temporarily failed (for example, because a signal was
+ /// received), and retrying may succeed.
+ ResourceUnavailable,
+ /// A parameter was incorrect in a way that caused an I/O error not part of
+ /// this list.
+ InvalidInput,
+ /// The I/O operation's timeout expired, causing it to be canceled.
+ TimedOut,
+ /// An error returned when an operation could not be completed because a
+ /// call to `write` returned `Ok(0)`.
+ ///
+ /// This typically means that an operation could only succeed if it wrote a
+ /// particular number of bytes but only a smaller number of bytes could be
+ /// written.
+ WriteZero,
+ /// This operation was interrupted
+ Interrupted,
+ /// Any I/O error not part of this list.
+ Other,
+}
+
+impl Error {
+ /// Creates a new custom error from a specified kind/description/detail.
+ pub fn new(kind: ErrorKind,
+ description: &'static str,
+ detail: Option<String>) -> Error {
+ Error {
+ repr: Repr::Custom(Box::new(Custom {
+ kind: kind,
+ desc: description,
+ detail: detail,
+ }))
+ }
+ }
+
+ /// Returns an error representing the last OS error which occurred.
+ ///
+ /// This function reads the value of `errno` for the target platform (e.g.
+ /// `GetLastError` on Windows) and will return a corresponding instance of
+ /// `Error` for the error code.
+ pub fn last_os_error() -> Error {
+ Error::from_os_error(sys::os::errno() as i32)
+ }
+
+ /// Creates a new instance of an `Error` from a particular OS error code.
+ pub fn from_os_error(code: i32) -> Error {
+ Error { repr: Repr::Os(code) }
+ }
+
+ /// Return the corresponding `ErrorKind` for this error.
+ pub fn kind(&self) -> ErrorKind {
+ match self.repr {
+ Repr::Os(code) => sys::decode_error_kind(code),
+ Repr::Custom(ref c) => c.kind,
+ }
+ }
+
+ /// Returns a short description for this error message
+ pub fn description(&self) -> &str {
+ match self.repr {
+ Repr::Os(..) => "os error",
+ Repr::Custom(ref c) => c.desc,
+ }
+ }
+
+ /// Returns a detailed error message for this error (if one is available)
+ pub fn detail(&self) -> Option<String> {
+ match self.repr {
+ Repr::Os(code) => Some(sys::os::error_string(code)),
+ Repr::Custom(ref s) => s.detail.clone(),
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self.repr {
+ Repr::Os(code) => {
+ let detail = sys::os::error_string(code);
+ write!(fmt, "{} (os error {})", detail, code)
+ }
+ Repr::Custom(ref c) => {
+ match **c {
+ Custom {
+ kind: ErrorKind::Other,
+ desc: "unknown error",
+ detail: Some(ref detail)
+ } => {
+ write!(fmt, "{}", detail)
+ }
+ Custom { detail: None, desc, .. } =>
+ write!(fmt, "{}", desc),
+ Custom { detail: Some(ref detail), desc, .. } =>
+ write!(fmt, "{} ({})", desc, detail)
+ }
+ }
+ }
+ }
+}
+
+impl StdError for Error {
+ fn description(&self) -> &str {
+ match self.repr {
+ Repr::Os(..) => "os error",
+ Repr::Custom(ref c) => c.desc,
+ }
+ }
+}
--- /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.
+
+use core::prelude::*;
+
+use boxed::Box;
+use cmp;
+use io::{self, SeekFrom, Read, Write, Seek, BufRead};
+use mem;
+use slice;
+use vec::Vec;
+
+// =============================================================================
+// Forwarding implementations
+
+impl<'a, R: Read + ?Sized> Read for &'a mut R {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) }
+}
+impl<'a, W: Write + ?Sized> Write for &'a mut W {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
+ fn flush(&mut self) -> io::Result<()> { (**self).flush() }
+}
+impl<'a, S: Seek + ?Sized> Seek for &'a mut S {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
+}
+impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }
+ fn consume(&mut self, amt: usize) { (**self).consume(amt) }
+}
+
+impl<R: Read + ?Sized> Read for Box<R> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) }
+}
+impl<W: Write + ?Sized> Write for Box<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
+ fn flush(&mut self) -> io::Result<()> { (**self).flush() }
+}
+impl<S: Seek + ?Sized> Seek for Box<S> {
+ fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
+}
+impl<B: BufRead + ?Sized> BufRead for Box<B> {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }
+ fn consume(&mut self, amt: usize) { (**self).consume(amt) }
+}
+
+// =============================================================================
+// In-memory buffer implementations
+
+impl<'a> Read for &'a [u8] {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let amt = cmp::min(buf.len(), self.len());
+ let (a, b) = self.split_at(amt);
+ slice::bytes::copy_memory(buf, a);
+ *self = b;
+ Ok(amt)
+ }
+}
+
+impl<'a> BufRead for &'a [u8] {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) }
+ fn consume(&mut self, amt: usize) { *self = &self[amt..]; }
+}
+
+impl<'a> Write for &'a mut [u8] {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let amt = cmp::min(data.len(), self.len());
+ let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
+ slice::bytes::copy_memory(a, &data[..amt]);
+ *self = b;
+ Ok(amt)
+ }
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+impl Write for Vec<u8> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.push_all(buf);
+ Ok(buf.len())
+ }
+ fn flush(&mut self) -> io::Result<()> { 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.
+
+//! Traits, helpers, and type definitions for core I/O functionality.
+//!
+//! > **NOTE**: This module is very much a work in progress and is under active
+//! > development. At this time it is still recommended to use the `old_io`
+//! > module while the details of this module shake out.
+
+#![unstable(feature = "io",
+ reason = "this new I/O module is still under active deveopment and \
+ APIs are subject to tweaks fairly regularly")]
+
+use cmp;
+use unicode::str as core_str;
+use error::Error as StdError;
+use fmt;
+use iter::Iterator;
+use marker::Sized;
+use mem;
+use ops::{Drop, FnOnce};
+use option::Option::{self, Some, None};
+use ptr::PtrExt;
+use result::Result::{Ok, Err};
+use result;
+use slice::{self, SliceExt};
+use string::String;
+use str::{self, StrExt};
+use vec::Vec;
+
+pub use self::buffered::{BufReader, BufWriter, BufStream, LineWriter};
+pub use self::buffered::IntoInnerError;
+pub use self::cursor::Cursor;
+pub use self::error::{Result, Error, ErrorKind};
+pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat};
+
+pub mod prelude;
+mod buffered;
+mod cursor;
+mod error;
+mod impls;
+mod util;
+
+const DEFAULT_BUF_SIZE: usize = 64 * 1024;
+
+// Acquires a slice of the vector `v` from its length to its capacity
+// (uninitialized data), reads into it, and then updates the length.
+//
+// This function is leveraged to efficiently read some bytes into a destination
+// vector without extra copying and taking advantage of the space that's already
+// in `v`.
+//
+// The buffer we're passing down, however, is pointing at uninitialized data
+// (the end of a `Vec`), and many operations will be *much* faster if we don't
+// have to zero it out. In order to prevent LLVM from generating an `undef`
+// value when reads happen from this uninitialized memory, we force LLVM to
+// think it's initialized by sending it through a black box. This should prevent
+// actual undefined behavior after optimizations.
+fn with_end_to_cap<F>(v: &mut Vec<u8>, f: F) -> Result<usize>
+ where F: FnOnce(&mut [u8]) -> Result<usize>
+{
+ unsafe {
+ let n = try!(f({
+ let base = v.as_mut_ptr().offset(v.len() as isize);
+ black_box(slice::from_raw_mut_buf(mem::copy_lifetime(v, &base),
+ v.capacity() - v.len()))
+ }));
+
+ // If the closure (typically a `read` implementation) reported that it
+ // read a larger number of bytes than the vector actually has, we need
+ // to be sure to clamp the vector to at most its capacity.
+ let new_len = cmp::min(v.capacity(), v.len() + n);
+ v.set_len(new_len);
+ return Ok(n);
+ }
+
+ // Semi-hack used to prevent LLVM from retaining any assumptions about
+ // `dummy` over this function call
+ unsafe fn black_box<T>(mut dummy: T) -> T {
+ asm!("" :: "r"(&mut dummy) : "memory");
+ dummy
+ }
+}
+
+// A few methods below (read_to_string, read_line) will append data into a
+// `String` buffer, but we need to be pretty careful when doing this. The
+// implementation will just call `.as_mut_vec()` and then delegate to a
+// byte-oriented reading method, but we must ensure that when returning we never
+// leave `buf` in a state such that it contains invalid UTF-8 in its bounds.
+//
+// To this end, we use an RAII guard (to protect against panics) which updates
+// the length of the string when it is dropped. This guard initially truncates
+// the string to the prior length and only afer we've validated that the
+// new contents are valid UTF-8 do we allow it to set a longer length.
+//
+// The unsafety in this function is twofold:
+//
+// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
+// checks.
+// 2. We're passing a raw buffer to the function `f`, and it is expected that
+// the function only *appends* bytes to the buffer. We'll get undefined
+// behavior if existing bytes are overwritten to have non-UTF-8 data.
+fn append_to_string<F>(buf: &mut String, f: F) -> Result<()>
+ where F: FnOnce(&mut Vec<u8>) -> Result<()>
+{
+ struct Guard<'a> { s: &'a mut Vec<u8>, len: usize }
+ #[unsafe_destructor]
+ impl<'a> Drop for Guard<'a> {
+ fn drop(&mut self) {
+ unsafe { self.s.set_len(self.len); }
+ }
+ }
+
+ unsafe {
+ let mut g = Guard { len: buf.len(), s: buf.as_mut_vec() };
+ let ret = f(g.s);
+ if str::from_utf8(&g.s[g.len..]).is_err() {
+ ret.and_then(|()| {
+ Err(Error::new(ErrorKind::InvalidInput,
+ "stream did not contain valid UTF-8", None))
+ })
+ } else {
+ g.len = g.s.len();
+ ret
+ }
+ }
+}
+
+fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<()> {
+ loop {
+ if buf.capacity() == buf.len() {
+ buf.reserve(DEFAULT_BUF_SIZE);
+ }
+ match with_end_to_cap(buf, |b| r.read(b)) {
+ Ok(0) => return Ok(()),
+ Ok(_) => {}
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => return Err(e),
+ }
+ }
+}
+
+/// A trait for objects which are byte-oriented sources.
+///
+/// Readers are defined by one method, `read`. Each call to `read` will attempt
+/// to pull bytes from this source into a provided buffer.
+///
+/// Readers are intended to be composable with one another. Many objects
+/// throughout the I/O and related libraries take and provide types which
+/// implement the `Read` trait.
+pub trait Read {
+ /// Pull some bytes from this source into the specified buffer, returning
+ /// how many bytes were read.
+ ///
+ /// This function does not provide any guarantees about whether it blocks
+ /// waiting for data, but if an object needs to block for a read but cannot
+ /// it will typically signal this via an `Err` return value.
+ ///
+ /// If the return value of this method is `Ok(n)`, then it must be
+ /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates
+ /// that the buffer `buf` has ben filled in with `n` bytes of data from this
+ /// source. If `n` is `0`, then it can indicate one of two scenarios:
+ ///
+ /// 1. This reader has reached its "end of file" and will likely no longer
+ /// be able to produce bytes. Note that this does not mean that the
+ /// reader will *always* no longer be able to produce bytes.
+ /// 2. The buffer specified was 0 bytes in length.
+ ///
+ /// No guarantees are provided about the contents of `buf` when this
+ /// function is called, implementations cannot rely on any property of the
+ /// contents of `buf` being true. It is recommended that implementations
+ /// only write data to `buf` instead of reading its contents.
+ ///
+ /// # Errors
+ ///
+ /// If this function encounters any form of I/O or other error, an error
+ /// variant will be returned. If an error is returned then it must be
+ /// guaranteed that no bytes were read.
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
+
+ /// Read all bytes until EOF in this source, placing them into `buf`.
+ ///
+ /// All bytes read from this source will be appended to the specified buffer
+ /// `buf`. This function will return a call to `read` either:
+ ///
+ /// 1. Returns `Ok(0)`.
+ /// 2. Returns an error which is not of the kind `ErrorKind::Interrupted`.
+ ///
+ /// Until one of these conditions is met the function will continuously
+ /// invoke `read` to append more data to `buf`.
+ ///
+ /// # Errors
+ ///
+ /// If this function encounters an error of the kind
+ /// `ErrorKind::Interrupted` then the error is ignored and the operation
+ /// will continue.
+ ///
+ /// If any other read error is encountered then this function immediately
+ /// returns. Any bytes which have already been read will be appended to
+ /// `buf`.
+ fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<()> {
+ read_to_end(self, buf)
+ }
+
+ /// Read all bytes until EOF in this source, placing them into `buf`.
+ ///
+ /// # Errors
+ ///
+ /// If the data in this stream is *not* valid UTF-8 then an error is
+ /// returned and `buf` is unchanged.
+ ///
+ /// See `read_to_end` for other error semantics.
+ fn read_to_string(&mut self, buf: &mut String) -> Result<()> {
+ // Note that we do *not* call `.read_to_end()` here. We are passing
+ // `&mut Vec<u8>` (the raw contents of `buf`) into the `read_to_end`
+ // method to fill it up. An arbitrary implementation could overwrite the
+ // entire contents of the vector, not just append to it (which is what
+ // we are expecting).
+ //
+ // To prevent extraneously checking the UTF-8-ness of the entire buffer
+ // we pass it to our hardcoded `read_to_end` implementation which we
+ // know is guaranteed to only read data into the end of the buffer.
+ append_to_string(buf, |b| read_to_end(self, b))
+ }
+}
+
+/// Extension methods for all instances of `Read`, typically imported through
+/// `std::io::prelude::*`.
+pub trait ReadExt: Read + Sized {
+ /// Create a "by reference" adaptor for this instance of `Read`.
+ ///
+ /// The returned adaptor also implements `Read` and will simply borrow this
+ /// current reader.
+ fn by_ref(&mut self) -> &mut Self { self }
+
+ /// Transform this `Read` instance to an `Iterator` over its bytes.
+ ///
+ /// The returned type implements `Iterator` where the `Item` is `Result<u8,
+ /// R::Err>`. The yielded item is `Ok` if a byte was successfully read and
+ /// `Err` otherwise for I/O errors. EOF is mapped to returning `None` from
+ /// this iterator.
+ fn bytes(self) -> Bytes<Self> {
+ Bytes { inner: self }
+ }
+
+ /// Transform this `Read` instance to an `Iterator` over `char`s.
+ ///
+ /// This adaptor will attempt to interpret this reader as an UTF-8 encoded
+ /// sequence of characters. The returned iterator will return `None` once
+ /// EOF is reached for this reader. Otherwise each element yielded will be a
+ /// `Result<char, E>` where `E` may contain information about what I/O error
+ /// occurred or where decoding failed.
+ ///
+ /// Currently this adaptor will discard intermediate data read, and should
+ /// be avoided if this is not desired.
+ fn chars(self) -> Chars<Self> {
+ Chars { inner: self }
+ }
+
+ /// Create an adaptor which will chain this stream with another.
+ ///
+ /// The returned `Read` instance will first read all bytes from this object
+ /// until EOF is encountered. Afterwards the output is equivalent to the
+ /// output of `next`.
+ fn chain<R: Read>(self, next: R) -> Chain<Self, R> {
+ Chain { first: self, second: next, done_first: false }
+ }
+
+ /// Create an adaptor which will read at most `limit` bytes from it.
+ ///
+ /// This function returns a new instance of `Read` which will read at most
+ /// `limit` bytes, after which it will always return EOF (`Ok(0)`). Any
+ /// read errors will not count towards the number of bytes read and future
+ /// calls to `read` may succeed.
+ fn take(self, limit: u64) -> Take<Self> {
+ Take { inner: self, limit: limit }
+ }
+
+ /// Creates a reader adaptor which will write all read data into the given
+ /// output stream.
+ ///
+ /// Whenever the returned `Read` instance is read it will write the read
+ /// data to `out`. The current semantics of this implementation imply that
+ /// a `write` error will not report how much data was initially read.
+ fn tee<W: Write>(self, out: W) -> Tee<Self, W> {
+ Tee { reader: self, writer: out }
+ }
+}
+
+impl<T: Read> ReadExt for T {}
+
+/// A trait for objects which are byte-oriented sinks.
+///
+/// The `write` method will attempt to write some data into the object,
+/// returning how many bytes were successfully written.
+///
+/// The `flush` method is useful for adaptors and explicit buffers themselves
+/// for ensuring that all buffered data has been pushed out to the "true sink".
+///
+/// Writers are intended to be composable with one another. Many objects
+/// throughout the I/O and related libraries take and provide types which
+/// implement the `Write` trait.
+pub trait Write {
+ /// Write a buffer into this object, returning how many bytes were written.
+ ///
+ /// This function will attempt to write the entire contents of `buf`, but
+ /// the entire write may not succeed, or the write may also generate an
+ /// error. A call to `write` represents *at most one* attempt to write to
+ /// any wrapped object.
+ ///
+ /// Calls to `write` are not guaranteed to block waiting for data to be
+ /// written, and a write which would otherwise block can indicated through
+ /// an `Err` variant.
+ ///
+ /// If the return value is `Ok(n)` then it must be guaranteed that
+ /// `0 <= n <= buf.len()`. A return value of `0` typically means that the
+ /// underlying object is no longer able to accept bytes and will likely not
+ /// be able to in the future as well, or that the buffer provided is empty.
+ ///
+ /// # Errors
+ ///
+ /// Each call to `write` may generate an I/O error indicating that the
+ /// operation could not be completed. If an error is returned then no bytes
+ /// in the buffer were written to this writer.
+ ///
+ /// It is **not** considered an error if the entire buffer could not be
+ /// written to this writer.
+ fn write(&mut self, buf: &[u8]) -> Result<usize>;
+
+ /// Flush this output stream, ensuring that all intermediately buffered
+ /// contents reach their destination.
+ ///
+ /// # Errors
+ ///
+ /// It is considered an error if not all bytes could be written due to
+ /// I/O errors or EOF being reached.
+ fn flush(&mut self) -> Result<()>;
+
+ /// Attempts to write an entire buffer into this write.
+ ///
+ /// This method will continuously call `write` while there is more data to
+ /// write. This method will not return until the entire buffer has been
+ /// successfully written or an error occurs. The first error generated from
+ /// this method will be returned.
+ ///
+ /// # Errors
+ ///
+ /// This function will return the first error that `write` returns.
+ fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
+ while buf.len() > 0 {
+ match self.write(buf) {
+ Ok(0) => return Err(Error::new(ErrorKind::WriteZero,
+ "failed to write whole buffer",
+ None)),
+ Ok(n) => buf = &buf[n..],
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(())
+ }
+
+ /// Writes a formatted string into this writer, returning any error
+ /// encountered.
+ ///
+ /// This method is primarily used to interface with the `format_args!`
+ /// macro, but it is rare that this should explicitly be called. The
+ /// `write!` macro should be favored to invoke this method instead.
+ ///
+ /// This function internally uses the `write_all` method on this trait and
+ /// hence will continuously write data so long as no errors are received.
+ /// This also means that partial writes are not indicated in this signature.
+ ///
+ /// # Errors
+ ///
+ /// This function will return any I/O error reported while formatting.
+ fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> {
+ // Create a shim which translates a Writer to a fmt::Writer and saves
+ // off I/O errors. instead of discarding them
+ struct Adaptor<'a, T: ?Sized + 'a> {
+ inner: &'a mut T,
+ error: Result<()>,
+ }
+
+ impl<'a, T: Write + ?Sized> fmt::Writer for Adaptor<'a, T> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ match self.inner.write_all(s.as_bytes()) {
+ Ok(()) => Ok(()),
+ Err(e) => {
+ self.error = Err(e);
+ Err(fmt::Error)
+ }
+ }
+ }
+ }
+
+ let mut output = Adaptor { inner: self, error: Ok(()) };
+ match fmt::write(&mut output, fmt) {
+ Ok(()) => Ok(()),
+ Err(..) => output.error
+ }
+ }
+}
+
+/// Extension methods for all instances of `Write`, typically imported through
+/// `std::io::prelude::*`.
+pub trait WriteExt: Write + Sized {
+ /// Create a "by reference" adaptor for this instance of `Write`.
+ ///
+ /// The returned adaptor also implements `Write` and will simply borrow this
+ /// current writer.
+ fn by_ref(&mut self) -> &mut Self { self }
+
+ /// Creates a new writer which will write all data to both this writer and
+ /// another writer.
+ ///
+ /// All data written to the returned writer will both be written to `self`
+ /// as well as `other`. Note that the error semantics of the current
+ /// implementation do not precisely track where errors happen. For example
+ /// an error on the second call to `write` will not report that the first
+ /// call to `write` succeeded.
+ fn broadcast<W: Write>(self, other: W) -> Broadcast<Self, W> {
+ Broadcast { first: self, second: other }
+ }
+}
+
+impl<T: Write> WriteExt for T {}
+
+/// An object implementing `Seek` internally has some form of cursor which can
+/// be moved within a stream of bytes.
+///
+/// The stream typically has a fixed size, allowing seeking relative to either
+/// end or the current offset.
+pub trait Seek {
+ /// Seek to an offset, in bytes, in a stream
+ ///
+ /// A seek beyond the end of a stream is allowed, but seeking before offset
+ /// 0 is an error.
+ ///
+ /// Seeking past the end of the stream does not modify the underlying
+ /// stream, but the next write may cause the previous data to be filled in
+ /// with a bit pattern.
+ ///
+ /// This method returns the new position within the stream if the seek
+ /// operation completed successfully.
+ ///
+ /// # Errors
+ ///
+ /// Seeking to a negative offset is considered an error
+ fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
+}
+
+/// Enumeration of possible methods to seek within an I/O object.
+#[derive(Copy, PartialEq, Eq, Clone, Debug)]
+pub enum SeekFrom {
+ /// Set the offset to the provided number of bytes.
+ Start(u64),
+
+ /// Set the offset to the size of this object plus the specified number of
+ /// bytes.
+ ///
+ /// It is possible to seek beyond the end of an object, but is an error to
+ /// seek before byte 0.
+ End(i64),
+
+ /// Set the offset to the current position plus the specified number of
+ /// bytes.
+ ///
+ /// It is possible to seek beyond the end of an object, but is an error to
+ /// seek before byte 0.
+ Current(i64),
+}
+
+fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
+ -> Result<()> {
+ loop {
+ let (done, used) = {
+ let available = match r.fill_buf() {
+ Ok(n) => n,
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+ Err(e) => return Err(e)
+ };
+ match available.position_elem(&delim) {
+ Some(i) => {
+ buf.push_all(&available[..i + 1]);
+ (true, i + 1)
+ }
+ None => {
+ buf.push_all(available);
+ (false, available.len())
+ }
+ }
+ };
+ r.consume(used);
+ if done || used == 0 {
+ return Ok(());
+ }
+ }
+}
+
+/// A Buffer is a type of reader which has some form of internal buffering to
+/// allow certain kinds of reading operations to be more optimized than others.
+///
+/// This type extends the `Read` trait with a few methods that are not
+/// possible to reasonably implement with purely a read interface.
+pub trait BufRead: Read {
+ /// Fills the internal buffer of this object, returning the buffer contents.
+ ///
+ /// None of the contents will be "read" in the sense that later calling
+ /// `read` may return the same contents.
+ ///
+ /// The `consume` function must be called with the number of bytes that are
+ /// consumed from this buffer returned to ensure that the bytes are never
+ /// returned twice.
+ ///
+ /// An empty buffer returned indicates that the stream has reached EOF.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an I/O error if the underlying reader was
+ /// read, but returned an error.
+ fn fill_buf(&mut self) -> Result<&[u8]>;
+
+ /// Tells this buffer that `amt` bytes have been consumed from the buffer,
+ /// so they should no longer be returned in calls to `read`.
+ fn consume(&mut self, amt: usize);
+
+ /// Read all bytes until the delimiter `byte` is reached.
+ ///
+ /// This function will continue to read (and buffer) bytes from the
+ /// underlying stream until the delimiter or EOF is found. Once found, all
+ /// bytes up to, and including, the delimiter (if found) will be appended to
+ /// `buf`.
+ ///
+ /// If this buffered reader is currently at EOF, then this function will not
+ /// place any more bytes into `buf` and will return `Ok(())`.
+ ///
+ /// # Errors
+ ///
+ /// This function will ignore all instances of `ErrorKind::Interrupted` and
+ /// will otherwise return any errors returned by `fill_buf`.
+ ///
+ /// If an I/O error is encountered then all bytes read so far will be
+ /// present in `buf` and its length will have been adjusted appropriately.
+ fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<()> {
+ read_until(self, byte, buf)
+ }
+
+ /// Read all bytes until a newline byte (the 0xA byte) is reached.
+ ///
+ /// This function will continue to read (and buffer) bytes from the
+ /// underlying stream until the newline delimiter (the 0xA byte) or EOF is
+ /// found. Once found, all bytes up to, and including, the delimiter (if
+ /// found) will be appended to `buf`.
+ ///
+ /// If this reader is currently at EOF then this function will not modify
+ /// `buf` and will return `Ok(())`.
+ ///
+ /// # Errors
+ ///
+ /// This function has the same error semantics as `read_until` and will also
+ /// return an error if the read bytes are not valid UTF-8. If an I/O error
+ /// is encountered then `buf` may contain some bytes already read in the
+ /// event that all data read so far was valid UTF-8.
+ fn read_line(&mut self, buf: &mut String) -> Result<()> {
+ // Note that we are not calling the `.read_until` method here, but
+ // rather our hardcoded implementation. For more details as to why, see
+ // the comments in `read_to_end`.
+ append_to_string(buf, |b| read_until(self, b'\n', b))
+ }
+}
+
+/// Extension methods for all instances of `BufRead`, typically imported through
+/// `std::io::prelude::*`.
+pub trait BufReadExt: BufRead + Sized {
+ /// Returns an iterator over the contents of this reader split on the byte
+ /// `byte`.
+ ///
+ /// The iterator returned from this function will return instances of
+ /// `io::Result<Vec<u8>>`. Each vector returned will *not* have the
+ /// delimiter byte at the end.
+ ///
+ /// This function will yield errors whenever `read_until` would have also
+ /// yielded an error.
+ fn split(self, byte: u8) -> Split<Self> {
+ Split { buf: self, delim: byte }
+ }
+
+ /// Returns an iterator over the lines of this reader.
+ ///
+ /// The iterator returned from this function will yield instances of
+ /// `io::Result<String>`. Each string returned will *not* have a newline
+ /// byte (the 0xA byte) at the end.
+ ///
+ /// This function will yield errors whenever `read_string` would have also
+ /// yielded an error.
+ fn lines(self) -> Lines<Self> {
+ Lines { buf: self }
+ }
+}
+
+impl<T: BufRead> BufReadExt for T {}
+
+/// A `Write` adaptor which will write data to multiple locations.
+///
+/// For more information, see `WriteExt::broadcast`.
+pub struct Broadcast<T, U> {
+ first: T,
+ second: U,
+}
+
+impl<T: Write, U: Write> Write for Broadcast<T, U> {
+ fn write(&mut self, data: &[u8]) -> Result<usize> {
+ let n = try!(self.first.write(data));
+ // FIXME: what if the write fails? (we wrote something)
+ try!(self.second.write_all(&data[..n]));
+ Ok(n)
+ }
+
+ fn flush(&mut self) -> Result<()> {
+ self.first.flush().and(self.second.flush())
+ }
+}
+
+/// Adaptor to chain together two instances of `Read`.
+///
+/// For more information, see `ReadExt::chain`.
+pub struct Chain<T, U> {
+ first: T,
+ second: U,
+ done_first: bool,
+}
+
+impl<T: Read, U: Read> Read for Chain<T, U> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ if !self.done_first {
+ match try!(self.first.read(buf)) {
+ 0 => { self.done_first = true; }
+ n => return Ok(n),
+ }
+ }
+ self.second.read(buf)
+ }
+}
+
+/// Reader adaptor which limits the bytes read from an underlying reader.
+///
+/// For more information, see `ReadExt::take`.
+pub struct Take<T> {
+ inner: T,
+ limit: u64,
+}
+
+impl<T> Take<T> {
+ /// Returns the number of bytes that can be read before this instance will
+ /// return EOF.
+ ///
+ /// # Note
+ ///
+ /// This instance may reach EOF after reading fewer bytes than indiccated by
+ /// this method if the underlying `Read` instance reaches EOF.
+ pub fn limit(&self) -> u64 { self.limit }
+}
+
+impl<T: Read> Read for Take<T> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let max = cmp::min(buf.len() as u64, self.limit) as usize;
+ let n = try!(self.inner.read(&mut buf[..max]));
+ self.limit -= n as u64;
+ Ok(n)
+ }
+}
+
+/// An adaptor which will emit all read data to a specified writer as well.
+///
+/// For more information see `ReadExt::tee`
+pub struct Tee<R, W> {
+ reader: R,
+ writer: W,
+}
+
+impl<R: Read, W: Write> Read for Tee<R, W> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let n = try!(self.reader.read(buf));
+ // FIXME: what if the write fails? (we read something)
+ try!(self.writer.write_all(&buf[..n]));
+ Ok(n)
+ }
+}
+
+/// A bridge from implementations of `Read` to an `Iterator` of `u8`.
+///
+/// See `ReadExt::bytes` for more information.
+pub struct Bytes<R> {
+ inner: R,
+}
+
+impl<R: Read> Iterator for Bytes<R> {
+ type Item = Result<u8>;
+
+ fn next(&mut self) -> Option<Result<u8>> {
+ let mut buf = [0];
+ match self.inner.read(&mut buf) {
+ Ok(0) => None,
+ Ok(..) => Some(Ok(buf[0])),
+ Err(e) => Some(Err(e)),
+ }
+ }
+}
+
+/// A bridge from implementations of `Read` to an `Iterator` of `char`.
+///
+/// See `ReadExt::chars` for more information.
+pub struct Chars<R> {
+ inner: R,
+}
+
+/// An enumeration of possible errors that can be generated from the `Chars`
+/// adapter.
+#[derive(PartialEq, Clone, Debug)]
+pub enum CharsError {
+ /// Variant representing that the underlying stream was read successfully
+ /// but it did not contain valid utf8 data.
+ NotUtf8,
+
+ /// Variant representing that an I/O error occurred.
+ Other(Error),
+}
+
+impl<R: Read> Iterator for Chars<R> {
+ type Item = result::Result<char, CharsError>;
+
+ fn next(&mut self) -> Option<result::Result<char, CharsError>> {
+ let mut buf = [0];
+ let first_byte = match self.inner.read(&mut buf) {
+ Ok(0) => return None,
+ Ok(..) => buf[0],
+ Err(e) => return Some(Err(CharsError::Other(e))),
+ };
+ let width = core_str::utf8_char_width(first_byte);
+ if width == 1 { return Some(Ok(first_byte as char)) }
+ if width == 0 { return Some(Err(CharsError::NotUtf8)) }
+ let mut buf = [first_byte, 0, 0, 0];
+ {
+ let mut start = 1;
+ while start < width {
+ match self.inner.read(&mut buf[start..width]) {
+ Ok(0) => return Some(Err(CharsError::NotUtf8)),
+ Ok(n) => start += n,
+ Err(e) => return Some(Err(CharsError::Other(e))),
+ }
+ }
+ }
+ Some(match str::from_utf8(&buf[..width]).ok() {
+ Some(s) => Ok(s.char_at(0)),
+ None => Err(CharsError::NotUtf8),
+ })
+ }
+}
+
+impl StdError for CharsError {
+ fn description(&self) -> &str {
+ match *self {
+ CharsError::NotUtf8 => "invalid utf8 encoding",
+ CharsError::Other(ref e) => e.description(),
+ }
+ }
+ fn cause(&self) -> Option<&StdError> {
+ match *self {
+ CharsError::NotUtf8 => None,
+ CharsError::Other(ref e) => e.cause(),
+ }
+ }
+}
+
+impl fmt::Display for CharsError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ CharsError::NotUtf8 => {
+ "byte stream did not contain valid utf8".fmt(f)
+ }
+ CharsError::Other(ref e) => e.fmt(f),
+ }
+ }
+}
+
+/// An iterator over the contents of an instance of `BufRead` split on a
+/// particular byte.
+///
+/// See `BufReadExt::split` for more information.
+pub struct Split<B> {
+ buf: B,
+ delim: u8,
+}
+
+impl<B: BufRead> Iterator for Split<B> {
+ type Item = Result<Vec<u8>>;
+
+ fn next(&mut self) -> Option<Result<Vec<u8>>> {
+ let mut buf = Vec::new();
+ match self.buf.read_until(self.delim, &mut buf) {
+ Ok(()) if buf.len() == 0 => None,
+ Ok(()) => {
+ if buf[buf.len() - 1] == self.delim {
+ buf.pop();
+ }
+ Some(Ok(buf))
+ }
+ Err(e) => Some(Err(e))
+ }
+ }
+}
+
+/// An iterator over the lines of an instance of `BufRead` split on a newline
+/// byte.
+///
+/// See `BufReadExt::lines` for more information.
+pub struct Lines<B> {
+ buf: B,
+}
+
+impl<B: BufRead> Iterator for Lines<B> {
+ type Item = Result<String>;
+
+ fn next(&mut self) -> Option<Result<String>> {
+ let mut buf = String::new();
+ match self.buf.read_line(&mut buf) {
+ Ok(()) if buf.len() == 0 => None,
+ Ok(()) => {
+ if buf.ends_with("\n") {
+ buf.pop();
+ }
+ Some(Ok(buf))
+ }
+ Err(e) => Some(Err(e))
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use prelude::v1::*;
+ use io::prelude::*;
+ use super::Cursor;
+
+ #[test]
+ fn read_until() {
+ let mut buf = Cursor::new(b"12");
+ let mut v = Vec::new();
+ assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
+ assert_eq!(v, b"12");
+
+ let mut buf = Cursor::new(b"1233");
+ let mut v = Vec::new();
+ assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
+ assert_eq!(v, b"123");
+ v.truncate(0);
+ assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
+ assert_eq!(v, b"3");
+ v.truncate(0);
+ assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
+ assert_eq!(v, []);
+ }
+
+ #[test]
+ fn split() {
+ let mut buf = Cursor::new(b"12");
+ let mut s = buf.split(b'3');
+ assert_eq!(s.next(), Some(Ok(vec![b'1', b'2'])));
+ assert_eq!(s.next(), None);
+
+ let mut buf = Cursor::new(b"1233");
+ let mut s = buf.split(b'3');
+ assert_eq!(s.next(), Some(Ok(vec![b'1', b'2'])));
+ assert_eq!(s.next(), Some(Ok(vec![])));
+ assert_eq!(s.next(), None);
+ }
+
+ #[test]
+ fn read_line() {
+ let mut buf = Cursor::new(b"12");
+ let mut v = String::new();
+ assert_eq!(buf.read_line(&mut v), Ok(()));
+ assert_eq!(v, "12");
+
+ let mut buf = Cursor::new(b"12\n\n");
+ let mut v = String::new();
+ assert_eq!(buf.read_line(&mut v), Ok(()));
+ assert_eq!(v, "12\n");
+ v.truncate(0);
+ assert_eq!(buf.read_line(&mut v), Ok(()));
+ assert_eq!(v, "\n");
+ v.truncate(0);
+ assert_eq!(buf.read_line(&mut v), Ok(()));
+ assert_eq!(v, "");
+ }
+
+ #[test]
+ fn lines() {
+ let mut buf = Cursor::new(b"12");
+ let mut s = buf.lines();
+ assert_eq!(s.next(), Some(Ok("12".to_string())));
+ assert_eq!(s.next(), None);
+
+ let mut buf = Cursor::new(b"12\n\n");
+ let mut s = buf.lines();
+ assert_eq!(s.next(), Some(Ok("12".to_string())));
+ assert_eq!(s.next(), Some(Ok(String::new())));
+ assert_eq!(s.next(), None);
+ }
+
+ #[test]
+ fn read_to_end() {
+ let mut c = Cursor::new(b"");
+ let mut v = Vec::new();
+ assert_eq!(c.read_to_end(&mut v), Ok(()));
+ assert_eq!(v, []);
+
+ let mut c = Cursor::new(b"1");
+ let mut v = Vec::new();
+ assert_eq!(c.read_to_end(&mut v), Ok(()));
+ assert_eq!(v, b"1");
+ }
+
+ #[test]
+ fn read_to_string() {
+ let mut c = Cursor::new(b"");
+ let mut v = String::new();
+ assert_eq!(c.read_to_string(&mut v), Ok(()));
+ assert_eq!(v, "");
+
+ let mut c = Cursor::new(b"1");
+ let mut v = String::new();
+ assert_eq!(c.read_to_string(&mut v), Ok(()));
+ assert_eq!(v, "1");
+
+ let mut c = Cursor::new(b"\xff");
+ let mut v = String::new();
+ assert!(c.read_to_string(&mut v).is_err());
+ }
+}
--- /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.
+
+//! The I/O Prelude
+//!
+//! The purpose of this module is to alleviate imports of many common I/O traits
+//! by adding a glob import to the top of I/O heavy modules:
+//!
+//! ```
+//! use std::io::prelude::*;
+//! ```
+//!
+//! This module contains reexports of many core I/O traits such as `Read`,
+//! `Write`, `ReadExt`, and `WriteExt`. Structures and functions are not
+//! contained in this module.
+
+pub use super::{Read, ReadExt, Write, WriteExt, BufRead, BufReadExt};
+
+// FIXME: pub use as `Seek` when the name isn't in the actual prelude any more
+pub use super::Seek as NewSeek;
--- /dev/null
+// Copyright 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.
+
+#![allow(missing_copy_implementations)]
+
+use prelude::v1::*;
+
+use io::{self, Read, Write, ErrorKind};
+
+/// Copies the entire contents of a reader into a writer.
+///
+/// This function will continuously read data from `r` and then write it into
+/// `w` in a streaming fashion until `r` returns EOF.
+///
+/// On success the total number of bytes that were copied from `r` to `w` is
+/// returned.
+///
+/// # Errors
+///
+/// This function will return an error immediately if any call to `read` or
+/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
+/// handled by this function and the underlying operation is retried.
+pub fn copy<R: Read, W: Write>(r: &mut R, w: &mut W) -> io::Result<u64> {
+ let mut buf = [0; super::DEFAULT_BUF_SIZE];
+ let mut written = 0;
+ loop {
+ let len = match r.read(&mut buf) {
+ Ok(0) => return Ok(written),
+ Ok(len) => len,
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+ Err(e) => return Err(e),
+ };
+ try!(w.write_all(&buf[..len]));
+ written += len as u64;
+ }
+}
+
+/// A reader which is always at EOF.
+pub struct Empty { _priv: () }
+
+/// Creates an instance of an empty reader.
+///
+/// All reads from the returned reader will return `Ok(0)`.
+pub fn empty() -> Empty { Empty { _priv: () } }
+
+impl Read for Empty {
+ fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { Ok(0) }
+}
+
+/// A reader which infinitely yields one byte.
+pub struct Repeat { byte: u8 }
+
+/// Creates an instance of a reader that infinitely repeats one byte.
+///
+/// All reads from this reader will succeed by filling the specified buffer with
+/// the given byte.
+pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } }
+
+impl Read for Repeat {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ for slot in buf.iter_mut() {
+ *slot = self.byte;
+ }
+ Ok(buf.len())
+ }
+}
+
+/// A writer which will move data into the void.
+pub struct Sink { _priv: () }
+
+/// Creates an instance of a writer which will successfully consume all data.
+///
+/// All calls to `write` on the returned instance will return `Ok(buf.len())`
+/// and the contents of the buffer will not be inspected.
+pub fn sink() -> Sink { Sink { _priv: () } }
+
+impl Write for Sink {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+#[cfg(test)]
+mod test {
+ use prelude::v1::*;
+
+ use io::prelude::*;
+ use io::{sink, empty, repeat};
+
+ #[test]
+ fn sink_sinks() {
+ let mut s = sink();
+ assert_eq!(s.write(&[]), Ok(0));
+ assert_eq!(s.write(&[0]), Ok(1));
+ assert_eq!(s.write(&[0; 1024]), Ok(1024));
+ assert_eq!(s.by_ref().write(&[0; 1024]), Ok(1024));
+ }
+
+ #[test]
+ fn empty_reads() {
+ let mut e = empty();
+ assert_eq!(e.read(&mut []), Ok(0));
+ assert_eq!(e.read(&mut [0]), Ok(0));
+ assert_eq!(e.read(&mut [0; 1024]), Ok(0));
+ assert_eq!(e.by_ref().read(&mut [0; 1024]), Ok(0));
+ }
+
+ #[test]
+ fn repeat_repeats() {
+ let mut r = repeat(4);
+ let mut b = [0; 1024];
+ assert_eq!(r.read(&mut b), Ok(1024));
+ assert!(b.iter().all(|b| *b == 4));
+ }
+
+ #[test]
+ fn take_some_bytes() {
+ assert_eq!(repeat(4).take(100).bytes().count(), 100);
+ assert_eq!(repeat(4).take(100).bytes().next(), Some(Ok(4)));
+ assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
+ }
+
+ #[test]
+ fn tee() {
+ let mut buf = [0; 10];
+ {
+ let mut ptr: &mut [u8] = &mut buf;
+ assert_eq!(repeat(4).tee(&mut ptr).take(5).read(&mut [0; 10]), Ok(5));
+ }
+ assert_eq!(buf, [4, 4, 4, 4, 4, 0, 0, 0, 0, 0]);
+ }
+
+ #[test]
+ fn broadcast() {
+ let mut buf1 = [0; 10];
+ let mut buf2 = [0; 10];
+ {
+ let mut ptr1: &mut [u8] = &mut buf1;
+ let mut ptr2: &mut [u8] = &mut buf2;
+
+ assert_eq!((&mut ptr1).broadcast(&mut ptr2)
+ .write(&[1, 2, 3]), Ok(3));
+ }
+ assert_eq!(buf1, buf2);
+ assert_eq!(buf1, [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]);
+ }
+}
#![deny(missing_docs)]
-#[cfg(test)]
-#[macro_use]
-extern crate log;
+#[cfg(test)] extern crate test;
+#[cfg(test)] #[macro_use] extern crate log;
#[macro_use]
#[macro_reexport(assert, assert_eq, debug_assert, debug_assert_eq,
#[macro_reexport(vec)]
extern crate "collections" as core_collections;
-extern crate "rand" as core_rand;
+#[allow(deprecated)] extern crate "rand" as core_rand;
extern crate alloc;
extern crate unicode;
extern crate libc;
pub mod ffi;
pub mod fmt;
pub mod old_io;
+pub mod io;
pub mod os;
pub mod env;
pub mod path;
+pub mod old_path;
pub mod rand;
pub mod time;
#[cfg(test)]
mod bench {
+ #![allow(deprecated)] // rand
extern crate test;
mod uint {
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 {
#[allow(unused_imports)]
#[allow(unused_variables)]
#[allow(unused_mut)]
+#[allow(deprecated)] // rand
mod test {
use prelude::v1::*;
use old_io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType};
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;
// except according to those terms.
//! Temporary files and directories
+#![allow(deprecated)] // rand
use env;
use iter::{IteratorExt};
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());
#[cfg(test)]
mod tests {
+ #![allow(deprecated)] // rand
+
use prelude::v1::*;
use iter::repeat;
--- /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, Debug, 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 char::CharExt as UnicodeCharExt;
+ use super::{os_str_as_u8_slice, u8_slice_as_os_str};
+ use ascii::*;
+ use ffi::OsStr;
+
+ #[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 {
+ 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> 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), VerbatimUNC(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, Debug)]
+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, Debug)]
+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");
+ if cfg!(unix) {
+ tfn!(".", "foo", "./foo");
+ tfn!("foo/", "bar", "foo/bar");
+ tfn!("foo/.", "bar", "foo/./bar");
+ tfn!("..", "foo", "../foo");
+ tfn!("foo/..", "bar", "foo/../bar");
+ tfn!("/", "foo", "/foo");
+ } else {
+ tfn!(".", "foo", r".\foo");
+ tfn!(r"foo\", "bar", r"foo\bar");
+ tfn!(r"foo\.", "bar", r"foo\.\bar");
+ tfn!("..", "foo", r"..\foo");
+ tfn!(r"foo\..", "bar", r"foo\..\bar");
+ tfn!(r"\", "foo", r"\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
//! ```
#![unstable(feature = "rand")]
+#![deprecated(reason = "use the crates.io `rand` library instead",
+ since = "1.0.0-alpha")]
+#![allow(deprecated)]
use cell::RefCell;
use clone::Clone;
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;
#[cfg(test)]
mod tests {
+ #![allow(deprecated)] // rand
+
use prelude::v1::*;
use rand::{self, Rng};
use sys::{last_error, retry};
use ffi::CString;
use num::Int;
-use path::BytesContainer;
+use old_path::BytesContainer;
use collections;
pub mod backtrace;
pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int;
pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int;
+ #[cfg(not(target_os = "ios"))]
pub fn getpwuid_r(uid: libc::uid_t,
pwd: *mut passwd,
buf: *mut libc::c_char,
use prelude::v1::*;
use ffi;
-use old_io::{self, IoResult, IoError};
+use io::ErrorKind;
use libc;
use num::{Int, SignedInt};
use num;
+use old_io::{self, IoResult, IoError};
use str;
use sys_common::mkerr_libc;
err
}
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno as libc::c_int {
+ libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
+ libc::ECONNRESET => ErrorKind::ConnectionReset,
+ libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+ libc::EPIPE => ErrorKind::BrokenPipe,
+ libc::ENOTCONN => ErrorKind::NotConnected,
+ libc::ECONNABORTED => ErrorKind::ConnectionAborted,
+ libc::EADDRNOTAVAIL => ErrorKind::ConnectionRefused,
+ libc::EADDRINUSE => ErrorKind::ConnectionRefused,
+ libc::ENOENT => ErrorKind::FileNotFound,
+ libc::EISDIR => ErrorKind::InvalidInput,
+ libc::EINTR => ErrorKind::Interrupted,
+ libc::EINVAL => ErrorKind::InvalidInput,
+ libc::ENOTTY => ErrorKind::MismatchedFileTypeForOperation,
+ libc::ETIMEDOUT => ErrorKind::TimedOut,
+ libc::ECANCELED => ErrorKind::TimedOut,
+ libc::consts::os::posix88::EEXIST => ErrorKind::PathAlreadyExists,
+
+ // These two constants can have the same value on some systems,
+ // but different values on others, so we can't use a match
+ // clause
+ x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
+ ErrorKind::ResourceUnavailable,
+
+ _ => ErrorKind::Other,
+ }
+}
+
#[inline]
pub fn retry<T, F> (mut f: F) -> T where
T: SignedInt,
let mut res = Vec::new();
unsafe {
- let processInfoSel = sel_registerName("processInfo\0".as_ptr());
- let argumentsSel = sel_registerName("arguments\0".as_ptr());
- let utf8Sel = sel_registerName("UTF8String\0".as_ptr());
- let countSel = sel_registerName("count\0".as_ptr());
- let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr());
+ let process_info_sel = sel_registerName("processInfo\0".as_ptr());
+ let arguments_sel = sel_registerName("arguments\0".as_ptr());
+ let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
+ let count_sel = sel_registerName("count\0".as_ptr());
+ let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
let klass = objc_getClass("NSProcessInfo\0".as_ptr());
- let info = objc_msgSend(klass, processInfoSel);
- let args = objc_msgSend(info, argumentsSel);
+ let info = objc_msgSend(klass, process_info_sel);
+ let args = objc_msgSend(info, arguments_sel);
- let cnt: int = mem::transmute(objc_msgSend(args, countSel));
+ let cnt: int = mem::transmute(objc_msgSend(args, count_sel));
for i in range(0, cnt) {
- let tmp = objc_msgSend(args, objectAtSel, i);
+ let tmp = objc_msgSend(args, object_at_sel, i);
let utf_c_str: *const libc::c_char =
- mem::transmute(objc_msgSend(tmp, utf8Sel));
- let bytes = ffi::c_str_to_bytes(&utf_c_str).to_vec();
- res.push(OsString::from_vec(bytes))
+ mem::transmute(objc_msgSend(tmp, utf8_sel));
+ let bytes = ffi::c_str_to_bytes(&utf_c_str);
+ res.push(OsString::from_str(str::from_utf8(bytes).unwrap()))
}
}
Path::new(os.into_vec())
});
- #[cfg(target_os = "android")]
+ #[cfg(any(target_os = "android",
+ target_os = "ios"))]
unsafe fn fallback() -> Option<OsString> { None }
- #[cfg(not(target_os = "android"))]
+ #[cfg(not(any(target_os = "android",
+ target_os = "ios")))]
unsafe fn fallback() -> Option<OsString> {
let mut amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) {
n if n < 0 => 512 as usize,
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 prelude::v1::*;
use ffi::OsStr;
+use io::ErrorKind;
use libc;
use mem;
use old_io::{self, IoResult, IoError};
err
}
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno as libc::c_int {
+ libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
+ libc::ERROR_ALREADY_EXISTS => ErrorKind::PathAlreadyExists,
+ libc::ERROR_BROKEN_PIPE => ErrorKind::BrokenPipe,
+ libc::ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound,
+ libc::ERROR_INVALID_FUNCTION => ErrorKind::InvalidInput,
+ libc::ERROR_INVALID_HANDLE => ErrorKind::MismatchedFileTypeForOperation,
+ libc::ERROR_INVALID_NAME => ErrorKind::InvalidInput,
+ libc::ERROR_NOTHING_TO_TERMINATE => ErrorKind::InvalidInput,
+ libc::ERROR_NO_DATA => ErrorKind::BrokenPipe,
+ libc::ERROR_OPERATION_ABORTED => ErrorKind::TimedOut,
+
+ libc::WSAEACCES => ErrorKind::PermissionDenied,
+ libc::WSAEADDRINUSE => ErrorKind::ConnectionRefused,
+ libc::WSAEADDRNOTAVAIL => ErrorKind::ConnectionRefused,
+ libc::WSAECONNABORTED => ErrorKind::ConnectionAborted,
+ libc::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
+ libc::WSAECONNRESET => ErrorKind::ConnectionReset,
+ libc::WSAEINVAL => ErrorKind::InvalidInput,
+ libc::WSAENOTCONN => ErrorKind::NotConnected,
+ libc::WSAEWOULDBLOCK => ErrorKind::ResourceUnavailable,
+
+ _ => ErrorKind::Other,
+ }
+}
+
+
#[inline]
pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020
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};
// Sure wish we had macro hygiene, no?
#[doc(hidden)]
+#[stable(feature = "rust1", since = "1.0.0")]
pub mod __impl {
pub use super::imp::Key as KeyInner;
pub use super::imp::destroy_value;
/// 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;
pub use self::Ty_::*;
pub use self::TyParamBound::*;
pub use self::UintTy::*;
-pub use self::ClosureKind::*;
pub use self::UnOp::*;
pub use self::UnsafeSource::*;
pub use self::VariantKind::*;
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprLoop(P<Block>, Option<Ident>),
ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
- ExprClosure(CaptureClause, Option<ClosureKind>, P<FnDecl>, P<Block>),
+ ExprClosure(CaptureClause, P<FnDecl>, P<Block>),
ExprBlock(P<Block>),
ExprAssign(P<Expr>, P<Expr>),
}
}
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
-pub enum ClosureKind {
- FnClosureKind,
- FnMutClosureKind,
- FnOnceClosureKind,
-}
-
/// The data we save and restore about an inlined item or method. This is not
/// part of the AST that we parse from a file, but it becomes part of the tree
/// that we trans.
}
}
ast_map::NodeExpr(e) => match e.node {
- ast::ExprClosure(_, _, ref decl, ref block) =>
+ ast::ExprClosure(_, ref decl, ref block) =>
closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
_ => panic!("expr FnLikeNode that is not fn-like"),
},
fn lambda_fn_decl(&self, span: Span,
fn_decl: P<ast::FnDecl>, blk: P<ast::Block>) -> P<ast::Expr> {
- self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
+ self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
}
fn lambda(&self, span: Span, ids: Vec<ast::Ident>, blk: P<ast::Block>) -> P<ast::Expr> {
let fn_decl = self.fn_decl(
ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
self.ty_infer(span));
- self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
+ self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
}
fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr> {
self.lambda(span, Vec::new(), blk)
push: F) where
F: FnOnce(P<Item>),
{
+ cx.span_warn(span,
+ "`#[derive(Rand)]` is deprecated in favour of `#[derive_Rand]` from \
+ `rand_macros` on crates.io");
+
let trait_def = TraitDef {
span: span,
attributes: Vec::new(),
cx.ident_of("Rand"),
cx.ident_of("rand")
);
- let mut rand_call = |&mut: cx: &mut ExtCtxt, span| {
+ let rand_call = |&: cx: &mut ExtCtxt, span| {
cx.expr_call_global(span,
rand_ident.clone(),
vec!(rng.clone()))
fld.cx.expr_match(span, into_iter_expr, vec![iter_arm])
}
- ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => {
+ ast::ExprClosure(capture_clause, fn_decl, block) => {
let (rewritten_fn_decl, rewritten_block)
= expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
let new_node = ast::ExprClosure(capture_clause,
- opt_kind,
rewritten_fn_decl,
rewritten_block);
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
// Allows use of #[staged_api]
("staged_api", "1.0.0", Active),
+
+ // Allows using items which are missing stability attributes
+ ("unmarked_api", "1.0.0", Active)
];
enum Status {
pub quote: bool,
pub old_orphan_check: bool,
pub simd_ffi: bool,
+ pub unmarked_api: bool,
pub lib_features: Vec<(InternedString, Span)>
}
quote: false,
old_orphan_check: false,
simd_ffi: false,
+ unmarked_api: false,
lib_features: Vec::new()
}
}
quote: cx.has_feature("quote"),
old_orphan_check: cx.has_feature("old_orphan_check"),
simd_ffi: cx.has_feature("simd_ffi"),
+ unmarked_api: cx.has_feature("unmarked_api"),
lib_features: unknown_features
}
}
arms.move_map(|x| folder.fold_arm(x)),
source)
}
- ExprClosure(capture_clause, opt_kind, decl, body) => {
+ ExprClosure(capture_clause, decl, body) => {
ExprClosure(capture_clause,
- opt_kind,
folder.fold_fn_decl(decl),
folder.fold_block(body))
}
ProcType,
ProcExpr,
ClosureType,
+ ClosureKind,
}
pub trait ParserObsoleteMethods {
"`|usize| -> bool` closure type syntax",
"use unboxed closures instead, no type annotation needed"
),
+ ObsoleteSyntax::ClosureKind => (
+ "`:`, `&mut:`, or `&:` syntax",
+ "rely on inference instead"
+ ),
ObsoleteSyntax::Sized => (
"`Sized? T` syntax for removing the `Sized` bound",
"write `T: ?Sized` instead"
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath};
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
-use ast::{FnClosureKind, FnMutClosureKind};
-use ast::{FnOnceClosureKind};
use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy};
use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic};
use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst};
use ast::{TyTypeof, TyInfer, TypeMethod};
use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath};
use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq};
-use ast::{TypeImplItem, TypeTraitItem, Typedef, ClosureKind};
+use ast::{TypeImplItem, TypeTraitItem, Typedef,};
use ast::{UnnamedField, UnsafeBlock};
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
use ast::{Visibility, WhereClause};
TyInfer
}
- /// Parses an optional closure kind (`&:`, `&mut:`, or `:`).
- pub fn parse_optional_closure_kind(&mut self) -> Option<ClosureKind> {
- if self.check(&token::BinOp(token::And)) &&
- self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
- self.look_ahead(2, |t| *t == token::Colon) {
+ /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`).
+ pub fn parse_obsolete_closure_kind(&mut self) {
+ // let lo = self.span.lo;
+ if
+ self.check(&token::BinOp(token::And)) &&
+ self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
+ self.look_ahead(2, |t| *t == token::Colon)
+ {
self.bump();
self.bump();
self.bump();
- return Some(FnMutClosureKind)
- }
-
- if self.token == token::BinOp(token::And) &&
- self.look_ahead(1, |t| *t == token::Colon) {
+ } else if
+ self.token == token::BinOp(token::And) &&
+ self.look_ahead(1, |t| *t == token::Colon)
+ {
self.bump();
self.bump();
- return Some(FnClosureKind)
- }
-
- if self.eat(&token::Colon) {
- return Some(FnOnceClosureKind)
+ return;
+ } else if
+ self.eat(&token::Colon)
+ {
+ /* nothing */
+ } else {
+ return;
}
- return None
+ // SNAP 474b324
+ // Enable these obsolete errors after snapshot:
+ // let span = mk_sp(lo, self.span.hi);
+ // self.obsolete(span, ObsoleteSyntax::ClosureKind);
}
pub fn parse_ty_bare_fn_or_ty_closure(&mut self, lifetime_defs: Vec<LifetimeDef>) -> Ty_ {
-> P<Expr>
{
let lo = self.span.lo;
- let (decl, optional_closure_kind) = self.parse_fn_block_decl();
+ let decl = self.parse_fn_block_decl();
let body = self.parse_expr();
let fakeblock = P(ast::Block {
id: ast::DUMMY_NODE_ID,
self.mk_expr(
lo,
fakeblock.span.hi,
- ExprClosure(capture_clause, optional_closure_kind, decl, fakeblock))
+ ExprClosure(capture_clause, decl, fakeblock))
}
pub fn parse_else_expr(&mut self) -> P<Expr> {
}
// parse the |arg, arg| header on a lambda
- fn parse_fn_block_decl(&mut self) -> (P<FnDecl>, Option<ClosureKind>) {
- let (optional_closure_kind, inputs_captures) = {
+ fn parse_fn_block_decl(&mut self) -> P<FnDecl> {
+ let inputs_captures = {
if self.eat(&token::OrOr) {
- (None, Vec::new())
+ Vec::new()
} else {
self.expect(&token::BinOp(token::Or));
- let optional_closure_kind =
- self.parse_optional_closure_kind();
+ self.parse_obsolete_closure_kind();
let args = self.parse_seq_to_before_end(
&token::BinOp(token::Or),
seq_sep_trailing_allowed(token::Comma),
|p| p.parse_fn_block_arg()
);
self.bump();
- (optional_closure_kind, args)
+ args
}
};
let output = self.parse_ret_ty();
- (P(FnDecl {
+ P(FnDecl {
inputs: inputs_captures,
output: output,
variadic: false
- }), optional_closure_kind)
+ })
}
/// Parses the `(arg, arg) -> return_type` header on a procedure.
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)]
pub use self::AnnNode::*;
use abi;
-use ast::{self, FnClosureKind, FnMutClosureKind};
-use ast::{FnOnceClosureKind};
+use ast;
use ast::{MethodImplItem, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use ast::{RequiredMethod, ProvidedMethod, TypeImplItem, TypeTraitItem};
-use ast::{ClosureKind};
use ast_util;
use owned_slice::OwnedSlice;
use attr::{AttrMetaMethods, AttributeMethods};
}
pub fn fn_block_to_string(p: &ast::FnDecl) -> String {
- $to_string(|s| s.print_fn_block_args(p, None))
+ $to_string(|s| s.print_fn_block_args(p))
}
pub fn path_to_string(p: &ast::Path) -> String {
}
try!(self.bclose_(expr.span, indent_unit));
}
- ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => {
+ ast::ExprClosure(capture_clause, ref decl, ref body) => {
try!(self.print_capture_clause(capture_clause));
- try!(self.print_fn_block_args(&**decl, opt_kind));
+ try!(self.print_fn_block_args(&**decl));
try!(space(&mut self.s));
if !body.stmts.is_empty() || !body.expr.is_some() {
pub fn print_fn_block_args(
&mut self,
- decl: &ast::FnDecl,
- closure_kind: Option<ClosureKind>)
+ decl: &ast::FnDecl)
-> IoResult<()> {
try!(word(&mut self.s, "|"));
- match closure_kind {
- None => {}
- Some(FnClosureKind) => try!(self.word_space("&:")),
- Some(FnMutClosureKind) => try!(self.word_space("&mut:")),
- Some(FnOnceClosureKind) => try!(self.word_space(":")),
- }
try!(self.print_fn_args(decl, None));
try!(word(&mut self.s, "|"));
visitor.visit_arm(arm)
}
}
- ExprClosure(_, _, ref function_declaration, ref body) => {
+ ExprClosure(_, ref function_declaration, ref body) => {
visitor.visit_fn(FkFnBlock,
&**function_declaration,
&**body,
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]
#![crate_type = "lib"]
#![feature(staged_api)]
#![staged_api]
+#![stable(feature = "lint_stability", since = "1.0.0")]
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature", reason = "text")]
pub fn unstable_text() {}
-pub fn unmarked() {}
-
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stable() {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", reason = "text")]
pub fn method_unstable_text(&self) {}
- pub fn method_unmarked(&self) {}
-
#[stable(feature = "rust1", since = "1.0.0")]
pub fn method_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
pub fn method_frozen_text(&self) {}
}
+#[stable(feature = "test_feature", since = "1.0.0")]
pub trait Trait {
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature", reason = "text")]
fn trait_unstable_text(&self) {}
- fn trait_unmarked(&self) {}
-
#[stable(feature = "rust1", since = "1.0.0")]
fn trait_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
pub struct DeprecatedUnstableStruct { pub i: int }
#[unstable(feature = "test_feature")]
pub struct UnstableStruct { pub i: int }
-pub struct UnmarkedStruct { pub i: int }
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableStruct { pub i: int }
pub struct DeprecatedUnstableUnitStruct;
#[unstable(feature = "test_feature")]
pub struct UnstableUnitStruct;
-pub struct UnmarkedUnitStruct;
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableUnitStruct;
+#[stable(feature = "test_feature", since = "1.0.0")]
pub enum Enum {
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature")]
UnstableVariant,
- UnmarkedVariant,
#[stable(feature = "rust1", since = "1.0.0")]
StableVariant,
}
pub struct DeprecatedUnstableTupleStruct(pub int);
#[unstable(feature = "test_feature")]
pub struct UnstableTupleStruct(pub int);
-pub struct UnmarkedTupleStruct(pub int);
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableTupleStruct(pub int);
pub use reexport::Reexported;
+pub struct Foo;
+pub enum Bar { X }
+
pub mod foo {
pub trait PubPub {
fn method(&self) {}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(unboxed_closures, overloaded_calls)]
+#![feature(unboxed_closures)]
// Tests that we can't assign to or mutably borrow upvars from `Fn`
// closures (issue #17780)
fn set(x: &mut usize) { *x = 5; }
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
fn main() {
// By-ref captures
{
let mut x = 0us;
- let _f = |&:| x = 42; //~ ERROR cannot assign
+ let _f = to_fn(|| x = 42); //~ ERROR cannot assign
let mut y = 0us;
- let _g = |&:| set(&mut y); //~ ERROR cannot borrow
+ let _g = to_fn(|| set(&mut y)); //~ ERROR cannot borrow
let mut z = 0us;
- let _h = |&mut:| { set(&mut z); |&:| z = 42; }; //~ ERROR cannot assign
+ let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); }); //~ ERROR cannot assign
}
+
// By-value captures
{
let mut x = 0us;
- let _f = move |&:| x = 42; //~ ERROR cannot assign
+ let _f = to_fn(move || x = 42); //~ ERROR cannot assign
let mut y = 0us;
- let _g = move |&:| set(&mut y); //~ ERROR cannot borrow
+ let _g = to_fn(move || set(&mut y)); //~ ERROR cannot borrow
let mut z = 0us;
- let _h = move |&mut:| { set(&mut z); move |&:| z = 42; }; //~ ERROR cannot assign
+ let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); }); //~ ERROR cannot assign
}
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(box_syntax)]
+#![feature(box_syntax,unboxed_closures)]
+
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
pub fn main() {
let bar = box 3;
- let _g = |&mut:| {
- let _h = move |:| -> isize { *bar }; //~ ERROR cannot move out of captured outer variable
- };
+ let _g = to_fn_mut(|| {
+ let _h = to_fn_once(move || -> isize { *bar }); //~ ERROR cannot move out of
+ });
}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#![feature(unboxed_closures)]
+
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
+
fn main() {
let x = 1;
- move|:| { x = 2; };
+ to_fn_once(move|:| { x = 2; });
//~^ ERROR: cannot assign to immutable captured outer variable
let s = std::old_io::stdin();
- move|:| { s.read_to_end(); };
+ to_fn_once(move|:| { s.read_to_end(); });
//~^ ERROR: cannot borrow immutable captured outer variable
}
+++ /dev/null
-// Copyright 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.
-
-// This file was auto-generated using 'src/etc/generate-deriving-span-tests.py'
-
-extern crate rand;
-
-
-struct Error;
-
-#[derive(Rand)]
-enum Enum {
- A {
- x: Error //~ ERROR
- }
-}
-
-fn main() {}
+++ /dev/null
-// Copyright 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.
-
-// This file was auto-generated using 'src/etc/generate-deriving-span-tests.py'
-
-extern crate rand;
-
-
-struct Error;
-
-#[derive(Rand)]
-enum Enum {
- A(
- Error //~ ERROR
- )
-}
-
-fn main() {}
+++ /dev/null
-// Copyright 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.
-
-// This file was auto-generated using 'src/etc/generate-deriving-span-tests.py'
-
-extern crate rand;
-
-
-struct Error;
-
-#[derive(Rand)]
-struct Struct {
- x: Error //~ ERROR
-}
-
-fn main() {}
+++ /dev/null
-// Copyright 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.
-
-// This file was auto-generated using 'src/etc/generate-deriving-span-tests.py'
-
-extern crate rand;
-
-
-struct Error;
-
-#[derive(Rand)]
-struct Struct(
- Error //~ ERROR
-);
-
-fn main() {}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(box_syntax)]
+#![feature(box_syntax, unboxed_closures)]
+
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
fn main() {
let r = {
let x = box 42;
- let f = move|:| &x; //~ ERROR: `x` does not live long enough
+ let f = to_fn_once(move|| &x); //~ ERROR: `x` does not live long enough
f()
};
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(box_syntax)]
+#![feature(box_syntax, unboxed_closures)]
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
fn do_it(x: &isize) { }
fn main() {
let x = box 22;
- let f = move|:| do_it(&*x);
- (move|:| {
+ let f = to_fn_once(move|| do_it(&*x));
+ to_fn_once(move|| {
f();
f();
//~^ ERROR: use of moved value: `f`
#![staged_api]
#[macro_use]
-extern crate lint_stability; //~ ERROR: use of unmarked library feature
+extern crate lint_stability;
mod cross_crate {
extern crate stability_cfg1;
foo.method_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
- unmarked(); //~ ERROR use of unmarked library feature
- foo.method_unmarked(); //~ ERROR use of unmarked library feature
- foo.trait_unmarked(); //~ ERROR use of unmarked library feature
-
stable();
foo.method_stable();
foo.trait_stable();
let _ = DeprecatedUnstableStruct { i: 0 }; //~ ERROR use of deprecated item
//~^ WARNING use of unstable library feature
let _ = UnstableStruct { i: 0 }; //~ WARNING use of unstable library feature
- let _ = UnmarkedStruct { i: 0 }; //~ ERROR use of unmarked library feature
let _ = StableStruct { i: 0 };
let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
let _ = DeprecatedUnstableUnitStruct; //~ ERROR use of deprecated item
//~^ WARNING use of unstable library feature
let _ = UnstableUnitStruct; //~ WARNING use of unstable library feature
- let _ = UnmarkedUnitStruct; //~ ERROR use of unmarked library feature
let _ = StableUnitStruct;
let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
let _ = Enum::DeprecatedUnstableVariant; //~ ERROR use of deprecated item
//~^ WARNING use of unstable library feature
let _ = Enum::UnstableVariant; //~ WARNING use of unstable library feature
- let _ = Enum::UnmarkedVariant; //~ ERROR use of unmarked library feature
let _ = Enum::StableVariant;
let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
let _ = DeprecatedUnstableTupleStruct (1); //~ ERROR use of deprecated item
//~^ WARNING use of unstable library feature
let _ = UnstableTupleStruct (1); //~ WARNING use of unstable library feature
- let _ = UnmarkedTupleStruct (1); //~ ERROR use of unmarked library feature
let _ = StableTupleStruct (1);
// At the moment, the lint checker only checks stability in
//~^ WARNING use of unstable library feature
foo.trait_unstable(); //~ WARNING use of unstable library feature
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
- foo.trait_unmarked(); //~ ERROR use of unmarked library feature
foo.trait_stable();
}
//~^ WARNING use of unstable library feature
foo.trait_unstable(); //~ WARNING use of unstable library feature
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
- foo.trait_unmarked(); //~ ERROR use of unmarked library feature
foo.trait_stable();
}
#[unstable(feature = "test_feature", reason = "text")]
pub fn unstable_text() {}
- pub fn unmarked() {}
-
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stable() {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", reason = "text")]
pub fn method_unstable_text(&self) {}
- pub fn method_unmarked(&self) {}
-
#[stable(feature = "rust1", since = "1.0.0")]
pub fn method_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", reason = "text")]
fn trait_unstable_text(&self) {}
- fn trait_unmarked(&self) {}
-
#[stable(feature = "rust1", since = "1.0.0")]
fn trait_stable(&self) {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
pub struct DeprecatedStruct { i: isize }
#[unstable(feature = "test_feature")]
pub struct UnstableStruct { i: isize }
- pub struct UnmarkedStruct { i: isize }
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableStruct { i: isize }
pub struct DeprecatedUnitStruct;
#[unstable(feature = "test_feature")]
pub struct UnstableUnitStruct;
- pub struct UnmarkedUnitStruct;
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableUnitStruct;
#[unstable(feature = "test_feature")]
UnstableVariant,
- UnmarkedVariant,
#[stable(feature = "rust1", since = "1.0.0")]
StableVariant,
}
pub struct DeprecatedTupleStruct(isize);
#[unstable(feature = "test_feature")]
pub struct UnstableTupleStruct(isize);
- pub struct UnmarkedTupleStruct(isize);
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableTupleStruct(isize);
foo.method_unstable_text();
foo.trait_unstable_text();
- unmarked();
- foo.method_unmarked();
- foo.trait_unmarked();
-
stable();
foo.method_stable();
foo.trait_stable();
let _ = DeprecatedStruct { i: 0 }; //~ ERROR use of deprecated item
let _ = UnstableStruct { i: 0 };
- let _ = UnmarkedStruct { i: 0 };
let _ = StableStruct { i: 0 };
let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
let _ = UnstableUnitStruct;
- let _ = UnmarkedUnitStruct;
let _ = StableUnitStruct;
let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
let _ = Enum::UnstableVariant;
- let _ = Enum::UnmarkedVariant;
let _ = Enum::StableVariant;
let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
let _ = UnstableTupleStruct (1);
- let _ = UnmarkedTupleStruct (1);
let _ = StableTupleStruct (1);
}
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_unstable();
foo.trait_unstable_text();
- foo.trait_unmarked();
foo.trait_stable();
}
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
foo.trait_unstable();
foo.trait_unstable_text();
- foo.trait_unmarked();
foo.trait_stable();
}
#![deny(unused_extern_crates)]
#![allow(unused_variables)]
+#![allow(deprecated)]
#![feature(libc)]
#![feature(collections)]
#![feature(rand)]
#![feature(trace_macros, concat_idents)]
#[derive(Default, //~ ERROR
- Rand, //~ ERROR
Zero)] //~ ERROR
enum CantDeriveThose {}
// 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());
--- /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.
+
+// Checks that exported items without stability attributes cause an error
+
+#![crate_type="lib"]
+#![feature(staged_api)]
+#![staged_api]
+
+pub fn unmarked() {
+ //~^ ERROR This node does not have a stability attribute
+ ()
+}
+
+#[unstable(feature = "foo")]
+pub mod foo {
+ // #[unstable] is inherited
+ pub fn unmarked() {}
+}
+
+#[stable(feature = "bar", since="1.0.0")]
+pub mod bar {
+ // #[stable] is not inherited
+ pub fn unmarked() {}
+ //~^ ERROR This node does not have a stability attribute
+}
\ No newline at end of file
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(box_syntax)]
+#![feature(box_syntax, unboxed_closures)]
use std::usize;
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+
fn test(_x: Box<usize>) {}
fn main() {
let i = box 3;
- let _f = |&:| test(i); //~ ERROR cannot move out
+ let _f = to_fn(|| test(i)); //~ ERROR cannot move out
}
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
+}
#![feature(unboxed_closures)]
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
+
fn main() {
- let f = move|:| ();
+ let f = to_fn_once(move|| ());
f();
f(); //~ ERROR use of moved value
}
// if the upvar is captured by ref or the closure takes self by
// reference.
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
+
fn main() {
// By-ref cases
{
let x = box 0us;
- let f = |&:| drop(x); //~ ERROR cannot move
+ let f = to_fn(|| drop(x)); //~ ERROR cannot move
}
{
let x = box 0us;
- let f = |&mut:| drop(x); //~ ERROR cannot move
+ let f = to_fn_mut(|| drop(x)); //~ ERROR cannot move
}
{
let x = box 0us;
- let f = |:| drop(x); // OK -- FnOnce
+ let f = to_fn_once(|| drop(x)); // OK -- FnOnce
}
// By-value cases
{
let x = box 0us;
- let f = move |&:| drop(x); //~ ERROR cannot move
+ let f = to_fn(move || drop(x)); //~ ERROR cannot move
}
{
let x = box 0us;
- let f = move |&mut:| drop(x); //~ ERROR cannot move
+ let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move
}
{
let x = box 0us;
- let f = move |:| drop(x); // this one is ok
+ let f = to_fn_once(move || drop(x)); // this one is ok
}
}
// as `mut` through a closure. Also test that we CAN mutate a moved copy,
// unless this is a `Fn` closure. Issue #16749.
+#![feature(unboxed_closures)]
+
use std::mem;
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
fn a() {
let n = 0u8;
- let mut f = |&mut:| { //~ ERROR closure cannot assign
+ let mut f = to_fn_mut(|| { //~ ERROR closure cannot assign
n += 1;
- };
+ });
}
fn b() {
let mut n = 0u8;
- let mut f = |&mut:| {
+ let mut f = to_fn_mut(|| {
n += 1; // OK
- };
+ });
}
fn c() {
let n = 0u8;
- let mut f = move |&mut:| {
+ let mut f = to_fn_mut(move || {
// If we just did a straight-forward desugaring, this would
// compile, but we do something a bit more subtle, and hence
// we get an error.
n += 1; //~ ERROR cannot assign
- };
+ });
}
fn d() {
let mut n = 0u8;
- let mut f = move |&mut:| {
+ let mut f = to_fn_mut(move || {
n += 1; // OK
- };
+ });
}
fn e() {
let n = 0u8;
- let mut f = move |&:| {
+ let mut f = to_fn(move || {
n += 1; //~ ERROR cannot assign
- };
+ });
}
fn f() {
let mut n = 0u8;
- let mut f = move |&:| {
+ let mut f = to_fn(move || {
n += 1; //~ ERROR cannot assign
- };
+ });
}
fn main() { }
#![feature(unboxed_closures)]
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
fn main() {
- let mut_ = |&mut: x| x;
+ let mut_ = to_fn_mut(|x| x);
mut_.call((0, )); //~ ERROR does not implement any method in scope named `call`
}
use std::ops::FnMut;
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
fn call_it<F:FnMut(isize,isize)->isize>(y: isize, mut f: F) -> isize {
f(2, y)
}
pub fn main() {
- let f = |&mut: x: usize, y: isize| -> isize { (x as isize) + y };
+ let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y });
let z = call_it(3, f);
//~^ ERROR type mismatch
//~| ERROR type mismatch
+++ /dev/null
-// Copyright 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.
-
-#![feature(lang_items, overloaded_calls, unboxed_closures)]
-
-fn c<F:Fn(isize, isize) -> isize>(f: F) -> isize {
- f(5, 6)
-}
-
-fn main() {
- let z: isize = 7;
- assert_eq!(c(|&mut: x: isize, y| x + y + z), 10);
- //~^ ERROR not implemented
- //~| ERROR not implemented
-}
-
assoc_enum(Enum::Variant2(8i64, 9i32));
}
-fn zzz() { () }
\ No newline at end of file
+fn zzz() { () }
// except according to those terms.
-fn foo(i: int) -> int { i + 1 }
+fn foo(i: isize) -> isize { i + 1 }
fn apply<A, F>(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) }
pub fn main() {
- let f = {|: i| foo(i)};
+ let f = {|i| foo(i)};
assert_eq!(apply(f, 2), 3);
}
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());
}
#![feature(box_syntax)]
#![feature(unboxed_closures)]
-struct A { a: Box<int> }
+struct A { a: Box<isize> }
-fn foo() -> Box<FnMut() -> int + 'static> {
+fn foo() -> Box<FnMut() -> isize + 'static> {
let k = box 22;
let _u = A {a: k.clone()};
let result = |&mut:| 22;
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();
#![feature(unboxed_closures)]
fn main() {
- let mut zero = |&mut:| {};
- let () = zero.call_mut(());
+ let mut zero = || {};
+ let () = zero();
}