From d2028340921658424c9ffbf4305c82e2394f46bd Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 23 Aug 2013 20:15:08 -0700 Subject: [PATCH 01/30] Initial sketching out of the new path module Define the basic types, and the GenericPath trait. This module is currently called path2. It will be renamed later. --- src/libstd/path2.rs | 285 ++++++++++++++++++++++++++++++++++++++++++++ src/libstd/std.rs | 1 + 2 files changed, 286 insertions(+) create mode 100644 src/libstd/path2.rs diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs new file mode 100644 index 0000000000000..b9f252098344c --- /dev/null +++ b/src/libstd/path2.rs @@ -0,0 +1,285 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Cross-platform file path handling (re-write) + +use c_str::{CString, ToCStr}; +use clone::Clone; +use cmp::Eq; +use from_str::FromStr; +use option::{Option, None, Some}; +use str; +use str::{OwnedStr, Str, StrSlice}; +use to_str::ToStr; + +/// Typedef for the platform-native path type +#[cfg(unix)] +pub type Path = PosixPath; +// /// Typedef for the platform-native path type +//#[cfg(windows)] +//pub type Path = WindowsPath; + +/// Represents a POSIX file path +#[deriving(Clone, DeepClone)] +pub struct PosixPath { + priv repr: ~str, // assumed to never be empty + priv sepidx: Option // index of the final separator in repr +} + +impl Eq for PosixPath { + fn eq(&self, other: &PosixPath) -> bool { + self.repr == other.repr + } +} + +impl FromStr for PosixPath { + fn from_str(s: &str) -> Option { + Some(PosixPath::new(s)) + } +} + +/// A trait that represents the generic operations available on paths +pub trait GenericPath: Clone { + /// Creates a new Path from a string. + /// The resulting path will always be normalized. + fn from_str(path: &str) -> Self; + + /// Returns the path as a string + fn as_str<'a>(&'a self) -> &'a str; + + /// Returns the directory component of `self`, as a string (with no trailing separator). + /// If `self` has no directory component, returns ".". + fn dirname<'a>(&'a self) -> &'a str; + /// Returns the file component of `self`, as a string. + /// If `self` represents the root of the file hierarchy, returns the empty string. + /// If `self` is ".", returns the empty string. + fn filename<'a>(&'a self) -> &'a str; + /// Returns the stem of the filename of `self`, as a string. + /// The stem is the portion of the filename just before the last '.'. + /// If there is no '.', the entire filename is returned. + fn filestem<'a>(&'a self) -> &'a str { + let name = self.filename(); + match name.rfind('.') { + None | Some(0) => name, + Some(1) if name == ".." => name, + Some(pos) => name.slice_to(pos) + } + } + /// Returns the extension of the filename of `self`, as a string option. + /// 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 string is returned. + fn extension<'a>(&'a self) -> Option<&'a str> { + let name = self.filename(); + match name.rfind('.') { + None | Some(0) => None, + Some(1) if name == ".." => None, + Some(pos) => Some(name.slice_from(pos+1)) + } + } + + /// Replaces the directory portion of the path with the given string. + /// If `self` represents the root of the filesystem hierarchy, the last path component + /// of the given string becomes the filename. + fn set_dirname(&mut self, dirname: &str); + /// Replaces the filename portion of the path with the given string. + /// If the replacement name is "", this is equivalent to popping the path. + fn set_filename(&mut self, filename: &str); + /// Replaces the filestem with the given string. + /// If there is no extension in `self` (or `self` has no filename), this is equivalent + /// to `set_filename`. Otherwise, if the given string is "", the extension (including + /// the preceding ".") becomes the new filename. + fn set_filestem(&mut self, filestem: &str) { + // borrowck is being a pain here + let val = { + let name = self.filename(); + if !name.is_empty() { + match name.rfind('.') { + None | Some(0) => None, + Some(idx) => { + let mut s = str::with_capacity(filestem.len() + name.len() - idx); + s.push_str(filestem); + s.push_str(name.slice_from(idx)); + Some(s) + } + } + } else { None } + }; + match val { + None => self.set_filename(filestem), + Some(s) => self.set_filename(s) + } + } + /// Replaces the extension with the given string. + /// If there is no extension in `self`, this adds one. + /// If the given string is "", this removes the extension. + /// If `self` has no filename, this is a no-op. + fn set_extension(&mut self, extension: &str) { + // borrowck causes problems here too + let val = { + let name = self.filename(); + if !name.is_empty() { + match name.rfind('.') { + None | Some(0) => { + if extension.is_empty() { + None + } else { + let mut s = str::with_capacity(name.len() + extension.len() + 1); + s.push_str(name); + s.push_char('.'); + s.push_str(extension); + Some(s) + } + } + Some(idx) => { + if extension.is_empty() { + Some(name.slice_to(idx).to_owned()) + } else { + let mut s = str::with_capacity(idx + extension.len() + 1); + s.push_str(name.slice_to(idx+1)); + s.push_str(extension); + Some(s) + } + } + } + } else { None } + }; + match val { + None => (), + Some(s) => self.set_filename(s) + } + } + + /// Returns a new Path constructed by replacing the dirname with the given string. + /// See `set_dirname` for details. + fn with_dirname(&self, dirname: &str) -> Self { + let mut p = self.clone(); + p.set_dirname(dirname); + p + } + /// Returns a new Path constructed by replacing the filename with the given string. + /// See `set_filename` for details. + fn with_filename(&self, filename: &str) -> Self { + let mut p = self.clone(); + p.set_filename(filename); + p + } + /// Returns a new Path constructed by setting the filestem to the given string. + /// See `set_filestem` for details. + fn with_filestem(&self, filestem: &str) -> Self { + let mut p = self.clone(); + p.set_filestem(filestem); + p + } + /// Returns a new Path constructed by setting the extension to the given string. + /// See `set_extension` for details. + fn with_extension(&self, extension: &str) -> 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`. + fn dir_path(&self) -> Self { + GenericPath::from_str(self.dirname()) + } + /// Returns the file component of `self`, as a relative Path. + /// If `self` represents the root of the filesystem hierarchy, returns None. + fn file_path(&self) -> Option { + match self.filename() { + "" => None, + s => Some(GenericPath::from_str(s)) + } + } + + /// Pushes a path (as a string) onto `self`. + /// If the argument represents an absolute path, it replaces `self`. + fn push(&mut self, path: &str); + /// Pushes a Path onto `self`. + /// If the argument represents an absolute path, it replaces `self`. + fn push_path(&mut self, path: &Self); + /// Pops the last path component off of `self` and returns it. + /// If `self` represents the root of the file hierarchy, None is returned. + fn pop_opt(&mut self) -> Option<~str>; + + /// Returns a new Path constructed by joining `self` with the given path (as a string). + /// If the given path is absolute, the new Path will represent just that. + fn join(&self, path: &str) -> Self { + let mut p = self.clone(); + p.push(path); + p + } + /// Returns a new Path constructed by joining `self` with the given path. + /// If the given path is absolute, the new Path will represent just that. + fn join_path(&self, path: &Self) -> Self { + let mut p = self.clone(); + p.push_path(path); + p + } + + /// Returns whether `self` represents an absolute path. + fn is_absolute(&self) -> bool; + + /// Returns whether `self` is equal to, or is an ancestor of, the given path. + /// If both paths are relative, they are compared as though they are relative + /// to the same parent path. + fn is_ancestor_of(&self, other: &Self) -> bool; + + /// Returns the Path that, were it joined to `base`, would yield `self`. + /// If no such path exists, None is returned. + /// If `self` is absolute and `base` is relative, or on Windows if both + /// paths refer to separate drives, an absolute path is returned. + fn path_relative_from(&self, base: &Self) -> Option; +} + +impl ToStr for PosixPath { + #[inline] + fn to_str(&self) -> ~str { + self.as_str().to_owned() + } +} + +impl ToCStr for PosixPath { + #[inline] + fn to_c_str(&self) -> CString { + self.as_str().to_c_str() + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + self.as_str().to_c_str_unchecked() + } +} + +/// Various POSIX helpers +pub mod posix { + /// The standard path separator character + pub static sep: char = '/'; + + /// Returns whether the given char is a path separator + #[inline] + pub fn is_sep(u: char) -> bool { + u == sep + } +} + +/// Various Windows helpers +pub mod windows { + /// The standard path separator character + pub static sep: char = '\\'; + + /// Returns whether the given char is a path separator (both / and \) + #[inline] + pub fn is_sep(u: char) -> bool { + u == sep || u == '/' + } +} diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 73cb6a5645de3..e8109124e57a0 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -177,6 +177,7 @@ pub mod libc; pub mod c_str; pub mod os; pub mod path; +pub mod path2; pub mod rand; pub mod run; pub mod sys; From e97d61672b3f31e4d54589bed20286aca02bf42b Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Fri, 23 Aug 2013 23:30:17 -0700 Subject: [PATCH 02/30] path2: Implement PosixPath Fixes #5389 (new conventions for Path constructor) --- src/libstd/path2.rs | 771 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 770 insertions(+), 1 deletion(-) diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs index b9f252098344c..a98fcc40e189d 100644 --- a/src/libstd/path2.rs +++ b/src/libstd/path2.rs @@ -10,14 +10,18 @@ //! Cross-platform file path handling (re-write) +use container::Container; use c_str::{CString, ToCStr}; use clone::Clone; use cmp::Eq; use from_str::FromStr; +use iterator::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; -use str::{OwnedStr, Str, StrSlice}; +use str::{OwnedStr, Str, StrSlice, StrVector}; use to_str::ToStr; +use util; +use vec::{ImmutableVector, OwnedVector}; /// Typedef for the platform-native path type #[cfg(unix)] @@ -26,6 +30,16 @@ pub type Path = PosixPath; //#[cfg(windows)] //pub type Path = WindowsPath; +/// Typedef for the platform-native component iterator +#[cfg(unix)] +pub type ComponentIter<'self> = PosixComponentIter<'self>; +// /// Typedef for the platform-native component iterator +//#[cfg(windows)] +//pub type ComponentIter<'self> = WindowsComponentIter<'self>; + +/// Iterator that yields successive components of a PosixPath +type PosixComponentIter<'self> = str::CharSplitIterator<'self, char>; + /// Represents a POSIX file path #[deriving(Clone, DeepClone)] pub struct PosixPath { @@ -260,6 +274,312 @@ impl ToCStr for PosixPath { } } +impl GenericPath for PosixPath { + #[inline] + fn from_str(s: &str) -> PosixPath { + PosixPath::new(s) + } + + #[inline] + fn as_str<'a>(&'a self) -> &'a str { + self.repr.as_slice() + } + + fn dirname<'a>(&'a self) -> &'a str { + match self.sepidx { + None if ".." == self.repr => "..", + None => ".", + Some(0) => self.repr.slice_to(1), + Some(idx) if self.repr.slice_from(idx+1) == ".." => self.repr.as_slice(), + Some(idx) => self.repr.slice_to(idx) + } + } + + fn filename<'a>(&'a self) -> &'a str { + match self.sepidx { + None if "." == self.repr || ".." == self.repr => "", + None => self.repr.as_slice(), + Some(idx) if self.repr.slice_from(idx+1) == ".." => "", + Some(idx) => self.repr.slice_from(idx+1) + } + } + + fn set_dirname(&mut self, dirname: &str) { + match self.sepidx { + None if "." == self.repr || ".." == self.repr => { + self.repr = PosixPath::normalize(dirname); + } + None => { + let mut s = str::with_capacity(dirname.len() + self.repr.len() + 1); + s.push_str(dirname); + s.push_char(posix::sep); + s.push_str(self.repr); + self.repr = PosixPath::normalize(s); + } + Some(0) if self.repr.len() == 1 && self.repr[0] == posix::sep as u8 => { + self.repr = PosixPath::normalize(dirname); + } + Some(idx) if dirname == "" => { + let s = PosixPath::normalize(self.repr.slice_from(idx+1)); + self.repr = s; + } + Some(idx) if self.repr.slice_from(idx+1) == ".." => { + self.repr = PosixPath::normalize(dirname); + } + Some(idx) => { + let mut s = str::with_capacity(dirname.len() + self.repr.len() - idx); + s.push_str(dirname); + s.push_str(self.repr.slice_from(idx)); + self.repr = PosixPath::normalize(s); + } + } + self.sepidx = self.repr.rfind(posix::sep); + } + + fn set_filename(&mut self, filename: &str) { + match self.sepidx { + None if ".." == self.repr => { + let mut s = str::with_capacity(3 + filename.len()); + s.push_str(".."); + s.push_char(posix::sep); + s.push_str(filename); + self.repr = PosixPath::normalize(s); + } + None => { + self.repr = PosixPath::normalize(filename); + } + Some(idx) if self.repr.slice_from(idx+1) == ".." => { + let mut s = str::with_capacity(self.repr.len() + 1 + filename.len()); + s.push_str(self.repr); + s.push_char(posix::sep); + s.push_str(filename); + self.repr = PosixPath::normalize(s); + } + Some(idx) => { + let mut s = str::with_capacity(self.repr.len() - idx + filename.len()); + s.push_str(self.repr.slice_to(idx+1)); + s.push_str(filename); + self.repr = PosixPath::normalize(s); + } + } + self.sepidx = self.repr.rfind(posix::sep); + } + + fn push(&mut self, path: &str) { + if !path.is_empty() { + if path[0] == posix::sep as u8 { + self.repr = PosixPath::normalize(path); + } else { + let mut s = str::with_capacity(self.repr.len() + path.len() + 1); + s.push_str(self.repr); + s.push_char(posix::sep); + s.push_str(path); + self.repr = PosixPath::normalize(s); + } + self.sepidx = self.repr.rfind(posix::sep); + } + } + + fn push_path(&mut self, path: &PosixPath) { + self.push(path.as_str()); + } + + fn pop_opt(&mut self) -> Option<~str> { + match self.sepidx { + None if "." == self.repr => None, + None => { + let mut s = ~"."; + util::swap(&mut s, &mut self.repr); + self.sepidx = None; + Some(s) + } + Some(0) if "/" == self.repr => None, + Some(idx) => { + let s = self.repr.slice_from(idx+1).to_owned(); + if idx == 0 { + self.repr.truncate(idx+1); + } else { + self.repr.truncate(idx); + } + self.sepidx = self.repr.rfind(posix::sep); + Some(s) + } + } + } + + #[inline] + fn is_absolute(&self) -> bool { + self.repr[0] == posix::sep as u8 + } + + fn is_ancestor_of(&self, other: &PosixPath) -> bool { + if self.is_absolute() != other.is_absolute() { + false + } else { + let mut ita = self.component_iter(); + let mut itb = other.component_iter(); + if "." == self.repr { + return match itb.next() { + Some("..") => false, + _ => true + }; + } + loop { + match (ita.next(), itb.next()) { + (None, _) => break, + (Some(a), Some(b)) if a == b => { loop }, + (Some(".."), _) => { + // if ita contains only .. components, it's an ancestor + return ita.all(|x| x == ".."); + } + _ => return false + } + } + true + } + } + + fn path_relative_from(&self, base: &PosixPath) -> Option { + if self.is_absolute() != base.is_absolute() { + if self.is_absolute() { + Some(self.clone()) + } else { + None + } + } else { + let mut ita = self.component_iter(); + let mut itb = base.component_iter(); + let mut comps = ~[]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(&mut ita); + break; + } + (None, _) => comps.push(".."), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(".")) => comps.push(a), + (Some(_), Some("..")) => return None, + (Some(a), Some(_)) => { + comps.push(".."); + for _ in itb { + comps.push(".."); + } + comps.push(a); + comps.extend(&mut ita); + break; + } + } + } + Some(PosixPath::new(comps.connect(str::from_char(posix::sep)))) + } + } +} + +impl PosixPath { + /// Returns a new PosixPath from a string + pub fn new(s: &str) -> PosixPath { + let s = PosixPath::normalize(s); + assert!(!s.is_empty()); + let idx = s.rfind(posix::sep); + PosixPath{ repr: s, sepidx: idx } + } + + /// Converts the PosixPath into an owned string + pub fn into_str(self) -> ~str { + self.repr + } + + /// Returns a normalized string representation of a path, by removing all empty + /// components, and unnecessary . and .. components. + pub fn normalize(s: S) -> ~str { + // borrowck is being very picky + let val = { + let is_abs = !s.as_slice().is_empty() && s.as_slice()[0] == posix::sep as u8; + let s_ = if is_abs { s.as_slice().slice_from(1) } else { s.as_slice() }; + let comps = normalize_helper(s_, is_abs, posix::sep); + match comps { + None => None, + Some(comps) => { + let sepstr = str::from_char(posix::sep); + if is_abs && comps.is_empty() { + Some(sepstr) + } else { + let n = if is_abs { comps.len() } else { comps.len() - 1} + + comps.iter().map(|s| s.len()).sum(); + let mut s = str::with_capacity(n); + let mut it = comps.move_iter(); + if !is_abs { + match it.next() { + None => (), + Some(comp) => s.push_str(comp) + } + } + for comp in it { + s.push_str(sepstr); + s.push_str(comp); + } + Some(s) + } + } + } + }; + match val { + None => s.into_owned(), + 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 component_iter<'a>(&'a self) -> PosixComponentIter<'a> { + let s = if self.repr[0] == posix::sep as u8 { + self.repr.slice_from(1) + } else { self.repr.as_slice() }; + let mut ret = s.split_iter(posix::sep); + if s.is_empty() { + // consume the empty "" component + ret.next(); + } + ret + } +} + +// None result means the string didn't need normalizing +fn normalize_helper<'a, Sep: str::CharEq>(s: &'a str, is_abs: bool, sep: Sep) -> Option<~[&'a str]> { + if is_abs && s.as_slice().is_empty() { + return None; + } + let mut comps: ~[&'a str] = ~[]; + let mut n_up = 0u; + let mut changed = false; + for comp in s.split_iter(sep) { + match comp { + "" => { changed = true; } + "." => { changed = true; } + ".." if is_abs && comps.is_empty() => { changed = true; } + ".." if comps.len() == n_up => { comps.push(".."); n_up += 1; } + ".." => { comps.pop_opt(); changed = true; } + x => comps.push(x) + } + } + if changed { + if comps.is_empty() && !is_abs { + if s == "." { + return None; + } + comps.push("."); + } + Some(comps) + } else { + None + } +} + /// Various POSIX helpers pub mod posix { /// The standard path separator character @@ -283,3 +603,452 @@ pub mod windows { u == sep || u == '/' } } + +#[cfg(test)] +mod tests { + use super::*; + use option::{Some, None}; + use iterator::Iterator; + use vec::Vector; + + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let path = $path; + assert_eq!(path.as_str(), $exp); + } + ) + ) + + #[test] + fn test_posix_paths() { + t!(PosixPath::new(""), "."); + t!(PosixPath::new("/"), "/"); + t!(PosixPath::new("hi"), "hi"); + t!(PosixPath::new("/lib"), "/lib"); + t!(PosixPath::new("hi/there"), "hi/there"); + t!(PosixPath::new("hi/there.txt"), "hi/there.txt"); + + t!(PosixPath::new("hi/there/"), "hi/there"); + t!(PosixPath::new("hi/../there"), "there"); + t!(PosixPath::new("../hi/there"), "../hi/there"); + t!(PosixPath::new("/../hi/there"), "/hi/there"); + t!(PosixPath::new("foo/.."), "."); + t!(PosixPath::new("/foo/.."), "/"); + t!(PosixPath::new("/foo/../.."), "/"); + t!(PosixPath::new("/foo/../../bar"), "/bar"); + t!(PosixPath::new("/./hi/./there/."), "/hi/there"); + t!(PosixPath::new("/./hi/./there/./.."), "/hi"); + t!(PosixPath::new("foo/../.."), ".."); + t!(PosixPath::new("foo/../../.."), "../.."); + t!(PosixPath::new("foo/../../bar"), "../bar"); + + assert_eq!(PosixPath::new("foo/bar").into_str(), ~"foo/bar"); + assert_eq!(PosixPath::new("/foo/../../bar").into_str(), ~"/bar"); + } + + #[test] + fn test_posix_components() { + macro_rules! t( + ($path:expr, $op:ident, $exp:expr) => ( + { + let path = PosixPath::new($path); + assert_eq!(path.$op(), $exp); + } + ) + ) + + t!("a/b/c", filename, "c"); + t!("/a/b/c", filename, "c"); + t!("a", filename, "a"); + t!("/a", filename, "a"); + t!(".", filename, ""); + t!("/", filename, ""); + t!("..", filename, ""); + t!("../..", filename, ""); + + t!("a/b/c", dirname, "a/b"); + t!("/a/b/c", dirname, "/a/b"); + t!("a", dirname, "."); + t!("/a", dirname, "/"); + t!(".", dirname, "."); + t!("/", dirname, "/"); + t!("..", dirname, ".."); + t!("../..", dirname, "../.."); + + t!("hi/there.txt", filestem, "there"); + t!("hi/there", filestem, "there"); + t!("there.txt", filestem, "there"); + t!("there", filestem, "there"); + t!(".", filestem, ""); + t!("/", filestem, ""); + t!("foo/.bar", filestem, ".bar"); + t!(".bar", filestem, ".bar"); + t!("..bar", filestem, "."); + t!("hi/there..txt", filestem, "there."); + t!("..", filestem, ""); + t!("../..", filestem, ""); + + t!("hi/there.txt", extension, Some("txt")); + t!("hi/there", extension, None); + t!("there.txt", extension, Some("txt")); + t!("there", extension, None); + t!(".", extension, None); + t!("/", extension, None); + t!("foo/.bar", extension, None); + t!(".bar", extension, None); + t!("..bar", extension, Some("bar")); + t!("hi/there..txt", extension, Some("txt")); + t!("..", extension, None); + t!("../..", extension, None); + } + + #[test] + fn test_posix_push() { + macro_rules! t( + ($path:expr, $join:expr) => ( + { + let path = ($path); + let join = ($join); + let mut p1 = PosixPath::new(path); + p1.push(join); + let p2 = PosixPath::new(path); + assert_eq!(p1, p2.join(join)); + } + ) + ) + + t!("a/b/c", ".."); + t!("/a/b/c", "d"); + t!("a/b", "c/d"); + t!("a/b", "/c/d"); + } + + #[test] + fn test_posix_push_path() { + macro_rules! t( + ($path:expr, $push:expr, $exp:expr) => ( + { + let mut p = PosixPath::new($path); + let push = PosixPath::new($push); + p.push_path(&push); + assert_eq!(p.as_str(), $exp); + } + ) + ) + + t!("a/b/c", "d", "a/b/c/d"); + t!("/a/b/c", "d", "/a/b/c/d"); + t!("a/b", "c/d", "a/b/c/d"); + t!("a/b", "/c/d", "/c/d"); + t!("a/b", ".", "a/b"); + t!("a/b", "../c", "a/c"); + } + + #[test] + fn test_posix_pop() { + macro_rules! t( + ($path:expr, $left:expr, $right:expr) => ( + { + let mut p = PosixPath::new($path); + let file = p.pop_opt(); + assert_eq!(p.as_str(), $left); + assert_eq!(file.map(|s| s.as_slice()), $right); + } + ) + ) + + t!("a/b/c", "a/b", Some("c")); + t!("a", ".", Some("a")); + t!(".", ".", None); + t!("/a", "/", Some("a")); + t!("/", "/", None); + } + + #[test] + fn test_posix_join() { + t!(PosixPath::new("a/b/c").join(".."), "a/b"); + t!(PosixPath::new("/a/b/c").join("d"), "/a/b/c/d"); + t!(PosixPath::new("a/b").join("c/d"), "a/b/c/d"); + t!(PosixPath::new("a/b").join("/c/d"), "/c/d"); + t!(PosixPath::new(".").join("a/b"), "a/b"); + t!(PosixPath::new("/").join("a/b"), "/a/b"); + } + + #[test] + fn test_posix_join_path() { + macro_rules! t( + ($path:expr, $join:expr, $exp:expr) => ( + { + let path = PosixPath::new($path); + let join = PosixPath::new($join); + let res = path.join_path(&join); + assert_eq!(res.as_str(), $exp); + } + ) + ) + + t!("a/b/c", "..", "a/b"); + t!("/a/b/c", "d", "/a/b/c/d"); + t!("a/b", "c/d", "a/b/c/d"); + t!("a/b", "/c/d", "/c/d"); + t!(".", "a/b", "a/b"); + t!("/", "a/b", "/a/b"); + } + + #[test] + fn test_posix_with_helpers() { + t!(PosixPath::new("a/b/c").with_dirname("d"), "d/c"); + t!(PosixPath::new("a/b/c").with_dirname("d/e"), "d/e/c"); + t!(PosixPath::new("a/b/c").with_dirname(""), "c"); + t!(PosixPath::new("a/b/c").with_dirname("/"), "/c"); + t!(PosixPath::new("a/b/c").with_dirname("."), "c"); + t!(PosixPath::new("a/b/c").with_dirname(".."), "../c"); + t!(PosixPath::new("/").with_dirname("foo"), "foo"); + t!(PosixPath::new("/").with_dirname(""), "."); + t!(PosixPath::new("/foo").with_dirname("bar"), "bar/foo"); + t!(PosixPath::new("..").with_dirname("foo"), "foo"); + t!(PosixPath::new("../..").with_dirname("foo"), "foo"); + t!(PosixPath::new("foo").with_dirname(".."), "../foo"); + t!(PosixPath::new("foo").with_dirname("../.."), "../../foo"); + + t!(PosixPath::new("a/b/c").with_filename("d"), "a/b/d"); + t!(PosixPath::new(".").with_filename("foo"), "foo"); + t!(PosixPath::new("/a/b/c").with_filename("d"), "/a/b/d"); + t!(PosixPath::new("/").with_filename("foo"), "/foo"); + t!(PosixPath::new("/a").with_filename("foo"), "/foo"); + t!(PosixPath::new("foo").with_filename("bar"), "bar"); + t!(PosixPath::new("a/b/c").with_filename(""), "a/b"); + t!(PosixPath::new("a/b/c").with_filename("."), "a/b"); + t!(PosixPath::new("a/b/c").with_filename(".."), "a"); + t!(PosixPath::new("/a").with_filename(""), "/"); + t!(PosixPath::new("foo").with_filename(""), "."); + t!(PosixPath::new("a/b/c").with_filename("d/e"), "a/b/d/e"); + t!(PosixPath::new("a/b/c").with_filename("/d"), "a/b/d"); + t!(PosixPath::new("..").with_filename("foo"), "../foo"); + t!(PosixPath::new("../..").with_filename("foo"), "../../foo"); + + t!(PosixPath::new("hi/there.txt").with_filestem("here"), "hi/here.txt"); + t!(PosixPath::new("hi/there.txt").with_filestem(""), "hi/.txt"); + t!(PosixPath::new("hi/there.txt").with_filestem("."), "hi/..txt"); + t!(PosixPath::new("hi/there.txt").with_filestem(".."), "hi/...txt"); + t!(PosixPath::new("hi/there.txt").with_filestem("/"), "hi/.txt"); + t!(PosixPath::new("hi/there.txt").with_filestem("foo/bar"), "hi/foo/bar.txt"); + t!(PosixPath::new("hi/there.foo.txt").with_filestem("here"), "hi/here.txt"); + t!(PosixPath::new("hi/there").with_filestem("here"), "hi/here"); + t!(PosixPath::new("hi/there").with_filestem(""), "hi"); + t!(PosixPath::new("hi").with_filestem(""), "."); + t!(PosixPath::new("/hi").with_filestem(""), "/"); + t!(PosixPath::new("hi/there").with_filestem(".."), "."); + t!(PosixPath::new("hi/there").with_filestem("."), "hi"); + t!(PosixPath::new("hi/there.").with_filestem("foo"), "hi/foo."); + t!(PosixPath::new("hi/there.").with_filestem(""), "hi"); + t!(PosixPath::new("hi/there.").with_filestem("."), "."); + t!(PosixPath::new("hi/there.").with_filestem(".."), "hi/..."); + t!(PosixPath::new("/").with_filestem("foo"), "/foo"); + t!(PosixPath::new(".").with_filestem("foo"), "foo"); + t!(PosixPath::new("hi/there..").with_filestem("here"), "hi/here."); + t!(PosixPath::new("hi/there..").with_filestem(""), "hi"); + + t!(PosixPath::new("hi/there.txt").with_extension("exe"), "hi/there.exe"); + t!(PosixPath::new("hi/there.txt").with_extension(""), "hi/there"); + t!(PosixPath::new("hi/there.txt").with_extension("."), "hi/there.."); + t!(PosixPath::new("hi/there.txt").with_extension(".."), "hi/there..."); + t!(PosixPath::new("hi/there").with_extension("txt"), "hi/there.txt"); + t!(PosixPath::new("hi/there").with_extension("."), "hi/there.."); + t!(PosixPath::new("hi/there").with_extension(".."), "hi/there..."); + t!(PosixPath::new("hi/there.").with_extension("txt"), "hi/there.txt"); + t!(PosixPath::new("hi/.foo").with_extension("txt"), "hi/.foo.txt"); + t!(PosixPath::new("hi/there.txt").with_extension(".foo"), "hi/there..foo"); + t!(PosixPath::new("/").with_extension("txt"), "/"); + t!(PosixPath::new("/").with_extension("."), "/"); + t!(PosixPath::new("/").with_extension(".."), "/"); + t!(PosixPath::new(".").with_extension("txt"), "."); + } + + #[test] + fn test_posix_setters() { + macro_rules! t( + ($path:expr, $set:ident, $with:ident, $arg:expr) => ( + { + let path = ($path); + let arg = ($arg); + let mut p1 = PosixPath::new(path); + p1.$set(arg); + let p2 = PosixPath::new(path); + assert_eq!(p1, p2.$with(arg)); + } + ) + ) + + t!("a/b/c", set_dirname, with_dirname, "d"); + t!("a/b/c", set_dirname, with_dirname, "d/e"); + t!("/", set_dirname, with_dirname, "foo"); + t!("/foo", set_dirname, with_dirname, "bar"); + t!("a/b/c", set_dirname, with_dirname, ""); + t!("../..", set_dirname, with_dirname, "x"); + t!("foo", set_dirname, with_dirname, "../.."); + + t!("a/b/c", set_filename, with_filename, "d"); + t!("/", set_filename, with_filename, "foo"); + t!(".", set_filename, with_filename, "foo"); + t!("a/b", set_filename, with_filename, ""); + t!("a", set_filename, with_filename, ""); + + t!("hi/there.txt", set_filestem, with_filestem, "here"); + t!("hi/there.", set_filestem, with_filestem, "here"); + t!("hi/there", set_filestem, with_filestem, "here"); + t!("hi/there.txt", set_filestem, with_filestem, ""); + t!("hi/there", set_filestem, with_filestem, ""); + + t!("hi/there.txt", set_extension, with_extension, "exe"); + t!("hi/there.", set_extension, with_extension, "txt"); + t!("hi/there", set_extension, with_extension, "txt"); + t!("hi/there.txt", set_extension, with_extension, ""); + t!("hi/there", set_extension, with_extension, ""); + t!(".", set_extension, with_extension, "txt"); + } + + #[test] + fn test_posix_dir_file_path() { + t!(PosixPath::new("hi/there").dir_path(), "hi"); + t!(PosixPath::new("hi").dir_path(), "."); + t!(PosixPath::new("/hi").dir_path(), "/"); + t!(PosixPath::new("/").dir_path(), "/"); + t!(PosixPath::new("..").dir_path(), ".."); + t!(PosixPath::new("../..").dir_path(), "../.."); + + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let path = $path; + let left = path.map(|p| p.as_str()); + assert_eq!(left, $exp); + } + ) + ) + + t!(PosixPath::new("hi/there").file_path(), Some("there")); + t!(PosixPath::new("hi").file_path(), Some("hi")); + t!(PosixPath::new(".").file_path(), None); + t!(PosixPath::new("/").file_path(), None); + t!(PosixPath::new("..").file_path(), None); + t!(PosixPath::new("../..").file_path(), None); + } + + #[test] + fn test_posix_is_absolute() { + assert_eq!(PosixPath::new("a/b/c").is_absolute(), false); + assert_eq!(PosixPath::new("/a/b/c").is_absolute(), true); + assert_eq!(PosixPath::new("a").is_absolute(), false); + assert_eq!(PosixPath::new("/a").is_absolute(), true); + assert_eq!(PosixPath::new(".").is_absolute(), false); + assert_eq!(PosixPath::new("/").is_absolute(), true); + assert_eq!(PosixPath::new("..").is_absolute(), false); + assert_eq!(PosixPath::new("../..").is_absolute(), false); + } + + #[test] + fn test_posix_is_ancestor_of() { + macro_rules! t( + ($path:expr, $dest:expr, $exp:expr) => ( + { + let path = PosixPath::new($path); + let dest = PosixPath::new($dest); + assert_eq!(path.is_ancestor_of(&dest), $exp); + } + ) + ) + + t!("a/b/c", "a/b/c/d", true); + t!("a/b/c", "a/b/c", true); + t!("a/b/c", "a/b", false); + t!("/a/b/c", "/a/b/c", true); + t!("/a/b", "/a/b/c", true); + t!("/a/b/c/d", "/a/b/c", false); + t!("/a/b", "a/b/c", false); + t!("a/b", "/a/b/c", false); + t!("a/b/c", "a/b/d", false); + t!("../a/b/c", "a/b/c", false); + t!("a/b/c", "../a/b/c", false); + t!("a/b/c", "a/b/cd", false); + t!("a/b/cd", "a/b/c", false); + t!("../a/b", "../a/b/c", true); + t!(".", "a/b", true); + t!(".", ".", true); + t!("/", "/", true); + t!("/", "/a/b", true); + t!("..", "a/b", true); + t!("../..", "a/b", true); + } + + #[test] + fn test_posix_path_relative_from() { + macro_rules! t( + ($path:expr, $other:expr, $exp:expr) => ( + { + let path = PosixPath::new($path); + let other = PosixPath::new($other); + let res = path.path_relative_from(&other); + assert_eq!(res.map(|x| x.as_str()), $exp); + } + ) + ) + + t!("a/b/c", "a/b", Some("c")); + t!("a/b/c", "a/b/d", Some("../c")); + t!("a/b/c", "a/b/c/d", Some("..")); + t!("a/b/c", "a/b/c", Some(".")); + t!("a/b/c", "a/b/c/d/e", Some("../..")); + t!("a/b/c", "a/d/e", Some("../../b/c")); + t!("a/b/c", "d/e/f", Some("../../../a/b/c")); + t!("a/b/c", "/a/b/c", None); + t!("/a/b/c", "a/b/c", Some("/a/b/c")); + t!("/a/b/c", "/a/b/c/d", Some("..")); + t!("/a/b/c", "/a/b", Some("c")); + t!("/a/b/c", "/a/b/c/d/e", Some("../..")); + t!("/a/b/c", "/a/d/e", Some("../../b/c")); + t!("/a/b/c", "/d/e/f", Some("../../../a/b/c")); + t!("hi/there.txt", "hi/there", Some("../there.txt")); + t!(".", "a", Some("..")); + t!(".", "a/b", Some("../..")); + t!(".", ".", Some(".")); + t!("a", ".", Some("a")); + t!("a/b", ".", Some("a/b")); + t!("..", ".", Some("..")); + t!("a/b/c", "a/b/c", Some(".")); + t!("/a/b/c", "/a/b/c", Some(".")); + t!("/", "/", Some(".")); + t!("/", ".", Some("/")); + t!("../../a", "b", Some("../../../a")); + t!("a", "../../b", None); + t!("../../a", "../../b", Some("../a")); + t!("../../a", "../../a/b", Some("..")); + t!("../../a/b", "../../a", Some("b")); + } + + #[test] + fn test_posix_component_iter() { + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let path = PosixPath::new($path); + let comps = path.component_iter().to_owned_vec(); + assert_eq!(comps.as_slice(), $exp); + } + ) + ) + + t!("a/b/c", ["a", "b", "c"]); + t!("a/b/d", ["a", "b", "d"]); + t!("a/b/cd", ["a", "b", "cd"]); + t!("/a/b/c", ["a", "b", "c"]); + t!("a", ["a"]); + t!("/a", ["a"]); + t!("/", []); + t!(".", ["."]); + t!("..", [".."]); + t!("../..", ["..", ".."]); + t!("../../foo", ["..", "..", "foo"]); + } +} From b41391306692c82d9b44721a47833eb809e0cbf1 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 25 Aug 2013 19:18:32 -0700 Subject: [PATCH 03/30] path2: Start reimplementing in terms of ~[u8] instead of ~str As documented in #7225, we cannot rely on paths being representable in utf-8. Specifically, Linux allows anything (besides NUL) in a path. Redesign GenericPath in light of this. PosixPath hasn't been reimplemented yet for ~[u8]. --- src/libstd/path2.rs | 445 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 357 insertions(+), 88 deletions(-) diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs index a98fcc40e189d..45ce6e94ae4aa 100644 --- a/src/libstd/path2.rs +++ b/src/libstd/path2.rs @@ -19,8 +19,8 @@ use iterator::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; use str::{OwnedStr, Str, StrSlice, StrVector}; -use to_str::ToStr; use util; +use vec; use vec::{ImmutableVector, OwnedVector}; /// Typedef for the platform-native path type @@ -40,10 +40,16 @@ pub type ComponentIter<'self> = PosixComponentIter<'self>; /// Iterator that yields successive components of a PosixPath type PosixComponentIter<'self> = str::CharSplitIterator<'self, char>; +// Condition that is raised when a NUL is found in a byte vector given to a Path function +condition! { + // this should be a &[u8] but there's a lifetime issue + null_byte: ~[u8] -> ~[u8]; +} + /// Represents a POSIX file path #[deriving(Clone, DeepClone)] pub struct PosixPath { - priv repr: ~str, // assumed to never be empty + priv repr: ~[u8], // assumed to never be empty or contain NULs priv sepidx: Option // index of the final separator in repr } @@ -55,111 +61,262 @@ impl Eq for PosixPath { impl FromStr for PosixPath { fn from_str(s: &str) -> Option { - Some(PosixPath::new(s)) + let v = s.as_bytes(); + if contains_nul(v) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) + } } } /// A trait that represents the generic operations available on paths -pub trait GenericPath: Clone { +pub trait GenericPath: Clone + GenericPathUnsafe { + /// Creates a new Path from a byte vector. + /// The resulting Path will always be normalized. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn from_vec(path: &[u8]) -> Self { + if contains_nul(path) { + let path = self::null_byte::cond.raise(path.to_owned()); + assert!(!contains_nul(path)); + unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + } else { + unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + } + } + /// Creates a new Path from a string. - /// The resulting path will always be normalized. - fn from_str(path: &str) -> Self; - - /// Returns the path as a string - fn as_str<'a>(&'a self) -> &'a str; - - /// Returns the directory component of `self`, as a string (with no trailing separator). - /// If `self` has no directory component, returns ".". - fn dirname<'a>(&'a self) -> &'a str; - /// Returns the file component of `self`, as a string. - /// If `self` represents the root of the file hierarchy, returns the empty string. - /// If `self` is ".", returns the empty string. - fn filename<'a>(&'a self) -> &'a str; - /// Returns the stem of the filename of `self`, as a string. + /// The resulting Path will always be normalized. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn from_str(path: &str) -> Self { + GenericPath::from_vec(path.as_bytes()) + } + + /// Creates a new Path from a CString. + /// The resulting Path will always be normalized. + #[inline] + fn from_c_str(path: CString) -> Self { + // CStrings can't contain NULs + unsafe { GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) } + } + + /// Returns the path as a string, if possible. + /// If the path is not representable in utf-8, this returns None. + #[inline] + fn as_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.as_vec()) + } + + /// Returns the path as a byte vector + fn as_vec<'a>(&'a self) -> &'a [u8]; + + /// Returns the directory component of `self`, as a byte vector (with no trailing separator). + /// If `self` has no directory component, returns ['.']. + fn dirname<'a>(&'a self) -> &'a [u8]; + /// Returns the directory component of `self`, as a string, if possible. + /// See `dirname` for details. + #[inline] + fn dirname_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.dirname()) + } + /// Returns the file component of `self`, as a byte vector. + /// If `self` represents the root of the file hierarchy, returns the empty vector. + /// If `self` is ".", returns the empty vector. + fn filename<'a>(&'a self) -> &'a [u8]; + /// Returns the file component of `self`, as a string, if possible. + /// See `filename` for details. + #[inline] + fn filename_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.filename()) + } + /// 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. - fn filestem<'a>(&'a self) -> &'a str { + fn filestem<'a>(&'a self) -> &'a [u8] { let name = self.filename(); - match name.rfind('.') { + let dot = '.' as u8; + match name.rposition_elem(&dot) { None | Some(0) => name, - Some(1) if name == ".." => name, + Some(1) if name == bytes!("..") => name, Some(pos) => name.slice_to(pos) } } - /// Returns the extension of the filename of `self`, as a string option. + /// Returns the stem of the filename of `self`, as a string, if possible. + /// See `filestem` for details. + #[inline] + fn filestem_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.filestem()) + } + /// 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 string is returned. - fn extension<'a>(&'a self) -> Option<&'a str> { + /// If the filename ends in '.', the empty vector is returned. + fn extension<'a>(&'a self) -> Option<&'a [u8]> { let name = self.filename(); - match name.rfind('.') { + let dot = '.' as u8; + match name.rposition_elem(&dot) { None | Some(0) => None, - Some(1) if name == ".." => None, + Some(1) if name == bytes!("..") => None, Some(pos) => Some(name.slice_from(pos+1)) } } + /// Returns the extension of the filename of `self`, as a string, if possible. + /// See `extension` for details. + #[inline] + fn extension_str<'a>(&'a self) -> Option<&'a str> { + self.extension().chain(|v| str::from_bytes_slice_opt(v)) + } - /// Replaces the directory portion of the path with the given string. + /// Replaces the directory portion of the path with the given byte vector. /// If `self` represents the root of the filesystem hierarchy, the last path component - /// of the given string becomes the filename. - fn set_dirname(&mut self, dirname: &str); + /// of the given byte vector becomes the filename. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the dirname contains a NUL. + #[inline] + fn set_dirname(&mut self, dirname: &[u8]) { + if contains_nul(dirname) { + let dirname = self::null_byte::cond.raise(dirname.to_owned()); + assert!(!contains_nul(dirname)); + unsafe { self.set_dirname_unchecked(dirname) } + } else { + unsafe { self.set_dirname_unchecked(dirname) } + } + } + /// Replaces the directory portion of the path with the given string. + /// See `set_dirname` for details. + #[inline] + fn set_dirname_str(&mut self, dirname: &str) { + self.set_dirname(dirname.as_bytes()) + } + /// Replaces the filename portion of the path with the given byte vector. + /// If the replacement name is [], this is equivalent to popping the path. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filename contains a NUL. + #[inline] + fn set_filename(&mut self, filename: &[u8]) { + if contains_nul(filename) { + let filename = self::null_byte::cond.raise(filename.to_owned()); + assert!(!contains_nul(filename)); + unsafe { self.set_filename_unchecked(filename) } + } else { + unsafe { self.set_filename_unchecked(filename) } + } + } /// Replaces the filename portion of the path with the given string. - /// If the replacement name is "", this is equivalent to popping the path. - fn set_filename(&mut self, filename: &str); - /// Replaces the filestem with the given string. + /// See `set_filename` for details. + #[inline] + fn set_filename_str(&mut self, filename: &str) { + self.set_filename(filename.as_bytes()) + } + /// Replaces the filestem with the given byte vector. /// If there is no extension in `self` (or `self` has no filename), this is equivalent - /// to `set_filename`. Otherwise, if the given string is "", the extension (including - /// the preceding ".") becomes the new filename. - fn set_filestem(&mut self, filestem: &str) { + /// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including + /// the preceding '.') becomes the new filename. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filestem contains a NUL. + fn set_filestem(&mut self, filestem: &[u8]) { // borrowck is being a pain here let val = { let name = self.filename(); if !name.is_empty() { - match name.rfind('.') { + let dot = '.' as u8; + match name.rposition_elem(&dot) { None | Some(0) => None, Some(idx) => { - let mut s = str::with_capacity(filestem.len() + name.len() - idx); - s.push_str(filestem); - s.push_str(name.slice_from(idx)); - Some(s) + let mut v; + if contains_nul(filestem) { + let filestem = self::null_byte::cond.raise(filestem.to_owned()); + assert!(!contains_nul(filestem)); + v = vec::with_capacity(filestem.len() + name.len() - idx); + v.push_all(filestem); + } else { + v = vec::with_capacity(filestem.len() + name.len() - idx); + v.push_all(filestem); + } + v.push_all(name.slice_from(idx)); + Some(v) } } } else { None } }; match val { None => self.set_filename(filestem), - Some(s) => self.set_filename(s) + Some(v) => unsafe { self.set_filename_unchecked(v) } } } - /// Replaces the extension with the given string. + /// Replaces the filestem with the given string. + /// See `set_filestem` for details. + #[inline] + fn set_filestem_str(&mut self, filestem: &str) { + self.set_filestem(filestem.as_bytes()) + } + /// Replaces the extension with the given byte vector. /// If there is no extension in `self`, this adds one. - /// If the given string is "", this removes the extension. + /// If the given byte vector is [], this removes the extension. /// If `self` has no filename, this is a no-op. - fn set_extension(&mut self, extension: &str) { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the extension contains a NUL. + fn set_extension(&mut self, extension: &[u8]) { // borrowck causes problems here too let val = { let name = self.filename(); if !name.is_empty() { - match name.rfind('.') { + let dot = '.' as u8; + match name.rposition_elem(&dot) { None | Some(0) => { if extension.is_empty() { None } else { - let mut s = str::with_capacity(name.len() + extension.len() + 1); - s.push_str(name); - s.push_char('.'); - s.push_str(extension); - Some(s) + let mut v; + if contains_nul(extension) { + let extension = self::null_byte::cond.raise(extension.to_owned()); + assert!(!contains_nul(extension)); + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } else { + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } + Some(v) } } Some(idx) => { if extension.is_empty() { Some(name.slice_to(idx).to_owned()) } else { - let mut s = str::with_capacity(idx + extension.len() + 1); - s.push_str(name.slice_to(idx+1)); - s.push_str(extension); - Some(s) + let mut v; + if contains_nul(extension) { + let extension = self::null_byte::cond.raise(extension.to_owned()); + assert!(!contains_nul(extension)); + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } else { + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } + Some(v) } } } @@ -167,73 +324,163 @@ pub trait GenericPath: Clone { }; match val { None => (), - Some(s) => self.set_filename(s) + Some(v) => unsafe { self.set_filename_unchecked(v) } } } + /// Replaces the extension with the given string. + /// See `set_extension` for details. + #[inline] + fn set_extension_str(&mut self, extension: &str) { + self.set_extension(extension.as_bytes()) + } - /// Returns a new Path constructed by replacing the dirname with the given string. + /// Returns a new Path constructed by replacing the dirname with the given byte vector. /// See `set_dirname` for details. - fn with_dirname(&self, dirname: &str) -> Self { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the dirname contains a NUL. + #[inline] + fn with_dirname(&self, dirname: &[u8]) -> Self { let mut p = self.clone(); p.set_dirname(dirname); p } - /// Returns a new Path constructed by replacing the filename with the given string. + /// Returns a new Path constructed by replacing the dirname with the given string. + /// See `set_dirname` for details. + #[inline] + fn with_dirname_str(&self, dirname: &str) -> Self { + self.with_dirname(dirname.as_bytes()) + } + /// Returns a new Path constructed by replacing the filename with the given byte vector. /// See `set_filename` for details. - fn with_filename(&self, filename: &str) -> Self { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filename contains a NUL. + #[inline] + fn with_filename(&self, filename: &[u8]) -> Self { let mut p = self.clone(); p.set_filename(filename); p } - /// Returns a new Path constructed by setting the filestem to the given string. + /// Returns a new Path constructed by replacing the filename with the given string. + /// See `set_filename` for details. + #[inline] + fn with_filename_str(&self, filename: &str) -> Self { + self.with_filename(filename.as_bytes()) + } + /// Returns a new Path constructed by setting the filestem to the given byte vector. /// See `set_filestem` for details. - fn with_filestem(&self, filestem: &str) -> Self { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filestem contains a NUL. + #[inline] + fn with_filestem(&self, filestem: &[u8]) -> Self { let mut p = self.clone(); p.set_filestem(filestem); p } - /// Returns a new Path constructed by setting the extension to the given string. + /// Returns a new Path constructed by setting the filestem to the given string. + /// See `set_filestem` for details. + #[inline] + fn with_filestem_str(&self, filestem: &str) -> Self { + self.with_filestem(filestem.as_bytes()) + } + /// Returns a new Path constructed by setting the extension to the given byte vector. /// See `set_extension` for details. - fn with_extension(&self, extension: &str) -> Self { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the extension contains a NUL. + #[inline] + fn with_extension(&self, extension: &[u8]) -> Self { let mut p = self.clone(); p.set_extension(extension); p } - + /// Returns a new Path constructed by setting the extension to the given string. + /// See `set_extension` for details. + #[inline] + fn with_extension_str(&self, extension: &str) -> Self { + self.with_extension(extension.as_bytes()) + } /// Returns the directory component of `self`, as a Path. /// If `self` represents the root of the filesystem hierarchy, returns `self`. fn dir_path(&self) -> Self { - GenericPath::from_str(self.dirname()) + GenericPath::from_vec(self.dirname()) } /// Returns the file component of `self`, as a relative Path. /// If `self` represents the root of the filesystem hierarchy, returns None. fn file_path(&self) -> Option { match self.filename() { - "" => None, - s => Some(GenericPath::from_str(s)) + [] => None, + v => Some(GenericPath::from_vec(v)) } } - /// Pushes a path (as a string) onto `self`. + /// Pushes a path (as a byte vector) onto `self`. /// If the argument represents an absolute path, it replaces `self`. - fn push(&mut self, path: &str); + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn push(&mut self, path: &[u8]) { + if contains_nul(path) { + let path = self::null_byte::cond.raise(path.to_owned()); + assert!(!contains_nul(path)); + unsafe { self.push_unchecked(path) } + } else { + unsafe { self.push_unchecked(path) } + } + } + /// Pushes a path (as a string) onto `self. + /// See `push` for details. + #[inline] + fn push_str(&mut self, path: &str) { + self.push(path.as_bytes()) + } /// Pushes a Path onto `self`. /// If the argument represents an absolute path, it replaces `self`. - fn push_path(&mut self, path: &Self); + #[inline] + fn push_path(&mut self, path: &Self) { + self.push(path.as_vec()) + } /// Pops the last path component off of `self` and returns it. /// If `self` represents the root of the file hierarchy, None is returned. - fn pop_opt(&mut self) -> Option<~str>; + fn pop_opt(&mut self) -> Option<~[u8]>; + /// Pops the last path component off of `self` and returns it as a string, if possible. + /// `self` will still be modified even if None is returned. + /// See `pop_opt` for details. + #[inline] + fn pop_opt_str(&mut self) -> Option<~str> { + self.pop_opt().chain(|v| str::from_bytes_owned_opt(v)) + } - /// Returns a new Path constructed by joining `self` with the given path (as a string). + /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). /// If the given path is absolute, the new Path will represent just that. - fn join(&self, path: &str) -> Self { + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn join(&self, path: &[u8]) -> Self { let mut p = self.clone(); p.push(path); p } + /// Returns a new Path constructed by joining `self` with the given path (as a string). + /// See `join` for details. + #[inline] + fn join_str(&self, path: &str) -> Self { + self.join(path.as_bytes()) + } /// Returns a new Path constructed by joining `self` with the given path. /// If the given path is absolute, the new Path will represent just that. + #[inline] fn join_path(&self, path: &Self) -> Self { let mut p = self.clone(); p.push_path(path); @@ -255,22 +502,42 @@ pub trait GenericPath: Clone { fn path_relative_from(&self, base: &Self) -> Option; } -impl ToStr for PosixPath { - #[inline] - fn to_str(&self) -> ~str { - self.as_str().to_owned() - } +/// A trait that represents the unsafe operations on GenericPaths +pub trait GenericPathUnsafe { + /// Creates a new Path from a byte vector without checking for null bytes. + /// The resulting Path will always be normalized. + unsafe fn from_vec_unchecked(path: &[u8]) -> Self; + + /// Replaces the directory portion of the path with the given byte vector without + /// checking for null bytes. + /// See `set_dirname` for details. + unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]); + + /// Replaces the filename portion of the path with the given byte vector without + /// checking for null bytes. + /// See `set_filename` for details. + unsafe fn set_filename_unchecked(&mut self, filename: &[u8]); + + /// Pushes a path onto `self` without checking for null bytes. + /// See `push` for details. + unsafe fn push_unchecked(&mut self, path: &[u8]); +} + +#[inline(always)] +fn contains_nul(v: &[u8]) -> bool { + v.iter().any(|&x| x == 0) } impl ToCStr for PosixPath { #[inline] fn to_c_str(&self) -> CString { - self.as_str().to_c_str() + // The Path impl guarantees no internal NUL + unsafe { self.as_vec().to_c_str_unchecked() } } #[inline] unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_str().to_c_str_unchecked() + self.as_vec().to_c_str_unchecked() } } @@ -583,24 +850,26 @@ fn normalize_helper<'a, Sep: str::CharEq>(s: &'a str, is_abs: bool, sep: Sep) -> /// Various POSIX helpers pub mod posix { /// The standard path separator character - pub static sep: char = '/'; + pub static sep: u8 = '/' as u8; - /// Returns whether the given char is a path separator + /// Returns whether the given byte is a path separator #[inline] - pub fn is_sep(u: char) -> bool { - u == sep + pub fn is_sep(u: &u8) -> bool { + *u == sep } } /// Various Windows helpers pub mod windows { /// The standard path separator character - pub static sep: char = '\\'; + pub static sep: u8 = '\\' as u8; + /// The alternative path separator character + pub static sep2: u8 = '/' as u8; - /// Returns whether the given char is a path separator (both / and \) + /// Returns whether the given byte is a path separator #[inline] - pub fn is_sep(u: char) -> bool { - u == sep || u == '/' + pub fn is_sep(u: &u8) -> bool { + *u == sep || *u == sep2 } } From 550bc9bd1c5aa90f6574b7d4ebd9922b986f9741 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 25 Aug 2013 20:46:26 -0700 Subject: [PATCH 04/30] path2: Reimplement PosixPath in terms of ~[u8] --- src/libstd/path2.rs | 292 +++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 138 deletions(-) diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs index 45ce6e94ae4aa..dea326fadfea3 100644 --- a/src/libstd/path2.rs +++ b/src/libstd/path2.rs @@ -18,10 +18,11 @@ use from_str::FromStr; use iterator::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; -use str::{OwnedStr, Str, StrSlice, StrVector}; +use str::{OwnedStr, Str, StrSlice}; use util; use vec; -use vec::{ImmutableVector, OwnedVector}; +use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; +use vec::{ImmutableEqVector, ImmutableVector, Vector, VectorVector}; /// Typedef for the platform-native path type #[cfg(unix)] @@ -38,7 +39,7 @@ pub type ComponentIter<'self> = PosixComponentIter<'self>; //pub type ComponentIter<'self> = WindowsComponentIter<'self>; /// Iterator that yields successive components of a PosixPath -type PosixComponentIter<'self> = str::CharSplitIterator<'self, char>; +type PosixComponentIter<'self> = vec::SplitIterator<'self, u8>; // Condition that is raised when a NUL is found in a byte vector given to a Path function condition! { @@ -541,142 +542,142 @@ impl ToCStr for PosixPath { } } -impl GenericPath for PosixPath { - #[inline] - fn from_str(s: &str) -> PosixPath { - PosixPath::new(s) +impl GenericPathUnsafe for PosixPath { + unsafe fn from_vec_unchecked(path: &[u8]) -> PosixPath { + let path = PosixPath::normalize(path); + assert!(!path.is_empty()); + let idx = path.rposition_elem(&posix::sep); + PosixPath{ repr: path, sepidx: idx } } - #[inline] - fn as_str<'a>(&'a self) -> &'a str { - self.repr.as_slice() - } - - fn dirname<'a>(&'a self) -> &'a str { + unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { match self.sepidx { - None if ".." == self.repr => "..", - None => ".", - Some(0) => self.repr.slice_to(1), - Some(idx) if self.repr.slice_from(idx+1) == ".." => self.repr.as_slice(), - Some(idx) => self.repr.slice_to(idx) - } - } - - fn filename<'a>(&'a self) -> &'a str { - match self.sepidx { - None if "." == self.repr || ".." == self.repr => "", - None => self.repr.as_slice(), - Some(idx) if self.repr.slice_from(idx+1) == ".." => "", - Some(idx) => self.repr.slice_from(idx+1) - } - } - - fn set_dirname(&mut self, dirname: &str) { - match self.sepidx { - None if "." == self.repr || ".." == self.repr => { + None if bytes!(".") == self.repr || bytes!("..") == self.repr => { self.repr = PosixPath::normalize(dirname); } None => { - let mut s = str::with_capacity(dirname.len() + self.repr.len() + 1); - s.push_str(dirname); - s.push_char(posix::sep); - s.push_str(self.repr); - self.repr = PosixPath::normalize(s); + let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); + v.push_all(dirname); + v.push(posix::sep); + v.push_all(self.repr); + self.repr = PosixPath::normalize(v); } - Some(0) if self.repr.len() == 1 && self.repr[0] == posix::sep as u8 => { + Some(0) if self.repr.len() == 1 && self.repr[0] == posix::sep => { self.repr = PosixPath::normalize(dirname); } - Some(idx) if dirname == "" => { - let s = PosixPath::normalize(self.repr.slice_from(idx+1)); - self.repr = s; + Some(idx) if dirname.is_empty() => { + let v = PosixPath::normalize(self.repr.slice_from(idx+1)); + self.repr = v; } - Some(idx) if self.repr.slice_from(idx+1) == ".." => { + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { self.repr = PosixPath::normalize(dirname); } Some(idx) => { - let mut s = str::with_capacity(dirname.len() + self.repr.len() - idx); - s.push_str(dirname); - s.push_str(self.repr.slice_from(idx)); - self.repr = PosixPath::normalize(s); + let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); + v.push_all(dirname); + v.push_all(self.repr.slice_from(idx)); + self.repr = PosixPath::normalize(v); } } - self.sepidx = self.repr.rfind(posix::sep); + self.sepidx = self.repr.rposition_elem(&posix::sep); } - fn set_filename(&mut self, filename: &str) { + unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { match self.sepidx { - None if ".." == self.repr => { - let mut s = str::with_capacity(3 + filename.len()); - s.push_str(".."); - s.push_char(posix::sep); - s.push_str(filename); - self.repr = PosixPath::normalize(s); + None if bytes!("..") == self.repr => { + let mut v = vec::with_capacity(3 + filename.len()); + v.push_all(dot_dot_static); + v.push(posix::sep); + v.push_all(filename); + self.repr = PosixPath::normalize(v); } None => { self.repr = PosixPath::normalize(filename); } - Some(idx) if self.repr.slice_from(idx+1) == ".." => { - let mut s = str::with_capacity(self.repr.len() + 1 + filename.len()); - s.push_str(self.repr); - s.push_char(posix::sep); - s.push_str(filename); - self.repr = PosixPath::normalize(s); + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { + let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); + v.push_all(self.repr); + v.push(posix::sep); + v.push_all(filename); + self.repr = PosixPath::normalize(v); } Some(idx) => { - let mut s = str::with_capacity(self.repr.len() - idx + filename.len()); - s.push_str(self.repr.slice_to(idx+1)); - s.push_str(filename); - self.repr = PosixPath::normalize(s); + let mut v = vec::with_capacity(self.repr.len() - idx + filename.len()); + v.push_all(self.repr.slice_to(idx+1)); + v.push_all(filename); + self.repr = PosixPath::normalize(v); } } - self.sepidx = self.repr.rfind(posix::sep); + self.sepidx = self.repr.rposition_elem(&posix::sep); } - fn push(&mut self, path: &str) { + unsafe fn push_unchecked(&mut self, path: &[u8]) { if !path.is_empty() { - if path[0] == posix::sep as u8 { + if path[0] == posix::sep { self.repr = PosixPath::normalize(path); } else { - let mut s = str::with_capacity(self.repr.len() + path.len() + 1); - s.push_str(self.repr); - s.push_char(posix::sep); - s.push_str(path); - self.repr = PosixPath::normalize(s); + let mut v = vec::with_capacity(self.repr.len() + path.len() + 1); + v.push_all(self.repr); + v.push(posix::sep); + v.push_all(path); + self.repr = PosixPath::normalize(v); } - self.sepidx = self.repr.rfind(posix::sep); + self.sepidx = self.repr.rposition_elem(&posix::sep); } } +} - fn push_path(&mut self, path: &PosixPath) { - self.push(path.as_str()); +impl GenericPath for PosixPath { + #[inline] + fn as_vec<'a>(&'a self) -> &'a [u8] { + self.repr.as_slice() } - fn pop_opt(&mut self) -> Option<~str> { + fn dirname<'a>(&'a self) -> &'a [u8] { match self.sepidx { - None if "." == self.repr => None, + None if bytes!("..") == self.repr => self.repr.as_slice(), + None => dot_static, + Some(0) => self.repr.slice_to(1), + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => self.repr.as_slice(), + Some(idx) => self.repr.slice_to(idx) + } + } + + fn filename<'a>(&'a self) -> &'a [u8] { + match self.sepidx { + None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[], + None => self.repr.as_slice(), + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[], + Some(idx) => self.repr.slice_from(idx+1) + } + } + + fn pop_opt(&mut self) -> Option<~[u8]> { + match self.sepidx { + None if bytes!(".") == self.repr => None, None => { - let mut s = ~"."; - util::swap(&mut s, &mut self.repr); + let mut v = ~['.' as u8]; + util::swap(&mut v, &mut self.repr); self.sepidx = None; - Some(s) + Some(v) } - Some(0) if "/" == self.repr => None, + Some(0) if bytes!("/") == self.repr => None, Some(idx) => { - let s = self.repr.slice_from(idx+1).to_owned(); + let v = self.repr.slice_from(idx+1).to_owned(); if idx == 0 { self.repr.truncate(idx+1); } else { self.repr.truncate(idx); } - self.sepidx = self.repr.rfind(posix::sep); - Some(s) + self.sepidx = self.repr.rposition_elem(&posix::sep); + Some(v) } } } #[inline] fn is_absolute(&self) -> bool { - self.repr[0] == posix::sep as u8 + self.repr[0] == posix::sep } fn is_ancestor_of(&self, other: &PosixPath) -> bool { @@ -685,19 +686,16 @@ impl GenericPath for PosixPath { } else { let mut ita = self.component_iter(); let mut itb = other.component_iter(); - if "." == self.repr { - return match itb.next() { - Some("..") => false, - _ => true - }; + if bytes!(".") == self.repr { + return itb.next() != Some(bytes!("..")); } loop { match (ita.next(), itb.next()) { (None, _) => break, (Some(a), Some(b)) if a == b => { loop }, - (Some(".."), _) => { + (Some(a), _) if a == bytes!("..") => { // if ita contains only .. components, it's an ancestor - return ita.all(|x| x == ".."); + return ita.all(|x| x == bytes!("..")); } _ => return false } @@ -725,14 +723,14 @@ impl GenericPath for PosixPath { comps.extend(&mut ita); break; } - (None, _) => comps.push(".."), + (None, _) => comps.push(dot_dot_static), (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(".")) => comps.push(a), - (Some(_), Some("..")) => return None, + (Some(a), Some(b)) if b == bytes!(".") => comps.push(a), + (Some(_), Some(b)) if b == bytes!("..") => return None, (Some(a), Some(_)) => { - comps.push(".."); + comps.push(dot_dot_static); for _ in itb { - comps.push(".."); + comps.push(dot_dot_static); } comps.push(a); comps.extend(&mut ita); @@ -740,61 +738,77 @@ impl GenericPath for PosixPath { } } } - Some(PosixPath::new(comps.connect(str::from_char(posix::sep)))) + Some(PosixPath::new(comps.connect_vec(&posix::sep))) } } } impl PosixPath { + /// Returns a new PosixPath from a byte vector + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the vector contains a NUL. + #[inline] + pub fn new(v: &[u8]) -> PosixPath { + GenericPath::from_vec(v) + } + /// Returns a new PosixPath from a string - pub fn new(s: &str) -> PosixPath { - let s = PosixPath::normalize(s); - assert!(!s.is_empty()); - let idx = s.rfind(posix::sep); - PosixPath{ repr: s, sepidx: idx } + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the str contains a NUL. + #[inline] + pub fn from_str(s: &str) -> PosixPath { + GenericPath::from_str(s) } - /// Converts the PosixPath into an owned string - pub fn into_str(self) -> ~str { + /// Converts the PosixPath into an owned byte vector + pub fn into_vec(self) -> ~[u8] { self.repr } - /// Returns a normalized string representation of a path, by removing all empty + /// Converts the PosixPath into an owned string, if possible + pub fn into_str(self) -> Option<~str> { + str::from_bytes_owned_opt(self.repr) + } + + /// Returns a normalized byte vector representation of a path, by removing all empty /// components, and unnecessary . and .. components. - pub fn normalize(s: S) -> ~str { + pub fn normalize+CopyableVector>(v: V) -> ~[u8] { // borrowck is being very picky let val = { - let is_abs = !s.as_slice().is_empty() && s.as_slice()[0] == posix::sep as u8; - let s_ = if is_abs { s.as_slice().slice_from(1) } else { s.as_slice() }; - let comps = normalize_helper(s_, is_abs, posix::sep); + let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == posix::sep; + let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() }; + let comps = normalize_helper(v_, is_abs, posix::is_sep); match comps { None => None, Some(comps) => { - let sepstr = str::from_char(posix::sep); if is_abs && comps.is_empty() { - Some(sepstr) + Some(~[posix::sep]) } else { let n = if is_abs { comps.len() } else { comps.len() - 1} + - comps.iter().map(|s| s.len()).sum(); - let mut s = str::with_capacity(n); + comps.iter().map(|v| v.len()).sum(); + let mut v = vec::with_capacity(n); let mut it = comps.move_iter(); if !is_abs { match it.next() { None => (), - Some(comp) => s.push_str(comp) + Some(comp) => v.push_all(comp) } } for comp in it { - s.push_str(sepstr); - s.push_str(comp); + v.push(posix::sep); + v.push_all(comp); } - Some(s) + Some(v) } } } }; match val { - None => s.into_owned(), + None => v.into_owned(), Some(val) => val } } @@ -804,11 +818,11 @@ impl PosixPath { /// /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 component_iter<'a>(&'a self) -> PosixComponentIter<'a> { - let s = if self.repr[0] == posix::sep as u8 { + let v = if self.repr[0] == posix::sep { self.repr.slice_from(1) } else { self.repr.as_slice() }; - let mut ret = s.split_iter(posix::sep); - if s.is_empty() { + let mut ret = v.split_iter(posix::is_sep); + if v.is_empty() { // consume the empty "" component ret.next(); } @@ -816,30 +830,29 @@ impl PosixPath { } } -// None result means the string didn't need normalizing -fn normalize_helper<'a, Sep: str::CharEq>(s: &'a str, is_abs: bool, sep: Sep) -> Option<~[&'a str]> { - if is_abs && s.as_slice().is_empty() { +// None result means the byte vector didn't need normalizing +fn normalize_helper<'a>(v: &'a [u8], is_abs: bool, f: &'a fn(&u8) -> bool) -> Option<~[&'a [u8]]> { + if is_abs && v.as_slice().is_empty() { return None; } - let mut comps: ~[&'a str] = ~[]; + let mut comps: ~[&'a [u8]] = ~[]; let mut n_up = 0u; let mut changed = false; - for comp in s.split_iter(sep) { - match comp { - "" => { changed = true; } - "." => { changed = true; } - ".." if is_abs && comps.is_empty() => { changed = true; } - ".." if comps.len() == n_up => { comps.push(".."); n_up += 1; } - ".." => { comps.pop_opt(); changed = true; } - x => comps.push(x) - } + for comp in v.split_iter(f) { + if comp.is_empty() { changed = true } + else if comp == bytes!(".") { changed = true } + else if comp == bytes!("..") { + 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_opt(); changed = true } + } else { comps.push(comp) } } if changed { if comps.is_empty() && !is_abs { - if s == "." { + if v == bytes!(".") { return None; } - comps.push("."); + comps.push(dot_static); } Some(comps) } else { @@ -847,6 +860,9 @@ fn normalize_helper<'a, Sep: str::CharEq>(s: &'a str, is_abs: bool, sep: Sep) -> } } +static dot_static: &'static [u8] = &'static ['.' as u8]; +static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; + /// Various POSIX helpers pub mod posix { /// The standard path separator character From f0fc0650c53b46b01f3746ca3de578520846fe36 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 25 Aug 2013 22:24:05 -0700 Subject: [PATCH 05/30] path2: Update all the tests for the new [u8]-based approach --- src/libstd/path2.rs | 368 +++++++++++++++++++++++--------------------- 1 file changed, 189 insertions(+), 179 deletions(-) diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs index dea326fadfea3..eac44db880d17 100644 --- a/src/libstd/path2.rs +++ b/src/libstd/path2.rs @@ -894,42 +894,43 @@ mod tests { use super::*; use option::{Some, None}; use iterator::Iterator; + use str; use vec::Vector; macro_rules! t( ($path:expr, $exp:expr) => ( { let path = $path; - assert_eq!(path.as_str(), $exp); + assert_eq!(path.as_str(), Some($exp)); } ) ) #[test] fn test_posix_paths() { - t!(PosixPath::new(""), "."); - t!(PosixPath::new("/"), "/"); - t!(PosixPath::new("hi"), "hi"); - t!(PosixPath::new("/lib"), "/lib"); - t!(PosixPath::new("hi/there"), "hi/there"); - t!(PosixPath::new("hi/there.txt"), "hi/there.txt"); - - t!(PosixPath::new("hi/there/"), "hi/there"); - t!(PosixPath::new("hi/../there"), "there"); - t!(PosixPath::new("../hi/there"), "../hi/there"); - t!(PosixPath::new("/../hi/there"), "/hi/there"); - t!(PosixPath::new("foo/.."), "."); - t!(PosixPath::new("/foo/.."), "/"); - t!(PosixPath::new("/foo/../.."), "/"); - t!(PosixPath::new("/foo/../../bar"), "/bar"); - t!(PosixPath::new("/./hi/./there/."), "/hi/there"); - t!(PosixPath::new("/./hi/./there/./.."), "/hi"); - t!(PosixPath::new("foo/../.."), ".."); - t!(PosixPath::new("foo/../../.."), "../.."); - t!(PosixPath::new("foo/../../bar"), "../bar"); - - assert_eq!(PosixPath::new("foo/bar").into_str(), ~"foo/bar"); - assert_eq!(PosixPath::new("/foo/../../bar").into_str(), ~"/bar"); + t!(PosixPath::from_str(""), "."); + t!(PosixPath::from_str("/"), "/"); + t!(PosixPath::from_str("hi"), "hi"); + t!(PosixPath::from_str("/lib"), "/lib"); + t!(PosixPath::from_str("hi/there"), "hi/there"); + t!(PosixPath::from_str("hi/there.txt"), "hi/there.txt"); + + t!(PosixPath::from_str("hi/there/"), "hi/there"); + t!(PosixPath::from_str("hi/../there"), "there"); + t!(PosixPath::from_str("../hi/there"), "../hi/there"); + t!(PosixPath::from_str("/../hi/there"), "/hi/there"); + t!(PosixPath::from_str("foo/.."), "."); + t!(PosixPath::from_str("/foo/.."), "/"); + t!(PosixPath::from_str("/foo/../.."), "/"); + t!(PosixPath::from_str("/foo/../../bar"), "/bar"); + t!(PosixPath::from_str("/./hi/./there/."), "/hi/there"); + t!(PosixPath::from_str("/./hi/./there/./.."), "/hi"); + t!(PosixPath::from_str("foo/../.."), ".."); + t!(PosixPath::from_str("foo/../../.."), "../.."); + t!(PosixPath::from_str("foo/../../bar"), "../bar"); + + assert_eq!(PosixPath::from_str("foo/bar").into_str(), Some(~"foo/bar")); + assert_eq!(PosixPath::from_str("/foo/../../bar").into_str(), Some(~"/bar")); } #[test] @@ -937,8 +938,15 @@ mod tests { macro_rules! t( ($path:expr, $op:ident, $exp:expr) => ( { - let path = PosixPath::new($path); - assert_eq!(path.$op(), $exp); + let path = PosixPath::from_str($path); + assert_eq!(path.$op(), ($exp).as_bytes()); + } + ); + ($path:expr, $op:ident, $exp:expr, opt) => ( + { + let path = PosixPath::from_str($path); + let left = path.$op().map(|&x| str::from_bytes_slice(x)); + assert_eq!(left, $exp); } ) ) @@ -974,18 +982,18 @@ mod tests { t!("..", filestem, ""); t!("../..", filestem, ""); - t!("hi/there.txt", extension, Some("txt")); - t!("hi/there", extension, None); - t!("there.txt", extension, Some("txt")); - t!("there", extension, None); - t!(".", extension, None); - t!("/", extension, None); - t!("foo/.bar", extension, None); - t!(".bar", extension, None); - t!("..bar", extension, Some("bar")); - t!("hi/there..txt", extension, Some("txt")); - t!("..", extension, None); - t!("../..", extension, None); + t!("hi/there.txt", extension, Some("txt"), opt); + t!("hi/there", extension, None, opt); + t!("there.txt", extension, Some("txt"), opt); + t!("there", extension, None, opt); + t!(".", extension, None, opt); + t!("/", extension, None, opt); + t!("foo/.bar", extension, None, opt); + t!(".bar", extension, None, opt); + t!("..bar", extension, Some("bar"), opt); + t!("hi/there..txt", extension, Some("txt"), opt); + t!("..", extension, None, opt); + t!("../..", extension, None, opt); } #[test] @@ -995,10 +1003,10 @@ mod tests { { let path = ($path); let join = ($join); - let mut p1 = PosixPath::new(path); - p1.push(join); - let p2 = PosixPath::new(path); - assert_eq!(p1, p2.join(join)); + let mut p1 = PosixPath::from_str(path); + let p2 = p1.clone(); + p1.push_str(join); + assert_eq!(p1, p2.join_str(join)); } ) ) @@ -1014,10 +1022,10 @@ mod tests { macro_rules! t( ($path:expr, $push:expr, $exp:expr) => ( { - let mut p = PosixPath::new($path); - let push = PosixPath::new($push); + let mut p = PosixPath::from_str($path); + let push = PosixPath::from_str($push); p.push_path(&push); - assert_eq!(p.as_str(), $exp); + assert_eq!(p.as_str(), Some($exp)); } ) ) @@ -1035,9 +1043,9 @@ mod tests { macro_rules! t( ($path:expr, $left:expr, $right:expr) => ( { - let mut p = PosixPath::new($path); - let file = p.pop_opt(); - assert_eq!(p.as_str(), $left); + let mut p = PosixPath::from_str($path); + let file = p.pop_opt_str(); + assert_eq!(p.as_str(), Some($left)); assert_eq!(file.map(|s| s.as_slice()), $right); } ) @@ -1052,12 +1060,12 @@ mod tests { #[test] fn test_posix_join() { - t!(PosixPath::new("a/b/c").join(".."), "a/b"); - t!(PosixPath::new("/a/b/c").join("d"), "/a/b/c/d"); - t!(PosixPath::new("a/b").join("c/d"), "a/b/c/d"); - t!(PosixPath::new("a/b").join("/c/d"), "/c/d"); - t!(PosixPath::new(".").join("a/b"), "a/b"); - t!(PosixPath::new("/").join("a/b"), "/a/b"); + t!(PosixPath::from_str("a/b/c").join_str(".."), "a/b"); + t!(PosixPath::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); + t!(PosixPath::from_str("a/b").join_str("c/d"), "a/b/c/d"); + t!(PosixPath::from_str("a/b").join_str("/c/d"), "/c/d"); + t!(PosixPath::from_str(".").join_str("a/b"), "a/b"); + t!(PosixPath::from_str("/").join_str("a/b"), "/a/b"); } #[test] @@ -1065,10 +1073,10 @@ mod tests { macro_rules! t( ($path:expr, $join:expr, $exp:expr) => ( { - let path = PosixPath::new($path); - let join = PosixPath::new($join); + let path = PosixPath::from_str($path); + let join = PosixPath::from_str($join); let res = path.join_path(&join); - assert_eq!(res.as_str(), $exp); + assert_eq!(res.as_str(), Some($exp)); } ) ) @@ -1083,72 +1091,72 @@ mod tests { #[test] fn test_posix_with_helpers() { - t!(PosixPath::new("a/b/c").with_dirname("d"), "d/c"); - t!(PosixPath::new("a/b/c").with_dirname("d/e"), "d/e/c"); - t!(PosixPath::new("a/b/c").with_dirname(""), "c"); - t!(PosixPath::new("a/b/c").with_dirname("/"), "/c"); - t!(PosixPath::new("a/b/c").with_dirname("."), "c"); - t!(PosixPath::new("a/b/c").with_dirname(".."), "../c"); - t!(PosixPath::new("/").with_dirname("foo"), "foo"); - t!(PosixPath::new("/").with_dirname(""), "."); - t!(PosixPath::new("/foo").with_dirname("bar"), "bar/foo"); - t!(PosixPath::new("..").with_dirname("foo"), "foo"); - t!(PosixPath::new("../..").with_dirname("foo"), "foo"); - t!(PosixPath::new("foo").with_dirname(".."), "../foo"); - t!(PosixPath::new("foo").with_dirname("../.."), "../../foo"); - - t!(PosixPath::new("a/b/c").with_filename("d"), "a/b/d"); - t!(PosixPath::new(".").with_filename("foo"), "foo"); - t!(PosixPath::new("/a/b/c").with_filename("d"), "/a/b/d"); - t!(PosixPath::new("/").with_filename("foo"), "/foo"); - t!(PosixPath::new("/a").with_filename("foo"), "/foo"); - t!(PosixPath::new("foo").with_filename("bar"), "bar"); - t!(PosixPath::new("a/b/c").with_filename(""), "a/b"); - t!(PosixPath::new("a/b/c").with_filename("."), "a/b"); - t!(PosixPath::new("a/b/c").with_filename(".."), "a"); - t!(PosixPath::new("/a").with_filename(""), "/"); - t!(PosixPath::new("foo").with_filename(""), "."); - t!(PosixPath::new("a/b/c").with_filename("d/e"), "a/b/d/e"); - t!(PosixPath::new("a/b/c").with_filename("/d"), "a/b/d"); - t!(PosixPath::new("..").with_filename("foo"), "../foo"); - t!(PosixPath::new("../..").with_filename("foo"), "../../foo"); - - t!(PosixPath::new("hi/there.txt").with_filestem("here"), "hi/here.txt"); - t!(PosixPath::new("hi/there.txt").with_filestem(""), "hi/.txt"); - t!(PosixPath::new("hi/there.txt").with_filestem("."), "hi/..txt"); - t!(PosixPath::new("hi/there.txt").with_filestem(".."), "hi/...txt"); - t!(PosixPath::new("hi/there.txt").with_filestem("/"), "hi/.txt"); - t!(PosixPath::new("hi/there.txt").with_filestem("foo/bar"), "hi/foo/bar.txt"); - t!(PosixPath::new("hi/there.foo.txt").with_filestem("here"), "hi/here.txt"); - t!(PosixPath::new("hi/there").with_filestem("here"), "hi/here"); - t!(PosixPath::new("hi/there").with_filestem(""), "hi"); - t!(PosixPath::new("hi").with_filestem(""), "."); - t!(PosixPath::new("/hi").with_filestem(""), "/"); - t!(PosixPath::new("hi/there").with_filestem(".."), "."); - t!(PosixPath::new("hi/there").with_filestem("."), "hi"); - t!(PosixPath::new("hi/there.").with_filestem("foo"), "hi/foo."); - t!(PosixPath::new("hi/there.").with_filestem(""), "hi"); - t!(PosixPath::new("hi/there.").with_filestem("."), "."); - t!(PosixPath::new("hi/there.").with_filestem(".."), "hi/..."); - t!(PosixPath::new("/").with_filestem("foo"), "/foo"); - t!(PosixPath::new(".").with_filestem("foo"), "foo"); - t!(PosixPath::new("hi/there..").with_filestem("here"), "hi/here."); - t!(PosixPath::new("hi/there..").with_filestem(""), "hi"); - - t!(PosixPath::new("hi/there.txt").with_extension("exe"), "hi/there.exe"); - t!(PosixPath::new("hi/there.txt").with_extension(""), "hi/there"); - t!(PosixPath::new("hi/there.txt").with_extension("."), "hi/there.."); - t!(PosixPath::new("hi/there.txt").with_extension(".."), "hi/there..."); - t!(PosixPath::new("hi/there").with_extension("txt"), "hi/there.txt"); - t!(PosixPath::new("hi/there").with_extension("."), "hi/there.."); - t!(PosixPath::new("hi/there").with_extension(".."), "hi/there..."); - t!(PosixPath::new("hi/there.").with_extension("txt"), "hi/there.txt"); - t!(PosixPath::new("hi/.foo").with_extension("txt"), "hi/.foo.txt"); - t!(PosixPath::new("hi/there.txt").with_extension(".foo"), "hi/there..foo"); - t!(PosixPath::new("/").with_extension("txt"), "/"); - t!(PosixPath::new("/").with_extension("."), "/"); - t!(PosixPath::new("/").with_extension(".."), "/"); - t!(PosixPath::new(".").with_extension("txt"), "."); + t!(PosixPath::from_str("a/b/c").with_dirname_str("d"), "d/c"); + t!(PosixPath::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); + t!(PosixPath::from_str("a/b/c").with_dirname_str(""), "c"); + t!(PosixPath::from_str("a/b/c").with_dirname_str("/"), "/c"); + t!(PosixPath::from_str("a/b/c").with_dirname_str("."), "c"); + t!(PosixPath::from_str("a/b/c").with_dirname_str(".."), "../c"); + t!(PosixPath::from_str("/").with_dirname_str("foo"), "foo"); + t!(PosixPath::from_str("/").with_dirname_str(""), "."); + t!(PosixPath::from_str("/foo").with_dirname_str("bar"), "bar/foo"); + t!(PosixPath::from_str("..").with_dirname_str("foo"), "foo"); + t!(PosixPath::from_str("../..").with_dirname_str("foo"), "foo"); + t!(PosixPath::from_str("foo").with_dirname_str(".."), "../foo"); + t!(PosixPath::from_str("foo").with_dirname_str("../.."), "../../foo"); + + t!(PosixPath::from_str("a/b/c").with_filename_str("d"), "a/b/d"); + t!(PosixPath::from_str(".").with_filename_str("foo"), "foo"); + t!(PosixPath::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); + t!(PosixPath::from_str("/").with_filename_str("foo"), "/foo"); + t!(PosixPath::from_str("/a").with_filename_str("foo"), "/foo"); + t!(PosixPath::from_str("foo").with_filename_str("bar"), "bar"); + t!(PosixPath::from_str("a/b/c").with_filename_str(""), "a/b"); + t!(PosixPath::from_str("a/b/c").with_filename_str("."), "a/b"); + t!(PosixPath::from_str("a/b/c").with_filename_str(".."), "a"); + t!(PosixPath::from_str("/a").with_filename_str(""), "/"); + t!(PosixPath::from_str("foo").with_filename_str(""), "."); + t!(PosixPath::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); + t!(PosixPath::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); + t!(PosixPath::from_str("..").with_filename_str("foo"), "../foo"); + t!(PosixPath::from_str("../..").with_filename_str("foo"), "../../foo"); + + t!(PosixPath::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); + t!(PosixPath::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); + t!(PosixPath::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); + t!(PosixPath::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); + t!(PosixPath::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); + t!(PosixPath::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); + t!(PosixPath::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); + t!(PosixPath::from_str("hi/there").with_filestem_str("here"), "hi/here"); + t!(PosixPath::from_str("hi/there").with_filestem_str(""), "hi"); + t!(PosixPath::from_str("hi").with_filestem_str(""), "."); + t!(PosixPath::from_str("/hi").with_filestem_str(""), "/"); + t!(PosixPath::from_str("hi/there").with_filestem_str(".."), "."); + t!(PosixPath::from_str("hi/there").with_filestem_str("."), "hi"); + t!(PosixPath::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); + t!(PosixPath::from_str("hi/there.").with_filestem_str(""), "hi"); + t!(PosixPath::from_str("hi/there.").with_filestem_str("."), "."); + t!(PosixPath::from_str("hi/there.").with_filestem_str(".."), "hi/..."); + t!(PosixPath::from_str("/").with_filestem_str("foo"), "/foo"); + t!(PosixPath::from_str(".").with_filestem_str("foo"), "foo"); + t!(PosixPath::from_str("hi/there..").with_filestem_str("here"), "hi/here."); + t!(PosixPath::from_str("hi/there..").with_filestem_str(""), "hi"); + + t!(PosixPath::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); + t!(PosixPath::from_str("hi/there.txt").with_extension_str(""), "hi/there"); + t!(PosixPath::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); + t!(PosixPath::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); + t!(PosixPath::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); + t!(PosixPath::from_str("hi/there").with_extension_str("."), "hi/there.."); + t!(PosixPath::from_str("hi/there").with_extension_str(".."), "hi/there..."); + t!(PosixPath::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); + t!(PosixPath::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); + t!(PosixPath::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); + t!(PosixPath::from_str("/").with_extension_str("txt"), "/"); + t!(PosixPath::from_str("/").with_extension_str("."), "/"); + t!(PosixPath::from_str("/").with_extension_str(".."), "/"); + t!(PosixPath::from_str(".").with_extension_str("txt"), "."); } #[test] @@ -1158,79 +1166,79 @@ mod tests { { let path = ($path); let arg = ($arg); - let mut p1 = PosixPath::new(path); + let mut p1 = PosixPath::from_str(path); p1.$set(arg); - let p2 = PosixPath::new(path); + let p2 = PosixPath::from_str(path); assert_eq!(p1, p2.$with(arg)); } ) ) - t!("a/b/c", set_dirname, with_dirname, "d"); - t!("a/b/c", set_dirname, with_dirname, "d/e"); - t!("/", set_dirname, with_dirname, "foo"); - t!("/foo", set_dirname, with_dirname, "bar"); - t!("a/b/c", set_dirname, with_dirname, ""); - t!("../..", set_dirname, with_dirname, "x"); - t!("foo", set_dirname, with_dirname, "../.."); - - t!("a/b/c", set_filename, with_filename, "d"); - t!("/", set_filename, with_filename, "foo"); - t!(".", set_filename, with_filename, "foo"); - t!("a/b", set_filename, with_filename, ""); - t!("a", set_filename, with_filename, ""); - - t!("hi/there.txt", set_filestem, with_filestem, "here"); - t!("hi/there.", set_filestem, with_filestem, "here"); - t!("hi/there", set_filestem, with_filestem, "here"); - t!("hi/there.txt", set_filestem, with_filestem, ""); - t!("hi/there", set_filestem, with_filestem, ""); - - t!("hi/there.txt", set_extension, with_extension, "exe"); - t!("hi/there.", set_extension, with_extension, "txt"); - t!("hi/there", set_extension, with_extension, "txt"); - t!("hi/there.txt", set_extension, with_extension, ""); - t!("hi/there", set_extension, with_extension, ""); - t!(".", set_extension, with_extension, "txt"); + t!("a/b/c", set_dirname_str, with_dirname_str, "d"); + t!("a/b/c", set_dirname_str, with_dirname_str, "d/e"); + t!("/", set_dirname_str, with_dirname_str, "foo"); + t!("/foo", set_dirname_str, with_dirname_str, "bar"); + t!("a/b/c", set_dirname_str, with_dirname_str, ""); + t!("../..", set_dirname_str, with_dirname_str, "x"); + t!("foo", set_dirname_str, with_dirname_str, "../.."); + + t!("a/b/c", set_filename_str, with_filename_str, "d"); + t!("/", set_filename_str, with_filename_str, "foo"); + t!(".", set_filename_str, with_filename_str, "foo"); + t!("a/b", set_filename_str, with_filename_str, ""); + t!("a", set_filename_str, with_filename_str, ""); + + t!("hi/there.txt", set_filestem_str, with_filestem_str, "here"); + t!("hi/there.", set_filestem_str, with_filestem_str, "here"); + t!("hi/there", set_filestem_str, with_filestem_str, "here"); + t!("hi/there.txt", set_filestem_str, with_filestem_str, ""); + t!("hi/there", set_filestem_str, with_filestem_str, ""); + + t!("hi/there.txt", set_extension_str, with_extension_str, "exe"); + t!("hi/there.", set_extension_str, with_extension_str, "txt"); + t!("hi/there", set_extension_str, with_extension_str, "txt"); + t!("hi/there.txt", set_extension_str, with_extension_str, ""); + t!("hi/there", set_extension_str, with_extension_str, ""); + t!(".", set_extension_str, with_extension_str, "txt"); } #[test] fn test_posix_dir_file_path() { - t!(PosixPath::new("hi/there").dir_path(), "hi"); - t!(PosixPath::new("hi").dir_path(), "."); - t!(PosixPath::new("/hi").dir_path(), "/"); - t!(PosixPath::new("/").dir_path(), "/"); - t!(PosixPath::new("..").dir_path(), ".."); - t!(PosixPath::new("../..").dir_path(), "../.."); + t!(PosixPath::from_str("hi/there").dir_path(), "hi"); + t!(PosixPath::from_str("hi").dir_path(), "."); + t!(PosixPath::from_str("/hi").dir_path(), "/"); + t!(PosixPath::from_str("/").dir_path(), "/"); + t!(PosixPath::from_str("..").dir_path(), ".."); + t!(PosixPath::from_str("../..").dir_path(), "../.."); macro_rules! t( ($path:expr, $exp:expr) => ( { let path = $path; - let left = path.map(|p| p.as_str()); + let left = path.chain_ref(|p| p.as_str()); assert_eq!(left, $exp); } ) ) - t!(PosixPath::new("hi/there").file_path(), Some("there")); - t!(PosixPath::new("hi").file_path(), Some("hi")); - t!(PosixPath::new(".").file_path(), None); - t!(PosixPath::new("/").file_path(), None); - t!(PosixPath::new("..").file_path(), None); - t!(PosixPath::new("../..").file_path(), None); + t!(PosixPath::from_str("hi/there").file_path(), Some("there")); + t!(PosixPath::from_str("hi").file_path(), Some("hi")); + t!(PosixPath::from_str(".").file_path(), None); + t!(PosixPath::from_str("/").file_path(), None); + t!(PosixPath::from_str("..").file_path(), None); + t!(PosixPath::from_str("../..").file_path(), None); } #[test] fn test_posix_is_absolute() { - assert_eq!(PosixPath::new("a/b/c").is_absolute(), false); - assert_eq!(PosixPath::new("/a/b/c").is_absolute(), true); - assert_eq!(PosixPath::new("a").is_absolute(), false); - assert_eq!(PosixPath::new("/a").is_absolute(), true); - assert_eq!(PosixPath::new(".").is_absolute(), false); - assert_eq!(PosixPath::new("/").is_absolute(), true); - assert_eq!(PosixPath::new("..").is_absolute(), false); - assert_eq!(PosixPath::new("../..").is_absolute(), false); + assert_eq!(PosixPath::from_str("a/b/c").is_absolute(), false); + assert_eq!(PosixPath::from_str("/a/b/c").is_absolute(), true); + assert_eq!(PosixPath::from_str("a").is_absolute(), false); + assert_eq!(PosixPath::from_str("/a").is_absolute(), true); + assert_eq!(PosixPath::from_str(".").is_absolute(), false); + assert_eq!(PosixPath::from_str("/").is_absolute(), true); + assert_eq!(PosixPath::from_str("..").is_absolute(), false); + assert_eq!(PosixPath::from_str("../..").is_absolute(), false); } #[test] @@ -1238,8 +1246,8 @@ mod tests { macro_rules! t( ($path:expr, $dest:expr, $exp:expr) => ( { - let path = PosixPath::new($path); - let dest = PosixPath::new($dest); + let path = PosixPath::from_str($path); + let dest = PosixPath::from_str($dest); assert_eq!(path.is_ancestor_of(&dest), $exp); } ) @@ -1272,10 +1280,10 @@ mod tests { macro_rules! t( ($path:expr, $other:expr, $exp:expr) => ( { - let path = PosixPath::new($path); - let other = PosixPath::new($other); + let path = PosixPath::from_str($path); + let other = PosixPath::from_str($other); let res = path.path_relative_from(&other); - assert_eq!(res.map(|x| x.as_str()), $exp); + assert_eq!(res.chain_ref(|x| x.as_str()), $exp); } ) ) @@ -1317,9 +1325,11 @@ mod tests { macro_rules! t( ($path:expr, $exp:expr) => ( { - let path = PosixPath::new($path); + let path = PosixPath::from_str($path); let comps = path.component_iter().to_owned_vec(); - assert_eq!(comps.as_slice(), $exp); + let exp: &[&str] = $exp; + let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); + assert_eq!(comps, exps); } ) ) From 17ca6f0dfac37196ae1fa8e4d7674431534437d3 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Mon, 26 Aug 2013 14:39:13 -0700 Subject: [PATCH 06/30] path2: Add tests for the [u8]-based methods --- src/libstd/path2.rs | 830 ++++++++++++++++++++++++++++++-------------- 1 file changed, 560 insertions(+), 270 deletions(-) diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs index eac44db880d17..c10b6eeeda830 100644 --- a/src/libstd/path2.rs +++ b/src/libstd/path2.rs @@ -898,108 +898,257 @@ mod tests { use vec::Vector; macro_rules! t( - ($path:expr, $exp:expr) => ( + (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); + } + ) + ) + + macro_rules! b( + ($($arg:expr),+) => ( + bytes!($($arg),+) ) ) #[test] fn test_posix_paths() { - t!(PosixPath::from_str(""), "."); - t!(PosixPath::from_str("/"), "/"); - t!(PosixPath::from_str("hi"), "hi"); - t!(PosixPath::from_str("/lib"), "/lib"); - t!(PosixPath::from_str("hi/there"), "hi/there"); - t!(PosixPath::from_str("hi/there.txt"), "hi/there.txt"); - - t!(PosixPath::from_str("hi/there/"), "hi/there"); - t!(PosixPath::from_str("hi/../there"), "there"); - t!(PosixPath::from_str("../hi/there"), "../hi/there"); - t!(PosixPath::from_str("/../hi/there"), "/hi/there"); - t!(PosixPath::from_str("foo/.."), "."); - t!(PosixPath::from_str("/foo/.."), "/"); - t!(PosixPath::from_str("/foo/../.."), "/"); - t!(PosixPath::from_str("/foo/../../bar"), "/bar"); - t!(PosixPath::from_str("/./hi/./there/."), "/hi/there"); - t!(PosixPath::from_str("/./hi/./there/./.."), "/hi"); - t!(PosixPath::from_str("foo/../.."), ".."); - t!(PosixPath::from_str("foo/../../.."), "../.."); - t!(PosixPath::from_str("foo/../../bar"), "../bar"); - + t!(v: PosixPath::new([]), b!(".")); + t!(v: PosixPath::new(b!("/")), b!("/")); + t!(v: PosixPath::new(b!("a/b/c")), b!("a/b/c")); + t!(v: PosixPath::new(b!("a/b/c", 0xff)), b!("a/b/c", 0xff)); + t!(v: PosixPath::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); + let p = PosixPath::new(b!("a/b/c", 0xff)); + assert_eq!(p.as_str(), None); + + t!(s: PosixPath::from_str(""), "."); + t!(s: PosixPath::from_str("/"), "/"); + t!(s: PosixPath::from_str("hi"), "hi"); + t!(s: PosixPath::from_str("/lib"), "/lib"); + t!(s: PosixPath::from_str("hi/there"), "hi/there"); + t!(s: PosixPath::from_str("hi/there.txt"), "hi/there.txt"); + + t!(s: PosixPath::from_str("hi/there/"), "hi/there"); + t!(s: PosixPath::from_str("hi/../there"), "there"); + t!(s: PosixPath::from_str("../hi/there"), "../hi/there"); + t!(s: PosixPath::from_str("/../hi/there"), "/hi/there"); + t!(s: PosixPath::from_str("foo/.."), "."); + t!(s: PosixPath::from_str("/foo/.."), "/"); + t!(s: PosixPath::from_str("/foo/../.."), "/"); + t!(s: PosixPath::from_str("/foo/../../bar"), "/bar"); + t!(s: PosixPath::from_str("/./hi/./there/."), "/hi/there"); + t!(s: PosixPath::from_str("/./hi/./there/./.."), "/hi"); + t!(s: PosixPath::from_str("foo/../.."), ".."); + t!(s: PosixPath::from_str("foo/../../.."), "../.."); + t!(s: PosixPath::from_str("foo/../../bar"), "../bar"); + + assert_eq!(PosixPath::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); + assert_eq!(PosixPath::new(b!("/foo/../../bar")).into_vec(), + b!("/bar").to_owned()); assert_eq!(PosixPath::from_str("foo/bar").into_str(), Some(~"foo/bar")); assert_eq!(PosixPath::from_str("/foo/../../bar").into_str(), Some(~"/bar")); + + let p = PosixPath::new(b!("foo/bar", 0x80)); + assert_eq!(p.as_str(), None); + assert_eq!(PosixPath::new(b!("foo", 0xff, "/bar")).into_str(), None); + } + + #[test] + fn test_posix_null_byte() { + use super::null_byte::cond; + + let mut handled = false; + let mut p = do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("foo/bar", 0)); + (b!("/bar").to_owned()) + }).inside { + PosixPath::new(b!("foo/bar", 0)) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("/bar")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.set_filename(b!("f", 0, "o")) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("/foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("null/", 0, "/byte")); + (b!("null/byte").to_owned()) + }).inside { + p.set_dirname(b!("null/", 0, "/byte")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null/byte/foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.push(b!("f", 0, "o")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null/byte/foo/foo")); + } + + #[test] + fn test_posix_null_byte_fail() { + use super::null_byte::cond; + use task; + + macro_rules! t( + ($name:expr => $code:block) => ( + { + let mut t = task::task(); + t.supervised(); + t.name($name); + let res = do t.try $code; + assert!(res.is_err()); + } + ) + ) + + t!(~"new() w/nul" => { + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + PosixPath::new(b!("foo/bar", 0)) + }; + }) + + t!(~"set_filename w/nul" => { + let mut p = PosixPath::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_filename(b!("foo", 0)) + }; + }) + + t!(~"set_dirname w/nul" => { + let mut p = PosixPath::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_dirname(b!("foo", 0)) + }; + }) + + t!(~"push w/nul" => { + let mut p = PosixPath::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.push(b!("foo", 0)) + }; + }) } #[test] fn test_posix_components() { macro_rules! t( - ($path:expr, $op:ident, $exp:expr) => ( + (s: $path:expr, $op:ident, $exp:expr) => ( { let path = PosixPath::from_str($path); assert_eq!(path.$op(), ($exp).as_bytes()); } ); - ($path:expr, $op:ident, $exp:expr, opt) => ( + (s: $path:expr, $op:ident, $exp:expr, opt) => ( { let path = PosixPath::from_str($path); let left = path.$op().map(|&x| str::from_bytes_slice(x)); assert_eq!(left, $exp); } + ); + (v: $path:expr, $op:ident, $exp:expr) => ( + { + let path = PosixPath::new($path); + assert_eq!(path.$op(), $exp); + } ) ) - t!("a/b/c", filename, "c"); - t!("/a/b/c", filename, "c"); - t!("a", filename, "a"); - t!("/a", filename, "a"); - t!(".", filename, ""); - t!("/", filename, ""); - t!("..", filename, ""); - t!("../..", filename, ""); - - t!("a/b/c", dirname, "a/b"); - t!("/a/b/c", dirname, "/a/b"); - t!("a", dirname, "."); - t!("/a", dirname, "/"); - t!(".", dirname, "."); - t!("/", dirname, "/"); - t!("..", dirname, ".."); - t!("../..", dirname, "../.."); - - t!("hi/there.txt", filestem, "there"); - t!("hi/there", filestem, "there"); - t!("there.txt", filestem, "there"); - t!("there", filestem, "there"); - t!(".", filestem, ""); - t!("/", filestem, ""); - t!("foo/.bar", filestem, ".bar"); - t!(".bar", filestem, ".bar"); - t!("..bar", filestem, "."); - t!("hi/there..txt", filestem, "there."); - t!("..", filestem, ""); - t!("../..", filestem, ""); - - t!("hi/there.txt", extension, Some("txt"), opt); - t!("hi/there", extension, None, opt); - t!("there.txt", extension, Some("txt"), opt); - t!("there", extension, None, opt); - t!(".", extension, None, opt); - t!("/", extension, None, opt); - t!("foo/.bar", extension, None, opt); - t!(".bar", extension, None, opt); - t!("..bar", extension, Some("bar"), opt); - t!("hi/there..txt", extension, Some("txt"), opt); - t!("..", extension, None, opt); - t!("../..", extension, None, opt); + t!(v: b!("a/b/c"), filename, b!("c")); + t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff)); + t!(v: b!("a/b", 0xff, "/c"), filename, b!("c")); + t!(s: "a/b/c", filename, "c"); + t!(s: "/a/b/c", filename, "c"); + t!(s: "a", filename, "a"); + t!(s: "/a", filename, "a"); + t!(s: ".", filename, ""); + t!(s: "/", filename, ""); + t!(s: "..", filename, ""); + t!(s: "../..", filename, ""); + + t!(v: b!("a/b/c"), dirname, b!("a/b")); + t!(v: b!("a/b/c", 0xff), dirname, b!("a/b")); + t!(v: b!("a/b", 0xff, "/c"), dirname, b!("a/b", 0xff)); + 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, b!("there")); + t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80)); + t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there")); + t!(s: "hi/there.txt", filestem, "there"); + t!(s: "hi/there", filestem, "there"); + t!(s: "there.txt", filestem, "there"); + t!(s: "there", filestem, "there"); + t!(s: ".", filestem, ""); + t!(s: "/", filestem, ""); + t!(s: "foo/.bar", filestem, ".bar"); + t!(s: ".bar", filestem, ".bar"); + t!(s: "..bar", filestem, "."); + t!(s: "hi/there..txt", filestem, "there."); + t!(s: "..", filestem, ""); + t!(s: "../..", filestem, ""); + + t!(v: b!("hi/there.txt"), extension, Some(b!("txt"))); + t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt"))); + t!(v: b!("hi/there.t", 0x80, "xt"), extension, Some(b!("t", 0x80, "xt"))); + t!(v: b!("hi/there"), extension, None); + t!(v: b!("hi/there", 0x80), 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_posix_push() { macro_rules! t( - ($path:expr, $join:expr) => ( + (s: $path:expr, $join:expr) => ( { let path = ($path); let join = ($join); @@ -1011,16 +1160,16 @@ mod tests { ) ) - t!("a/b/c", ".."); - t!("/a/b/c", "d"); - t!("a/b", "c/d"); - t!("a/b", "/c/d"); + 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_posix_push_path() { macro_rules! t( - ($path:expr, $push:expr, $exp:expr) => ( + (s: $path:expr, $push:expr, $exp:expr) => ( { let mut p = PosixPath::from_str($path); let push = PosixPath::from_str($push); @@ -1030,48 +1179,79 @@ mod tests { ) ) - t!("a/b/c", "d", "a/b/c/d"); - t!("/a/b/c", "d", "/a/b/c/d"); - t!("a/b", "c/d", "a/b/c/d"); - t!("a/b", "/c/d", "/c/d"); - t!("a/b", ".", "a/b"); - t!("a/b", "../c", "a/c"); + 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_posix_pop() { macro_rules! t( - ($path:expr, $left:expr, $right:expr) => ( + (s: $path:expr, $left:expr, $right:expr) => ( { let mut p = PosixPath::from_str($path); let file = p.pop_opt_str(); assert_eq!(p.as_str(), Some($left)); assert_eq!(file.map(|s| s.as_slice()), $right); } + ); + (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( + { + let mut p = PosixPath::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); + } + ); + (v: [$($path:expr),+], [$($left:expr),+], None) => ( + { + let mut p = PosixPath::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file, None); + } ) ) - t!("a/b/c", "a/b", Some("c")); - t!("a", ".", Some("a")); - t!(".", ".", None); - t!("/a", "/", Some("a")); - t!("/", "/", None); + t!(v: ["a/b/c"], ["a/b"], Some("c")); + t!(v: ["a"], ["."], Some("a")); + t!(v: ["."], ["."], None); + t!(v: ["/a"], ["/"], Some("a")); + t!(v: ["/"], ["/"], None); + t!(v: ["a/b/c", 0x80], ["a/b"], Some("c", 0x80)); + t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], Some("c")); + t!(v: [0xff], ["."], Some(0xff)); + t!(v: ["/", 0xff], ["/"], Some(0xff)); + t!(s: "a/b/c", "a/b", Some("c")); + t!(s: "a", ".", Some("a")); + t!(s: ".", ".", None); + t!(s: "/a", "/", Some("a")); + t!(s: "/", "/", None); + + assert_eq!(PosixPath::new(b!("foo/bar", 0x80)).pop_opt_str(), None); + assert_eq!(PosixPath::new(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); } #[test] fn test_posix_join() { - t!(PosixPath::from_str("a/b/c").join_str(".."), "a/b"); - t!(PosixPath::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); - t!(PosixPath::from_str("a/b").join_str("c/d"), "a/b/c/d"); - t!(PosixPath::from_str("a/b").join_str("/c/d"), "/c/d"); - t!(PosixPath::from_str(".").join_str("a/b"), "a/b"); - t!(PosixPath::from_str("/").join_str("a/b"), "/a/b"); + t!(v: PosixPath::new(b!("a/b/c")).join(b!("..")), b!("a/b")); + t!(v: PosixPath::new(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d")); + t!(v: PosixPath::new(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); + t!(s: PosixPath::from_str("a/b/c").join_str(".."), "a/b"); + t!(s: PosixPath::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); + t!(s: PosixPath::from_str("a/b").join_str("c/d"), "a/b/c/d"); + t!(s: PosixPath::from_str("a/b").join_str("/c/d"), "/c/d"); + t!(s: PosixPath::from_str(".").join_str("a/b"), "a/b"); + t!(s: PosixPath::from_str("/").join_str("a/b"), "/a/b"); } #[test] fn test_posix_join_path() { macro_rules! t( - ($path:expr, $join:expr, $exp:expr) => ( + (s: $path:expr, $join:expr, $exp:expr) => ( { let path = PosixPath::from_str($path); let join = PosixPath::from_str($join); @@ -1081,152 +1261,251 @@ mod tests { ) ) - t!("a/b/c", "..", "a/b"); - t!("/a/b/c", "d", "/a/b/c/d"); - t!("a/b", "c/d", "a/b/c/d"); - t!("a/b", "/c/d", "/c/d"); - t!(".", "a/b", "a/b"); - t!("/", "a/b", "/a/b"); + 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_posix_with_helpers() { - t!(PosixPath::from_str("a/b/c").with_dirname_str("d"), "d/c"); - t!(PosixPath::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); - t!(PosixPath::from_str("a/b/c").with_dirname_str(""), "c"); - t!(PosixPath::from_str("a/b/c").with_dirname_str("/"), "/c"); - t!(PosixPath::from_str("a/b/c").with_dirname_str("."), "c"); - t!(PosixPath::from_str("a/b/c").with_dirname_str(".."), "../c"); - t!(PosixPath::from_str("/").with_dirname_str("foo"), "foo"); - t!(PosixPath::from_str("/").with_dirname_str(""), "."); - t!(PosixPath::from_str("/foo").with_dirname_str("bar"), "bar/foo"); - t!(PosixPath::from_str("..").with_dirname_str("foo"), "foo"); - t!(PosixPath::from_str("../..").with_dirname_str("foo"), "foo"); - t!(PosixPath::from_str("foo").with_dirname_str(".."), "../foo"); - t!(PosixPath::from_str("foo").with_dirname_str("../.."), "../../foo"); - - t!(PosixPath::from_str("a/b/c").with_filename_str("d"), "a/b/d"); - t!(PosixPath::from_str(".").with_filename_str("foo"), "foo"); - t!(PosixPath::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); - t!(PosixPath::from_str("/").with_filename_str("foo"), "/foo"); - t!(PosixPath::from_str("/a").with_filename_str("foo"), "/foo"); - t!(PosixPath::from_str("foo").with_filename_str("bar"), "bar"); - t!(PosixPath::from_str("a/b/c").with_filename_str(""), "a/b"); - t!(PosixPath::from_str("a/b/c").with_filename_str("."), "a/b"); - t!(PosixPath::from_str("a/b/c").with_filename_str(".."), "a"); - t!(PosixPath::from_str("/a").with_filename_str(""), "/"); - t!(PosixPath::from_str("foo").with_filename_str(""), "."); - t!(PosixPath::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); - t!(PosixPath::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); - t!(PosixPath::from_str("..").with_filename_str("foo"), "../foo"); - t!(PosixPath::from_str("../..").with_filename_str("foo"), "../../foo"); - - t!(PosixPath::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); - t!(PosixPath::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); - t!(PosixPath::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); - t!(PosixPath::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); - t!(PosixPath::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); - t!(PosixPath::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); - t!(PosixPath::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); - t!(PosixPath::from_str("hi/there").with_filestem_str("here"), "hi/here"); - t!(PosixPath::from_str("hi/there").with_filestem_str(""), "hi"); - t!(PosixPath::from_str("hi").with_filestem_str(""), "."); - t!(PosixPath::from_str("/hi").with_filestem_str(""), "/"); - t!(PosixPath::from_str("hi/there").with_filestem_str(".."), "."); - t!(PosixPath::from_str("hi/there").with_filestem_str("."), "hi"); - t!(PosixPath::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); - t!(PosixPath::from_str("hi/there.").with_filestem_str(""), "hi"); - t!(PosixPath::from_str("hi/there.").with_filestem_str("."), "."); - t!(PosixPath::from_str("hi/there.").with_filestem_str(".."), "hi/..."); - t!(PosixPath::from_str("/").with_filestem_str("foo"), "/foo"); - t!(PosixPath::from_str(".").with_filestem_str("foo"), "foo"); - t!(PosixPath::from_str("hi/there..").with_filestem_str("here"), "hi/here."); - t!(PosixPath::from_str("hi/there..").with_filestem_str(""), "hi"); - - t!(PosixPath::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); - t!(PosixPath::from_str("hi/there.txt").with_extension_str(""), "hi/there"); - t!(PosixPath::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); - t!(PosixPath::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); - t!(PosixPath::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); - t!(PosixPath::from_str("hi/there").with_extension_str("."), "hi/there.."); - t!(PosixPath::from_str("hi/there").with_extension_str(".."), "hi/there..."); - t!(PosixPath::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); - t!(PosixPath::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); - t!(PosixPath::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); - t!(PosixPath::from_str("/").with_extension_str("txt"), "/"); - t!(PosixPath::from_str("/").with_extension_str("."), "/"); - t!(PosixPath::from_str("/").with_extension_str(".."), "/"); - t!(PosixPath::from_str(".").with_extension_str("txt"), "."); + t!(v: PosixPath::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); + t!(v: PosixPath::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); + t!(v: PosixPath::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); + t!(v: PosixPath::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), + b!("/", 0xcd, "/", 0x80)); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str("d"), "d/c"); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str(""), "c"); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str("/"), "/c"); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str("."), "c"); + t!(s: PosixPath::from_str("a/b/c").with_dirname_str(".."), "../c"); + t!(s: PosixPath::from_str("/").with_dirname_str("foo"), "foo"); + t!(s: PosixPath::from_str("/").with_dirname_str(""), "."); + t!(s: PosixPath::from_str("/foo").with_dirname_str("bar"), "bar/foo"); + t!(s: PosixPath::from_str("..").with_dirname_str("foo"), "foo"); + t!(s: PosixPath::from_str("../..").with_dirname_str("foo"), "foo"); + t!(s: PosixPath::from_str("foo").with_dirname_str(".."), "../foo"); + t!(s: PosixPath::from_str("foo").with_dirname_str("../.."), "../../foo"); + + t!(v: PosixPath::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); + t!(v: PosixPath::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); + t!(v: PosixPath::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), + b!("/", 0xff, "/", 0xcd)); + t!(s: PosixPath::from_str("a/b/c").with_filename_str("d"), "a/b/d"); + t!(s: PosixPath::from_str(".").with_filename_str("foo"), "foo"); + t!(s: PosixPath::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); + t!(s: PosixPath::from_str("/").with_filename_str("foo"), "/foo"); + t!(s: PosixPath::from_str("/a").with_filename_str("foo"), "/foo"); + t!(s: PosixPath::from_str("foo").with_filename_str("bar"), "bar"); + t!(s: PosixPath::from_str("a/b/c").with_filename_str(""), "a/b"); + t!(s: PosixPath::from_str("a/b/c").with_filename_str("."), "a/b"); + t!(s: PosixPath::from_str("a/b/c").with_filename_str(".."), "a"); + t!(s: PosixPath::from_str("/a").with_filename_str(""), "/"); + t!(s: PosixPath::from_str("foo").with_filename_str(""), "."); + t!(s: PosixPath::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); + t!(s: PosixPath::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); + t!(s: PosixPath::from_str("..").with_filename_str("foo"), "../foo"); + t!(s: PosixPath::from_str("../..").with_filename_str("foo"), "../../foo"); + + t!(v: PosixPath::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), + b!("hi/", 0xff, ".txt")); + t!(v: PosixPath::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), + b!("hi/", 0xff, ".txt", 0x80)); + t!(v: PosixPath::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); + t!(v: PosixPath::new(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); + t!(s: PosixPath::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); + t!(s: PosixPath::from_str("hi/there").with_filestem_str("here"), "hi/here"); + t!(s: PosixPath::from_str("hi/there").with_filestem_str(""), "hi"); + t!(s: PosixPath::from_str("hi").with_filestem_str(""), "."); + t!(s: PosixPath::from_str("/hi").with_filestem_str(""), "/"); + t!(s: PosixPath::from_str("hi/there").with_filestem_str(".."), "."); + t!(s: PosixPath::from_str("hi/there").with_filestem_str("."), "hi"); + t!(s: PosixPath::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); + t!(s: PosixPath::from_str("hi/there.").with_filestem_str(""), "hi"); + t!(s: PosixPath::from_str("hi/there.").with_filestem_str("."), "."); + t!(s: PosixPath::from_str("hi/there.").with_filestem_str(".."), "hi/..."); + t!(s: PosixPath::from_str("/").with_filestem_str("foo"), "/foo"); + t!(s: PosixPath::from_str(".").with_filestem_str("foo"), "foo"); + t!(s: PosixPath::from_str("hi/there..").with_filestem_str("here"), "hi/here."); + t!(s: PosixPath::from_str("hi/there..").with_filestem_str(""), "hi"); + + t!(v: PosixPath::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), + b!("hi/there", 0x80, ".exe")); + t!(v: PosixPath::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), + b!("hi/there.", 0xff)); + t!(v: PosixPath::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), + b!("hi/there", 0x80, ".", 0xff)); + t!(v: PosixPath::new(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); + t!(s: PosixPath::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); + t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(""), "hi/there"); + t!(s: PosixPath::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); + t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); + t!(s: PosixPath::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); + t!(s: PosixPath::from_str("hi/there").with_extension_str("."), "hi/there.."); + t!(s: PosixPath::from_str("hi/there").with_extension_str(".."), "hi/there..."); + t!(s: PosixPath::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); + t!(s: PosixPath::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); + t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); + t!(s: PosixPath::from_str("/").with_extension_str("txt"), "/"); + t!(s: PosixPath::from_str("/").with_extension_str("."), "/"); + t!(s: PosixPath::from_str("/").with_extension_str(".."), "/"); + t!(s: PosixPath::from_str(".").with_extension_str("txt"), "."); } #[test] fn test_posix_setters() { macro_rules! t( - ($path:expr, $set:ident, $with:ident, $arg:expr) => ( + (s: $path:expr, $set:ident, $with:ident, $arg:expr) => ( { - let path = ($path); - let arg = ($arg); + let path = $path; + let arg = $arg; let mut p1 = PosixPath::from_str(path); p1.$set(arg); let p2 = PosixPath::from_str(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 = PosixPath::new(path); + p1.$set(arg); + let p2 = PosixPath::new(path); + assert_eq!(p1, p2.$with(arg)); + } + ) + ) + + t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d")); + t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); + t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d"); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d/e"); + t!(s: "/", set_dirname_str, with_dirname_str, "foo"); + t!(s: "/foo", set_dirname_str, with_dirname_str, "bar"); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, ""); + t!(s: "../..", set_dirname_str, with_dirname_str, "x"); + t!(s: "foo", set_dirname_str, with_dirname_str, "../.."); + + t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); + t!(v: b!("/"), set_filename, with_filename, b!("foo")); + t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); + t!(s: "a/b/c", set_filename_str, with_filename_str, "d"); + t!(s: "/", set_filename_str, with_filename_str, "foo"); + t!(s: ".", set_filename_str, with_filename_str, "foo"); + t!(s: "a/b", set_filename_str, with_filename_str, ""); + t!(s: "a", set_filename_str, with_filename_str, ""); + + t!(v: b!("hi/there.txt"), set_filestem, with_filestem, b!("here")); + t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); + t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there.", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, ""); + t!(s: "hi/there", set_filestem_str, with_filestem_str, ""); + + t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); + t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); + t!(s: "hi/there.txt", set_extension_str, with_extension_str, "exe"); + t!(s: "hi/there.", set_extension_str, with_extension_str, "txt"); + t!(s: "hi/there", set_extension_str, with_extension_str, "txt"); + t!(s: "hi/there.txt", set_extension_str, with_extension_str, ""); + t!(s: "hi/there", set_extension_str, with_extension_str, ""); + t!(s: ".", set_extension_str, with_extension_str, "txt"); + } + + #[test] + fn test_posix_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!("a/b/c", set_dirname_str, with_dirname_str, "d"); - t!("a/b/c", set_dirname_str, with_dirname_str, "d/e"); - t!("/", set_dirname_str, with_dirname_str, "foo"); - t!("/foo", set_dirname_str, with_dirname_str, "bar"); - t!("a/b/c", set_dirname_str, with_dirname_str, ""); - t!("../..", set_dirname_str, with_dirname_str, "x"); - t!("foo", set_dirname_str, with_dirname_str, "../.."); - - t!("a/b/c", set_filename_str, with_filename_str, "d"); - t!("/", set_filename_str, with_filename_str, "foo"); - t!(".", set_filename_str, with_filename_str, "foo"); - t!("a/b", set_filename_str, with_filename_str, ""); - t!("a", set_filename_str, with_filename_str, ""); - - t!("hi/there.txt", set_filestem_str, with_filestem_str, "here"); - t!("hi/there.", set_filestem_str, with_filestem_str, "here"); - t!("hi/there", set_filestem_str, with_filestem_str, "here"); - t!("hi/there.txt", set_filestem_str, with_filestem_str, ""); - t!("hi/there", set_filestem_str, with_filestem_str, ""); - - t!("hi/there.txt", set_extension_str, with_extension_str, "exe"); - t!("hi/there.", set_extension_str, with_extension_str, "txt"); - t!("hi/there", set_extension_str, with_extension_str, "txt"); - t!("hi/there.txt", set_extension_str, with_extension_str, ""); - t!("hi/there", set_extension_str, with_extension_str, ""); - t!(".", set_extension_str, with_extension_str, "txt"); + t!(v: PosixPath::new(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); + t!(v: PosixPath::new(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); + t!(v: PosixPath::new(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), + b!("there"), Some(b!(0xff))); + t!(s: PosixPath::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); + t!(s: PosixPath::from_str("."), Some(""), Some("."), Some(""), None); + t!(s: PosixPath::from_str("/"), Some(""), Some("/"), Some(""), None); + t!(s: PosixPath::from_str(".."), Some(""), Some(".."), Some(""), None); + t!(s: PosixPath::from_str("../.."), Some(""), Some("../.."), Some(""), None); + t!(s: PosixPath::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), + Some("there"), Some("txt")); + t!(s: PosixPath::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); + t!(s: PosixPath::from_str("hi/there."), Some("there."), Some("hi"), + Some("there"), Some("")); + t!(s: PosixPath::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); + t!(s: PosixPath::from_str("hi/..there"), Some("..there"), Some("hi"), + Some("."), Some("there")); + t!(s: PosixPath::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); + t!(s: PosixPath::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); + t!(s: PosixPath::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); + t!(s: PosixPath::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); } #[test] fn test_posix_dir_file_path() { - t!(PosixPath::from_str("hi/there").dir_path(), "hi"); - t!(PosixPath::from_str("hi").dir_path(), "."); - t!(PosixPath::from_str("/hi").dir_path(), "/"); - t!(PosixPath::from_str("/").dir_path(), "/"); - t!(PosixPath::from_str("..").dir_path(), ".."); - t!(PosixPath::from_str("../..").dir_path(), "../.."); + t!(v: PosixPath::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); + t!(v: PosixPath::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); + t!(s: PosixPath::from_str("hi/there").dir_path(), "hi"); + t!(s: PosixPath::from_str("hi").dir_path(), "."); + t!(s: PosixPath::from_str("/hi").dir_path(), "/"); + t!(s: PosixPath::from_str("/").dir_path(), "/"); + t!(s: PosixPath::from_str("..").dir_path(), ".."); + t!(s: PosixPath::from_str("../..").dir_path(), "../.."); macro_rules! t( - ($path:expr, $exp:expr) => ( + (s: $path:expr, $exp:expr) => ( { let path = $path; let left = path.chain_ref(|p| p.as_str()); assert_eq!(left, $exp); } + ); + (v: $path:expr, $exp:expr) => ( + { + let path = $path; + let left = path.map(|p| p.as_vec()); + assert_eq!(left, $exp); + } ) ) - t!(PosixPath::from_str("hi/there").file_path(), Some("there")); - t!(PosixPath::from_str("hi").file_path(), Some("hi")); - t!(PosixPath::from_str(".").file_path(), None); - t!(PosixPath::from_str("/").file_path(), None); - t!(PosixPath::from_str("..").file_path(), None); - t!(PosixPath::from_str("../..").file_path(), None); + t!(v: PosixPath::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); + t!(v: PosixPath::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); + t!(s: PosixPath::from_str("hi/there").file_path(), Some("there")); + t!(s: PosixPath::from_str("hi").file_path(), Some("hi")); + t!(s: PosixPath::from_str(".").file_path(), None); + t!(s: PosixPath::from_str("/").file_path(), None); + t!(s: PosixPath::from_str("..").file_path(), None); + t!(s: PosixPath::from_str("../..").file_path(), None); } #[test] @@ -1244,7 +1523,7 @@ mod tests { #[test] fn test_posix_is_ancestor_of() { macro_rules! t( - ($path:expr, $dest:expr, $exp:expr) => ( + (s: $path:expr, $dest:expr, $exp:expr) => ( { let path = PosixPath::from_str($path); let dest = PosixPath::from_str($dest); @@ -1253,32 +1532,32 @@ mod tests { ) ) - t!("a/b/c", "a/b/c/d", true); - t!("a/b/c", "a/b/c", true); - t!("a/b/c", "a/b", false); - t!("/a/b/c", "/a/b/c", true); - t!("/a/b", "/a/b/c", true); - t!("/a/b/c/d", "/a/b/c", false); - t!("/a/b", "a/b/c", false); - t!("a/b", "/a/b/c", false); - t!("a/b/c", "a/b/d", false); - t!("../a/b/c", "a/b/c", false); - t!("a/b/c", "../a/b/c", false); - t!("a/b/c", "a/b/cd", false); - t!("a/b/cd", "a/b/c", false); - t!("../a/b", "../a/b/c", true); - t!(".", "a/b", true); - t!(".", ".", true); - t!("/", "/", true); - t!("/", "/a/b", true); - t!("..", "a/b", true); - t!("../..", "a/b", true); + 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_posix_path_relative_from() { macro_rules! t( - ($path:expr, $other:expr, $exp:expr) => ( + (s: $path:expr, $other:expr, $exp:expr) => ( { let path = PosixPath::from_str($path); let other = PosixPath::from_str($other); @@ -1288,42 +1567,42 @@ mod tests { ) ) - t!("a/b/c", "a/b", Some("c")); - t!("a/b/c", "a/b/d", Some("../c")); - t!("a/b/c", "a/b/c/d", Some("..")); - t!("a/b/c", "a/b/c", Some(".")); - t!("a/b/c", "a/b/c/d/e", Some("../..")); - t!("a/b/c", "a/d/e", Some("../../b/c")); - t!("a/b/c", "d/e/f", Some("../../../a/b/c")); - t!("a/b/c", "/a/b/c", None); - t!("/a/b/c", "a/b/c", Some("/a/b/c")); - t!("/a/b/c", "/a/b/c/d", Some("..")); - t!("/a/b/c", "/a/b", Some("c")); - t!("/a/b/c", "/a/b/c/d/e", Some("../..")); - t!("/a/b/c", "/a/d/e", Some("../../b/c")); - t!("/a/b/c", "/d/e/f", Some("../../../a/b/c")); - t!("hi/there.txt", "hi/there", Some("../there.txt")); - t!(".", "a", Some("..")); - t!(".", "a/b", Some("../..")); - t!(".", ".", Some(".")); - t!("a", ".", Some("a")); - t!("a/b", ".", Some("a/b")); - t!("..", ".", Some("..")); - t!("a/b/c", "a/b/c", Some(".")); - t!("/a/b/c", "/a/b/c", Some(".")); - t!("/", "/", Some(".")); - t!("/", ".", Some("/")); - t!("../../a", "b", Some("../../../a")); - t!("a", "../../b", None); - t!("../../a", "../../b", Some("../a")); - t!("../../a", "../../a/b", Some("..")); - t!("../../a/b", "../../a", Some("b")); + 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_posix_component_iter() { macro_rules! t( - ($path:expr, $exp:expr) => ( + (s: $path:expr, $exp:expr) => ( { let path = PosixPath::from_str($path); let comps = path.component_iter().to_owned_vec(); @@ -1331,19 +1610,30 @@ mod tests { let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); assert_eq!(comps, exps); } + ); + (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( + { + let path = PosixPath::new(b!($($arg),+)); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&[u8]] = [$(b!($($exp),*)),*]; + assert_eq!(comps.as_slice(), exp); + } ) ) - t!("a/b/c", ["a", "b", "c"]); - t!("a/b/d", ["a", "b", "d"]); - t!("a/b/cd", ["a", "b", "cd"]); - t!("/a/b/c", ["a", "b", "c"]); - t!("a", ["a"]); - t!("/a", ["a"]); - t!("/", []); - t!(".", ["."]); - t!("..", [".."]); - t!("../..", ["..", ".."]); - t!("../../foo", ["..", "..", "foo"]); + t!(v: ["a/b/c"], [["a"], ["b"], ["c"]]); + t!(v: ["/", 0xff, "/a/", 0x80], [[0xff], ["a"], [0x80]]); + t!(v: ["../../foo", 0xcd, "bar"], [[".."], [".."], ["foo", 0xcd, "bar"]]); + 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"]); } } From 6d29142219d92f886124057e9ecfdb51ffca19f2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 1 Sep 2013 12:44:07 -0700 Subject: [PATCH 07/30] path2: Extract posix/windows into their own files Move PosixPath into posix::Path. --- src/libstd/path2.rs | 1639 ----------------------------------- src/libstd/path2/mod.rs | 519 +++++++++++ src/libstd/path2/posix.rs | 1152 ++++++++++++++++++++++++ src/libstd/path2/windows.rs | 22 + 4 files changed, 1693 insertions(+), 1639 deletions(-) delete mode 100644 src/libstd/path2.rs create mode 100644 src/libstd/path2/mod.rs create mode 100644 src/libstd/path2/posix.rs create mode 100644 src/libstd/path2/windows.rs diff --git a/src/libstd/path2.rs b/src/libstd/path2.rs deleted file mode 100644 index c10b6eeeda830..0000000000000 --- a/src/libstd/path2.rs +++ /dev/null @@ -1,1639 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Cross-platform file path handling (re-write) - -use container::Container; -use c_str::{CString, ToCStr}; -use clone::Clone; -use cmp::Eq; -use from_str::FromStr; -use iterator::{AdditiveIterator, Extendable, Iterator}; -use option::{Option, None, Some}; -use str; -use str::{OwnedStr, Str, StrSlice}; -use util; -use vec; -use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; -use vec::{ImmutableEqVector, ImmutableVector, Vector, VectorVector}; - -/// Typedef for the platform-native path type -#[cfg(unix)] -pub type Path = PosixPath; -// /// Typedef for the platform-native path type -//#[cfg(windows)] -//pub type Path = WindowsPath; - -/// Typedef for the platform-native component iterator -#[cfg(unix)] -pub type ComponentIter<'self> = PosixComponentIter<'self>; -// /// Typedef for the platform-native component iterator -//#[cfg(windows)] -//pub type ComponentIter<'self> = WindowsComponentIter<'self>; - -/// Iterator that yields successive components of a PosixPath -type PosixComponentIter<'self> = vec::SplitIterator<'self, u8>; - -// Condition that is raised when a NUL is found in a byte vector given to a Path function -condition! { - // this should be a &[u8] but there's a lifetime issue - null_byte: ~[u8] -> ~[u8]; -} - -/// Represents a POSIX file path -#[deriving(Clone, DeepClone)] -pub struct PosixPath { - priv repr: ~[u8], // assumed to never be empty or contain NULs - priv sepidx: Option // index of the final separator in repr -} - -impl Eq for PosixPath { - fn eq(&self, other: &PosixPath) -> bool { - self.repr == other.repr - } -} - -impl FromStr for PosixPath { - fn from_str(s: &str) -> Option { - let v = s.as_bytes(); - if contains_nul(v) { - None - } else { - Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) - } - } -} - -/// A trait that represents the generic operations available on paths -pub trait GenericPath: Clone + GenericPathUnsafe { - /// Creates a new Path from a byte vector. - /// The resulting Path will always be normalized. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the path contains a NUL. - #[inline] - fn from_vec(path: &[u8]) -> Self { - if contains_nul(path) { - let path = self::null_byte::cond.raise(path.to_owned()); - assert!(!contains_nul(path)); - unsafe { GenericPathUnsafe::from_vec_unchecked(path) } - } else { - unsafe { GenericPathUnsafe::from_vec_unchecked(path) } - } - } - - /// Creates a new Path from a string. - /// The resulting Path will always be normalized. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the path contains a NUL. - #[inline] - fn from_str(path: &str) -> Self { - GenericPath::from_vec(path.as_bytes()) - } - - /// Creates a new Path from a CString. - /// The resulting Path will always be normalized. - #[inline] - fn from_c_str(path: CString) -> Self { - // CStrings can't contain NULs - unsafe { GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) } - } - - /// Returns the path as a string, if possible. - /// If the path is not representable in utf-8, this returns None. - #[inline] - fn as_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.as_vec()) - } - - /// Returns the path as a byte vector - fn as_vec<'a>(&'a self) -> &'a [u8]; - - /// Returns the directory component of `self`, as a byte vector (with no trailing separator). - /// If `self` has no directory component, returns ['.']. - fn dirname<'a>(&'a self) -> &'a [u8]; - /// Returns the directory component of `self`, as a string, if possible. - /// See `dirname` for details. - #[inline] - fn dirname_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.dirname()) - } - /// Returns the file component of `self`, as a byte vector. - /// If `self` represents the root of the file hierarchy, returns the empty vector. - /// If `self` is ".", returns the empty vector. - fn filename<'a>(&'a self) -> &'a [u8]; - /// Returns the file component of `self`, as a string, if possible. - /// See `filename` for details. - #[inline] - fn filename_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.filename()) - } - /// 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. - fn filestem<'a>(&'a self) -> &'a [u8] { - let name = self.filename(); - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => name, - Some(1) if name == bytes!("..") => name, - Some(pos) => name.slice_to(pos) - } - } - /// Returns the stem of the filename of `self`, as a string, if possible. - /// See `filestem` for details. - #[inline] - fn filestem_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.filestem()) - } - /// Returns the extension of the filename of `self`, as an optional byte vector. - /// The extension is the portion of the filename just after the last '.'. - /// If there is no extension, None is returned. - /// If the filename ends in '.', the empty vector is returned. - fn extension<'a>(&'a self) -> Option<&'a [u8]> { - let name = self.filename(); - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => None, - Some(1) if name == bytes!("..") => None, - Some(pos) => Some(name.slice_from(pos+1)) - } - } - /// Returns the extension of the filename of `self`, as a string, if possible. - /// See `extension` for details. - #[inline] - fn extension_str<'a>(&'a self) -> Option<&'a str> { - self.extension().chain(|v| str::from_bytes_slice_opt(v)) - } - - /// Replaces the directory portion of the path with the given byte vector. - /// If `self` represents the root of the filesystem hierarchy, the last path component - /// of the given byte vector becomes the filename. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the dirname contains a NUL. - #[inline] - fn set_dirname(&mut self, dirname: &[u8]) { - if contains_nul(dirname) { - let dirname = self::null_byte::cond.raise(dirname.to_owned()); - assert!(!contains_nul(dirname)); - unsafe { self.set_dirname_unchecked(dirname) } - } else { - unsafe { self.set_dirname_unchecked(dirname) } - } - } - /// Replaces the directory portion of the path with the given string. - /// See `set_dirname` for details. - #[inline] - fn set_dirname_str(&mut self, dirname: &str) { - self.set_dirname(dirname.as_bytes()) - } - /// Replaces the filename portion of the path with the given byte vector. - /// If the replacement name is [], this is equivalent to popping the path. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filename contains a NUL. - #[inline] - fn set_filename(&mut self, filename: &[u8]) { - if contains_nul(filename) { - let filename = self::null_byte::cond.raise(filename.to_owned()); - assert!(!contains_nul(filename)); - unsafe { self.set_filename_unchecked(filename) } - } else { - unsafe { self.set_filename_unchecked(filename) } - } - } - /// Replaces the filename portion of the path with the given string. - /// See `set_filename` for details. - #[inline] - fn set_filename_str(&mut self, filename: &str) { - self.set_filename(filename.as_bytes()) - } - /// Replaces the filestem with the given byte vector. - /// If there is no extension in `self` (or `self` has no filename), this is equivalent - /// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including - /// the preceding '.') becomes the new filename. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filestem contains a NUL. - fn set_filestem(&mut self, filestem: &[u8]) { - // borrowck is being a pain here - let val = { - let name = self.filename(); - if !name.is_empty() { - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => None, - Some(idx) => { - let mut v; - if contains_nul(filestem) { - let filestem = self::null_byte::cond.raise(filestem.to_owned()); - assert!(!contains_nul(filestem)); - v = vec::with_capacity(filestem.len() + name.len() - idx); - v.push_all(filestem); - } else { - v = vec::with_capacity(filestem.len() + name.len() - idx); - v.push_all(filestem); - } - v.push_all(name.slice_from(idx)); - Some(v) - } - } - } else { None } - }; - match val { - None => self.set_filename(filestem), - Some(v) => unsafe { self.set_filename_unchecked(v) } - } - } - /// Replaces the filestem with the given string. - /// See `set_filestem` for details. - #[inline] - fn set_filestem_str(&mut self, filestem: &str) { - self.set_filestem(filestem.as_bytes()) - } - /// Replaces the extension with the given byte vector. - /// If there is no extension in `self`, this adds one. - /// If the given byte vector is [], this removes the extension. - /// If `self` has no filename, this is a no-op. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the extension contains a NUL. - fn set_extension(&mut self, extension: &[u8]) { - // borrowck causes problems here too - let val = { - let name = self.filename(); - if !name.is_empty() { - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => { - if extension.is_empty() { - None - } else { - let mut v; - if contains_nul(extension) { - let extension = self::null_byte::cond.raise(extension.to_owned()); - assert!(!contains_nul(extension)); - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); - } else { - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); - } - Some(v) - } - } - Some(idx) => { - if extension.is_empty() { - Some(name.slice_to(idx).to_owned()) - } else { - let mut v; - if contains_nul(extension) { - let extension = self::null_byte::cond.raise(extension.to_owned()); - assert!(!contains_nul(extension)); - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); - } else { - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); - } - Some(v) - } - } - } - } else { None } - }; - match val { - None => (), - Some(v) => unsafe { self.set_filename_unchecked(v) } - } - } - /// Replaces the extension with the given string. - /// See `set_extension` for details. - #[inline] - fn set_extension_str(&mut self, extension: &str) { - self.set_extension(extension.as_bytes()) - } - - /// Returns a new Path constructed by replacing the dirname with the given byte vector. - /// See `set_dirname` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the dirname contains a NUL. - #[inline] - fn with_dirname(&self, dirname: &[u8]) -> Self { - let mut p = self.clone(); - p.set_dirname(dirname); - p - } - /// Returns a new Path constructed by replacing the dirname with the given string. - /// See `set_dirname` for details. - #[inline] - fn with_dirname_str(&self, dirname: &str) -> Self { - self.with_dirname(dirname.as_bytes()) - } - /// Returns a new Path constructed by replacing the filename with the given byte vector. - /// See `set_filename` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filename contains a NUL. - #[inline] - fn with_filename(&self, filename: &[u8]) -> Self { - let mut p = self.clone(); - p.set_filename(filename); - p - } - /// Returns a new Path constructed by replacing the filename with the given string. - /// See `set_filename` for details. - #[inline] - fn with_filename_str(&self, filename: &str) -> Self { - self.with_filename(filename.as_bytes()) - } - /// Returns a new Path constructed by setting the filestem to the given byte vector. - /// See `set_filestem` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filestem contains a NUL. - #[inline] - fn with_filestem(&self, filestem: &[u8]) -> Self { - let mut p = self.clone(); - p.set_filestem(filestem); - p - } - /// Returns a new Path constructed by setting the filestem to the given string. - /// See `set_filestem` for details. - #[inline] - fn with_filestem_str(&self, filestem: &str) -> Self { - self.with_filestem(filestem.as_bytes()) - } - /// Returns a new Path constructed by setting the extension to the given byte vector. - /// See `set_extension` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the extension contains a NUL. - #[inline] - fn with_extension(&self, extension: &[u8]) -> Self { - let mut p = self.clone(); - p.set_extension(extension); - p - } - /// Returns a new Path constructed by setting the extension to the given string. - /// See `set_extension` for details. - #[inline] - fn with_extension_str(&self, extension: &str) -> Self { - self.with_extension(extension.as_bytes()) - } - - /// Returns the directory component of `self`, as a Path. - /// If `self` represents the root of the filesystem hierarchy, returns `self`. - fn dir_path(&self) -> Self { - GenericPath::from_vec(self.dirname()) - } - /// Returns the file component of `self`, as a relative Path. - /// If `self` represents the root of the filesystem hierarchy, returns None. - fn file_path(&self) -> Option { - match self.filename() { - [] => None, - v => Some(GenericPath::from_vec(v)) - } - } - - /// Pushes a path (as a byte vector) onto `self`. - /// If the argument represents an absolute path, it replaces `self`. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the path contains a NUL. - #[inline] - fn push(&mut self, path: &[u8]) { - if contains_nul(path) { - let path = self::null_byte::cond.raise(path.to_owned()); - assert!(!contains_nul(path)); - unsafe { self.push_unchecked(path) } - } else { - unsafe { self.push_unchecked(path) } - } - } - /// Pushes a path (as a string) onto `self. - /// See `push` for details. - #[inline] - fn push_str(&mut self, path: &str) { - self.push(path.as_bytes()) - } - /// Pushes a Path onto `self`. - /// If the argument represents an absolute path, it replaces `self`. - #[inline] - fn push_path(&mut self, path: &Self) { - self.push(path.as_vec()) - } - /// Pops the last path component off of `self` and returns it. - /// If `self` represents the root of the file hierarchy, None is returned. - fn pop_opt(&mut self) -> Option<~[u8]>; - /// Pops the last path component off of `self` and returns it as a string, if possible. - /// `self` will still be modified even if None is returned. - /// See `pop_opt` for details. - #[inline] - fn pop_opt_str(&mut self) -> Option<~str> { - self.pop_opt().chain(|v| str::from_bytes_owned_opt(v)) - } - - /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). - /// If the given path is absolute, the new Path will represent just that. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the path contains a NUL. - #[inline] - fn join(&self, path: &[u8]) -> Self { - let mut p = self.clone(); - p.push(path); - p - } - /// Returns a new Path constructed by joining `self` with the given path (as a string). - /// See `join` for details. - #[inline] - fn join_str(&self, path: &str) -> Self { - self.join(path.as_bytes()) - } - /// Returns a new Path constructed by joining `self` with the given path. - /// If the given path is absolute, the new Path will represent just that. - #[inline] - fn join_path(&self, path: &Self) -> Self { - let mut p = self.clone(); - p.push_path(path); - p - } - - /// Returns whether `self` represents an absolute path. - fn is_absolute(&self) -> bool; - - /// Returns whether `self` is equal to, or is an ancestor of, the given path. - /// If both paths are relative, they are compared as though they are relative - /// to the same parent path. - fn is_ancestor_of(&self, other: &Self) -> bool; - - /// Returns the Path that, were it joined to `base`, would yield `self`. - /// If no such path exists, None is returned. - /// If `self` is absolute and `base` is relative, or on Windows if both - /// paths refer to separate drives, an absolute path is returned. - fn path_relative_from(&self, base: &Self) -> Option; -} - -/// A trait that represents the unsafe operations on GenericPaths -pub trait GenericPathUnsafe { - /// Creates a new Path from a byte vector without checking for null bytes. - /// The resulting Path will always be normalized. - unsafe fn from_vec_unchecked(path: &[u8]) -> Self; - - /// Replaces the directory portion of the path with the given byte vector without - /// checking for null bytes. - /// See `set_dirname` for details. - unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]); - - /// Replaces the filename portion of the path with the given byte vector without - /// checking for null bytes. - /// See `set_filename` for details. - unsafe fn set_filename_unchecked(&mut self, filename: &[u8]); - - /// Pushes a path onto `self` without checking for null bytes. - /// See `push` for details. - unsafe fn push_unchecked(&mut self, path: &[u8]); -} - -#[inline(always)] -fn contains_nul(v: &[u8]) -> bool { - v.iter().any(|&x| x == 0) -} - -impl ToCStr for PosixPath { - #[inline] - fn to_c_str(&self) -> CString { - // The Path impl guarantees no internal NUL - unsafe { self.as_vec().to_c_str_unchecked() } - } - - #[inline] - unsafe fn to_c_str_unchecked(&self) -> CString { - self.as_vec().to_c_str_unchecked() - } -} - -impl GenericPathUnsafe for PosixPath { - unsafe fn from_vec_unchecked(path: &[u8]) -> PosixPath { - let path = PosixPath::normalize(path); - assert!(!path.is_empty()); - let idx = path.rposition_elem(&posix::sep); - PosixPath{ repr: path, sepidx: idx } - } - - unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { - match self.sepidx { - None if bytes!(".") == self.repr || bytes!("..") == self.repr => { - self.repr = PosixPath::normalize(dirname); - } - None => { - let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); - v.push_all(dirname); - v.push(posix::sep); - v.push_all(self.repr); - self.repr = PosixPath::normalize(v); - } - Some(0) if self.repr.len() == 1 && self.repr[0] == posix::sep => { - self.repr = PosixPath::normalize(dirname); - } - Some(idx) if dirname.is_empty() => { - let v = PosixPath::normalize(self.repr.slice_from(idx+1)); - self.repr = v; - } - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { - self.repr = PosixPath::normalize(dirname); - } - Some(idx) => { - let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); - v.push_all(dirname); - v.push_all(self.repr.slice_from(idx)); - self.repr = PosixPath::normalize(v); - } - } - self.sepidx = self.repr.rposition_elem(&posix::sep); - } - - unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { - match self.sepidx { - None if bytes!("..") == self.repr => { - let mut v = vec::with_capacity(3 + filename.len()); - v.push_all(dot_dot_static); - v.push(posix::sep); - v.push_all(filename); - self.repr = PosixPath::normalize(v); - } - None => { - self.repr = PosixPath::normalize(filename); - } - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { - let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); - v.push_all(self.repr); - v.push(posix::sep); - v.push_all(filename); - self.repr = PosixPath::normalize(v); - } - Some(idx) => { - let mut v = vec::with_capacity(self.repr.len() - idx + filename.len()); - v.push_all(self.repr.slice_to(idx+1)); - v.push_all(filename); - self.repr = PosixPath::normalize(v); - } - } - self.sepidx = self.repr.rposition_elem(&posix::sep); - } - - unsafe fn push_unchecked(&mut self, path: &[u8]) { - if !path.is_empty() { - if path[0] == posix::sep { - self.repr = PosixPath::normalize(path); - } else { - let mut v = vec::with_capacity(self.repr.len() + path.len() + 1); - v.push_all(self.repr); - v.push(posix::sep); - v.push_all(path); - self.repr = PosixPath::normalize(v); - } - self.sepidx = self.repr.rposition_elem(&posix::sep); - } - } -} - -impl GenericPath for PosixPath { - #[inline] - fn as_vec<'a>(&'a self) -> &'a [u8] { - self.repr.as_slice() - } - - fn dirname<'a>(&'a self) -> &'a [u8] { - match self.sepidx { - None if bytes!("..") == self.repr => self.repr.as_slice(), - None => dot_static, - Some(0) => self.repr.slice_to(1), - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => self.repr.as_slice(), - Some(idx) => self.repr.slice_to(idx) - } - } - - fn filename<'a>(&'a self) -> &'a [u8] { - match self.sepidx { - None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[], - None => self.repr.as_slice(), - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[], - Some(idx) => self.repr.slice_from(idx+1) - } - } - - fn pop_opt(&mut self) -> Option<~[u8]> { - match self.sepidx { - None if bytes!(".") == self.repr => None, - None => { - let mut v = ~['.' as u8]; - util::swap(&mut v, &mut self.repr); - self.sepidx = None; - Some(v) - } - Some(0) if bytes!("/") == self.repr => None, - Some(idx) => { - let v = self.repr.slice_from(idx+1).to_owned(); - if idx == 0 { - self.repr.truncate(idx+1); - } else { - self.repr.truncate(idx); - } - self.sepidx = self.repr.rposition_elem(&posix::sep); - Some(v) - } - } - } - - #[inline] - fn is_absolute(&self) -> bool { - self.repr[0] == posix::sep - } - - fn is_ancestor_of(&self, other: &PosixPath) -> bool { - if self.is_absolute() != other.is_absolute() { - false - } else { - let mut ita = self.component_iter(); - let mut itb = other.component_iter(); - if bytes!(".") == self.repr { - return itb.next() != Some(bytes!("..")); - } - loop { - match (ita.next(), itb.next()) { - (None, _) => break, - (Some(a), Some(b)) if a == b => { loop }, - (Some(a), _) if a == bytes!("..") => { - // if ita contains only .. components, it's an ancestor - return ita.all(|x| x == bytes!("..")); - } - _ => return false - } - } - true - } - } - - fn path_relative_from(&self, base: &PosixPath) -> Option { - if self.is_absolute() != base.is_absolute() { - if self.is_absolute() { - Some(self.clone()) - } else { - None - } - } else { - let mut ita = self.component_iter(); - let mut itb = base.component_iter(); - let mut comps = ~[]; - loop { - match (ita.next(), itb.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a); - comps.extend(&mut ita); - break; - } - (None, _) => comps.push(dot_dot_static), - (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(b)) if b == bytes!(".") => comps.push(a), - (Some(_), Some(b)) if b == bytes!("..") => return None, - (Some(a), Some(_)) => { - comps.push(dot_dot_static); - for _ in itb { - comps.push(dot_dot_static); - } - comps.push(a); - comps.extend(&mut ita); - break; - } - } - } - Some(PosixPath::new(comps.connect_vec(&posix::sep))) - } - } -} - -impl PosixPath { - /// Returns a new PosixPath from a byte vector - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the vector contains a NUL. - #[inline] - pub fn new(v: &[u8]) -> PosixPath { - GenericPath::from_vec(v) - } - - /// Returns a new PosixPath from a string - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the str contains a NUL. - #[inline] - pub fn from_str(s: &str) -> PosixPath { - GenericPath::from_str(s) - } - - /// Converts the PosixPath into an owned byte vector - pub fn into_vec(self) -> ~[u8] { - self.repr - } - - /// Converts the PosixPath into an owned string, if possible - pub fn into_str(self) -> Option<~str> { - str::from_bytes_owned_opt(self.repr) - } - - /// Returns a normalized byte vector representation of a path, by removing all empty - /// components, and unnecessary . and .. components. - pub fn normalize+CopyableVector>(v: V) -> ~[u8] { - // borrowck is being very picky - let val = { - let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == posix::sep; - let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() }; - let comps = normalize_helper(v_, is_abs, posix::is_sep); - match comps { - None => None, - Some(comps) => { - if is_abs && comps.is_empty() { - Some(~[posix::sep]) - } 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.move_iter(); - if !is_abs { - match it.next() { - None => (), - Some(comp) => v.push_all(comp) - } - } - for comp in it { - v.push(posix::sep); - v.push_all(comp); - } - Some(v) - } - } - } - }; - match val { - None => v.into_owned(), - 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 component_iter<'a>(&'a self) -> PosixComponentIter<'a> { - let v = if self.repr[0] == posix::sep { - self.repr.slice_from(1) - } else { self.repr.as_slice() }; - let mut ret = v.split_iter(posix::is_sep); - if v.is_empty() { - // consume the empty "" component - ret.next(); - } - ret - } -} - -// None result means the byte vector didn't need normalizing -fn normalize_helper<'a>(v: &'a [u8], is_abs: bool, f: &'a fn(&u8) -> bool) -> Option<~[&'a [u8]]> { - if is_abs && v.as_slice().is_empty() { - return None; - } - let mut comps: ~[&'a [u8]] = ~[]; - let mut n_up = 0u; - let mut changed = false; - for comp in v.split_iter(f) { - if comp.is_empty() { changed = true } - else if comp == bytes!(".") { changed = true } - else if comp == bytes!("..") { - 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_opt(); changed = true } - } else { comps.push(comp) } - } - if changed { - if comps.is_empty() && !is_abs { - if v == bytes!(".") { - return None; - } - comps.push(dot_static); - } - Some(comps) - } else { - None - } -} - -static dot_static: &'static [u8] = &'static ['.' as u8]; -static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; - -/// Various POSIX helpers -pub mod posix { - /// The standard path separator character - pub static sep: u8 = '/' as u8; - - /// Returns whether the given byte is a path separator - #[inline] - pub fn is_sep(u: &u8) -> bool { - *u == sep - } -} - -/// Various Windows helpers -pub mod windows { - /// The standard path separator character - pub static sep: u8 = '\\' as u8; - /// The alternative path separator character - pub static sep2: u8 = '/' as u8; - - /// Returns whether the given byte is a path separator - #[inline] - pub fn is_sep(u: &u8) -> bool { - *u == sep || *u == sep2 - } -} - -#[cfg(test)] -mod tests { - use super::*; - use option::{Some, None}; - use iterator::Iterator; - use str; - use vec::Vector; - - 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); - } - ) - ) - - macro_rules! b( - ($($arg:expr),+) => ( - bytes!($($arg),+) - ) - ) - - #[test] - fn test_posix_paths() { - t!(v: PosixPath::new([]), b!(".")); - t!(v: PosixPath::new(b!("/")), b!("/")); - t!(v: PosixPath::new(b!("a/b/c")), b!("a/b/c")); - t!(v: PosixPath::new(b!("a/b/c", 0xff)), b!("a/b/c", 0xff)); - t!(v: PosixPath::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); - let p = PosixPath::new(b!("a/b/c", 0xff)); - assert_eq!(p.as_str(), None); - - t!(s: PosixPath::from_str(""), "."); - t!(s: PosixPath::from_str("/"), "/"); - t!(s: PosixPath::from_str("hi"), "hi"); - t!(s: PosixPath::from_str("/lib"), "/lib"); - t!(s: PosixPath::from_str("hi/there"), "hi/there"); - t!(s: PosixPath::from_str("hi/there.txt"), "hi/there.txt"); - - t!(s: PosixPath::from_str("hi/there/"), "hi/there"); - t!(s: PosixPath::from_str("hi/../there"), "there"); - t!(s: PosixPath::from_str("../hi/there"), "../hi/there"); - t!(s: PosixPath::from_str("/../hi/there"), "/hi/there"); - t!(s: PosixPath::from_str("foo/.."), "."); - t!(s: PosixPath::from_str("/foo/.."), "/"); - t!(s: PosixPath::from_str("/foo/../.."), "/"); - t!(s: PosixPath::from_str("/foo/../../bar"), "/bar"); - t!(s: PosixPath::from_str("/./hi/./there/."), "/hi/there"); - t!(s: PosixPath::from_str("/./hi/./there/./.."), "/hi"); - t!(s: PosixPath::from_str("foo/../.."), ".."); - t!(s: PosixPath::from_str("foo/../../.."), "../.."); - t!(s: PosixPath::from_str("foo/../../bar"), "../bar"); - - assert_eq!(PosixPath::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); - assert_eq!(PosixPath::new(b!("/foo/../../bar")).into_vec(), - b!("/bar").to_owned()); - assert_eq!(PosixPath::from_str("foo/bar").into_str(), Some(~"foo/bar")); - assert_eq!(PosixPath::from_str("/foo/../../bar").into_str(), Some(~"/bar")); - - let p = PosixPath::new(b!("foo/bar", 0x80)); - assert_eq!(p.as_str(), None); - assert_eq!(PosixPath::new(b!("foo", 0xff, "/bar")).into_str(), None); - } - - #[test] - fn test_posix_null_byte() { - use super::null_byte::cond; - - let mut handled = false; - let mut p = do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("foo/bar", 0)); - (b!("/bar").to_owned()) - }).inside { - PosixPath::new(b!("foo/bar", 0)) - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("/bar")); - - handled = false; - do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside { - p.set_filename(b!("f", 0, "o")) - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("/foo")); - - handled = false; - do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("null/", 0, "/byte")); - (b!("null/byte").to_owned()) - }).inside { - p.set_dirname(b!("null/", 0, "/byte")); - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("null/byte/foo")); - - handled = false; - do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("f", 0, "o")); - (b!("foo").to_owned()) - }).inside { - p.push(b!("f", 0, "o")); - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("null/byte/foo/foo")); - } - - #[test] - fn test_posix_null_byte_fail() { - use super::null_byte::cond; - use task; - - macro_rules! t( - ($name:expr => $code:block) => ( - { - let mut t = task::task(); - t.supervised(); - t.name($name); - let res = do t.try $code; - assert!(res.is_err()); - } - ) - ) - - t!(~"new() w/nul" => { - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - PosixPath::new(b!("foo/bar", 0)) - }; - }) - - t!(~"set_filename w/nul" => { - let mut p = PosixPath::new(b!("foo/bar")); - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - p.set_filename(b!("foo", 0)) - }; - }) - - t!(~"set_dirname w/nul" => { - let mut p = PosixPath::new(b!("foo/bar")); - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - p.set_dirname(b!("foo", 0)) - }; - }) - - t!(~"push w/nul" => { - let mut p = PosixPath::new(b!("foo/bar")); - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - p.push(b!("foo", 0)) - }; - }) - } - - #[test] - fn test_posix_components() { - macro_rules! t( - (s: $path:expr, $op:ident, $exp:expr) => ( - { - let path = PosixPath::from_str($path); - assert_eq!(path.$op(), ($exp).as_bytes()); - } - ); - (s: $path:expr, $op:ident, $exp:expr, opt) => ( - { - let path = PosixPath::from_str($path); - let left = path.$op().map(|&x| str::from_bytes_slice(x)); - assert_eq!(left, $exp); - } - ); - (v: $path:expr, $op:ident, $exp:expr) => ( - { - let path = PosixPath::new($path); - assert_eq!(path.$op(), $exp); - } - ) - ) - - t!(v: b!("a/b/c"), filename, b!("c")); - t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff)); - t!(v: b!("a/b", 0xff, "/c"), filename, b!("c")); - t!(s: "a/b/c", filename, "c"); - t!(s: "/a/b/c", filename, "c"); - t!(s: "a", filename, "a"); - t!(s: "/a", filename, "a"); - t!(s: ".", filename, ""); - t!(s: "/", filename, ""); - t!(s: "..", filename, ""); - t!(s: "../..", filename, ""); - - t!(v: b!("a/b/c"), dirname, b!("a/b")); - t!(v: b!("a/b/c", 0xff), dirname, b!("a/b")); - t!(v: b!("a/b", 0xff, "/c"), dirname, b!("a/b", 0xff)); - 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, b!("there")); - t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80)); - t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there")); - t!(s: "hi/there.txt", filestem, "there"); - t!(s: "hi/there", filestem, "there"); - t!(s: "there.txt", filestem, "there"); - t!(s: "there", filestem, "there"); - t!(s: ".", filestem, ""); - t!(s: "/", filestem, ""); - t!(s: "foo/.bar", filestem, ".bar"); - t!(s: ".bar", filestem, ".bar"); - t!(s: "..bar", filestem, "."); - t!(s: "hi/there..txt", filestem, "there."); - t!(s: "..", filestem, ""); - t!(s: "../..", filestem, ""); - - t!(v: b!("hi/there.txt"), extension, Some(b!("txt"))); - t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt"))); - t!(v: b!("hi/there.t", 0x80, "xt"), extension, Some(b!("t", 0x80, "xt"))); - t!(v: b!("hi/there"), extension, None); - t!(v: b!("hi/there", 0x80), 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_posix_push() { - macro_rules! t( - (s: $path:expr, $join:expr) => ( - { - let path = ($path); - let join = ($join); - let mut p1 = PosixPath::from_str(path); - let p2 = p1.clone(); - p1.push_str(join); - assert_eq!(p1, p2.join_str(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_posix_push_path() { - macro_rules! t( - (s: $path:expr, $push:expr, $exp:expr) => ( - { - let mut p = PosixPath::from_str($path); - let push = PosixPath::from_str($push); - p.push_path(&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_posix_pop() { - macro_rules! t( - (s: $path:expr, $left:expr, $right:expr) => ( - { - let mut p = PosixPath::from_str($path); - let file = p.pop_opt_str(); - assert_eq!(p.as_str(), Some($left)); - assert_eq!(file.map(|s| s.as_slice()), $right); - } - ); - (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( - { - let mut p = PosixPath::new(b!($($path),+)); - let file = p.pop_opt(); - assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); - } - ); - (v: [$($path:expr),+], [$($left:expr),+], None) => ( - { - let mut p = PosixPath::new(b!($($path),+)); - let file = p.pop_opt(); - assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file, None); - } - ) - ) - - t!(v: ["a/b/c"], ["a/b"], Some("c")); - t!(v: ["a"], ["."], Some("a")); - t!(v: ["."], ["."], None); - t!(v: ["/a"], ["/"], Some("a")); - t!(v: ["/"], ["/"], None); - t!(v: ["a/b/c", 0x80], ["a/b"], Some("c", 0x80)); - t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], Some("c")); - t!(v: [0xff], ["."], Some(0xff)); - t!(v: ["/", 0xff], ["/"], Some(0xff)); - t!(s: "a/b/c", "a/b", Some("c")); - t!(s: "a", ".", Some("a")); - t!(s: ".", ".", None); - t!(s: "/a", "/", Some("a")); - t!(s: "/", "/", None); - - assert_eq!(PosixPath::new(b!("foo/bar", 0x80)).pop_opt_str(), None); - assert_eq!(PosixPath::new(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); - } - - #[test] - fn test_posix_join() { - t!(v: PosixPath::new(b!("a/b/c")).join(b!("..")), b!("a/b")); - t!(v: PosixPath::new(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d")); - t!(v: PosixPath::new(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); - t!(s: PosixPath::from_str("a/b/c").join_str(".."), "a/b"); - t!(s: PosixPath::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); - t!(s: PosixPath::from_str("a/b").join_str("c/d"), "a/b/c/d"); - t!(s: PosixPath::from_str("a/b").join_str("/c/d"), "/c/d"); - t!(s: PosixPath::from_str(".").join_str("a/b"), "a/b"); - t!(s: PosixPath::from_str("/").join_str("a/b"), "/a/b"); - } - - #[test] - fn test_posix_join_path() { - macro_rules! t( - (s: $path:expr, $join:expr, $exp:expr) => ( - { - let path = PosixPath::from_str($path); - let join = PosixPath::from_str($join); - let res = path.join_path(&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_posix_with_helpers() { - t!(v: PosixPath::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); - t!(v: PosixPath::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); - t!(v: PosixPath::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); - t!(v: PosixPath::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), - b!("/", 0xcd, "/", 0x80)); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str("d"), "d/c"); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str(""), "c"); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str("/"), "/c"); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str("."), "c"); - t!(s: PosixPath::from_str("a/b/c").with_dirname_str(".."), "../c"); - t!(s: PosixPath::from_str("/").with_dirname_str("foo"), "foo"); - t!(s: PosixPath::from_str("/").with_dirname_str(""), "."); - t!(s: PosixPath::from_str("/foo").with_dirname_str("bar"), "bar/foo"); - t!(s: PosixPath::from_str("..").with_dirname_str("foo"), "foo"); - t!(s: PosixPath::from_str("../..").with_dirname_str("foo"), "foo"); - t!(s: PosixPath::from_str("foo").with_dirname_str(".."), "../foo"); - t!(s: PosixPath::from_str("foo").with_dirname_str("../.."), "../../foo"); - - t!(v: PosixPath::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); - t!(v: PosixPath::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); - t!(v: PosixPath::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), - b!("/", 0xff, "/", 0xcd)); - t!(s: PosixPath::from_str("a/b/c").with_filename_str("d"), "a/b/d"); - t!(s: PosixPath::from_str(".").with_filename_str("foo"), "foo"); - t!(s: PosixPath::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); - t!(s: PosixPath::from_str("/").with_filename_str("foo"), "/foo"); - t!(s: PosixPath::from_str("/a").with_filename_str("foo"), "/foo"); - t!(s: PosixPath::from_str("foo").with_filename_str("bar"), "bar"); - t!(s: PosixPath::from_str("a/b/c").with_filename_str(""), "a/b"); - t!(s: PosixPath::from_str("a/b/c").with_filename_str("."), "a/b"); - t!(s: PosixPath::from_str("a/b/c").with_filename_str(".."), "a"); - t!(s: PosixPath::from_str("/a").with_filename_str(""), "/"); - t!(s: PosixPath::from_str("foo").with_filename_str(""), "."); - t!(s: PosixPath::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); - t!(s: PosixPath::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); - t!(s: PosixPath::from_str("..").with_filename_str("foo"), "../foo"); - t!(s: PosixPath::from_str("../..").with_filename_str("foo"), "../../foo"); - - t!(v: PosixPath::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), - b!("hi/", 0xff, ".txt")); - t!(v: PosixPath::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), - b!("hi/", 0xff, ".txt", 0x80)); - t!(v: PosixPath::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); - t!(v: PosixPath::new(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); - t!(s: PosixPath::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); - t!(s: PosixPath::from_str("hi/there").with_filestem_str("here"), "hi/here"); - t!(s: PosixPath::from_str("hi/there").with_filestem_str(""), "hi"); - t!(s: PosixPath::from_str("hi").with_filestem_str(""), "."); - t!(s: PosixPath::from_str("/hi").with_filestem_str(""), "/"); - t!(s: PosixPath::from_str("hi/there").with_filestem_str(".."), "."); - t!(s: PosixPath::from_str("hi/there").with_filestem_str("."), "hi"); - t!(s: PosixPath::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); - t!(s: PosixPath::from_str("hi/there.").with_filestem_str(""), "hi"); - t!(s: PosixPath::from_str("hi/there.").with_filestem_str("."), "."); - t!(s: PosixPath::from_str("hi/there.").with_filestem_str(".."), "hi/..."); - t!(s: PosixPath::from_str("/").with_filestem_str("foo"), "/foo"); - t!(s: PosixPath::from_str(".").with_filestem_str("foo"), "foo"); - t!(s: PosixPath::from_str("hi/there..").with_filestem_str("here"), "hi/here."); - t!(s: PosixPath::from_str("hi/there..").with_filestem_str(""), "hi"); - - t!(v: PosixPath::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), - b!("hi/there", 0x80, ".exe")); - t!(v: PosixPath::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), - b!("hi/there.", 0xff)); - t!(v: PosixPath::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), - b!("hi/there", 0x80, ".", 0xff)); - t!(v: PosixPath::new(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); - t!(s: PosixPath::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); - t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(""), "hi/there"); - t!(s: PosixPath::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); - t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); - t!(s: PosixPath::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); - t!(s: PosixPath::from_str("hi/there").with_extension_str("."), "hi/there.."); - t!(s: PosixPath::from_str("hi/there").with_extension_str(".."), "hi/there..."); - t!(s: PosixPath::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); - t!(s: PosixPath::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); - t!(s: PosixPath::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); - t!(s: PosixPath::from_str("/").with_extension_str("txt"), "/"); - t!(s: PosixPath::from_str("/").with_extension_str("."), "/"); - t!(s: PosixPath::from_str("/").with_extension_str(".."), "/"); - t!(s: PosixPath::from_str(".").with_extension_str("txt"), "."); - } - - #[test] - fn test_posix_setters() { - macro_rules! t( - (s: $path:expr, $set:ident, $with:ident, $arg:expr) => ( - { - let path = $path; - let arg = $arg; - let mut p1 = PosixPath::from_str(path); - p1.$set(arg); - let p2 = PosixPath::from_str(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 = PosixPath::new(path); - p1.$set(arg); - let p2 = PosixPath::new(path); - assert_eq!(p1, p2.$with(arg)); - } - ) - ) - - t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d")); - t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); - t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d"); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d/e"); - t!(s: "/", set_dirname_str, with_dirname_str, "foo"); - t!(s: "/foo", set_dirname_str, with_dirname_str, "bar"); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, ""); - t!(s: "../..", set_dirname_str, with_dirname_str, "x"); - t!(s: "foo", set_dirname_str, with_dirname_str, "../.."); - - t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); - t!(v: b!("/"), set_filename, with_filename, b!("foo")); - t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); - t!(s: "a/b/c", set_filename_str, with_filename_str, "d"); - t!(s: "/", set_filename_str, with_filename_str, "foo"); - t!(s: ".", set_filename_str, with_filename_str, "foo"); - t!(s: "a/b", set_filename_str, with_filename_str, ""); - t!(s: "a", set_filename_str, with_filename_str, ""); - - t!(v: b!("hi/there.txt"), set_filestem, with_filestem, b!("here")); - t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); - t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there.", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, ""); - t!(s: "hi/there", set_filestem_str, with_filestem_str, ""); - - t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); - t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); - t!(s: "hi/there.txt", set_extension_str, with_extension_str, "exe"); - t!(s: "hi/there.", set_extension_str, with_extension_str, "txt"); - t!(s: "hi/there", set_extension_str, with_extension_str, "txt"); - t!(s: "hi/there.txt", set_extension_str, with_extension_str, ""); - t!(s: "hi/there", set_extension_str, with_extension_str, ""); - t!(s: ".", set_extension_str, with_extension_str, "txt"); - } - - #[test] - fn test_posix_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: PosixPath::new(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); - t!(v: PosixPath::new(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); - t!(v: PosixPath::new(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), - b!("there"), Some(b!(0xff))); - t!(s: PosixPath::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); - t!(s: PosixPath::from_str("."), Some(""), Some("."), Some(""), None); - t!(s: PosixPath::from_str("/"), Some(""), Some("/"), Some(""), None); - t!(s: PosixPath::from_str(".."), Some(""), Some(".."), Some(""), None); - t!(s: PosixPath::from_str("../.."), Some(""), Some("../.."), Some(""), None); - t!(s: PosixPath::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), - Some("there"), Some("txt")); - t!(s: PosixPath::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); - t!(s: PosixPath::from_str("hi/there."), Some("there."), Some("hi"), - Some("there"), Some("")); - t!(s: PosixPath::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); - t!(s: PosixPath::from_str("hi/..there"), Some("..there"), Some("hi"), - Some("."), Some("there")); - t!(s: PosixPath::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); - t!(s: PosixPath::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); - t!(s: PosixPath::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); - t!(s: PosixPath::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); - } - - #[test] - fn test_posix_dir_file_path() { - t!(v: PosixPath::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); - t!(v: PosixPath::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); - t!(s: PosixPath::from_str("hi/there").dir_path(), "hi"); - t!(s: PosixPath::from_str("hi").dir_path(), "."); - t!(s: PosixPath::from_str("/hi").dir_path(), "/"); - t!(s: PosixPath::from_str("/").dir_path(), "/"); - t!(s: PosixPath::from_str("..").dir_path(), ".."); - t!(s: PosixPath::from_str("../..").dir_path(), "../.."); - - macro_rules! t( - (s: $path:expr, $exp:expr) => ( - { - let path = $path; - let left = path.chain_ref(|p| p.as_str()); - assert_eq!(left, $exp); - } - ); - (v: $path:expr, $exp:expr) => ( - { - let path = $path; - let left = path.map(|p| p.as_vec()); - assert_eq!(left, $exp); - } - ) - ) - - t!(v: PosixPath::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); - t!(v: PosixPath::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); - t!(s: PosixPath::from_str("hi/there").file_path(), Some("there")); - t!(s: PosixPath::from_str("hi").file_path(), Some("hi")); - t!(s: PosixPath::from_str(".").file_path(), None); - t!(s: PosixPath::from_str("/").file_path(), None); - t!(s: PosixPath::from_str("..").file_path(), None); - t!(s: PosixPath::from_str("../..").file_path(), None); - } - - #[test] - fn test_posix_is_absolute() { - assert_eq!(PosixPath::from_str("a/b/c").is_absolute(), false); - assert_eq!(PosixPath::from_str("/a/b/c").is_absolute(), true); - assert_eq!(PosixPath::from_str("a").is_absolute(), false); - assert_eq!(PosixPath::from_str("/a").is_absolute(), true); - assert_eq!(PosixPath::from_str(".").is_absolute(), false); - assert_eq!(PosixPath::from_str("/").is_absolute(), true); - assert_eq!(PosixPath::from_str("..").is_absolute(), false); - assert_eq!(PosixPath::from_str("../..").is_absolute(), false); - } - - #[test] - fn test_posix_is_ancestor_of() { - macro_rules! t( - (s: $path:expr, $dest:expr, $exp:expr) => ( - { - let path = PosixPath::from_str($path); - let dest = PosixPath::from_str($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_posix_path_relative_from() { - macro_rules! t( - (s: $path:expr, $other:expr, $exp:expr) => ( - { - let path = PosixPath::from_str($path); - let other = PosixPath::from_str($other); - let res = path.path_relative_from(&other); - assert_eq!(res.chain_ref(|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_posix_component_iter() { - macro_rules! t( - (s: $path:expr, $exp:expr) => ( - { - let path = PosixPath::from_str($path); - let comps = path.component_iter().to_owned_vec(); - let exp: &[&str] = $exp; - let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); - assert_eq!(comps, exps); - } - ); - (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( - { - let path = PosixPath::new(b!($($arg),+)); - let comps = path.component_iter().to_owned_vec(); - let exp: &[&[u8]] = [$(b!($($exp),*)),*]; - assert_eq!(comps.as_slice(), exp); - } - ) - ) - - t!(v: ["a/b/c"], [["a"], ["b"], ["c"]]); - t!(v: ["/", 0xff, "/a/", 0x80], [[0xff], ["a"], [0x80]]); - t!(v: ["../../foo", 0xcd, "bar"], [[".."], [".."], ["foo", 0xcd, "bar"]]); - 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"]); - } -} diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs new file mode 100644 index 0000000000000..148af0057f52c --- /dev/null +++ b/src/libstd/path2/mod.rs @@ -0,0 +1,519 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Cross-platform file path handling (re-write) + +use container::Container; +use c_str::CString; +use clone::Clone; +use iterator::Iterator; +use option::{Option, None, Some}; +use str; +use str::StrSlice; +use vec; +use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; +use vec::{ImmutableEqVector, ImmutableVector}; + +pub mod posix; +pub mod windows; + +/// Typedef for POSIX file paths. +/// See `posix::Path` for more info. +pub type PosixPath = posix::Path; + +// /// Typedef for Windows file paths. +// /// See `windows::Path` for more info. +// pub type WindowsPath = windows::Path; + +/// Typedef for the platform-native path type +#[cfg(unix)] +pub type Path = PosixPath; +// /// Typedef for the platform-native path type +//#[cfg(windows)] +//pub type Path = WindowsPath; + +/// Typedef for the POSIX path component iterator. +/// See `posix::ComponentIter` for more info. +pub type PosixComponentIter<'self> = posix::ComponentIter<'self>; + +// /// Typedef for the Windows path component iterator. +// /// See `windows::ComponentIter` for more info. +// pub type WindowsComponentIter<'self> = windows::ComponentIter<'self>; + +/// Typedef for the platform-native component iterator +#[cfg(unix)] +pub type ComponentIter<'self> = PosixComponentIter<'self>; +// /// Typedef for the platform-native component iterator +//#[cfg(windows)] +//pub type ComponentIter<'self> = WindowsComponentIter<'self>; + +// Condition that is raised when a NUL is found in a byte vector given to a Path function +condition! { + // this should be a &[u8] but there's a lifetime issue + null_byte: ~[u8] -> ~[u8]; +} + +/// A trait that represents the generic operations available on paths +pub trait GenericPath: Clone + GenericPathUnsafe { + /// Creates a new Path from a byte vector. + /// The resulting Path will always be normalized. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn from_vec(path: &[u8]) -> Self { + if contains_nul(path) { + let path = self::null_byte::cond.raise(path.to_owned()); + assert!(!contains_nul(path)); + unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + } else { + unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + } + } + + /// Creates a new Path from a string. + /// The resulting Path will always be normalized. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn from_str(path: &str) -> Self { + GenericPath::from_vec(path.as_bytes()) + } + + /// Creates a new Path from a CString. + /// The resulting Path will always be normalized. + #[inline] + fn from_c_str(path: CString) -> Self { + // CStrings can't contain NULs + unsafe { GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) } + } + + /// Returns the path as a string, if possible. + /// If the path is not representable in utf-8, this returns None. + #[inline] + fn as_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.as_vec()) + } + + /// Returns the path as a byte vector + fn as_vec<'a>(&'a self) -> &'a [u8]; + + /// Returns the directory component of `self`, as a byte vector (with no trailing separator). + /// If `self` has no directory component, returns ['.']. + fn dirname<'a>(&'a self) -> &'a [u8]; + /// Returns the directory component of `self`, as a string, if possible. + /// See `dirname` for details. + #[inline] + fn dirname_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.dirname()) + } + /// Returns the file component of `self`, as a byte vector. + /// If `self` represents the root of the file hierarchy, returns the empty vector. + /// If `self` is ".", returns the empty vector. + fn filename<'a>(&'a self) -> &'a [u8]; + /// Returns the file component of `self`, as a string, if possible. + /// See `filename` for details. + #[inline] + fn filename_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.filename()) + } + /// 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. + fn filestem<'a>(&'a self) -> &'a [u8] { + let name = self.filename(); + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => name, + Some(1) if name == bytes!("..") => name, + Some(pos) => name.slice_to(pos) + } + } + /// Returns the stem of the filename of `self`, as a string, if possible. + /// See `filestem` for details. + #[inline] + fn filestem_str<'a>(&'a self) -> Option<&'a str> { + str::from_bytes_slice_opt(self.filestem()) + } + /// Returns the extension of the filename of `self`, as an optional byte vector. + /// The extension is the portion of the filename just after the last '.'. + /// If there is no extension, None is returned. + /// If the filename ends in '.', the empty vector is returned. + fn extension<'a>(&'a self) -> Option<&'a [u8]> { + let name = self.filename(); + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => None, + Some(1) if name == bytes!("..") => None, + Some(pos) => Some(name.slice_from(pos+1)) + } + } + /// Returns the extension of the filename of `self`, as a string, if possible. + /// See `extension` for details. + #[inline] + fn extension_str<'a>(&'a self) -> Option<&'a str> { + self.extension().chain(|v| str::from_bytes_slice_opt(v)) + } + + /// Replaces the directory portion of the path with the given byte vector. + /// If `self` represents the root of the filesystem hierarchy, the last path component + /// of the given byte vector becomes the filename. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the dirname contains a NUL. + #[inline] + fn set_dirname(&mut self, dirname: &[u8]) { + if contains_nul(dirname) { + let dirname = self::null_byte::cond.raise(dirname.to_owned()); + assert!(!contains_nul(dirname)); + unsafe { self.set_dirname_unchecked(dirname) } + } else { + unsafe { self.set_dirname_unchecked(dirname) } + } + } + /// Replaces the directory portion of the path with the given string. + /// See `set_dirname` for details. + #[inline] + fn set_dirname_str(&mut self, dirname: &str) { + self.set_dirname(dirname.as_bytes()) + } + /// Replaces the filename portion of the path with the given byte vector. + /// If the replacement name is [], this is equivalent to popping the path. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filename contains a NUL. + #[inline] + fn set_filename(&mut self, filename: &[u8]) { + if contains_nul(filename) { + let filename = self::null_byte::cond.raise(filename.to_owned()); + assert!(!contains_nul(filename)); + unsafe { self.set_filename_unchecked(filename) } + } else { + unsafe { self.set_filename_unchecked(filename) } + } + } + /// Replaces the filename portion of the path with the given string. + /// See `set_filename` for details. + #[inline] + fn set_filename_str(&mut self, filename: &str) { + self.set_filename(filename.as_bytes()) + } + /// Replaces the filestem with the given byte vector. + /// If there is no extension in `self` (or `self` has no filename), this is equivalent + /// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including + /// the preceding '.') becomes the new filename. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filestem contains a NUL. + fn set_filestem(&mut self, filestem: &[u8]) { + // borrowck is being a pain here + let val = { + let name = self.filename(); + if !name.is_empty() { + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => None, + Some(idx) => { + let mut v; + if contains_nul(filestem) { + let filestem = self::null_byte::cond.raise(filestem.to_owned()); + assert!(!contains_nul(filestem)); + v = vec::with_capacity(filestem.len() + name.len() - idx); + v.push_all(filestem); + } else { + v = vec::with_capacity(filestem.len() + name.len() - idx); + v.push_all(filestem); + } + v.push_all(name.slice_from(idx)); + Some(v) + } + } + } else { None } + }; + match val { + None => self.set_filename(filestem), + Some(v) => unsafe { self.set_filename_unchecked(v) } + } + } + /// Replaces the filestem with the given string. + /// See `set_filestem` for details. + #[inline] + fn set_filestem_str(&mut self, filestem: &str) { + self.set_filestem(filestem.as_bytes()) + } + /// Replaces the extension with the given byte vector. + /// If there is no extension in `self`, this adds one. + /// If the given byte vector is [], this removes the extension. + /// If `self` has no filename, this is a no-op. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the extension contains a NUL. + fn set_extension(&mut self, extension: &[u8]) { + // borrowck causes problems here too + let val = { + let name = self.filename(); + if !name.is_empty() { + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => { + if extension.is_empty() { + None + } else { + let mut v; + if contains_nul(extension) { + let extension = self::null_byte::cond.raise(extension.to_owned()); + assert!(!contains_nul(extension)); + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } else { + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } + Some(v) + } + } + Some(idx) => { + if extension.is_empty() { + Some(name.slice_to(idx).to_owned()) + } else { + let mut v; + if contains_nul(extension) { + let extension = self::null_byte::cond.raise(extension.to_owned()); + assert!(!contains_nul(extension)); + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } else { + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } + Some(v) + } + } + } + } else { None } + }; + match val { + None => (), + Some(v) => unsafe { self.set_filename_unchecked(v) } + } + } + /// Replaces the extension with the given string. + /// See `set_extension` for details. + #[inline] + fn set_extension_str(&mut self, extension: &str) { + self.set_extension(extension.as_bytes()) + } + + /// Returns a new Path constructed by replacing the dirname with the given byte vector. + /// See `set_dirname` for details. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the dirname contains a NUL. + #[inline] + fn with_dirname(&self, dirname: &[u8]) -> Self { + let mut p = self.clone(); + p.set_dirname(dirname); + p + } + /// Returns a new Path constructed by replacing the dirname with the given string. + /// See `set_dirname` for details. + #[inline] + fn with_dirname_str(&self, dirname: &str) -> Self { + self.with_dirname(dirname.as_bytes()) + } + /// Returns a new Path constructed by replacing the filename with the given byte vector. + /// See `set_filename` for details. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filename contains a NUL. + #[inline] + fn with_filename(&self, filename: &[u8]) -> Self { + let mut p = self.clone(); + p.set_filename(filename); + p + } + /// Returns a new Path constructed by replacing the filename with the given string. + /// See `set_filename` for details. + #[inline] + fn with_filename_str(&self, filename: &str) -> Self { + self.with_filename(filename.as_bytes()) + } + /// Returns a new Path constructed by setting the filestem to the given byte vector. + /// See `set_filestem` for details. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the filestem contains a NUL. + #[inline] + fn with_filestem(&self, filestem: &[u8]) -> Self { + let mut p = self.clone(); + p.set_filestem(filestem); + p + } + /// Returns a new Path constructed by setting the filestem to the given string. + /// See `set_filestem` for details. + #[inline] + fn with_filestem_str(&self, filestem: &str) -> Self { + self.with_filestem(filestem.as_bytes()) + } + /// Returns a new Path constructed by setting the extension to the given byte vector. + /// See `set_extension` for details. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the extension contains a NUL. + #[inline] + fn with_extension(&self, extension: &[u8]) -> Self { + let mut p = self.clone(); + p.set_extension(extension); + p + } + /// Returns a new Path constructed by setting the extension to the given string. + /// See `set_extension` for details. + #[inline] + fn with_extension_str(&self, extension: &str) -> Self { + self.with_extension(extension.as_bytes()) + } + + /// Returns the directory component of `self`, as a Path. + /// If `self` represents the root of the filesystem hierarchy, returns `self`. + fn dir_path(&self) -> Self { + GenericPath::from_vec(self.dirname()) + } + /// Returns the file component of `self`, as a relative Path. + /// If `self` represents the root of the filesystem hierarchy, returns None. + fn file_path(&self) -> Option { + match self.filename() { + [] => None, + v => Some(GenericPath::from_vec(v)) + } + } + + /// Pushes a path (as a byte vector) onto `self`. + /// If the argument represents an absolute path, it replaces `self`. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn push(&mut self, path: &[u8]) { + if contains_nul(path) { + let path = self::null_byte::cond.raise(path.to_owned()); + assert!(!contains_nul(path)); + unsafe { self.push_unchecked(path) } + } else { + unsafe { self.push_unchecked(path) } + } + } + /// Pushes a path (as a string) onto `self. + /// See `push` for details. + #[inline] + fn push_str(&mut self, path: &str) { + self.push(path.as_bytes()) + } + /// Pushes a Path onto `self`. + /// If the argument represents an absolute path, it replaces `self`. + #[inline] + fn push_path(&mut self, path: &Self) { + self.push(path.as_vec()) + } + /// Pops the last path component off of `self` and returns it. + /// If `self` represents the root of the file hierarchy, None is returned. + fn pop_opt(&mut self) -> Option<~[u8]>; + /// Pops the last path component off of `self` and returns it as a string, if possible. + /// `self` will still be modified even if None is returned. + /// See `pop_opt` for details. + #[inline] + fn pop_opt_str(&mut self) -> Option<~str> { + self.pop_opt().chain(|v| str::from_bytes_owned_opt(v)) + } + + /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). + /// If the given path is absolute, the new Path will represent just that. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the path contains a NUL. + #[inline] + fn join(&self, path: &[u8]) -> Self { + let mut p = self.clone(); + p.push(path); + p + } + /// Returns a new Path constructed by joining `self` with the given path (as a string). + /// See `join` for details. + #[inline] + fn join_str(&self, path: &str) -> Self { + self.join(path.as_bytes()) + } + /// Returns a new Path constructed by joining `self` with the given path. + /// If the given path is absolute, the new Path will represent just that. + #[inline] + fn join_path(&self, path: &Self) -> Self { + let mut p = self.clone(); + p.push_path(path); + p + } + + /// Returns whether `self` represents an absolute path. + fn is_absolute(&self) -> bool; + + /// Returns whether `self` is equal to, or is an ancestor of, the given path. + /// If both paths are relative, they are compared as though they are relative + /// to the same parent path. + fn is_ancestor_of(&self, other: &Self) -> bool; + + /// Returns the Path that, were it joined to `base`, would yield `self`. + /// If no such path exists, None is returned. + /// If `self` is absolute and `base` is relative, or on Windows if both + /// paths refer to separate drives, an absolute path is returned. + fn path_relative_from(&self, base: &Self) -> Option; +} + +/// A trait that represents the unsafe operations on GenericPaths +pub trait GenericPathUnsafe { + /// Creates a new Path from a byte vector without checking for null bytes. + /// The resulting Path will always be normalized. + unsafe fn from_vec_unchecked(path: &[u8]) -> Self; + + /// Replaces the directory portion of the path with the given byte vector without + /// checking for null bytes. + /// See `set_dirname` for details. + unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]); + + /// Replaces the filename portion of the path with the given byte vector without + /// checking for null bytes. + /// See `set_filename` for details. + unsafe fn set_filename_unchecked(&mut self, filename: &[u8]); + + /// Pushes a path onto `self` without checking for null bytes. + /// See `push` for details. + unsafe fn push_unchecked(&mut self, path: &[u8]); +} + +#[inline(always)] +fn contains_nul(v: &[u8]) -> bool { + v.iter().any(|&x| x == 0) +} diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs new file mode 100644 index 0000000000000..fa6d1e32ebd09 --- /dev/null +++ b/src/libstd/path2/posix.rs @@ -0,0 +1,1152 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! POSIX file path handling + +use container::Container; +use c_str::{CString, ToCStr}; +use clone::Clone; +use cmp::Eq; +use from_str::FromStr; +use iterator::{AdditiveIterator, Extendable, Iterator}; +use option::{Option, None, Some}; +use str; +use str::Str; +use util; +use vec; +use vec::CopyableVector; +use vec::{Vector, VectorVector}; +use super::{GenericPath, GenericPathUnsafe}; + +/// Iterator that yields successive components of a Path +pub type ComponentIter<'self> = vec::SplitIterator<'self, u8>; + +/// Represents a POSIX file path +#[deriving(Clone, DeepClone)] +pub struct Path { + priv repr: ~[u8], // assumed to never be empty or contain NULs + priv sepidx: Option // index of the final separator in repr +} + +/// The standard path separator character +pub static sep: u8 = '/' as u8; + +/// Returns whether the given byte is a path separator +#[inline] +pub fn is_sep(u: &u8) -> bool { + *u == sep +} + +impl Eq for Path { + fn eq(&self, other: &Path) -> bool { + self.repr == other.repr + } +} + +impl FromStr for Path { + fn from_str(s: &str) -> Option { + let v = s.as_bytes(); + if contains_nul(v) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) + } + } +} + +impl ToCStr for Path { + #[inline] + fn to_c_str(&self) -> CString { + // The Path impl guarantees no internal NUL + unsafe { self.as_vec().to_c_str_unchecked() } + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + self.as_vec().to_c_str_unchecked() + } +} + +impl GenericPathUnsafe for Path { + unsafe fn from_vec_unchecked(path: &[u8]) -> Path { + let path = Path::normalize(path); + assert!(!path.is_empty()); + let idx = path.rposition_elem(&sep); + Path{ repr: path, sepidx: idx } + } + + unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { + match self.sepidx { + None if bytes!(".") == self.repr || bytes!("..") == self.repr => { + self.repr = Path::normalize(dirname); + } + None => { + let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); + v.push_all(dirname); + v.push(sep); + v.push_all(self.repr); + self.repr = Path::normalize(v); + } + Some(0) if self.repr.len() == 1 && self.repr[0] == sep => { + self.repr = Path::normalize(dirname); + } + Some(idx) if dirname.is_empty() => { + let v = Path::normalize(self.repr.slice_from(idx+1)); + self.repr = v; + } + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { + self.repr = Path::normalize(dirname); + } + Some(idx) => { + let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); + v.push_all(dirname); + v.push_all(self.repr.slice_from(idx)); + self.repr = Path::normalize(v); + } + } + self.sepidx = self.repr.rposition_elem(&sep); + } + + unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { + match self.sepidx { + None if bytes!("..") == self.repr => { + let mut v = vec::with_capacity(3 + filename.len()); + v.push_all(dot_dot_static); + v.push(sep); + v.push_all(filename); + self.repr = Path::normalize(v); + } + None => { + self.repr = Path::normalize(filename); + } + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { + let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); + v.push_all(self.repr); + v.push(sep); + v.push_all(filename); + self.repr = Path::normalize(v); + } + Some(idx) => { + let mut v = vec::with_capacity(self.repr.len() - idx + filename.len()); + v.push_all(self.repr.slice_to(idx+1)); + v.push_all(filename); + self.repr = Path::normalize(v); + } + } + self.sepidx = self.repr.rposition_elem(&sep); + } + + unsafe fn push_unchecked(&mut self, path: &[u8]) { + if !path.is_empty() { + if path[0] == sep { + self.repr = Path::normalize(path); + } else { + let mut v = vec::with_capacity(self.repr.len() + path.len() + 1); + v.push_all(self.repr); + v.push(sep); + v.push_all(path); + self.repr = Path::normalize(v); + } + self.sepidx = self.repr.rposition_elem(&sep); + } + } +} + +impl GenericPath for Path { + #[inline] + fn as_vec<'a>(&'a self) -> &'a [u8] { + self.repr.as_slice() + } + + fn dirname<'a>(&'a self) -> &'a [u8] { + match self.sepidx { + None if bytes!("..") == self.repr => self.repr.as_slice(), + None => dot_static, + Some(0) => self.repr.slice_to(1), + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => self.repr.as_slice(), + Some(idx) => self.repr.slice_to(idx) + } + } + + fn filename<'a>(&'a self) -> &'a [u8] { + match self.sepidx { + None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[], + None => self.repr.as_slice(), + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[], + Some(idx) => self.repr.slice_from(idx+1) + } + } + + fn pop_opt(&mut self) -> Option<~[u8]> { + match self.sepidx { + None if bytes!(".") == self.repr => None, + None => { + let mut v = ~['.' as u8]; + util::swap(&mut v, &mut self.repr); + self.sepidx = None; + Some(v) + } + Some(0) if bytes!("/") == self.repr => None, + Some(idx) => { + let v = self.repr.slice_from(idx+1).to_owned(); + if idx == 0 { + self.repr.truncate(idx+1); + } else { + self.repr.truncate(idx); + } + self.sepidx = self.repr.rposition_elem(&sep); + Some(v) + } + } + } + + #[inline] + fn is_absolute(&self) -> bool { + self.repr[0] == sep + } + + fn is_ancestor_of(&self, other: &Path) -> bool { + if self.is_absolute() != other.is_absolute() { + false + } else { + let mut ita = self.component_iter(); + let mut itb = other.component_iter(); + if bytes!(".") == self.repr { + return itb.next() != Some(bytes!("..")); + } + loop { + match (ita.next(), itb.next()) { + (None, _) => break, + (Some(a), Some(b)) if a == b => { loop }, + (Some(a), _) if a == bytes!("..") => { + // if ita contains only .. components, it's an ancestor + return ita.all(|x| x == bytes!("..")); + } + _ => return false + } + } + true + } + } + + fn path_relative_from(&self, base: &Path) -> Option { + if self.is_absolute() != base.is_absolute() { + if self.is_absolute() { + Some(self.clone()) + } else { + None + } + } else { + let mut ita = self.component_iter(); + let mut itb = base.component_iter(); + let mut comps = ~[]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(&mut ita); + break; + } + (None, _) => comps.push(dot_dot_static), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == bytes!(".") => comps.push(a), + (Some(_), Some(b)) if b == bytes!("..") => return None, + (Some(a), Some(_)) => { + comps.push(dot_dot_static); + for _ in itb { + comps.push(dot_dot_static); + } + comps.push(a); + comps.extend(&mut ita); + break; + } + } + } + Some(Path::new(comps.connect_vec(&sep))) + } + } +} + +impl Path { + /// Returns a new Path from a byte vector + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the vector contains a NUL. + #[inline] + pub fn new(v: &[u8]) -> Path { + GenericPath::from_vec(v) + } + + /// Returns a new Path from a string + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the str contains a NUL. + #[inline] + pub fn from_str(s: &str) -> Path { + GenericPath::from_str(s) + } + + /// Converts the Path into an owned byte vector + pub fn into_vec(self) -> ~[u8] { + self.repr + } + + /// Converts the Path into an owned string, if possible + pub fn into_str(self) -> Option<~str> { + str::from_bytes_owned_opt(self.repr) + } + + /// Returns a normalized byte vector representation of a path, by removing all empty + /// components, and unnecessary . and .. components. + pub fn normalize+CopyableVector>(v: V) -> ~[u8] { + // borrowck is being very picky + let val = { + let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep; + let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() }; + let comps = normalize_helper(v_, is_abs, is_sep); + match comps { + None => None, + Some(comps) => { + if is_abs && comps.is_empty() { + Some(~[sep]) + } 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.move_iter(); + if !is_abs { + match it.next() { + None => (), + Some(comp) => v.push_all(comp) + } + } + for comp in it { + v.push(sep); + v.push_all(comp); + } + Some(v) + } + } + } + }; + match val { + None => v.into_owned(), + 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 component_iter<'a>(&'a self) -> ComponentIter<'a> { + let v = if self.repr[0] == sep { + self.repr.slice_from(1) + } else { self.repr.as_slice() }; + let mut ret = v.split_iter(is_sep); + if v.is_empty() { + // consume the empty "" component + ret.next(); + } + ret + } +} + +// None result means the byte vector didn't need normalizing +// FIXME (#8169): Pull this into parent module once visibility works +fn normalize_helper<'a>(v: &'a [u8], is_abs: bool, f: &'a fn(&u8) -> bool) -> Option<~[&'a [u8]]> { + if is_abs && v.as_slice().is_empty() { + return None; + } + let mut comps: ~[&'a [u8]] = ~[]; + let mut n_up = 0u; + let mut changed = false; + for comp in v.split_iter(f) { + if comp.is_empty() { changed = true } + else if comp == bytes!(".") { changed = true } + else if comp == bytes!("..") { + 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_opt(); changed = true } + } else { comps.push(comp) } + } + if changed { + if comps.is_empty() && !is_abs { + if v == bytes!(".") { + return None; + } + comps.push(dot_static); + } + Some(comps) + } else { + None + } +} + +// FIXME (#8169): Pull this into parent module once visibility works +#[inline(always)] +fn contains_nul(v: &[u8]) -> bool { + v.iter().any(|&x| x == 0) +} + +static dot_static: &'static [u8] = &'static ['.' as u8]; +static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; + +#[cfg(test)] +mod tests { + use super::*; + use option::{Some, None}; + use iterator::Iterator; + use str; + use vec::Vector; + + 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); + } + ) + ) + + macro_rules! b( + ($($arg:expr),+) => ( + bytes!($($arg),+) + ) + ) + + #[test] + fn test_paths() { + t!(v: Path::new([]), 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", 0xff)), b!("a/b/c", 0xff)); + t!(v: Path::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); + let p = Path::new(b!("a/b/c", 0xff)); + assert_eq!(p.as_str(), None); + + t!(s: Path::from_str(""), "."); + t!(s: Path::from_str("/"), "/"); + t!(s: Path::from_str("hi"), "hi"); + t!(s: Path::from_str("/lib"), "/lib"); + t!(s: Path::from_str("hi/there"), "hi/there"); + t!(s: Path::from_str("hi/there.txt"), "hi/there.txt"); + + t!(s: Path::from_str("hi/there/"), "hi/there"); + t!(s: Path::from_str("hi/../there"), "there"); + t!(s: Path::from_str("../hi/there"), "../hi/there"); + t!(s: Path::from_str("/../hi/there"), "/hi/there"); + t!(s: Path::from_str("foo/.."), "."); + t!(s: Path::from_str("/foo/.."), "/"); + t!(s: Path::from_str("/foo/../.."), "/"); + t!(s: Path::from_str("/foo/../../bar"), "/bar"); + t!(s: Path::from_str("/./hi/./there/."), "/hi/there"); + t!(s: Path::from_str("/./hi/./there/./.."), "/hi"); + t!(s: Path::from_str("foo/../.."), ".."); + t!(s: Path::from_str("foo/../../.."), "../.."); + t!(s: Path::from_str("foo/../../bar"), "../bar"); + + assert_eq!(Path::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); + assert_eq!(Path::new(b!("/foo/../../bar")).into_vec(), + b!("/bar").to_owned()); + assert_eq!(Path::from_str("foo/bar").into_str(), Some(~"foo/bar")); + assert_eq!(Path::from_str("/foo/../../bar").into_str(), Some(~"/bar")); + + let p = Path::new(b!("foo/bar", 0x80)); + assert_eq!(p.as_str(), None); + assert_eq!(Path::new(b!("foo", 0xff, "/bar")).into_str(), None); + } + + #[test] + fn test_null_byte() { + use path2::null_byte::cond; + + let mut handled = false; + let mut p = do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("foo/bar", 0)); + (b!("/bar").to_owned()) + }).inside { + Path::new(b!("foo/bar", 0)) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("/bar")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.set_filename(b!("f", 0, "o")) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("/foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("null/", 0, "/byte")); + (b!("null/byte").to_owned()) + }).inside { + p.set_dirname(b!("null/", 0, "/byte")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null/byte/foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.push(b!("f", 0, "o")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null/byte/foo/foo")); + } + + #[test] + fn test_null_byte_fail() { + use path2::null_byte::cond; + use task; + + macro_rules! t( + ($name:expr => $code:block) => ( + { + let mut t = task::task(); + t.supervised(); + t.name($name); + let res = do t.try $code; + assert!(res.is_err()); + } + ) + ) + + t!(~"new() w/nul" => { + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + Path::new(b!("foo/bar", 0)) + }; + }) + + t!(~"set_filename w/nul" => { + let mut p = Path::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_filename(b!("foo", 0)) + }; + }) + + t!(~"set_dirname w/nul" => { + let mut p = Path::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_dirname(b!("foo", 0)) + }; + }) + + t!(~"push w/nul" => { + let mut p = Path::new(b!("foo/bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.push(b!("foo", 0)) + }; + }) + } + + #[test] + fn test_components() { + macro_rules! t( + (s: $path:expr, $op:ident, $exp:expr) => ( + { + let path = Path::from_str($path); + assert_eq!(path.$op(), ($exp).as_bytes()); + } + ); + (s: $path:expr, $op:ident, $exp:expr, opt) => ( + { + let path = Path::from_str($path); + let left = path.$op().map(|&x| str::from_bytes_slice(x)); + assert_eq!(left, $exp); + } + ); + (v: $path:expr, $op:ident, $exp:expr) => ( + { + let path = Path::new($path); + assert_eq!(path.$op(), $exp); + } + ) + ) + + t!(v: b!("a/b/c"), filename, b!("c")); + t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff)); + t!(v: b!("a/b", 0xff, "/c"), filename, b!("c")); + t!(s: "a/b/c", filename, "c"); + t!(s: "/a/b/c", filename, "c"); + t!(s: "a", filename, "a"); + t!(s: "/a", filename, "a"); + t!(s: ".", filename, ""); + t!(s: "/", filename, ""); + t!(s: "..", filename, ""); + t!(s: "../..", filename, ""); + + t!(v: b!("a/b/c"), dirname, b!("a/b")); + t!(v: b!("a/b/c", 0xff), dirname, b!("a/b")); + t!(v: b!("a/b", 0xff, "/c"), dirname, b!("a/b", 0xff)); + 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, b!("there")); + t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80)); + t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there")); + t!(s: "hi/there.txt", filestem, "there"); + t!(s: "hi/there", filestem, "there"); + t!(s: "there.txt", filestem, "there"); + t!(s: "there", filestem, "there"); + t!(s: ".", filestem, ""); + t!(s: "/", filestem, ""); + t!(s: "foo/.bar", filestem, ".bar"); + t!(s: ".bar", filestem, ".bar"); + t!(s: "..bar", filestem, "."); + t!(s: "hi/there..txt", filestem, "there."); + t!(s: "..", filestem, ""); + t!(s: "../..", filestem, ""); + + t!(v: b!("hi/there.txt"), extension, Some(b!("txt"))); + t!(v: b!("hi/there", 0x80, ".txt"), extension, Some(b!("txt"))); + t!(v: b!("hi/there.t", 0x80, "xt"), extension, Some(b!("t", 0x80, "xt"))); + t!(v: b!("hi/there"), extension, None); + t!(v: b!("hi/there", 0x80), 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::from_str(path); + let p2 = p1.clone(); + p1.push_str(join); + assert_eq!(p1, p2.join_str(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::from_str($path); + let push = Path::from_str($push); + p.push_path(&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_pop() { + macro_rules! t( + (s: $path:expr, $left:expr, $right:expr) => ( + { + let mut p = Path::from_str($path); + let file = p.pop_opt_str(); + assert_eq!(p.as_str(), Some($left)); + assert_eq!(file.map(|s| s.as_slice()), $right); + } + ); + (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( + { + let mut p = Path::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); + } + ); + (v: [$($path:expr),+], [$($left:expr),+], None) => ( + { + let mut p = Path::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file, None); + } + ) + ) + + t!(v: ["a/b/c"], ["a/b"], Some("c")); + t!(v: ["a"], ["."], Some("a")); + t!(v: ["."], ["."], None); + t!(v: ["/a"], ["/"], Some("a")); + t!(v: ["/"], ["/"], None); + t!(v: ["a/b/c", 0x80], ["a/b"], Some("c", 0x80)); + t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], Some("c")); + t!(v: [0xff], ["."], Some(0xff)); + t!(v: ["/", 0xff], ["/"], Some(0xff)); + t!(s: "a/b/c", "a/b", Some("c")); + t!(s: "a", ".", Some("a")); + t!(s: ".", ".", None); + t!(s: "/a", "/", Some("a")); + t!(s: "/", "/", None); + + assert_eq!(Path::new(b!("foo/bar", 0x80)).pop_opt_str(), None); + assert_eq!(Path::new(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); + } + + #[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/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); + t!(s: Path::from_str("a/b/c").join_str(".."), "a/b"); + t!(s: Path::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); + t!(s: Path::from_str("a/b").join_str("c/d"), "a/b/c/d"); + t!(s: Path::from_str("a/b").join_str("/c/d"), "/c/d"); + t!(s: Path::from_str(".").join_str("a/b"), "a/b"); + t!(s: Path::from_str("/").join_str("a/b"), "/a/b"); + } + + #[test] + fn test_join_path() { + macro_rules! t( + (s: $path:expr, $join:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let join = Path::from_str($join); + let res = path.join_path(&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_with_helpers() { + t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); + t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); + t!(v: Path::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); + t!(v: Path::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), + b!("/", 0xcd, "/", 0x80)); + t!(s: Path::from_str("a/b/c").with_dirname_str("d"), "d/c"); + t!(s: Path::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); + t!(s: Path::from_str("a/b/c").with_dirname_str(""), "c"); + t!(s: Path::from_str("a/b/c").with_dirname_str("/"), "/c"); + t!(s: Path::from_str("a/b/c").with_dirname_str("."), "c"); + t!(s: Path::from_str("a/b/c").with_dirname_str(".."), "../c"); + t!(s: Path::from_str("/").with_dirname_str("foo"), "foo"); + t!(s: Path::from_str("/").with_dirname_str(""), "."); + t!(s: Path::from_str("/foo").with_dirname_str("bar"), "bar/foo"); + t!(s: Path::from_str("..").with_dirname_str("foo"), "foo"); + t!(s: Path::from_str("../..").with_dirname_str("foo"), "foo"); + t!(s: Path::from_str("foo").with_dirname_str(".."), "../foo"); + t!(s: Path::from_str("foo").with_dirname_str("../.."), "../../foo"); + + t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); + t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); + t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), + b!("/", 0xff, "/", 0xcd)); + t!(s: Path::from_str("a/b/c").with_filename_str("d"), "a/b/d"); + t!(s: Path::from_str(".").with_filename_str("foo"), "foo"); + t!(s: Path::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); + t!(s: Path::from_str("/").with_filename_str("foo"), "/foo"); + t!(s: Path::from_str("/a").with_filename_str("foo"), "/foo"); + t!(s: Path::from_str("foo").with_filename_str("bar"), "bar"); + t!(s: Path::from_str("a/b/c").with_filename_str(""), "a/b"); + t!(s: Path::from_str("a/b/c").with_filename_str("."), "a/b"); + t!(s: Path::from_str("a/b/c").with_filename_str(".."), "a"); + t!(s: Path::from_str("/a").with_filename_str(""), "/"); + t!(s: Path::from_str("foo").with_filename_str(""), "."); + t!(s: Path::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); + t!(s: Path::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); + t!(s: Path::from_str("..").with_filename_str("foo"), "../foo"); + t!(s: Path::from_str("../..").with_filename_str("foo"), "../../foo"); + + t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), + b!("hi/", 0xff, ".txt")); + t!(v: Path::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), + b!("hi/", 0xff, ".txt", 0x80)); + t!(v: Path::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); + t!(v: Path::new(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); + t!(s: Path::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); + t!(s: Path::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); + t!(s: Path::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); + t!(s: Path::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); + t!(s: Path::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); + t!(s: Path::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); + t!(s: Path::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); + t!(s: Path::from_str("hi/there").with_filestem_str("here"), "hi/here"); + t!(s: Path::from_str("hi/there").with_filestem_str(""), "hi"); + t!(s: Path::from_str("hi").with_filestem_str(""), "."); + t!(s: Path::from_str("/hi").with_filestem_str(""), "/"); + t!(s: Path::from_str("hi/there").with_filestem_str(".."), "."); + t!(s: Path::from_str("hi/there").with_filestem_str("."), "hi"); + t!(s: Path::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); + t!(s: Path::from_str("hi/there.").with_filestem_str(""), "hi"); + t!(s: Path::from_str("hi/there.").with_filestem_str("."), "."); + t!(s: Path::from_str("hi/there.").with_filestem_str(".."), "hi/..."); + t!(s: Path::from_str("/").with_filestem_str("foo"), "/foo"); + t!(s: Path::from_str(".").with_filestem_str("foo"), "foo"); + t!(s: Path::from_str("hi/there..").with_filestem_str("here"), "hi/here."); + t!(s: Path::from_str("hi/there..").with_filestem_str(""), "hi"); + + t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), + b!("hi/there", 0x80, ".exe")); + t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), + b!("hi/there.", 0xff)); + t!(v: Path::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), + b!("hi/there", 0x80, ".", 0xff)); + t!(v: Path::new(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); + t!(s: Path::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); + t!(s: Path::from_str("hi/there.txt").with_extension_str(""), "hi/there"); + t!(s: Path::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); + t!(s: Path::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); + t!(s: Path::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); + t!(s: Path::from_str("hi/there").with_extension_str("."), "hi/there.."); + t!(s: Path::from_str("hi/there").with_extension_str(".."), "hi/there..."); + t!(s: Path::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); + t!(s: Path::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); + t!(s: Path::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); + t!(s: Path::from_str("/").with_extension_str("txt"), "/"); + t!(s: Path::from_str("/").with_extension_str("."), "/"); + t!(s: Path::from_str("/").with_extension_str(".."), "/"); + t!(s: Path::from_str(".").with_extension_str("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::from_str(path); + p1.$set(arg); + let p2 = Path::from_str(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_dirname, with_dirname, b!("d")); + t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); + t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d"); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d/e"); + t!(s: "/", set_dirname_str, with_dirname_str, "foo"); + t!(s: "/foo", set_dirname_str, with_dirname_str, "bar"); + t!(s: "a/b/c", set_dirname_str, with_dirname_str, ""); + t!(s: "../..", set_dirname_str, with_dirname_str, "x"); + t!(s: "foo", set_dirname_str, with_dirname_str, "../.."); + + t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); + t!(v: b!("/"), set_filename, with_filename, b!("foo")); + t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); + t!(s: "a/b/c", set_filename_str, with_filename_str, "d"); + t!(s: "/", set_filename_str, with_filename_str, "foo"); + t!(s: ".", set_filename_str, with_filename_str, "foo"); + t!(s: "a/b", set_filename_str, with_filename_str, ""); + t!(s: "a", set_filename_str, with_filename_str, ""); + + t!(v: b!("hi/there.txt"), set_filestem, with_filestem, b!("here")); + t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); + t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there.", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, ""); + t!(s: "hi/there", set_filestem_str, with_filestem_str, ""); + + t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); + t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); + t!(s: "hi/there.txt", set_extension_str, with_extension_str, "exe"); + t!(s: "hi/there.", set_extension_str, with_extension_str, "txt"); + t!(s: "hi/there", set_extension_str, with_extension_str, "txt"); + t!(s: "hi/there.txt", set_extension_str, with_extension_str, ""); + t!(s: "hi/there", set_extension_str, with_extension_str, ""); + t!(s: ".", set_extension_str, with_extension_str, "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")), b!("c"), b!("a/b"), b!("c"), None); + t!(v: Path::new(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); + t!(v: Path::new(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), + b!("there"), Some(b!(0xff))); + t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); + t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); + t!(s: Path::from_str("/"), Some(""), Some("/"), Some(""), None); + t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None); + t!(s: Path::from_str("../.."), Some(""), Some("../.."), Some(""), None); + t!(s: Path::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), + Some("there"), Some("txt")); + t!(s: Path::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); + t!(s: Path::from_str("hi/there."), Some("there."), Some("hi"), + Some("there"), Some("")); + t!(s: Path::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); + t!(s: Path::from_str("hi/..there"), Some("..there"), Some("hi"), + Some("."), Some("there")); + t!(s: Path::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); + t!(s: Path::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); + t!(s: Path::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); + t!(s: Path::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); + } + + #[test] + fn test_dir_file_path() { + t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); + t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); + t!(s: Path::from_str("hi/there").dir_path(), "hi"); + t!(s: Path::from_str("hi").dir_path(), "."); + t!(s: Path::from_str("/hi").dir_path(), "/"); + t!(s: Path::from_str("/").dir_path(), "/"); + t!(s: Path::from_str("..").dir_path(), ".."); + t!(s: Path::from_str("../..").dir_path(), "../.."); + + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = $path; + let left = path.chain_ref(|p| p.as_str()); + assert_eq!(left, $exp); + } + ); + (v: $path:expr, $exp:expr) => ( + { + let path = $path; + let left = path.map(|p| p.as_vec()); + assert_eq!(left, $exp); + } + ) + ) + + t!(v: Path::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); + t!(v: Path::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); + t!(s: Path::from_str("hi/there").file_path(), Some("there")); + t!(s: Path::from_str("hi").file_path(), Some("hi")); + t!(s: Path::from_str(".").file_path(), None); + t!(s: Path::from_str("/").file_path(), None); + t!(s: Path::from_str("..").file_path(), None); + t!(s: Path::from_str("../..").file_path(), None); + } + + #[test] + fn test_is_absolute() { + assert_eq!(Path::from_str("a/b/c").is_absolute(), false); + assert_eq!(Path::from_str("/a/b/c").is_absolute(), true); + assert_eq!(Path::from_str("a").is_absolute(), false); + assert_eq!(Path::from_str("/a").is_absolute(), true); + assert_eq!(Path::from_str(".").is_absolute(), false); + assert_eq!(Path::from_str("/").is_absolute(), true); + assert_eq!(Path::from_str("..").is_absolute(), false); + assert_eq!(Path::from_str("../..").is_absolute(), false); + } + + #[test] + fn test_is_ancestor_of() { + macro_rules! t( + (s: $path:expr, $dest:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let dest = Path::from_str($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_path_relative_from() { + macro_rules! t( + (s: $path:expr, $other:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let other = Path::from_str($other); + let res = path.path_relative_from(&other); + assert_eq!(res.chain_ref(|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_component_iter() { + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&str] = $exp; + let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); + assert_eq!(comps, exps); + } + ); + (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( + { + let path = Path::new(b!($($arg),+)); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&[u8]] = [$(b!($($exp),*)),*]; + assert_eq!(comps.as_slice(), exp); + } + ) + ) + + t!(v: ["a/b/c"], [["a"], ["b"], ["c"]]); + t!(v: ["/", 0xff, "/a/", 0x80], [[0xff], ["a"], [0x80]]); + t!(v: ["../../foo", 0xcd, "bar"], [[".."], [".."], ["foo", 0xcd, "bar"]]); + 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"]); + } +} diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs new file mode 100644 index 0000000000000..1c3eb5c291dc6 --- /dev/null +++ b/src/libstd/path2/windows.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows file path handling + +/// The standard path separator character +pub static sep: u8 = '\\' as u8; +/// The alternative path separator character +pub static sep2: u8 = '/' as u8; + +/// Returns whether the given byte is a path separator +#[inline] +pub fn is_sep(u: &u8) -> bool { + *u == sep || *u == sep2 +} From 6f5b809775fb1f8dbf27edded8e955d64377749c Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 1 Sep 2013 23:35:32 -0700 Subject: [PATCH 08/30] path2: Add more tests to posix impl --- src/libstd/path2/posix.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index fa6d1e32ebd09..d66f51894b53d 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -97,13 +97,13 @@ impl GenericPathUnsafe for Path { Some(0) if self.repr.len() == 1 && self.repr[0] == sep => { self.repr = Path::normalize(dirname); } + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { + self.repr = Path::normalize(dirname); + } Some(idx) if dirname.is_empty() => { let v = Path::normalize(self.repr.slice_from(idx+1)); self.repr = v; } - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { - self.repr = Path::normalize(dirname); - } Some(idx) => { let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); v.push_all(dirname); @@ -444,7 +444,9 @@ mod tests { t!(s: Path::from_str(""), "."); t!(s: Path::from_str("/"), "/"); t!(s: Path::from_str("hi"), "hi"); + t!(s: Path::from_str("hi/"), "hi"); t!(s: Path::from_str("/lib"), "/lib"); + t!(s: Path::from_str("/lib/"), "/lib"); t!(s: Path::from_str("hi/there"), "hi/there"); t!(s: Path::from_str("hi/there.txt"), "hi/there.txt"); @@ -800,6 +802,8 @@ mod tests { t!(s: Path::from_str("/foo").with_dirname_str("bar"), "bar/foo"); t!(s: Path::from_str("..").with_dirname_str("foo"), "foo"); t!(s: Path::from_str("../..").with_dirname_str("foo"), "foo"); + t!(s: Path::from_str("..").with_dirname_str(""), "."); + t!(s: Path::from_str("../..").with_dirname_str(""), "."); t!(s: Path::from_str("foo").with_dirname_str(".."), "../foo"); t!(s: Path::from_str("foo").with_dirname_str("../.."), "../../foo"); @@ -813,6 +817,8 @@ mod tests { t!(s: Path::from_str("/").with_filename_str("foo"), "/foo"); t!(s: Path::from_str("/a").with_filename_str("foo"), "/foo"); t!(s: Path::from_str("foo").with_filename_str("bar"), "bar"); + t!(s: Path::from_str("/").with_filename_str("foo/"), "/foo"); + t!(s: Path::from_str("/a").with_filename_str("foo/"), "/foo"); t!(s: Path::from_str("a/b/c").with_filename_str(""), "a/b"); t!(s: Path::from_str("a/b/c").with_filename_str("."), "a/b"); t!(s: Path::from_str("a/b/c").with_filename_str(".."), "a"); @@ -822,6 +828,8 @@ mod tests { t!(s: Path::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); t!(s: Path::from_str("..").with_filename_str("foo"), "../foo"); t!(s: Path::from_str("../..").with_filename_str("foo"), "../../foo"); + t!(s: Path::from_str("..").with_filename_str(""), ".."); + t!(s: Path::from_str("../..").with_filename_str(""), "../.."); t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt")); From 3d80a2f1f1910fd681b8fd99906da3446dc06a90 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Mon, 30 Sep 2013 17:13:13 -0700 Subject: [PATCH 09/30] path2: Update for changes from master --- src/libstd/path2/mod.rs | 14 +++++++------- src/libstd/path2/posix.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index 148af0057f52c..c8f57bff84ab6 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -13,7 +13,7 @@ use container::Container; use c_str::CString; use clone::Clone; -use iterator::Iterator; +use iter::Iterator; use option::{Option, None, Some}; use str; use str::StrSlice; @@ -102,7 +102,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If the path is not representable in utf-8, this returns None. #[inline] fn as_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.as_vec()) + str::from_utf8_slice_opt(self.as_vec()) } /// Returns the path as a byte vector @@ -115,7 +115,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `dirname` for details. #[inline] fn dirname_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.dirname()) + str::from_utf8_slice_opt(self.dirname()) } /// Returns the file component of `self`, as a byte vector. /// If `self` represents the root of the file hierarchy, returns the empty vector. @@ -125,7 +125,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `filename` for details. #[inline] fn filename_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.filename()) + str::from_utf8_slice_opt(self.filename()) } /// Returns the stem of the filename of `self`, as a byte vector. /// The stem is the portion of the filename just before the last '.'. @@ -143,7 +143,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `filestem` for details. #[inline] fn filestem_str<'a>(&'a self) -> Option<&'a str> { - str::from_bytes_slice_opt(self.filestem()) + str::from_utf8_slice_opt(self.filestem()) } /// 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 '.'. @@ -162,7 +162,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `extension` for details. #[inline] fn extension_str<'a>(&'a self) -> Option<&'a str> { - self.extension().chain(|v| str::from_bytes_slice_opt(v)) + self.extension().and_then(|v| str::from_utf8_slice_opt(v)) } /// Replaces the directory portion of the path with the given byte vector. @@ -447,7 +447,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `pop_opt` for details. #[inline] fn pop_opt_str(&mut self) -> Option<~str> { - self.pop_opt().chain(|v| str::from_bytes_owned_opt(v)) + self.pop_opt().and_then(|v| str::from_utf8_owned_opt(v)) } /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index d66f51894b53d..e039843cabf12 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -15,7 +15,7 @@ use c_str::{CString, ToCStr}; use clone::Clone; use cmp::Eq; use from_str::FromStr; -use iterator::{AdditiveIterator, Extendable, Iterator}; +use iter::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; use str::Str; @@ -303,7 +303,7 @@ impl Path { /// Converts the Path into an owned string, if possible pub fn into_str(self) -> Option<~str> { - str::from_bytes_owned_opt(self.repr) + str::from_utf8_owned_opt(self.repr) } /// Returns a normalized byte vector representation of a path, by removing all empty @@ -406,7 +406,7 @@ static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; mod tests { use super::*; use option::{Some, None}; - use iterator::Iterator; + use iter::Iterator; use str; use vec::Vector; @@ -589,7 +589,7 @@ mod tests { (s: $path:expr, $op:ident, $exp:expr, opt) => ( { let path = Path::from_str($path); - let left = path.$op().map(|&x| str::from_bytes_slice(x)); + let left = path.$op().map(|&x| str::from_utf8_slice(x)); assert_eq!(left, $exp); } ); @@ -1006,7 +1006,7 @@ mod tests { (s: $path:expr, $exp:expr) => ( { let path = $path; - let left = path.chain_ref(|p| p.as_str()); + let left = path.and_then_ref(|p| p.as_str()); assert_eq!(left, $exp); } ); @@ -1083,7 +1083,7 @@ mod tests { let path = Path::from_str($path); let other = Path::from_str($other); let res = path.path_relative_from(&other); - assert_eq!(res.chain_ref(|x| x.as_str()), $exp); + assert_eq!(res.and_then_ref(|x| x.as_str()), $exp); } ) ) From f25835c2210295bed25096807eed201e4d3b206e Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 27 Aug 2013 00:51:08 -0700 Subject: [PATCH 10/30] path2: Implement WindowsPath --- src/libstd/path2/mod.rs | 101 +- src/libstd/path2/posix.rs | 10 +- src/libstd/path2/windows.rs | 2231 ++++++++++++++++++++++++++++++++++- 3 files changed, 2314 insertions(+), 28 deletions(-) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index c8f57bff84ab6..183086a785bea 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -28,16 +28,16 @@ pub mod windows; /// See `posix::Path` for more info. pub type PosixPath = posix::Path; -// /// Typedef for Windows file paths. -// /// See `windows::Path` for more info. -// pub type WindowsPath = windows::Path; +/// Typedef for Windows file paths. +/// See `windows::Path` for more info. +pub type WindowsPath = windows::Path; /// Typedef for the platform-native path type #[cfg(unix)] pub type Path = PosixPath; -// /// Typedef for the platform-native path type -//#[cfg(windows)] -//pub type Path = WindowsPath; +/// Typedef for the platform-native path type +#[cfg(windows)] +pub type Path = WindowsPath; /// Typedef for the POSIX path component iterator. /// See `posix::ComponentIter` for more info. @@ -68,6 +68,8 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// # Failure /// /// Raises the `null_byte` condition if the path contains a NUL. + /// + /// See individual Path impls for additional restrictions. #[inline] fn from_vec(path: &[u8]) -> Self { if contains_nul(path) { @@ -87,11 +89,18 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// Raises the `null_byte` condition if the path contains a NUL. #[inline] fn from_str(path: &str) -> Self { - GenericPath::from_vec(path.as_bytes()) + let v = path.as_bytes(); + if contains_nul(v) { + GenericPath::from_vec(path.as_bytes()) // let from_vec handle the condition + } else { + unsafe { GenericPathUnsafe::from_str_unchecked(path) } + } } /// Creates a new Path from a CString. /// The resulting Path will always be normalized. + /// + /// See individual Path impls for potential restrictions. #[inline] fn from_c_str(path: CString) -> Self { // CStrings can't contain NULs @@ -186,7 +195,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_dirname` for details. #[inline] fn set_dirname_str(&mut self, dirname: &str) { - self.set_dirname(dirname.as_bytes()) + if contains_nul(dirname.as_bytes()) { + self.set_dirname(dirname.as_bytes()) // triggers null_byte condition + } else { + unsafe { self.set_dirname_str_unchecked(dirname) } + } } /// Replaces the filename portion of the path with the given byte vector. /// If the replacement name is [], this is equivalent to popping the path. @@ -208,7 +221,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_filename` for details. #[inline] fn set_filename_str(&mut self, filename: &str) { - self.set_filename(filename.as_bytes()) + if contains_nul(filename.as_bytes()) { + self.set_filename(filename.as_bytes()) // triggers null_byte condition + } else { + unsafe { self.set_filename_str_unchecked(filename) } + } } /// Replaces the filestem with the given byte vector. /// If there is no extension in `self` (or `self` has no filename), this is equivalent @@ -340,7 +357,9 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_dirname` for details. #[inline] fn with_dirname_str(&self, dirname: &str) -> Self { - self.with_dirname(dirname.as_bytes()) + let mut p = self.clone(); + p.set_dirname_str(dirname); + p } /// Returns a new Path constructed by replacing the filename with the given byte vector. /// See `set_filename` for details. @@ -358,7 +377,9 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_filename` for details. #[inline] fn with_filename_str(&self, filename: &str) -> Self { - self.with_filename(filename.as_bytes()) + let mut p = self.clone(); + p.set_filename_str(filename); + p } /// Returns a new Path constructed by setting the filestem to the given byte vector. /// See `set_filestem` for details. @@ -376,7 +397,9 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_filestem` for details. #[inline] fn with_filestem_str(&self, filestem: &str) -> Self { - self.with_filestem(filestem.as_bytes()) + let mut p = self.clone(); + p.set_filestem_str(filestem); + p } /// Returns a new Path constructed by setting the extension to the given byte vector. /// See `set_extension` for details. @@ -394,20 +417,24 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `set_extension` for details. #[inline] fn with_extension_str(&self, extension: &str) -> Self { - self.with_extension(extension.as_bytes()) + let mut p = self.clone(); + p.set_extension_str(extension); + p } /// Returns the directory component of `self`, as a Path. /// If `self` represents the root of the filesystem hierarchy, returns `self`. fn dir_path(&self) -> Self { - GenericPath::from_vec(self.dirname()) + // self.dirname() returns a NUL-free vector + unsafe { GenericPathUnsafe::from_vec_unchecked(self.dirname()) } } /// Returns the file component of `self`, as a relative Path. /// If `self` represents the root of the filesystem hierarchy, returns None. fn file_path(&self) -> Option { + // self.filename() returns a NUL-free vector match self.filename() { [] => None, - v => Some(GenericPath::from_vec(v)) + v => Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) } } @@ -431,7 +458,11 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `push` for details. #[inline] fn push_str(&mut self, path: &str) { - self.push(path.as_bytes()) + if contains_nul(path.as_bytes()) { + self.push(path.as_bytes()) // triggers null_byte condition + } else { + unsafe { self.push_str_unchecked(path) } + } } /// Pushes a Path onto `self`. /// If the argument represents an absolute path, it replaces `self`. @@ -466,7 +497,9 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// See `join` for details. #[inline] fn join_str(&self, path: &str) -> Self { - self.join(path.as_bytes()) + let mut p = self.clone(); + p.push_str(path); + p } /// Returns a new Path constructed by joining `self` with the given path. /// If the given path is absolute, the new Path will represent just that. @@ -478,6 +511,8 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } /// 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. fn is_absolute(&self) -> bool; /// Returns whether `self` is equal to, or is an ancestor of, the given path. @@ -498,19 +533,49 @@ pub trait GenericPathUnsafe { /// The resulting Path will always be normalized. unsafe fn from_vec_unchecked(path: &[u8]) -> Self; + /// Creates a new Path from a str without checking for null bytes. + /// The resulting Path will always be normalized. + #[inline] + unsafe fn from_str_unchecked(path: &str) -> Self { + GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) + } + /// Replaces the directory portion of the path with the given byte vector without /// checking for null bytes. /// See `set_dirname` for details. unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]); + /// Replaces the directory portion of the path with the given str without + /// checking for null bytes. + /// See `set_dirname_str` for details. + #[inline] + unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) { + self.set_dirname_unchecked(dirname.as_bytes()) + } + /// Replaces the filename portion of the path with the given byte vector without /// checking for null bytes. /// See `set_filename` for details. unsafe fn set_filename_unchecked(&mut self, filename: &[u8]); - /// Pushes a path onto `self` without checking for null bytes. + /// Replaces the filename portion of the path with the given str without + /// checking for null bytes. + /// See `set_filename_str` for details. + #[inline] + unsafe fn set_filename_str_unchecked(&mut self, filename: &str) { + self.set_filename_unchecked(filename.as_bytes()) + } + + /// Pushes a byte vector onto `self` without checking for null bytes. /// See `push` for details. unsafe fn push_unchecked(&mut self, path: &[u8]); + + /// Pushes a str onto `self` without checking for null bytes. + /// See `push_str` for details. + #[inline] + unsafe fn push_str_unchecked(&mut self, path: &str) { + self.push_unchecked(path.as_bytes()) + } } #[inline(always)] diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index e039843cabf12..e873774b06776 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -45,6 +45,7 @@ pub fn is_sep(u: &u8) -> bool { } impl Eq for Path { + #[inline] fn eq(&self, other: &Path) -> bool { self.repr == other.repr } @@ -134,7 +135,7 @@ impl GenericPathUnsafe for Path { self.repr = Path::normalize(v); } Some(idx) => { - let mut v = vec::with_capacity(self.repr.len() - idx + filename.len()); + let mut v = vec::with_capacity(idx + 1 + filename.len()); v.push_all(self.repr.slice_to(idx+1)); v.push_all(filename); self.repr = Path::normalize(v); @@ -313,7 +314,7 @@ impl Path { let val = { let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep; let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() }; - let comps = normalize_helper(v_, is_abs, is_sep); + let comps = normalize_helper(v_, is_abs); match comps { None => None, Some(comps) => { @@ -363,15 +364,14 @@ impl Path { } // None result means the byte vector didn't need normalizing -// FIXME (#8169): Pull this into parent module once visibility works -fn normalize_helper<'a>(v: &'a [u8], is_abs: bool, f: &'a fn(&u8) -> bool) -> Option<~[&'a [u8]]> { +fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> { if is_abs && v.as_slice().is_empty() { return None; } let mut comps: ~[&'a [u8]] = ~[]; let mut n_up = 0u; let mut changed = false; - for comp in v.split_iter(f) { + for comp in v.split_iter(is_sep) { if comp.is_empty() { changed = true } else if comp == bytes!(".") { changed = true } else if comp == bytes!("..") { diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 1c3eb5c291dc6..1f51f2962f6b7 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -10,13 +10,2234 @@ //! Windows file path handling +use ascii::AsciiCast; +use c_str::{CString, ToCStr}; +use cast; +use cmp::Eq; +use from_str::FromStr; +use iter::{AdditiveIterator, Extendable, Iterator}; +use option::{Option, Some, None}; +use str; +use str::{OwnedStr, Str, StrVector}; +use util; +use vec::Vector; +use super::{GenericPath, GenericPathUnsafe}; + +/// Iterator that yields successive components of a Path +pub type ComponentIter<'self> = str::CharSplitIterator<'self, char>; + +/// 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. +#[deriving(Clone, DeepClone)] +pub struct Path { + priv repr: ~str, // assumed to never be empty + priv prefix: Option, + priv sepidx: Option // index of the final separator in the non-prefix portion of repr +} + +impl Eq for Path { + #[inline] + fn eq(&self, other: &Path) -> bool { + self.repr == other.repr + } +} + +impl FromStr for Path { + fn from_str(s: &str) -> Option { + if contains_nul(s.as_bytes()) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_str_unchecked(s) }) + } + } +} + +impl ToCStr for Path { + #[inline] + fn to_c_str(&self) -> CString { + // The Path impl guarantees no embedded NULs + unsafe { self.as_vec().to_c_str_unchecked() } + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + self.as_vec().to_c_str_unchecked() + } +} + +impl GenericPathUnsafe for Path { + /// See `GenericPathUnsafe::from_vec_unchecked`. + /// + /// # Failure + /// + /// Raises the `str::not_utf8` condition if not valid UTF-8. + #[inline] + unsafe fn from_vec_unchecked(path: &[u8]) -> Path { + if !str::is_utf8(path) { + let path = str::from_utf8(path); // triggers not_utf8 condition + GenericPathUnsafe::from_str_unchecked(path) + } else { + GenericPathUnsafe::from_str_unchecked(cast::transmute(path)) + } + } + + #[inline] + unsafe fn from_str_unchecked(path: &str) -> Path { + let (prefix, path) = Path::normalize_(path); + assert!(!path.is_empty()); + let mut ret = Path{ repr: path, prefix: prefix, sepidx: None }; + ret.update_sepidx(); + ret + } + + /// See `GenericPathUnsafe::set_dirname_unchecked`. + /// + /// # Failure + /// + /// Raises the `str::not_utf8` condition if not valid UTF-8. + #[inline] + unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { + if !str::is_utf8(dirname) { + let dirname = str::from_utf8(dirname); // triggers not_utf8 condition + self.set_dirname_str_unchecked(dirname); + } else { + self.set_dirname_str_unchecked(cast::transmute(dirname)) + } + } + + unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) { + match self.sepidx_or_prefix_len() { + None if "." == self.repr || ".." == self.repr => { + self.update_normalized(dirname); + } + None => { + let mut s = str::with_capacity(dirname.len() + self.repr.len() + 1); + s.push_str(dirname); + s.push_char(sep); + s.push_str(self.repr); + self.update_normalized(s); + } + Some((_,idxa,end)) if self.repr.slice(idxa,end) == ".." => { + self.update_normalized(dirname); + } + Some((_,idxa,end)) if dirname.is_empty() => { + let (prefix, path) = Path::normalize_(self.repr.slice(idxa,end)); + self.repr = path; + self.prefix = prefix; + self.update_sepidx(); + } + Some((idxb,idxa,end)) => { + let idx = if dirname.ends_with("\\") { idxa } + else { + let prefix = parse_prefix(dirname); + if prefix == Some(DiskPrefix) && prefix_len(prefix) == dirname.len() { + idxa + } else { idxb } + }; + let mut s = str::with_capacity(dirname.len() + end - idx); + s.push_str(dirname); + s.push_str(self.repr.slice(idx,end)); + self.update_normalized(s); + } + } + } + + /// See `GenericPathUnsafe::set_filename_unchecekd`. + /// + /// # Failure + /// + /// Raises the `str::not_utf8` condition if not valid UTF-8. + #[inline] + unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { + if !str::is_utf8(filename) { + let filename = str::from_utf8(filename); // triggers not_utf8 condition + self.set_filename_str_unchecked(filename) + } else { + self.set_filename_str_unchecked(cast::transmute(filename)) + } + } + + unsafe fn set_filename_str_unchecked(&mut self, filename: &str) { + match self.sepidx_or_prefix_len() { + None if ".." == self.repr => { + let mut s = str::with_capacity(3 + filename.len()); + s.push_str(".."); + s.push_char(sep); + s.push_str(filename); + self.update_normalized(s); + } + None => { + self.update_normalized(filename); + } + Some((_,idxa,end)) if self.repr.slice(idxa,end) == ".." => { + let mut s = str::with_capacity(end + 1 + filename.len()); + s.push_str(self.repr.slice_to(end)); + s.push_char(sep); + s.push_str(filename); + self.update_normalized(s); + } + Some((idxb,idxa,_)) if self.prefix == Some(DiskPrefix) && idxa == self.prefix_len() => { + let mut s = str::with_capacity(idxb + filename.len()); + s.push_str(self.repr.slice_to(idxb)); + s.push_str(filename); + self.update_normalized(s); + } + Some((idxb,_,_)) => { + let mut s = str::with_capacity(idxb + 1 + filename.len()); + s.push_str(self.repr.slice_to(idxb)); + s.push_char(sep); + s.push_str(filename); + self.update_normalized(s); + } + } + } + + /// See `GenericPathUnsafe::push_unchecked`. + /// + /// # Failure + /// + /// Raises the `str::not_utf8` condition if not valid UTF-8. + unsafe fn push_unchecked(&mut self, path: &[u8]) { + if !str::is_utf8(path) { + let path = str::from_utf8(path); // triggers not_utf8 condition + self.push_str_unchecked(path); + } else { + self.push_str_unchecked(cast::transmute(path)); + } + } + + /// See `GenericPathUnsafe::push_str_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_str_unchecked(&mut self, path: &str) { + fn is_vol_abs(path: &str, prefix: Option) -> bool { + // assume prefix is Some(DiskPrefix) + let rest = path.slice_from(prefix_len(prefix)); + !rest.is_empty() && rest[0].is_ascii() && is_sep2(rest[0] as char) + } + fn shares_volume(me: &Path, path: &str) -> bool { + // path is assumed to have a prefix of Some(DiskPrefix) + match me.prefix { + Some(DiskPrefix) => me.repr[0] == path[0].to_ascii().to_upper().to_byte(), + Some(VerbatimDiskPrefix) => me.repr[4] == path[0].to_ascii().to_upper().to_byte(), + _ => false + } + } + fn is_sep_(prefix: Option, u: u8) -> bool { + u.is_ascii() && if prefix_is_verbatim(prefix) { is_sep(u as char) } + else { is_sep2(u as char) } + } + + fn replace_path(me: &mut Path, path: &str, prefix: Option) { + let newpath = Path::normalize__(path, prefix); + me.repr = match newpath { + Some(p) => p, + None => path.to_owned() + }; + 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 me.is_verbatim() { Path::normalize__(path, None) } + else { None }; + let pathlen = path_.map_default(path.len(), |p| p.len()); + let mut s = str::with_capacity(me.repr.len() + 1 + pathlen); + s.push_str(me.repr); + let plen = me.prefix_len(); + if !(me.repr.len() > plen && me.repr[me.repr.len()-1] == sep as u8) { + s.push_char(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.slice_from(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[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 { + /// See `GenericPath::as_str` for info. + /// Always returns a `Some` value. + #[inline] + fn as_str<'a>(&'a self) -> Option<&'a str> { + Some(self.repr.as_slice()) + } + + #[inline] + fn as_vec<'a>(&'a self) -> &'a [u8] { + self.repr.as_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.as_slice(), + None => ".", + Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => { + self.repr.as_slice() + } + Some((idxb,_,end)) if self.repr.slice(idxb, end) == "\\" => { + self.repr.as_slice() + } + Some((0,idxa,_)) => self.repr.slice_to(idxa), + Some((idxb,idxa,_)) => { + match self.prefix { + Some(DiskPrefix) | Some(VerbatimDiskPrefix) if idxb == self.prefix_len() => { + self.repr.slice_to(idxa) + } + _ => self.repr.slice_to(idxb) + } + } + }) + } + + #[inline] + fn filename<'a>(&'a self) -> &'a [u8] { + self.filename_str().unwrap().as_bytes() + } + + /// See `GenericPath::filename_str` for info. + /// Always returns a `Some` value. + fn filename_str<'a>(&'a self) -> Option<&'a str> { + Some(match self.sepidx_or_prefix_len() { + None if "." == self.repr || ".." == self.repr => "", + None => self.repr.as_slice(), + Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => "", + Some((_,idxa,end)) => self.repr.slice(idxa, end) + }) + } + + /// See `GenericPath::filestem_str` for info. + /// Always 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 + Some(unsafe { cast::transmute(self.filestem()) }) + } + + #[inline] + fn extension_str<'a>(&'a self) -> Option<&'a str> { + // extension() returns a byte vector that's guaranteed valid UTF-8 + self.extension().map_move(|v| unsafe { cast::transmute(v) }) + } + + fn dir_path(&self) -> Path { + unsafe { GenericPathUnsafe::from_str_unchecked(self.dirname_str().unwrap()) } + } + + fn file_path(&self) -> Option { + match self.filename_str() { + None | Some("") => None, + Some(s) => Some(unsafe { GenericPathUnsafe::from_str_unchecked(s) }) + } + } + + #[inline] + fn push_path(&mut self, path: &Path) { + self.push_str(path.as_str().unwrap()) + } + + #[inline] + fn pop_opt(&mut self) -> Option<~[u8]> { + self.pop_opt_str().map_move(|s| s.into_bytes()) + } + + fn pop_opt_str(&mut self) -> Option<~str> { + match self.sepidx_or_prefix_len() { + None if "." == self.repr => None, + None => { + let mut s = ~"."; + util::swap(&mut s, &mut self.repr); + self.sepidx = None; + Some(s) + } + Some((idxb,idxa,end)) if idxb == idxa && idxb == end => None, + Some((idxb,_,end)) if self.repr.slice(idxb, end) == "\\" => None, + Some((idxb,idxa,end)) => { + let s = self.repr.slice(idxa, end).to_owned(); + 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(); + Some(s) + } + } + } + + /// 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.slice_from(self.prefix_len()); + rest.len() > 0 && rest[0] == sep as u8 + } + Some(_) => true, + None => false + } + } + + fn is_ancestor_of(&self, other: &Path) -> bool { + if !self.equiv_prefix(other) { + false + } else if self.is_absolute() != other.is_absolute() || + self.is_vol_relative() != other.is_vol_relative() { + false + } else { + let mut ita = self.component_iter(); + let mut itb = other.component_iter(); + if "." == self.repr { + return itb.next() != Some(".."); + } + loop { + match (ita.next(), itb.next()) { + (None, _) => break, + (Some(a), Some(b)) if a == b => { loop }, + (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 { + 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 self.is_vol_relative() != base.is_vol_relative() { + if self.is_vol_relative() { + Some(self.clone()) + } else { + None + } + } else { + let mut ita = self.component_iter(); + let mut itb = base.component_iter(); + let mut comps = ~[]; + + let a_verb = self.is_verbatim(); + let b_verb = base.is_verbatim(); + 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(&mut ita); + 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 { + comps.push(".."); + } + comps.push(a); + if !a_verb { + comps.extend(&mut ita); + break; + } + } + } + } + Some(Path::from_str(comps.connect("\\"))) + } + } +} + +impl Path { + /// Returns a new Path from a byte vector + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the vector contains a NUL. + /// Raises the `str::not_utf8` condition if invalid UTF-8. + #[inline] + pub fn new(v: &[u8]) -> Path { + GenericPath::from_vec(v) + } + + /// Returns a new Path from a string + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the vector contains a NUL. + #[inline] + pub fn from_str(s: &str) -> Path { + GenericPath::from_str(s) + } + + /// Converts the Path into an owned byte vector + pub fn into_vec(self) -> ~[u8] { + self.repr.into_bytes() + } + + /// Converts the Path into an owned string + /// Returns an Option for compatibility with posix::Path, but the + /// return value will always be Some. + pub fn into_str(self) -> Option<~str> { + Some(self.repr) + } + + /// Returns a normalized string representation of a path, by removing all empty + /// components, and unnecessary . and .. components. + pub fn normalize(s: S) -> ~str { + let (_, path) = Path::normalize_(s); + path + } + + /// Returns an iterator that yields each component of the path in turn. + /// 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 component_iter<'a>(&'a self) -> ComponentIter<'a> { + let s = match self.prefix { + Some(_) => { + let plen = self.prefix_len(); + if self.repr.len() > plen && self.repr[plen] == sep as u8 { + self.repr.slice_from(plen+1) + } else { self.repr.slice_from(plen) } + } + None if self.repr[0] == sep as u8 => self.repr.slice_from(1), + None => self.repr.as_slice() + }; + let ret = s.split_terminator_iter(sep); + ret + } + + /// 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(&self) -> bool { + self.prefix.is_none() && self.repr[0] == sep as u8 + } + + /// 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(&self) -> bool { + self.prefix == Some(DiskPrefix) && !self.is_absolute() + } + + /// Returns the PathPrefix for this Path + #[inline] + pub fn prefix(&self) -> Option { + self.prefix + } + + /// Returns whether the prefix is a verbatim prefix, i.e. \\?\ + #[inline] + pub fn is_verbatim(&self) -> bool { + prefix_is_verbatim(self.prefix) + } + + fn equiv_prefix(&self, other: &Path) -> bool { + match (self.prefix, other.prefix) { + (Some(DiskPrefix), Some(VerbatimDiskPrefix)) => { + self.is_absolute() && + self.repr[0].to_ascii().eq_ignore_case(other.repr[4].to_ascii()) + } + (Some(VerbatimDiskPrefix), Some(DiskPrefix)) => { + other.is_absolute() && + self.repr[4].to_ascii().eq_ignore_case(other.repr[0].to_ascii()) + } + (Some(VerbatimDiskPrefix), Some(VerbatimDiskPrefix)) => { + self.repr[4].to_ascii().eq_ignore_case(other.repr[4].to_ascii()) + } + (Some(UNCPrefix(_,_)), Some(VerbatimUNCPrefix(_,_))) => { + self.repr.slice(2, self.prefix_len()) == other.repr.slice(8, other.prefix_len()) + } + (Some(VerbatimUNCPrefix(_,_)), Some(UNCPrefix(_,_))) => { + self.repr.slice(8, self.prefix_len()) == other.repr.slice(2, other.prefix_len()) + } + (None, None) => true, + (a, b) if a == b => { + self.repr.slice_to(self.prefix_len()) == other.repr.slice_to(other.prefix_len()) + } + _ => false + } + } + + fn normalize_(s: S) -> (Option, ~str) { + // make borrowck happy + let (prefix, val) = { + let prefix = parse_prefix(s.as_slice()); + let path = Path::normalize__(s.as_slice(), prefix); + (prefix, path) + }; + (prefix, match val { + None => s.into_owned(), + Some(val) => val + }) + } + + fn normalize__(s: &str, prefix: Option) -> Option<~str> { + 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 = s.into_owned(); + s.push_char(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[0] >= 'a' as u8 && s[0] <= 'z' as u8 { + comps = Some(~[]); + } + } + (false, Some(VerbatimDiskPrefix)) => { + if s[4] >= 'a' as u8 && s[0] <= 'z' as u8 { + comps = Some(~[]); + } + } + _ => () + } + 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 = s.slice_to(len).to_owned(); + s[0] = s[0].to_ascii().to_upper().to_byte(); + if is_abs { + s[2] = sep as u8; // normalize C:/ to C:\ + } + Some(s) + } + VerbatimDiskPrefix => { + let len = prefix_len(prefix) + is_abs as uint; + let mut s = s.slice_to(len).to_owned(); + s[4] = s[4].to_ascii().to_upper().to_byte(); + Some(s) + } + _ => { + let plen = prefix_len(prefix); + if s.len() > plen { + Some(s.slice_to(plen).to_owned()) + } else { None } + } + } + } else if is_abs && comps.is_empty() { + Some(str::from_char(sep)) + } else { + let prefix_ = s.slice_to(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 = str::with_capacity(n); + match prefix { + Some(DiskPrefix) => { + s.push_char(prefix_[0].to_ascii().to_upper().to_char()); + s.push_char(':'); + } + Some(VerbatimDiskPrefix) => { + s.push_str(prefix_.slice_to(4)); + s.push_char(prefix_[4].to_ascii().to_upper().to_char()); + s.push_str(prefix_.slice_from(5)); + } + Some(UNCPrefix(a,b)) => { + s.push_str("\\\\"); + s.push_str(prefix_.slice(2, a+2)); + s.push_char(sep); + s.push_str(prefix_.slice(3+a, 3+a+b)); + } + Some(_) => s.push_str(prefix_), + None => () + } + let mut it = comps.move_iter(); + if !is_abs { + match it.next() { + None => (), + Some(comp) => s.push_str(comp) + } + } + for comp in it { + s.push_char(sep); + s.push_str(comp); + } + Some(s) + } + } + } + } + } + + fn update_sepidx(&mut self) { + let s = if self.has_nonsemantic_trailing_slash() { + self.repr.slice_to(self.repr.len()-1) + } else { self.repr.as_slice() }; + let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep2 } + else { is_sep }); + 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 { + self.is_verbatim() && self.repr.len() > self.prefix_len()+1 && + self.repr[self.repr.len()-1] == sep as u8 + } + + fn update_normalized(&mut self, s: S) { + let (prefix, path) = Path::normalize_(s); + self.repr = path; + self.prefix = prefix; + self.update_sepidx(); + } +} + /// The standard path separator character -pub static sep: u8 = '\\' as u8; +pub static sep: char = '\\'; /// The alternative path separator character -pub static sep2: u8 = '/' as u8; +pub static sep2: char = '/'; + +/// Returns whether the given byte is a path separator. +/// Only allows the primary separator '\'; use is_sep2 to allow '/'. +#[inline] +pub fn is_sep(c: char) -> bool { + c == sep +} -/// Returns whether the given byte is a path separator +/// Returns whether the given byte is a path separator. +/// Allows both the primary separator '\' and the alternative separator '/'. #[inline] -pub fn is_sep(u: &u8) -> bool { - *u == sep || *u == sep2 +pub fn is_sep2(c: char) -> bool { + c == sep || c == sep2 +} + +/// Prefix types for Path +#[deriving(Eq, Clone, DeepClone)] +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 +} + +/// Internal function; only public for tests. Don't use. +// FIXME (#8169): Make private once visibility is fixed +pub fn parse_prefix<'a>(mut path: &'a str) -> Option { + if path.starts_with("\\\\") { + // \\ + path = path.slice_from(2); + if path.starts_with("?\\") { + // \\?\ + path = path.slice_from(2); + if path.starts_with("UNC\\") { + // \\?\UNC\server\share + path = path.slice_from(4); + let (idx_a, idx_b) = match parse_two_comps(path, is_sep) { + 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[1] == ':' as u8 { + let c = path[0]; + if c.is_ascii() && ::char::is_alphabetic(c as char) { + // \\?\C:\ path + return Some(VerbatimDiskPrefix); + } + } + let idx = idx.unwrap_or(path.len()); + return Some(VerbatimPrefix(idx)); + } + } else if path.starts_with(".\\") { + // \\.\path + path = path.slice_from(2); + let idx = path.find('\\').unwrap_or(path.len()); + return Some(DeviceNSPrefix(idx)); + } + match parse_two_comps(path, is_sep2) { + 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[1] == ':' as u8 { + // C: + let c = path[0]; + if c.is_ascii() && ::char::is_alphabetic(c as char) { + return Some(DiskPrefix); + } + } + return None; + + fn parse_two_comps<'a>(mut path: &'a str, f: &fn(char)->bool) -> Option<(uint, uint)> { + let idx_a = match path.find(|x| f(x)) { + None => return None, + Some(x) => x + }; + path = path.slice_from(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) -> (bool,Option<~[&'a str]>) { + let f = if !prefix_is_verbatim(prefix) { is_sep2 } else { is_sep }; + let is_abs = s.len() > prefix_len(prefix) && f(s.char_at(prefix_len(prefix))); + let s_ = s.slice_from(prefix_len(prefix)); + let s_ = if is_abs { s_.slice_from(1) } else { s_ }; + + if is_abs && s_.is_empty() { + return (is_abs, match prefix { + Some(DiskPrefix) | None => (if is_sep(s.char_at(prefix_len(prefix))) { None } + else { Some(~[]) }), + Some(_) => Some(~[]), // need to trim the trailing separator + }); + } + let mut comps: ~[&'a str] = ~[]; + let mut n_up = 0u; + let mut changed = false; + for comp in s_.split_iter(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_opt(); changed = true } + } else { comps.push(comp) } + } + if !changed && !prefix_is_verbatim(prefix) { + changed = s.find(is_sep2).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) + } +} + +// FIXME (#8169): Pull this into parent module once visibility works +#[inline(always)] +fn contains_nul(v: &[u8]) -> bool { + v.iter().any(|&x| x == 0) +} + +fn prefix_is_verbatim(p: Option) -> 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) -> 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 + } +} + +fn prefix_is_sep(p: Option, c: u8) -> bool { + c.is_ascii() && if !prefix_is_verbatim(p) { is_sep2(c as char) } + else { is_sep(c as char) } +} + +#[cfg(test)] +mod tests { + use super::*; + use option::{Some,None}; + use iter::Iterator; + use vec::Vector; + + 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); + } + ) + ) + + macro_rules! b( + ($($arg:expr),+) => ( + bytes!($($arg),+) + ) + ) + + #[test] + fn test_parse_prefix() { + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let path = $path; + let exp = $exp; + let res = parse_prefix(path); + assert!(res == exp, + "parse_prefix(\"%s\"): expected %?, found %?", path, exp, res); + } + ) + ) + + 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() { + t!(v: Path::new([]), b!(".")); + t!(v: Path::new(b!("\\")), b!("\\")); + t!(v: Path::new(b!("a\\b\\c")), b!("a\\b\\c")); + + t!(s: Path::from_str(""), "."); + t!(s: Path::from_str("\\"), "\\"); + t!(s: Path::from_str("hi"), "hi"); + t!(s: Path::from_str("hi\\"), "hi"); + t!(s: Path::from_str("\\lib"), "\\lib"); + t!(s: Path::from_str("\\lib\\"), "\\lib"); + t!(s: Path::from_str("hi\\there"), "hi\\there"); + t!(s: Path::from_str("hi\\there.txt"), "hi\\there.txt"); + t!(s: Path::from_str("/"), "\\"); + t!(s: Path::from_str("hi/"), "hi"); + t!(s: Path::from_str("/lib"), "\\lib"); + t!(s: Path::from_str("/lib/"), "\\lib"); + t!(s: Path::from_str("hi/there"), "hi\\there"); + + t!(s: Path::from_str("hi\\there\\"), "hi\\there"); + t!(s: Path::from_str("hi\\..\\there"), "there"); + t!(s: Path::from_str("hi/../there"), "there"); + t!(s: Path::from_str("..\\hi\\there"), "..\\hi\\there"); + t!(s: Path::from_str("\\..\\hi\\there"), "\\hi\\there"); + t!(s: Path::from_str("/../hi/there"), "\\hi\\there"); + t!(s: Path::from_str("foo\\.."), "."); + t!(s: Path::from_str("\\foo\\.."), "\\"); + t!(s: Path::from_str("\\foo\\..\\.."), "\\"); + t!(s: Path::from_str("\\foo\\..\\..\\bar"), "\\bar"); + t!(s: Path::from_str("\\.\\hi\\.\\there\\."), "\\hi\\there"); + t!(s: Path::from_str("\\.\\hi\\.\\there\\.\\.."), "\\hi"); + t!(s: Path::from_str("foo\\..\\.."), ".."); + t!(s: Path::from_str("foo\\..\\..\\.."), "..\\.."); + t!(s: Path::from_str("foo\\..\\..\\bar"), "..\\bar"); + + assert_eq!(Path::new(b!("foo\\bar")).into_vec(), b!("foo\\bar").to_owned()); + assert_eq!(Path::new(b!("\\foo\\..\\..\\bar")).into_vec(), + b!("\\bar").to_owned()); + assert_eq!(Path::from_str("foo\\bar").into_str(), Some(~"foo\\bar")); + assert_eq!(Path::from_str("\\foo\\..\\..\\bar").into_str(), Some(~"\\bar")); + + t!(s: Path::from_str("\\\\a"), "\\a"); + t!(s: Path::from_str("\\\\a\\"), "\\a"); + t!(s: Path::from_str("\\\\a\\b"), "\\\\a\\b"); + t!(s: Path::from_str("\\\\a\\b\\"), "\\\\a\\b"); + t!(s: Path::from_str("\\\\a\\b/"), "\\\\a\\b"); + t!(s: Path::from_str("\\\\\\b"), "\\b"); + t!(s: Path::from_str("\\\\a\\\\b"), "\\a\\b"); + t!(s: Path::from_str("\\\\a\\b\\c"), "\\\\a\\b\\c"); + t!(s: Path::from_str("\\\\server\\share/path"), "\\\\server\\share\\path"); + t!(s: Path::from_str("\\\\server/share/path"), "\\\\server\\share\\path"); + t!(s: Path::from_str("C:a\\b.txt"), "C:a\\b.txt"); + t!(s: Path::from_str("C:a/b.txt"), "C:a\\b.txt"); + t!(s: Path::from_str("z:\\a\\b.txt"), "Z:\\a\\b.txt"); + t!(s: Path::from_str("z:/a/b.txt"), "Z:\\a\\b.txt"); + t!(s: Path::from_str("ab:/a/b.txt"), "ab:\\a\\b.txt"); + t!(s: Path::from_str("C:\\"), "C:\\"); + t!(s: Path::from_str("C:"), "C:"); + t!(s: Path::from_str("q:"), "Q:"); + t!(s: Path::from_str("C:/"), "C:\\"); + t!(s: Path::from_str("C:\\foo\\.."), "C:\\"); + t!(s: Path::from_str("C:foo\\.."), "C:"); + t!(s: Path::from_str("C:\\a\\"), "C:\\a"); + t!(s: Path::from_str("C:\\a/"), "C:\\a"); + t!(s: Path::from_str("C:\\a\\b\\"), "C:\\a\\b"); + t!(s: Path::from_str("C:\\a\\b/"), "C:\\a\\b"); + t!(s: Path::from_str("C:a\\"), "C:a"); + t!(s: Path::from_str("C:a/"), "C:a"); + t!(s: Path::from_str("C:a\\b\\"), "C:a\\b"); + t!(s: Path::from_str("C:a\\b/"), "C:a\\b"); + t!(s: Path::from_str("\\\\?\\z:\\a\\b.txt"), "\\\\?\\z:\\a\\b.txt"); + t!(s: Path::from_str("\\\\?\\C:/a/b.txt"), "\\\\?\\C:/a/b.txt"); + t!(s: Path::from_str("\\\\?\\C:\\a/b.txt"), "\\\\?\\C:\\a/b.txt"); + t!(s: Path::from_str("\\\\?\\test\\a\\b.txt"), "\\\\?\\test\\a\\b.txt"); + t!(s: Path::from_str("\\\\?\\foo\\bar\\"), "\\\\?\\foo\\bar\\"); + t!(s: Path::from_str("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); + t!(s: Path::from_str("\\\\.\\"), "\\\\.\\"); + t!(s: Path::from_str("\\\\?\\UNC\\server\\share\\foo"), "\\\\?\\UNC\\server\\share\\foo"); + t!(s: Path::from_str("\\\\?\\UNC\\server/share"), "\\\\?\\UNC\\server/share\\"); + t!(s: Path::from_str("\\\\?\\UNC\\server"), "\\\\?\\UNC\\server\\"); + t!(s: Path::from_str("\\\\?\\UNC\\"), "\\\\?\\UNC\\\\"); + t!(s: Path::from_str("\\\\?\\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::from_str("\\\\.\\foo/bar"), "\\\\.\\foo/bar"); + t!(s: Path::from_str("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); + } + + #[test] + fn test_null_byte() { + use path2::null_byte::cond; + + let mut handled = false; + let mut p = do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("foo\\bar", 0)); + (b!("\\bar").to_owned()) + }).inside { + Path::new(b!("foo\\bar", 0)) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("\\bar")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.set_filename(b!("f", 0, "o")) + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("\\foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("null\\", 0, "\\byte")); + (b!("null\\byte").to_owned()) + }).inside { + p.set_dirname(b!("null\\", 0, "\\byte")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null\\byte\\foo")); + + handled = false; + do cond.trap(|v| { + handled = true; + assert_eq!(v.as_slice(), b!("f", 0, "o")); + (b!("foo").to_owned()) + }).inside { + p.push(b!("f", 0, "o")); + }; + assert!(handled); + assert_eq!(p.as_vec(), b!("null\\byte\\foo\\foo")); + } + + #[test] + fn test_null_byte_fail() { + use path2::null_byte::cond; + use task; + + macro_rules! t( + ($name:expr => $code:block) => ( + { + let mut t = task::task(); + t.supervised(); + t.name($name); + let res = do t.try $code; + assert!(res.is_err()); + } + ) + ) + + t!(~"new() w\\nul" => { + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + Path::new(b!("foo\\bar", 0)) + }; + }) + + t!(~"set_filename w\\nul" => { + let mut p = Path::new(b!("foo\\bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_filename(b!("foo", 0)) + }; + }) + + t!(~"set_dirname w\\nul" => { + let mut p = Path::new(b!("foo\\bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.set_dirname(b!("foo", 0)) + }; + }) + + t!(~"push w\\nul" => { + let mut p = Path::new(b!("foo\\bar")); + do cond.trap(|_| { + (b!("null", 0).to_owned()) + }).inside { + p.push(b!("foo", 0)) + }; + }) + } + + #[test] + #[should_fail] + fn test_not_utf8_fail() { + Path::new(b!("hello", 0x80, ".txt")); + } + + #[test] + fn test_components() { + macro_rules! t( + (s: $path:expr, $op:ident, $exp:expr) => ( + { + let path = Path::from_str($path); + assert_eq!(path.$op(), Some($exp)); + } + ); + (s: $path:expr, $op:ident, $exp:expr, opt) => ( + { + let path = Path::from_str($path); + let left = path.$op(); + assert_eq!(left, $exp); + } + ); + (v: $path:expr, $op:ident, $exp:expr) => ( + { + let path = Path::new($path); + assert_eq!(path.$op(), $exp); + } + ) + ) + + t!(v: b!("a\\b\\c"), filename, 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, ""); + t!(s: "\\", filename_str, ""); + t!(s: "..", filename_str, ""); + t!(s: "..\\..", filename_str, ""); + t!(s: "c:\\foo.txt", filename_str, "foo.txt"); + t!(s: "C:\\", filename_str, ""); + t!(s: "C:", filename_str, ""); + t!(s: "\\\\server\\share\\foo.txt", filename_str, "foo.txt"); + t!(s: "\\\\server\\share", filename_str, ""); + t!(s: "\\\\server", filename_str, "server"); + t!(s: "\\\\?\\bar\\foo.txt", filename_str, "foo.txt"); + t!(s: "\\\\?\\bar", filename_str, ""); + t!(s: "\\\\?\\", filename_str, ""); + t!(s: "\\\\?\\UNC\\server\\share\\foo.txt", filename_str, "foo.txt"); + t!(s: "\\\\?\\UNC\\server", filename_str, ""); + t!(s: "\\\\?\\UNC\\", filename_str, ""); + t!(s: "\\\\?\\C:\\foo.txt", filename_str, "foo.txt"); + t!(s: "\\\\?\\C:\\", filename_str, ""); + t!(s: "\\\\?\\C:", filename_str, ""); + t!(s: "\\\\?\\foo/bar", filename_str, ""); + t!(s: "\\\\?\\C:/foo", filename_str, ""); + t!(s: "\\\\.\\foo\\bar", filename_str, "bar"); + t!(s: "\\\\.\\foo", filename_str, ""); + t!(s: "\\\\.\\foo/bar", filename_str, ""); + t!(s: "\\\\.\\foo\\bar/baz", filename_str, "bar/baz"); + t!(s: "\\\\.\\", filename_str, ""); + 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, 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, ""); + t!(s: "\\", filestem_str, ""); + 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, ""); + t!(s: "..\\..", filestem_str, ""); + // 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::from_str(path); + let p2 = p1.clone(); + p1.push_str(join); + assert_eq!(p1, p2.join_str(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_str and join_str 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::from_str("\\\\?\\C:"); + assert_eq!(p.prefix(), Some(VerbatimPrefix(2))); + p.push_str("foo"); + assert_eq!(p.prefix(), Some(VerbatimDiskPrefix)); + assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo")); + + // and another with verbatim non-normalized paths + let mut p = Path::from_str("\\\\?\\C:\\a\\"); + p.push_str("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::from_str($path); + let push = Path::from_str($push); + p.push_path(&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: "\\\\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_pop() { + macro_rules! t( + (s: $path:expr, $left:expr, $right:expr) => ( + { + let pstr = $path; + let mut p = Path::from_str(pstr); + let file = p.pop_opt_str(); + let left = $left; + assert!(p.as_str() == Some(left), + "`%s`.pop() failed; expected remainder `%s`, found `%s`", + pstr, left, p.as_str().unwrap()); + let right = $right; + let res = file.map(|s| s.as_slice()); + assert!(res == right, "`%s`.pop() failed; expected `%?`, found `%?`", + pstr, right, res); + } + ); + (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( + { + let mut p = Path::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); + } + ); + (v: [$($path:expr),+], [$($left:expr),+], None) => ( + { + let mut p = Path::new(b!($($path),+)); + let file = p.pop_opt(); + assert_eq!(p.as_vec(), b!($($left),+)); + assert_eq!(file, None); + } + ) + ) + + t!(s: "a\\b\\c", "a\\b", Some("c")); + t!(s: "a", ".", Some("a")); + t!(s: ".", ".", None); + t!(s: "\\a", "\\", Some("a")); + t!(s: "\\", "\\", None); + t!(v: ["a\\b\\c"], ["a\\b"], Some("c")); + t!(v: ["a"], ["."], Some("a")); + t!(v: ["."], ["."], None); + t!(v: ["\\a"], ["\\"], Some("a")); + t!(v: ["\\"], ["\\"], None); + + t!(s: "C:\\a\\b", "C:\\a", Some("b")); + t!(s: "C:\\a", "C:\\", Some("a")); + t!(s: "C:\\", "C:\\", None); + t!(s: "C:a\\b", "C:a", Some("b")); + t!(s: "C:a", "C:", Some("a")); + t!(s: "C:", "C:", None); + t!(s: "\\\\server\\share\\a\\b", "\\\\server\\share\\a", Some("b")); + t!(s: "\\\\server\\share\\a", "\\\\server\\share", Some("a")); + t!(s: "\\\\server\\share", "\\\\server\\share", None); + t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", Some("c")); + t!(s: "\\\\?\\a\\b", "\\\\?\\a", Some("b")); + t!(s: "\\\\?\\a", "\\\\?\\a", None); + t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", Some("b")); + t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\", Some("a")); + t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\", None); + t!(s: "\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", Some("b")); + t!(s: "\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share", Some("a")); + t!(s: "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", None); + t!(s: "\\\\.\\a\\b\\c", "\\\\.\\a\\b", Some("c")); + t!(s: "\\\\.\\a\\b", "\\\\.\\a", Some("b")); + t!(s: "\\\\.\\a", "\\\\.\\a", None); + + t!(s: "\\\\?\\a\\b\\", "\\\\?\\a", Some("b")); + } + + #[test] + fn test_join() { + t!(s: Path::from_str("a\\b\\c").join_str(".."), "a\\b"); + t!(s: Path::from_str("\\a\\b\\c").join_str("d"), "\\a\\b\\c\\d"); + t!(s: Path::from_str("a\\b").join_str("c\\d"), "a\\b\\c\\d"); + t!(s: Path::from_str("a\\b").join_str("\\c\\d"), "\\c\\d"); + t!(s: Path::from_str(".").join_str("a\\b"), "a\\b"); + t!(s: Path::from_str("\\").join_str("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::from_str($path); + let join = Path::from_str($join); + let res = path.join_path(&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_path is implemented using push_path, so there's no need for + // the full set of prefix tests + } + + #[test] + fn test_with_helpers() { + macro_rules! t( + (s: $path:expr, $op:ident, $arg:expr, $res:expr) => ( + { + let pstr = $path; + let path = Path::from_str(pstr); + let arg = $arg; + let res = path.$op(arg); + let exp = $res; + assert!(res.as_str() == Some(exp), + "`%s`.%s(\"%s\"): Expected `%s`, found `%s`", + pstr, stringify!($op), arg, exp, res.as_str().unwrap()); + } + ) + ) + t!(s: "a\\b\\c", with_dirname_str, "d", "d\\c"); + t!(s: "a\\b\\c", with_dirname_str, "d\\e", "d\\e\\c"); + t!(s: "a\\b\\c", with_dirname_str, "", "c"); + t!(s: "a\\b\\c", with_dirname_str, "\\", "\\c"); + t!(s: "a\\b\\c", with_dirname_str, "/", "\\c"); + t!(s: "a\\b\\c", with_dirname_str, ".", "c"); + t!(s: "a\\b\\c", with_dirname_str, "..", "..\\c"); + t!(s: "\\", with_dirname_str, "foo", "foo"); + t!(s: "\\", with_dirname_str, "", "."); + t!(s: "\\foo", with_dirname_str, "bar", "bar\\foo"); + t!(s: "..", with_dirname_str, "foo", "foo"); + t!(s: "..\\..", with_dirname_str, "foo", "foo"); + t!(s: "..", with_dirname_str, "", "."); + t!(s: "..\\..", with_dirname_str, "", "."); + t!(s: ".", with_dirname_str, "foo", "foo"); + t!(s: "foo", with_dirname_str, "..", "..\\foo"); + t!(s: "foo", with_dirname_str, "..\\..", "..\\..\\foo"); + t!(s: "C:\\a\\b", with_dirname_str, "foo", "foo\\b"); + t!(s: "foo", with_dirname_str, "C:\\a\\b", "C:\\a\\b\\foo"); + t!(s: "C:a\\b", with_dirname_str, "\\\\server\\share", "\\\\server\\share\\b"); + t!(s: "a", with_dirname_str, "\\\\server\\share", "\\\\server\\share\\a"); + t!(s: "a\\b", with_dirname_str, "\\\\?\\", "\\\\?\\b"); + t!(s: "a\\b", with_dirname_str, "C:", "C:b"); + t!(s: "a\\b", with_dirname_str, "C:\\", "C:\\b"); + t!(s: "a\\b", with_dirname_str, "C:/", "C:\\b"); + t!(s: "C:\\", with_dirname_str, "foo", "foo"); + t!(s: "C:", with_dirname_str, "foo", "foo"); + t!(s: ".", with_dirname_str, "C:\\", "C:\\"); + t!(s: ".", with_dirname_str, "C:/", "C:\\"); + t!(s: "\\\\?\\C:\\foo", with_dirname_str, "C:\\", "C:\\foo"); + t!(s: "\\\\?\\C:\\", with_dirname_str, "bar", "bar"); + t!(s: "foo\\bar", with_dirname_str, "\\\\?\\C:\\baz", "\\\\?\\C:\\baz\\bar"); + t!(s: "\\\\?\\foo", with_dirname_str, "C:\\bar", "C:\\bar"); + t!(s: "\\\\?\\a\\foo", with_dirname_str, "C:\\bar", "C:\\bar\\foo"); + t!(s: "\\\\?\\a\\foo/bar", with_dirname_str, "C:\\baz", "C:\\baz\\foo\\bar"); + t!(s: "\\\\?\\UNC\\server\\share\\baz", with_dirname_str, "a", "a\\baz"); + t!(s: "foo\\bar", with_dirname_str, "\\\\?\\UNC\\server\\share\\baz", + "\\\\?\\UNC\\server\\share\\baz\\bar"); + t!(s: "\\\\.\\foo", with_dirname_str, "bar", "bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz", "baz\\bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz\\", "baz\\bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz/", "baz\\bar"); + + t!(s: "a\\b\\c", with_filename_str, "d", "a\\b\\d"); + t!(s: ".", with_filename_str, "foo", "foo"); + t!(s: "\\a\\b\\c", with_filename_str, "d", "\\a\\b\\d"); + t!(s: "\\", with_filename_str, "foo", "\\foo"); + t!(s: "\\a", with_filename_str, "foo", "\\foo"); + t!(s: "foo", with_filename_str, "bar", "bar"); + t!(s: "\\", with_filename_str, "foo\\", "\\foo"); + t!(s: "\\a", with_filename_str, "foo\\", "\\foo"); + t!(s: "a\\b\\c", with_filename_str, "", "a\\b"); + t!(s: "a\\b\\c", with_filename_str, ".", "a\\b"); + t!(s: "a\\b\\c", with_filename_str, "..", "a"); + t!(s: "\\a", with_filename_str, "", "\\"); + t!(s: "foo", with_filename_str, "", "."); + t!(s: "a\\b\\c", with_filename_str, "d\\e", "a\\b\\d\\e"); + t!(s: "a\\b\\c", with_filename_str, "\\d", "a\\b\\d"); + t!(s: "..", with_filename_str, "foo", "..\\foo"); + t!(s: "..\\..", with_filename_str, "foo", "..\\..\\foo"); + t!(s: "..", with_filename_str, "", ".."); + t!(s: "..\\..", with_filename_str, "", "..\\.."); + t!(s: "C:\\foo\\bar", with_filename_str, "baz", "C:\\foo\\baz"); + t!(s: "C:\\foo", with_filename_str, "bar", "C:\\bar"); + t!(s: "C:\\", with_filename_str, "foo", "C:\\foo"); + t!(s: "C:foo\\bar", with_filename_str, "baz", "C:foo\\baz"); + t!(s: "C:foo", with_filename_str, "bar", "C:bar"); + t!(s: "C:", with_filename_str, "foo", "C:foo"); + t!(s: "C:\\foo", with_filename_str, "", "C:\\"); + t!(s: "C:foo", with_filename_str, "", "C:"); + t!(s: "C:\\foo\\bar", with_filename_str, "..", "C:\\"); + t!(s: "C:\\foo", with_filename_str, "..", "C:\\"); + t!(s: "C:\\", with_filename_str, "..", "C:\\"); + t!(s: "C:foo\\bar", with_filename_str, "..", "C:"); + t!(s: "C:foo", with_filename_str, "..", "C:.."); + t!(s: "C:", with_filename_str, "..", "C:.."); + t!(s: "\\\\server\\share\\foo", with_filename_str, "bar", "\\\\server\\share\\bar"); + t!(s: "\\\\server\\share", with_filename_str, "foo", "\\\\server\\share\\foo"); + t!(s: "\\\\server\\share\\foo", with_filename_str, "", "\\\\server\\share"); + t!(s: "\\\\server\\share", with_filename_str, "", "\\\\server\\share"); + t!(s: "\\\\server\\share\\foo", with_filename_str, "..", "\\\\server\\share"); + t!(s: "\\\\server\\share", with_filename_str, "..", "\\\\server\\share"); + t!(s: "\\\\?\\C:\\foo\\bar", with_filename_str, "baz", "\\\\?\\C:\\foo\\baz"); + t!(s: "\\\\?\\C:\\foo", with_filename_str, "bar", "\\\\?\\C:\\bar"); + t!(s: "\\\\?\\C:\\", with_filename_str, "foo", "\\\\?\\C:\\foo"); + t!(s: "\\\\?\\C:\\foo", with_filename_str, "..", "\\\\?\\C:\\.."); + t!(s: "\\\\?\\foo\\bar", with_filename_str, "baz", "\\\\?\\foo\\baz"); + t!(s: "\\\\?\\foo", with_filename_str, "bar", "\\\\?\\foo\\bar"); + t!(s: "\\\\?\\", with_filename_str, "foo", "\\\\?\\\\foo"); + t!(s: "\\\\?\\foo\\bar", with_filename_str, "..", "\\\\?\\foo\\.."); + t!(s: "\\\\.\\foo\\bar", with_filename_str, "baz", "\\\\.\\foo\\baz"); + t!(s: "\\\\.\\foo", with_filename_str, "bar", "\\\\.\\foo\\bar"); + t!(s: "\\\\.\\foo\\bar", with_filename_str, "..", "\\\\.\\foo\\.."); + + t!(s: "hi\\there.txt", with_filestem_str, "here", "hi\\here.txt"); + t!(s: "hi\\there.txt", with_filestem_str, "", "hi\\.txt"); + t!(s: "hi\\there.txt", with_filestem_str, ".", "hi\\..txt"); + t!(s: "hi\\there.txt", with_filestem_str, "..", "hi\\...txt"); + t!(s: "hi\\there.txt", with_filestem_str, "\\", "hi\\.txt"); + t!(s: "hi\\there.txt", with_filestem_str, "foo\\bar", "hi\\foo\\bar.txt"); + t!(s: "hi\\there.foo.txt", with_filestem_str, "here", "hi\\here.txt"); + t!(s: "hi\\there", with_filestem_str, "here", "hi\\here"); + t!(s: "hi\\there", with_filestem_str, "", "hi"); + t!(s: "hi", with_filestem_str, "", "."); + t!(s: "\\hi", with_filestem_str, "", "\\"); + t!(s: "hi\\there", with_filestem_str, "..", "."); + t!(s: "hi\\there", with_filestem_str, ".", "hi"); + t!(s: "hi\\there.", with_filestem_str, "foo", "hi\\foo."); + t!(s: "hi\\there.", with_filestem_str, "", "hi"); + t!(s: "hi\\there.", with_filestem_str, ".", "."); + t!(s: "hi\\there.", with_filestem_str, "..", "hi\\..."); + t!(s: "\\", with_filestem_str, "foo", "\\foo"); + t!(s: ".", with_filestem_str, "foo", "foo"); + t!(s: "hi\\there..", with_filestem_str, "here", "hi\\here."); + t!(s: "hi\\there..", with_filestem_str, "", "hi"); + // filestem setter calls filename setter internally, no need for extended tests + + t!(s: "hi\\there.txt", with_extension_str, "exe", "hi\\there.exe"); + t!(s: "hi\\there.txt", with_extension_str, "", "hi\\there"); + t!(s: "hi\\there.txt", with_extension_str, ".", "hi\\there.."); + t!(s: "hi\\there.txt", with_extension_str, "..", "hi\\there..."); + t!(s: "hi\\there", with_extension_str, "txt", "hi\\there.txt"); + t!(s: "hi\\there", with_extension_str, ".", "hi\\there.."); + t!(s: "hi\\there", with_extension_str, "..", "hi\\there..."); + t!(s: "hi\\there.", with_extension_str, "txt", "hi\\there.txt"); + t!(s: "hi\\.foo", with_extension_str, "txt", "hi\\.foo.txt"); + t!(s: "hi\\there.txt", with_extension_str, ".foo", "hi\\there..foo"); + t!(s: "\\", with_extension_str, "txt", "\\"); + t!(s: "\\", with_extension_str, ".", "\\"); + t!(s: "\\", with_extension_str, "..", "\\"); + t!(s: ".", with_extension_str, "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::from_str(path); + p1.$set(arg); + let p2 = Path::from_str(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_dirname, with_dirname, b!("d")); + t!(v: b!("a\\b\\c"), set_dirname, with_dirname, b!("d\\e")); + t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, "d"); + t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, "d\\e"); + t!(s: "\\", set_dirname_str, with_dirname_str, "foo"); + t!(s: "\\foo", set_dirname_str, with_dirname_str, "bar"); + t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, ""); + t!(s: "..\\..", set_dirname_str, with_dirname_str, "x"); + t!(s: "foo", set_dirname_str, with_dirname_str, "..\\.."); + + 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_str, with_filename_str, "d"); + t!(s: "\\", set_filename_str, with_filename_str, "foo"); + t!(s: ".", set_filename_str, with_filename_str, "foo"); + t!(s: "a\\b", set_filename_str, with_filename_str, ""); + t!(s: "a", set_filename_str, with_filename_str, ""); + + t!(v: b!("hi\\there.txt"), set_filestem, with_filestem, b!("here")); + t!(s: "hi\\there.txt", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi\\there.", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi\\there", set_filestem_str, with_filestem_str, "here"); + t!(s: "hi\\there.txt", set_filestem_str, with_filestem_str, ""); + t!(s: "hi\\there", set_filestem_str, with_filestem_str, ""); + + t!(v: b!("hi\\there.txt"), set_extension, with_extension, b!("exe")); + t!(s: "hi\\there.txt", set_extension_str, with_extension_str, "exe"); + t!(s: "hi\\there.", set_extension_str, with_extension_str, "txt"); + t!(s: "hi\\there", set_extension_str, with_extension_str, "txt"); + t!(s: "hi\\there.txt", set_extension_str, with_extension_str, ""); + t!(s: "hi\\there", set_extension_str, with_extension_str, ""); + t!(s: ".", set_extension_str, with_extension_str, "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")), b!("c"), b!("a\\b"), b!("c"), None); + t!(s: Path::from_str("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); + t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); + t!(s: Path::from_str("\\"), Some(""), Some("\\"), Some(""), None); + t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None); + t!(s: Path::from_str("..\\.."), Some(""), Some("..\\.."), Some(""), None); + t!(s: Path::from_str("hi\\there.txt"), Some("there.txt"), Some("hi"), + Some("there"), Some("txt")); + t!(s: Path::from_str("hi\\there"), Some("there"), Some("hi"), Some("there"), None); + t!(s: Path::from_str("hi\\there."), Some("there."), Some("hi"), + Some("there"), Some("")); + t!(s: Path::from_str("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None); + t!(s: Path::from_str("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_file_path() { + t!(s: Path::from_str("hi\\there").dir_path(), "hi"); + t!(s: Path::from_str("hi").dir_path(), "."); + t!(s: Path::from_str("\\hi").dir_path(), "\\"); + t!(s: Path::from_str("\\").dir_path(), "\\"); + t!(s: Path::from_str("..").dir_path(), ".."); + t!(s: Path::from_str("..\\..").dir_path(), "..\\.."); + + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let path = $path; + let left = path.and_then_ref(|p| p.as_str()); + assert_eq!(left, $exp); + } + ); + ) + + t!(Path::from_str("hi\\there").file_path(), Some("there")); + t!(Path::from_str("hi").file_path(), Some("hi")); + t!(Path::from_str(".").file_path(), None); + t!(Path::from_str("\\").file_path(), None); + t!(Path::from_str("..").file_path(), None); + t!(Path::from_str("..\\..").file_path(), None); + + // dir_path and file_path are just dirname and filename interpreted as paths. + // No need for extended tests + } + + #[test] + fn test_is_absolute() { + macro_rules! t( + ($path:expr, $abs:expr, $vol:expr, $cwd:expr) => ( + { + let path = Path::from_str($path); + let (abs, vol, cwd) = ($abs, $vol, $cwd); + let b = path.is_absolute(); + assert!(b == abs, "Path '%s'.is_absolute(): expected %?, found %?", + path.as_str().unwrap(), abs, b); + let b = path.is_vol_relative(); + assert!(b == vol, "Path '%s'.is_vol_relative(): expected %?, found %?", + path.as_str().unwrap(), vol, b); + let b = path.is_cwd_relative(); + assert!(b == cwd, "Path '%s'.is_cwd_relative(): expected %?, found %?", + path.as_str().unwrap(), cwd, b); + } + ) + ) + t!("a\\b\\c", false, false, false); + t!("\\a\\b\\c", false, true, false); + t!("a", false, false, false); + t!("\\a", false, true, false); + t!(".", false, false, false); + t!("\\", false, true, false); + t!("..", false, false, false); + t!("..\\..", false, false, false); + t!("C:a\\b.txt", false, false, true); + t!("C:\\a\\b.txt", true, false, false); + t!("\\\\server\\share\\a\\b.txt", true, false, false); + t!("\\\\?\\a\\b\\c.txt", true, false, false); + t!("\\\\?\\C:\\a\\b.txt", true, false, false); + t!("\\\\?\\C:a\\b.txt", true, false, false); // NB: not equivalent to C:a\b.txt + t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false); + t!("\\\\.\\a\\b", true, false, false); + } + + #[test] + fn test_is_ancestor_of() { + macro_rules! t( + (s: $path:expr, $dest:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let dest = Path::from_str($dest); + let exp = $exp; + let res = path.is_ancestor_of(&dest); + assert!(res == exp, + "`%s`.is_ancestor_of(`%s`): Expected %?, found %?", + path.as_str().unwrap(), dest.as_str().unwrap(), exp, res); + } + ) + ) + + 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_path_relative_from() { + macro_rules! t( + (s: $path:expr, $other:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let other = Path::from_str($other); + let res = path.path_relative_from(&other); + let exp = $exp; + assert!(res.and_then_ref(|x| x.as_str()) == exp, + "`%s`.path_relative_from(`%s`): Expected %?, got %?", + path.as_str().unwrap(), other.as_str().unwrap(), exp, + res.and_then_ref(|x| x.as_str())); + } + ) + ) + + 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_component_iter() { + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&str] = $exp; + assert_eq!(comps.as_slice(), exp); + } + ); + (v: [$($arg:expr),+], $exp:expr) => ( + { + let path = Path::new(b!($($arg),+)); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&str] = $exp; + assert_eq!(comps.as_slice(), exp); + } + ) + ) + + t!(v: ["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", []); + } } From c16d7a439441ecfb8ac53deee070bb616fdc759e Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 25 Sep 2013 23:19:30 -0700 Subject: [PATCH 11/30] path2: Rename Path::new() to Path::from_vec() --- src/libstd/path2/posix.rs | 110 ++++++++++++++++++------------------ src/libstd/path2/windows.rs | 44 +++++++-------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index e873774b06776..66b4a6fb32672 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -271,7 +271,7 @@ impl GenericPath for Path { } } } - Some(Path::new(comps.connect_vec(&sep))) + Some(Path::from_vec(comps.connect_vec(&sep))) } } } @@ -283,7 +283,7 @@ impl Path { /// /// Raises the `null_byte` condition if the vector contains a NUL. #[inline] - pub fn new(v: &[u8]) -> Path { + pub fn from_vec(v: &[u8]) -> Path { GenericPath::from_vec(v) } @@ -433,12 +433,12 @@ mod tests { #[test] fn test_paths() { - t!(v: Path::new([]), 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", 0xff)), b!("a/b/c", 0xff)); - t!(v: Path::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); - let p = Path::new(b!("a/b/c", 0xff)); + t!(v: Path::from_vec([]), b!(".")); + t!(v: Path::from_vec(b!("/")), b!("/")); + t!(v: Path::from_vec(b!("a/b/c")), b!("a/b/c")); + t!(v: Path::from_vec(b!("a/b/c", 0xff)), b!("a/b/c", 0xff)); + t!(v: Path::from_vec(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); + let p = Path::from_vec(b!("a/b/c", 0xff)); assert_eq!(p.as_str(), None); t!(s: Path::from_str(""), "."); @@ -464,15 +464,15 @@ mod tests { t!(s: Path::from_str("foo/../../.."), "../.."); t!(s: Path::from_str("foo/../../bar"), "../bar"); - assert_eq!(Path::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); - assert_eq!(Path::new(b!("/foo/../../bar")).into_vec(), + assert_eq!(Path::from_vec(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); + assert_eq!(Path::from_vec(b!("/foo/../../bar")).into_vec(), b!("/bar").to_owned()); assert_eq!(Path::from_str("foo/bar").into_str(), Some(~"foo/bar")); assert_eq!(Path::from_str("/foo/../../bar").into_str(), Some(~"/bar")); - let p = Path::new(b!("foo/bar", 0x80)); + let p = Path::from_vec(b!("foo/bar", 0x80)); assert_eq!(p.as_str(), None); - assert_eq!(Path::new(b!("foo", 0xff, "/bar")).into_str(), None); + assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).into_str(), None); } #[test] @@ -485,7 +485,7 @@ mod tests { assert_eq!(v.as_slice(), b!("foo/bar", 0)); (b!("/bar").to_owned()) }).inside { - Path::new(b!("foo/bar", 0)) + Path::from_vec(b!("foo/bar", 0)) }; assert!(handled); assert_eq!(p.as_vec(), b!("/bar")); @@ -541,16 +541,16 @@ mod tests { ) ) - t!(~"new() w/nul" => { + t!(~"from_vec() w/nul" => { do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { - Path::new(b!("foo/bar", 0)) + Path::from_vec(b!("foo/bar", 0)) }; }) t!(~"set_filename w/nul" => { - let mut p = Path::new(b!("foo/bar")); + let mut p = Path::from_vec(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -559,7 +559,7 @@ mod tests { }) t!(~"set_dirname w/nul" => { - let mut p = Path::new(b!("foo/bar")); + let mut p = Path::from_vec(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -568,7 +568,7 @@ mod tests { }) t!(~"push w/nul" => { - let mut p = Path::new(b!("foo/bar")); + let mut p = Path::from_vec(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -595,7 +595,7 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); + let path = Path::from_vec($path); assert_eq!(path.$op(), $exp); } ) @@ -715,7 +715,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { - let mut p = Path::new(b!($($path),+)); + let mut p = Path::from_vec(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); @@ -723,7 +723,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], None) => ( { - let mut p = Path::new(b!($($path),+)); + let mut p = Path::from_vec(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); @@ -746,15 +746,15 @@ mod tests { t!(s: "/a", "/", Some("a")); t!(s: "/", "/", None); - assert_eq!(Path::new(b!("foo/bar", 0x80)).pop_opt_str(), None); - assert_eq!(Path::new(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); + assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_opt_str(), None); + assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); } #[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/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); + t!(v: Path::from_vec(b!("a/b/c")).join(b!("..")), b!("a/b")); + t!(v: Path::from_vec(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d")); + t!(v: Path::from_vec(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); t!(s: Path::from_str("a/b/c").join_str(".."), "a/b"); t!(s: Path::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); t!(s: Path::from_str("a/b").join_str("c/d"), "a/b/c/d"); @@ -786,10 +786,10 @@ mod tests { #[test] fn test_with_helpers() { - t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); - t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); - t!(v: Path::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); - t!(v: Path::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), + t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); + t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); + t!(v: Path::from_vec(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); + t!(v: Path::from_vec(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), b!("/", 0xcd, "/", 0x80)); t!(s: Path::from_str("a/b/c").with_dirname_str("d"), "d/c"); t!(s: Path::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); @@ -807,9 +807,9 @@ mod tests { t!(s: Path::from_str("foo").with_dirname_str(".."), "../foo"); t!(s: Path::from_str("foo").with_dirname_str("../.."), "../../foo"); - t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); - t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); - t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), + t!(v: Path::from_vec(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); + t!(v: Path::from_vec(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); + t!(v: Path::from_vec(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), b!("/", 0xff, "/", 0xcd)); t!(s: Path::from_str("a/b/c").with_filename_str("d"), "a/b/d"); t!(s: Path::from_str(".").with_filename_str("foo"), "foo"); @@ -831,12 +831,12 @@ mod tests { t!(s: Path::from_str("..").with_filename_str(""), ".."); t!(s: Path::from_str("../..").with_filename_str(""), "../.."); - t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), + t!(v: Path::from_vec(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt")); - t!(v: Path::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), + t!(v: Path::from_vec(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt", 0x80)); - t!(v: Path::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); - t!(v: Path::new(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); + t!(v: Path::from_vec(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); + t!(v: Path::from_vec(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); t!(s: Path::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); t!(s: Path::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); @@ -859,13 +859,13 @@ mod tests { t!(s: Path::from_str("hi/there..").with_filestem_str("here"), "hi/here."); t!(s: Path::from_str("hi/there..").with_filestem_str(""), "hi"); - t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), + t!(v: Path::from_vec(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), b!("hi/there", 0x80, ".exe")); - t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), + t!(v: Path::from_vec(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), b!("hi/there.", 0xff)); - t!(v: Path::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), + t!(v: Path::from_vec(b!("hi/there", 0x80)).with_extension(b!(0xff)), b!("hi/there", 0x80, ".", 0xff)); - t!(v: Path::new(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); + t!(v: Path::from_vec(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); t!(s: Path::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); t!(s: Path::from_str("hi/there.txt").with_extension_str(""), "hi/there"); t!(s: Path::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); @@ -899,9 +899,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::new(path); + let mut p1 = Path::from_vec(path); p1.$set(arg); - let p2 = Path::new(path); + let p2 = Path::from_vec(path); assert_eq!(p1, p2.$with(arg)); } ) @@ -968,9 +968,9 @@ mod tests { ) ) - t!(v: Path::new(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); - t!(v: Path::new(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); - t!(v: Path::new(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), + t!(v: Path::from_vec(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); + t!(v: Path::from_vec(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); + t!(v: Path::from_vec(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), b!("there"), Some(b!(0xff))); t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); @@ -985,16 +985,16 @@ mod tests { t!(s: Path::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::from_str("hi/..there"), Some("..there"), Some("hi"), Some("."), Some("there")); - t!(s: Path::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); - t!(s: Path::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); - t!(s: Path::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); - t!(s: Path::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); + t!(s: Path::from_vec(b!("a/b/", 0xff)), None, Some("a/b"), None, None); + t!(s: Path::from_vec(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); + t!(s: Path::from_vec(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); + t!(s: Path::from_vec(b!(0xff, "/b")), Some("b"), None, Some("b"), None); } #[test] fn test_dir_file_path() { - t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); - t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); + t!(v: Path::from_vec(b!("hi/there", 0x80)).dir_path(), b!("hi")); + t!(v: Path::from_vec(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); t!(s: Path::from_str("hi/there").dir_path(), "hi"); t!(s: Path::from_str("hi").dir_path(), "."); t!(s: Path::from_str("/hi").dir_path(), "/"); @@ -1019,8 +1019,8 @@ mod tests { ) ) - t!(v: Path::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); - t!(v: Path::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); + t!(v: Path::from_vec(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); + t!(v: Path::from_vec(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); t!(s: Path::from_str("hi/there").file_path(), Some("there")); t!(s: Path::from_str("hi").file_path(), Some("hi")); t!(s: Path::from_str(".").file_path(), None); @@ -1134,7 +1134,7 @@ mod tests { ); (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( { - let path = Path::new(b!($($arg),+)); + let path = Path::from_vec(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = [$(b!($($exp),*)),*]; assert_eq!(comps.as_slice(), exp); diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 1f51f2962f6b7..e4c194ba6caea 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -579,7 +579,7 @@ impl Path { /// Raises the `null_byte` condition if the vector contains a NUL. /// Raises the `str::not_utf8` condition if invalid UTF-8. #[inline] - pub fn new(v: &[u8]) -> Path { + pub fn from_vec(v: &[u8]) -> Path { GenericPath::from_vec(v) } @@ -1112,9 +1112,9 @@ mod tests { #[test] fn test_paths() { - t!(v: Path::new([]), b!(".")); - t!(v: Path::new(b!("\\")), b!("\\")); - t!(v: Path::new(b!("a\\b\\c")), b!("a\\b\\c")); + t!(v: Path::from_vec([]), b!(".")); + t!(v: Path::from_vec(b!("\\")), b!("\\")); + t!(v: Path::from_vec(b!("a\\b\\c")), b!("a\\b\\c")); t!(s: Path::from_str(""), "."); t!(s: Path::from_str("\\"), "\\"); @@ -1146,8 +1146,8 @@ mod tests { t!(s: Path::from_str("foo\\..\\..\\.."), "..\\.."); t!(s: Path::from_str("foo\\..\\..\\bar"), "..\\bar"); - assert_eq!(Path::new(b!("foo\\bar")).into_vec(), b!("foo\\bar").to_owned()); - assert_eq!(Path::new(b!("\\foo\\..\\..\\bar")).into_vec(), + assert_eq!(Path::from_vec(b!("foo\\bar")).into_vec(), b!("foo\\bar").to_owned()); + assert_eq!(Path::from_vec(b!("\\foo\\..\\..\\bar")).into_vec(), b!("\\bar").to_owned()); assert_eq!(Path::from_str("foo\\bar").into_str(), Some(~"foo\\bar")); assert_eq!(Path::from_str("\\foo\\..\\..\\bar").into_str(), Some(~"\\bar")); @@ -1211,7 +1211,7 @@ mod tests { assert_eq!(v.as_slice(), b!("foo\\bar", 0)); (b!("\\bar").to_owned()) }).inside { - Path::new(b!("foo\\bar", 0)) + Path::from_vec(b!("foo\\bar", 0)) }; assert!(handled); assert_eq!(p.as_vec(), b!("\\bar")); @@ -1267,16 +1267,16 @@ mod tests { ) ) - t!(~"new() w\\nul" => { + t!(~"from_vec() w\\nul" => { do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { - Path::new(b!("foo\\bar", 0)) + Path::from_vec(b!("foo\\bar", 0)) }; }) t!(~"set_filename w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); + let mut p = Path::from_vec(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1285,7 +1285,7 @@ mod tests { }) t!(~"set_dirname w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); + let mut p = Path::from_vec(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1294,7 +1294,7 @@ mod tests { }) t!(~"push w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); + let mut p = Path::from_vec(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1306,7 +1306,7 @@ mod tests { #[test] #[should_fail] fn test_not_utf8_fail() { - Path::new(b!("hello", 0x80, ".txt")); + Path::from_vec(b!("hello", 0x80, ".txt")); } #[test] @@ -1327,7 +1327,7 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::new($path); + let path = Path::from_vec($path); assert_eq!(path.$op(), $exp); } ) @@ -1534,7 +1534,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { - let mut p = Path::new(b!($($path),+)); + let mut p = Path::from_vec(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); @@ -1542,7 +1542,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], None) => ( { - let mut p = Path::new(b!($($path),+)); + let mut p = Path::from_vec(b!($($path),+)); let file = p.pop_opt(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); @@ -1594,8 +1594,8 @@ mod tests { t!(s: Path::from_str("a\\b").join_str("\\c\\d"), "\\c\\d"); t!(s: Path::from_str(".").join_str("a\\b"), "a\\b"); t!(s: Path::from_str("\\").join_str("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")); + t!(v: Path::from_vec(b!("a\\b\\c")).join(b!("..")), b!("a\\b")); + t!(v: Path::from_vec(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 } @@ -1790,9 +1790,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::new(path); + let mut p1 = Path::from_vec(path); p1.$set(arg); - let p2 = Path::new(path); + let p2 = Path::from_vec(path); assert_eq!(p1, p2.$with(arg)); } ) @@ -1858,7 +1858,7 @@ mod tests { ) ) - t!(v: Path::new(b!("a\\b\\c")), b!("c"), b!("a\\b"), b!("c"), None); + t!(v: Path::from_vec(b!("a\\b\\c")), b!("c"), b!("a\\b"), b!("c"), None); t!(s: Path::from_str("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); t!(s: Path::from_str("\\"), Some(""), Some("\\"), Some(""), None); @@ -2193,7 +2193,7 @@ mod tests { ); (v: [$($arg:expr),+], $exp:expr) => ( { - let path = Path::new(b!($($arg),+)); + let path = Path::from_vec(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&str] = $exp; assert_eq!(comps.as_slice(), exp); From 1dfe5088d85df13a172810d0df92c25dbcc37e98 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 00:54:30 -0700 Subject: [PATCH 12/30] path2: Add opt variants for from_vec/from_str --- src/libstd/path2/mod.rs | 23 +++++++++++++++++++++++ src/libstd/path2/posix.rs | 20 ++++++++++++++++++++ src/libstd/path2/windows.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index 183086a785bea..054a8a36eb5fb 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -81,6 +81,17 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } } + /// Creates a new Path from a byte vector, if possible. + /// The resulting Path will always be normalized. + #[inline] + fn from_vec_opt(path: &[u8]) -> Option { + if contains_nul(path) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_vec_unchecked(path) }) + } + } + /// Creates a new Path from a string. /// The resulting Path will always be normalized. /// @@ -97,6 +108,18 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } } + /// Creates a new Path from a string, if possible. + /// The resulting Path will always be normalized. + #[inline] + fn from_str_opt(path: &str) -> Option { + let v = path.as_bytes(); + if contains_nul(v) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_str_unchecked(path) }) + } + } + /// Creates a new Path from a CString. /// The resulting Path will always be normalized. /// diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index 66b4a6fb32672..fb39be7b6b171 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -287,6 +287,12 @@ impl Path { GenericPath::from_vec(v) } + /// Returns a new Path from a byte vector, if possible + #[inline] + pub fn from_vec_opt(v: &[u8]) -> Option { + GenericPath::from_vec_opt(v) + } + /// Returns a new Path from a string /// /// # Failure @@ -297,6 +303,12 @@ impl Path { GenericPath::from_str(s) } + /// Returns a new Path from a string, if possible + #[inline] + pub fn from_str_opt(s: &str) -> Option { + GenericPath::from_str_opt(s) + } + /// Converts the Path into an owned byte vector pub fn into_vec(self) -> ~[u8] { self.repr @@ -475,6 +487,14 @@ mod tests { assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).into_str(), None); } + #[test] + fn test_opt_paths() { + assert_eq!(Path::from_vec_opt(b!("foo/bar", 0)), None); + t!(v: Path::from_vec_opt(b!("foo/bar")).unwrap(), b!("foo/bar")); + assert_eq!(Path::from_str_opt("foo/bar\0"), None); + t!(s: Path::from_str_opt("foo/bar").unwrap(), "foo/bar"); + } + #[test] fn test_null_byte() { use path2::null_byte::cond; diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index e4c194ba6caea..747f803f08cea 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -328,6 +328,15 @@ impl GenericPathUnsafe for Path { } impl GenericPath for Path { + #[inline] + fn from_vec_opt(v: &[u8]) -> Option { + if contains_nul(v) || !str::is_utf8(v) { + None + } else { + Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) + } + } + /// See `GenericPath::as_str` for info. /// Always returns a `Some` value. #[inline] @@ -583,6 +592,12 @@ impl Path { GenericPath::from_vec(v) } + /// Returns a new Path from a byte vector, if possible + #[inline] + pub fn from_vec_opt(v: &[u8]) -> Option { + GenericPath::from_vec_opt(v) + } + /// Returns a new Path from a string /// /// # Failure @@ -593,6 +608,12 @@ impl Path { GenericPath::from_str(s) } + /// Returns a new Path from a string, if possible + #[inline] + pub fn from_str_opt(s: &str) -> Option { + GenericPath::from_str_opt(s) + } + /// Converts the Path into an owned byte vector pub fn into_vec(self) -> ~[u8] { self.repr.into_bytes() @@ -1201,6 +1222,15 @@ mod tests { t!(s: Path::from_str("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); } + #[test] + fn test_opt_paths() { + assert_eq!(Path::from_vec_opt(b!("foo\\bar", 0)), None); + assert_eq!(Path::from_vec_opt(b!("foo\\bar", 0x80)), None); + t!(v: Path::from_vec_opt(b!("foo\\bar")).unwrap(), b!("foo\\bar")); + assert_eq!(Path::from_str_opt("foo\\bar\0"), None); + t!(s: Path::from_str_opt("foo\\bar").unwrap(), "foo\\bar"); + } + #[test] fn test_null_byte() { use path2::null_byte::cond; From 33adf6dd6e0dd69af29ee11a1a7a2e11c998938c Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 02:10:16 -0700 Subject: [PATCH 13/30] path2: Implement .as_display_str() and .to_display_str() These functions are for working with a string representation of the path even if it's not UTF-8 encoded. They replace invalid UTF-8 sequences with the replacement char. --- src/libstd/path2/mod.rs | 47 ++++++++++++++++++++++++++++++++++++- src/libstd/path2/posix.rs | 26 ++++++++++++++++++++ src/libstd/path2/windows.rs | 22 +++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index 054a8a36eb5fb..5282eed40dba0 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -16,7 +16,7 @@ use clone::Clone; use iter::Iterator; use option::{Option, None, Some}; use str; -use str::StrSlice; +use str::{OwnedStr, Str, StrSlice}; use vec; use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; use vec::{ImmutableEqVector, ImmutableVector}; @@ -140,6 +140,51 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// Returns the path as a byte vector fn as_vec<'a>(&'a self) -> &'a [u8]; + /// Provides the path as a string + /// + /// If the path is not UTF-8, invalid sequences will be replaced with the unicode + /// replacement char. This involves allocation. + #[inline] + fn as_display_str(&self, f: &fn(&str) -> T) -> T { + match self.as_str() { + Some(s) => f(s), + None => { + let s = self.to_display_str(); + f(s.as_slice()) + } + } + } + + /// Returns the path as a string + /// + /// If the path is not UTF-8, invalid sequences will be replaced with the unicode + /// replacement char. This involves allocation. + /// + /// This is similar to `as_display_str()` except it will always allocate a new ~str. + fn to_display_str(&self) -> ~str { + // FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str + // This is a truly horrifically bad implementation, done as a functionality stopgap until + // we have a proper utf-8 decoder. I don't really want to write one here. + static REPLACEMENT_CHAR: char = '\uFFFD'; + + let mut v = self.as_vec(); + let mut s = str::with_capacity(v.len()); + while !v.is_empty() { + let w = str::utf8_char_width(v[0]); + if w == 0u { + s.push_char(REPLACEMENT_CHAR); + v = v.slice_from(1); + } else if v.len() < w || !str::is_utf8(v.slice_to(w)) { + s.push_char(REPLACEMENT_CHAR); + v = v.slice_from(1); + } else { + s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) }); + v = v.slice_from(w); + } + } + s + } + /// Returns the directory component of `self`, as a byte vector (with no trailing separator). /// If `self` has no directory component, returns ['.']. fn dirname<'a>(&'a self) -> &'a [u8]; diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index fb39be7b6b171..11579ba09564e 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -597,6 +597,32 @@ mod tests { }) } + #[test] + fn test_display_str() { + assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); + assert_eq!(Path::from_vec(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD"); + assert_eq!(Path::from_vec(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar"); + + let mut called = false; + do Path::from_str("foo").as_display_str |s| { + assert_eq!(s, "foo"); + called = true; + }; + assert!(called); + called = false; + do Path::from_vec(b!("foo", 0x80)).as_display_str |s| { + assert_eq!(s, "foo\uFFFD"); + called = true; + }; + assert!(called); + called = false; + do Path::from_vec(b!("foo", 0xff, "bar")).as_display_str |s| { + assert_eq!(s, "foo\uFFFDbar"); + called = true; + }; + assert!(called); + } + #[test] fn test_components() { macro_rules! t( diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 747f803f08cea..2545ba8ada2a5 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -349,6 +349,16 @@ impl GenericPath for Path { self.repr.as_bytes() } + #[inline] + fn as_display_str(&self, f: &fn(&str) -> T) -> T { + f(self.repr.as_slice()) + } + + #[inline] + fn to_display_str(&self) -> ~str { + self.repr.clone() + } + #[inline] fn dirname<'a>(&'a self) -> &'a [u8] { self.dirname_str().unwrap().as_bytes() @@ -1339,6 +1349,18 @@ mod tests { Path::from_vec(b!("hello", 0x80, ".txt")); } + #[test] + fn test_display_str() { + assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); + + let mut called = false; + do Path::from_str("foo").as_display_str |s| { + assert_eq!(s, "foo"); + called = true; + }; + assert!(called); + } + #[test] fn test_components() { macro_rules! t( From 7fcdcaeda7c264cde63df79ce5d189d91567f70b Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 12:58:56 -0700 Subject: [PATCH 14/30] path2: Implement IterBytes for Path --- src/libstd/path2/posix.rs | 8 ++++++++ src/libstd/path2/windows.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index 11579ba09564e..9157243244e16 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -19,6 +19,7 @@ use iter::{AdditiveIterator, Extendable, Iterator}; use option::{Option, None, Some}; use str; use str::Str; +use to_bytes::IterBytes; use util; use vec; use vec::CopyableVector; @@ -75,6 +76,13 @@ impl ToCStr for Path { } } +impl IterBytes for Path { + #[inline] + fn iter_bytes(&self, lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool { + self.repr.iter_bytes(lsb0, f) + } +} + impl GenericPathUnsafe for Path { unsafe fn from_vec_unchecked(path: &[u8]) -> Path { let path = Path::normalize(path); diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 2545ba8ada2a5..2ab8a2c0140ca 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -19,6 +19,7 @@ use iter::{AdditiveIterator, Extendable, Iterator}; use option::{Option, Some, None}; use str; use str::{OwnedStr, Str, StrVector}; +use to_bytes::IterBytes; use util; use vec::Vector; use super::{GenericPath, GenericPathUnsafe}; @@ -97,6 +98,13 @@ impl ToCStr for Path { } } +impl IterBytes for Path { + #[inline] + fn iter_bytes(&self, lsb0: bool, f: &fn(&[u8]) -> bool) -> bool { + self.repr.iter_bytes(lsb0, f) + } +} + impl GenericPathUnsafe for Path { /// See `GenericPathUnsafe::from_vec_unchecked`. /// From 56b96a3bfcbb37856c333a0042c365c314a54e6a Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 14:24:06 -0700 Subject: [PATCH 15/30] path2: Implement stat support --- src/libstd/path2/mod.rs | 221 ++++++++++++++++++++++++++++++++++++ src/libstd/path2/posix.rs | 99 ++++++++++++++++ src/libstd/path2/windows.rs | 73 ++++++++++++ 3 files changed, 393 insertions(+) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index 5282eed40dba0..5831b200e0988 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -650,3 +650,224 @@ pub trait GenericPathUnsafe { fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) } + +// FIXME (#9537): libc::stat should derive Default +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +mod stat { + #[allow(missing_doc)]; + + #[cfg(target_arch = "x86")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + __pad1: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + __pad2: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + __unused4: 0, + __unused5: 0, + } + } + } + + #[cfg(target_arch = "arm")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + __pad0: [0, ..4], + __st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + __pad3: [0, ..4], + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_ino: 0 + } + } + } + + #[cfg(target_arch = "mips")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_pad1: [0, ..3], + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_pad2: [0, ..2], + st_size: 0, + st_pad3: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_blksize: 0, + st_blocks: 0, + st_pad5: [0, ..14], + } + } + } + + #[cfg(target_arch = "x86_64")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_nlink: 0, + st_mode: 0, + st_uid: 0, + st_gid: 0, + __pad0: 0, + st_rdev: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + __unused: [0, 0, 0], + } + } + } +} + +#[cfg(target_os = "freebsd")] +mod stat { + #[allow(missing_doc)]; + + #[cfg(target_arch = "x86_64")] + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_size: 0, + st_blocks: 0, + st_blksize: 0, + st_flags: 0, + st_gen: 0, + st_lspare: 0, + st_birthtime: 0, + st_birthtime_nsec: 0, + __unused: [0, 0], + } + } + } +} + +#[cfg(target_os = "macos")] +mod stat { + #[allow(missing_doc)]; + + pub mod arch { + use libc; + + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_mode: 0, + st_nlink: 0, + st_ino: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_atime: 0, + st_atime_nsec: 0, + st_mtime: 0, + st_mtime_nsec: 0, + st_ctime: 0, + st_ctime_nsec: 0, + st_birthtime: 0, + st_birthtime_nsec: 0, + st_size: 0, + st_blocks: 0, + st_blksize: 0, + st_flags: 0, + st_gen: 0, + st_lspare: 0, + st_qspare: [0, 0], + } + } + } +} + +#[cfg(target_os = "win32")] +mod stat { + #[allow(missing_doc)]; + + pub mod arch { + use libc; + pub fn default_stat() -> libc::stat { + libc::stat { + st_dev: 0, + st_ino: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_size: 0, + st_atime: 0, + st_mtime: 0, + st_ctime: 0, + } + } + } +} diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index 9157243244e16..8e928f1df78cb 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -26,6 +26,9 @@ use vec::CopyableVector; use vec::{Vector, VectorVector}; use super::{GenericPath, GenericPathUnsafe}; +#[cfg(not(target_os = "win32"))] +use libc; + /// Iterator that yields successive components of a Path pub type ComponentIter<'self> = vec::SplitIterator<'self, u8>; @@ -422,6 +425,102 @@ fn contains_nul(v: &[u8]) -> bool { static dot_static: &'static [u8] = &'static ['.' as u8]; static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; +// Stat support +#[cfg(not(target_os = "win32"))] +impl Path { + /// Calls stat() on the represented file and returns the resulting libc::stat + pub fn stat(&self) -> Option { + #[fixed_stack_segment]; #[inline(never)]; + do self.with_c_str |buf| { + let mut st = super::stat::arch::default_stat(); + match unsafe { libc::stat(buf as *libc::c_char, &mut st) } { + 0 => Some(st), + _ => None + } + } + } + + /// Returns whether the represented file exists + pub fn exists(&self) -> bool { + match self.stat() { + None => false, + Some(_) => true + } + } + + /// Returns the filesize of the represented file + pub fn get_size(&self) -> Option { + match self.stat() { + None => None, + Some(st) => Some(st.st_size as i64) + } + } + + /// Returns the mode of the represented file + pub fn get_mode(&self) -> Option { + match self.stat() { + None => None, + Some(st) => Some(st.st_mode as uint) + } + } +} + +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +impl Path { + /// Returns the atime of the represented file, as (secs, nsecs) + pub fn get_atime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_atime as i64, st.st_atime_nsec as int)) + } + } + + /// Returns the mtime of the represented file, as (secs, nsecs) + pub fn get_mtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_mtime as i64, st.st_mtime_nsec as int)) + } + } + + /// Returns the ctime of the represented file, as (secs, nsecs) + pub fn get_ctime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_ctime as i64, st.st_ctime_nsec as int)) + } + } +} + +#[cfg(unix)] +impl Path { + /// Calls lstat() on the represented file and returns the resulting libc::stat + pub fn lstat(&self) -> Option { + #[fixed_stack_segment]; #[inline(never)]; + do self.with_c_str |buf| { + let mut st = super::stat::arch::default_stat(); + match unsafe { libc::lstat(buf, &mut st) } { + 0 => Some(st), + _ => None + } + } + } +} + +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "macos")] +impl Path { + /// Returns the birthtime of the represented file + pub fn get_birthtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_birthtime as i64, st.st_birthtime_nsec as int)) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 2ab8a2c0140ca..728adf25ae137 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -24,6 +24,9 @@ use util; use vec::Vector; use super::{GenericPath, GenericPathUnsafe}; +#[cfg(target_os = "win32")] +use libc; + /// Iterator that yields successive components of a Path pub type ComponentIter<'self> = str::CharSplitIterator<'self, char>; @@ -1060,6 +1063,76 @@ fn prefix_is_sep(p: Option, c: u8) -> bool { else { is_sep(c as char) } } +// Stat support +#[cfg(target_os = "win32")] +impl Path { + /// Calls stat() on the represented file and returns the resulting libc::stat + pub fn stat(&self) -> Option { + #[fixed_stack_segment]; #[inline(never)]; + do self.with_c_str |buf| { + let mut st = super::stat::arch::default_stat(); + match unsafe { libc::stat(buf, &mut st) } { + 0 => Some(st), + _ => None + } + } + } + + /// Returns whether the represented file exists + pub fn exists(&self) -> bool { + match self.stat() { + None => None, + Some(st) => Some(st.st_size as i64) + } + } + + /// Returns the filesize of the represented file + pub fn get_size(&self) -> Option { + match self.stat() { + None => None, + Some(st) => Some(st.st_size as i64) + } + } + + /// Returns the mode of the represented file + pub fn get_mode(&self) -> Option { + match self.stat() { + None => None, + Some(st) => Some(st.st_mode as uint) + } + } + + /// Returns the atime of the represented file, as (secs, nsecs) + /// + /// nsecs is always 0 + pub fn get_atime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_atime as i64, 0)) + } + } + + /// Returns the mtime of the represented file, as (secs, nsecs) + /// + /// nsecs is always 0 + pub fn get_mtime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_mtime as i64, 0)) + } + } + + /// Returns the ctime of the represented file, as (secs, nsecs) + /// + /// nsecs is always 0 + pub fn get_ctime(&self) -> Option<(i64, int)> { + match self.stat() { + None => None, + Some(st) => Some((st.st_ctime as i64, 0)) + } + } +} + #[cfg(test)] mod tests { use super::*; From 179f50f7c8a4071eb4e28e5b812106d6a3c148c7 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 14:38:26 -0700 Subject: [PATCH 16/30] path2: Rename pop_opt() to pop() and add each_parent() --- src/libstd/path2/mod.rs | 23 +++++++++-- src/libstd/path2/posix.rs | 68 +++++++++++++++++++++++++++---- src/libstd/path2/windows.rs | 79 +++++++++++++++++++++++++++++++++---- 3 files changed, 152 insertions(+), 18 deletions(-) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index 5831b200e0988..ba572cce78a6d 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -540,13 +540,13 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } /// Pops the last path component off of `self` and returns it. /// If `self` represents the root of the file hierarchy, None is returned. - fn pop_opt(&mut self) -> Option<~[u8]>; + fn pop(&mut self) -> Option<~[u8]>; /// Pops the last path component off of `self` and returns it as a string, if possible. /// `self` will still be modified even if None is returned. - /// See `pop_opt` for details. + /// See `pop` for details. #[inline] - fn pop_opt_str(&mut self) -> Option<~str> { - self.pop_opt().and_then(|v| str::from_utf8_owned_opt(v)) + fn pop_str(&mut self) -> Option<~str> { + self.pop().and_then(|v| str::from_utf8_owned_opt(v)) } /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). @@ -593,6 +593,21 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If `self` is absolute and `base` is relative, or on Windows if both /// paths refer to separate drives, an absolute path is returned. fn path_relative_from(&self, base: &Self) -> Option; + + /// Executes a callback with the receiver and every parent + fn each_parent(&self, f: &fn(&Self) -> bool) -> bool { + let mut p = self.clone(); + loop { + if !f(&p) { + return false; + } + let f = p.pop(); + if f.is_none() || bytes!("..") == f.unwrap() { + break; + } + } + true + } } /// A trait that represents the unsafe operations on GenericPaths diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index 8e928f1df78cb..233458dfc9b12 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -196,7 +196,7 @@ impl GenericPath for Path { } } - fn pop_opt(&mut self) -> Option<~[u8]> { + fn pop(&mut self) -> Option<~[u8]> { match self.sepidx { None if bytes!(".") == self.repr => None, None => { @@ -400,7 +400,7 @@ fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> { else if comp == bytes!("..") { 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_opt(); changed = true } + else { comps.pop(); changed = true } } else { comps.push(comp) } } if changed { @@ -861,7 +861,7 @@ mod tests { (s: $path:expr, $left:expr, $right:expr) => ( { let mut p = Path::from_str($path); - let file = p.pop_opt_str(); + let file = p.pop_str(); assert_eq!(p.as_str(), Some($left)); assert_eq!(file.map(|s| s.as_slice()), $right); } @@ -869,7 +869,7 @@ mod tests { (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { let mut p = Path::from_vec(b!($($path),+)); - let file = p.pop_opt(); + let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); } @@ -877,7 +877,7 @@ mod tests { (v: [$($path:expr),+], [$($left:expr),+], None) => ( { let mut p = Path::from_vec(b!($($path),+)); - let file = p.pop_opt(); + let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); } @@ -899,8 +899,8 @@ mod tests { t!(s: "/a", "/", Some("a")); t!(s: "/", "/", None); - assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_opt_str(), None); - assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar")); + assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_str(), None); + assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar")); } #[test] @@ -1310,4 +1310,58 @@ mod tests { t!(s: "../..", ["..", ".."]); t!(s: "../../foo", ["..", "..", "foo"]); } + + #[test] + fn test_each_parent() { + assert!(Path::from_str("/foo/bar").each_parent(|_| true)); + assert!(!Path::from_str("/foo/bar").each_parent(|_| false)); + + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let exp: &[&str] = $exp; + let mut comps = exp.iter().map(|&x|x); + do path.each_parent |p| { + let p = p.as_str(); + assert!(p.is_some()); + let e = comps.next(); + assert!(e.is_some()); + assert_eq!(p.unwrap(), e.unwrap()); + true + }; + assert!(comps.next().is_none()); + } + ); + (v: $path:expr, $exp:expr) => ( + { + let path = Path::from_vec($path); + let exp: &[&[u8]] = $exp; + let mut comps = exp.iter().map(|&x|x); + do path.each_parent |p| { + let p = p.as_vec(); + let e = comps.next(); + assert!(e.is_some()); + assert_eq!(p, e.unwrap()); + true + }; + assert!(comps.next().is_none()); + } + ) + ) + + t!(s: "/foo/bar", ["/foo/bar", "/foo", "/"]); + t!(s: "/foo/bar/baz", ["/foo/bar/baz", "/foo/bar", "/foo", "/"]); + t!(s: "/foo", ["/foo", "/"]); + t!(s: "/", ["/"]); + t!(s: "foo/bar/baz", ["foo/bar/baz", "foo/bar", "foo", "."]); + t!(s: "foo/bar", ["foo/bar", "foo", "."]); + t!(s: "foo", ["foo", "."]); + t!(s: ".", ["."]); + t!(s: "..", [".."]); + t!(s: "../../foo", ["../../foo", "../.."]); + + t!(v: b!("foo/bar", 0x80), [b!("foo/bar", 0x80), b!("foo"), b!(".")]); + t!(v: b!(0xff, "/bar"), [b!(0xff, "/bar"), b!(0xff), b!(".")]); + } } diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index 728adf25ae137..c23dc86d7dfb2 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -446,11 +446,11 @@ impl GenericPath for Path { } #[inline] - fn pop_opt(&mut self) -> Option<~[u8]> { - self.pop_opt_str().map_move(|s| s.into_bytes()) + fn pop(&mut self) -> Option<~[u8]> { + self.pop_str().map_move(|s| s.into_bytes()) } - fn pop_opt_str(&mut self) -> Option<~str> { + fn pop_str(&mut self) -> Option<~str> { match self.sepidx_or_prefix_len() { None if "." == self.repr => None, None => { @@ -599,6 +599,21 @@ impl GenericPath for Path { Some(Path::from_str(comps.connect("\\"))) } } + + /// Executes a callback with the receiver and every parent + fn each_parent(&self, f: &fn(&Path) -> bool) -> bool { + let mut p = self.clone(); + loop { + if !f(&p) { + return false; + } + let f = p.pop(); + if f.is_none() || (!p.is_verbatim() && bytes!("..") == f.unwrap()) { + break; + } + } + true + } } impl Path { @@ -1013,7 +1028,7 @@ fn normalize_helper<'a>(s: &'a str, prefix: Option) -> (bool,Option< }; 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_opt(); changed = true } + else { comps.pop(); changed = true } } else { comps.push(comp) } } if !changed && !prefix_is_verbatim(prefix) { @@ -1654,7 +1669,7 @@ mod tests { { let pstr = $path; let mut p = Path::from_str(pstr); - let file = p.pop_opt_str(); + let file = p.pop_str(); let left = $left; assert!(p.as_str() == Some(left), "`%s`.pop() failed; expected remainder `%s`, found `%s`", @@ -1668,7 +1683,7 @@ mod tests { (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { let mut p = Path::from_vec(b!($($path),+)); - let file = p.pop_opt(); + let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); } @@ -1676,7 +1691,7 @@ mod tests { (v: [$($path:expr),+], [$($left:expr),+], None) => ( { let mut p = Path::from_vec(b!($($path),+)); - let file = p.pop_opt(); + let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); } @@ -2373,4 +2388,54 @@ mod tests { t!(s: "\\\\.\\foo\\bar", ["bar"]); t!(s: "\\\\.\\foo", []); } + + #[test] + fn test_each_parent() { + assert!(Path::from_str("/foo/bar").each_parent(|_| true)); + assert!(!Path::from_str("/foo/bar").each_parent(|_| false)); + + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let exp: &[&str] = $exp; + let mut comps = exp.iter().map(|&x|x); + do path.each_parent |p| { + let p = p.as_str(); + assert!(p.is_some()); + let e = comps.next(); + assert!(e.is_some()); + assert_eq!(p.unwrap(), e.unwrap()); + true + }; + assert!(comps.next().is_none()); + } + ) + ) + + t!(s: "\\foo\\bar", ["\\foo\\bar", "\\foo", "\\"]); + t!(s: "\\foo\\bar\\baz", ["\\foo\\bar\\baz", "\\foo\\bar", "\\foo", "\\"]); + t!(s: "\\foo", ["\\foo", "\\"]); + t!(s: "\\", ["\\"]); + t!(s: "foo\\bar\\baz", ["foo\\bar\\baz", "foo\\bar", "foo", "."]); + t!(s: "foo\\bar", ["foo\\bar", "foo", "."]); + t!(s: "foo", ["foo", "."]); + t!(s: ".", ["."]); + t!(s: "..", [".."]); + t!(s: "..\\..\\foo", ["..\\..\\foo", "..\\.."]); + t!(s: "C:\\a\\b", ["C:\\a\\b", "C:\\a", "C:\\"]); + t!(s: "C:\\", ["C:\\"]); + t!(s: "C:a\\b", ["C:a\\b", "C:a", "C:"]); + t!(s: "C:", ["C:"]); + t!(s: "C:..\\..\\a", ["C:..\\..\\a", "C:..\\.."]); + t!(s: "C:..", ["C:.."]); + t!(s: "\\\\a\\b\\c", ["\\\\a\\b\\c", "\\\\a\\b"]); + t!(s: "\\\\a\\b", ["\\\\a\\b"]); + t!(s: "\\\\?\\a\\b\\c", ["\\\\?\\a\\b\\c", "\\\\?\\a\\b", "\\\\?\\a"]); + t!(s: "\\\\?\\C:\\a\\b", ["\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", "\\\\?\\C:\\"]); + t!(s: "\\\\?\\UNC\\a\\b\\c", ["\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b"]); + t!(s: "\\\\.\\a\\b\\c", ["\\\\.\\a\\b\\c", "\\\\.\\a\\b", "\\\\.\\a"]); + t!(s: "\\\\?\\a\\..\\b\\.\\c/d", ["\\\\?\\a\\..\\b\\.\\c/d", "\\\\?\\a\\..\\b\\.", + "\\\\?\\a\\..\\b", "\\\\?\\a\\..", "\\\\?\\a"]); + } } From 3a2735cb11efc997aa02646c13c008a883de6792 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 17:10:55 -0700 Subject: [PATCH 17/30] path2: Convert typedefs to `pub use`s Turns out you can't call static methods on typedefs. Use `pub use` instead to work around this issue. --- src/libstd/path2/mod.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index ba572cce78a6d..e385cea51f9d0 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -21,38 +21,38 @@ use vec; use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; use vec::{ImmutableEqVector, ImmutableVector}; -pub mod posix; -pub mod windows; - /// Typedef for POSIX file paths. /// See `posix::Path` for more info. -pub type PosixPath = posix::Path; +pub use PosixPath = self::posix::Path; /// Typedef for Windows file paths. /// See `windows::Path` for more info. -pub type WindowsPath = windows::Path; +pub use WindowsPath = self::windows::Path; /// Typedef for the platform-native path type #[cfg(unix)] -pub type Path = PosixPath; +pub use Path = self::posix::Path; /// Typedef for the platform-native path type #[cfg(windows)] -pub type Path = WindowsPath; +pub use Path = self::windows::Path; /// Typedef for the POSIX path component iterator. /// See `posix::ComponentIter` for more info. -pub type PosixComponentIter<'self> = posix::ComponentIter<'self>; +pub use PosixComponentIter = self::posix::ComponentIter; -// /// Typedef for the Windows path component iterator. -// /// See `windows::ComponentIter` for more info. -// pub type WindowsComponentIter<'self> = windows::ComponentIter<'self>; +/// Typedef for the Windows path component iterator. +/// See `windows::ComponentIter` for more info. +pub use WindowsComponentIter = self::windows::ComponentIter; /// Typedef for the platform-native component iterator #[cfg(unix)] -pub type ComponentIter<'self> = PosixComponentIter<'self>; -// /// Typedef for the platform-native component iterator -//#[cfg(windows)] -//pub type ComponentIter<'self> = WindowsComponentIter<'self>; +pub use ComponentIter = self::posix::ComponentIter; +/// Typedef for the platform-native component iterator +#[cfg(windows)] +pub use ComponentIter = self::windows::ComponentIter; + +pub mod posix; +pub mod windows; // Condition that is raised when a NUL is found in a byte vector given to a Path function condition! { From 6741241f4046aea4014b1a23618593fb481c8606 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 1 Oct 2013 14:03:41 -0700 Subject: [PATCH 18/30] path2: Add format helpers .display() and .filename_display() These methods return an object that can be formatted using {} to print display strings. Path itself does not implement fmt::Default to avoid accidental usage of display strings in incorrect places (e.g. process arguments). --- src/libstd/path2/mod.rs | 41 +++++++++++++++++++++++++++++++++++++ src/libstd/path2/posix.rs | 23 +++++++++++++++++++++ src/libstd/path2/windows.rs | 19 +++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/src/libstd/path2/mod.rs b/src/libstd/path2/mod.rs index e385cea51f9d0..38a48866e2248 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path2/mod.rs @@ -13,6 +13,7 @@ use container::Container; use c_str::CString; use clone::Clone; +use fmt; use iter::Iterator; use option::{Option, None, Some}; use str; @@ -185,6 +186,21 @@ pub trait GenericPath: Clone + GenericPathUnsafe { s } + /// Returns an object that implements `fmt::Default` for printing paths + /// + /// This will print the equivalent of `to_display_str()` when used with a {} format parameter. + fn display<'a>(&'a self) -> Display<'a, Self> { + Display{ path: self } + } + + /// Returns an object that implements `fmt::Default` for printing filenames + /// + /// This will print the equivalent of `to_filename_display_str()` when used with a {} + /// format parameter. If there is no filename, nothing will be printed. + fn filename_display<'a>(&'a self) -> FilenameDisplay<'a, Self> { + FilenameDisplay{ path: self } + } + /// Returns the directory component of `self`, as a byte vector (with no trailing separator). /// If `self` has no directory component, returns ['.']. fn dirname<'a>(&'a self) -> &'a [u8]; @@ -661,6 +677,31 @@ pub trait GenericPathUnsafe { } } +/// Helper struct for printing paths with format!() +pub struct Display<'self, P> { + priv path: &'self P +} +/// Helper struct for printing filenames with format!() +pub struct FilenameDisplay<'self, P> { + priv path: &'self P +} + +impl<'self, P: GenericPath> fmt::Default for Display<'self, P> { + fn fmt(d: &Display

, f: &mut fmt::Formatter) { + do d.path.with_display_str |s| { + f.pad(s) + } + } +} + +impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> { + fn fmt(d: &FilenameDisplay

, f: &mut fmt::Formatter) { + do d.path.with_filename_display_str |s| { + f.pad(s.unwrap_or("")) + } + } +} + #[inline(always)] fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) diff --git a/src/libstd/path2/posix.rs b/src/libstd/path2/posix.rs index 233458dfc9b12..4aa2eb531e5c3 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path2/posix.rs @@ -730,6 +730,29 @@ mod tests { assert!(called); } + #[test] + fn test_display() { + macro_rules! t( + ($path:expr, $exp:expr, $expf:expr) => ( + { + let path = Path::from_vec($path); + let f = format!("{}", path.display()); + assert_eq!(f.as_slice(), $exp); + let f = format!("{}", path.filename_display()); + assert_eq!(f.as_slice(), $expf); + } + ) + ) + + t!(b!("foo"), "foo", "foo"); + t!(b!("foo/bar"), "foo/bar", "bar"); + t!(b!("/"), "/", ""); + t!(b!("foo", 0xff), "foo\uFFFD", "foo\uFFFD"); + t!(b!("foo", 0xff, "/bar"), "foo\uFFFD/bar", "bar"); + t!(b!("foo/", 0xff, "bar"), "foo/\uFFFDbar", "\uFFFDbar"); + t!(b!(0xff, "foo/bar", 0xff), "\uFFFDfoo/bar\uFFFD", "bar\uFFFD"); + } + #[test] fn test_components() { macro_rules! t( diff --git a/src/libstd/path2/windows.rs b/src/libstd/path2/windows.rs index c23dc86d7dfb2..7cc7a5610bde5 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path2/windows.rs @@ -1457,6 +1457,25 @@ mod tests { assert!(called); } + #[test] + fn test_display() { + macro_rules! t( + ($path:expr, $exp:expr, $expf:expr) => ( + { + let path = Path::from_str($path); + let f = format!("{}", path.display()); + assert_eq!(f.as_slice(), $exp); + let f = format!("{}", path.filename_display()); + assert_eq!(f.as_slice(), $expf); + } + ) + ) + + t!("foo", "foo", "foo"); + t!("foo\\bar", "foo\\bar", "bar"); + t!("\\", "\\", ""); + } + #[test] fn test_components() { macro_rules! t( From 73d3d00ec437f87ac665b4e4da3bedec8ce4f9ef Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Thu, 26 Sep 2013 17:21:59 -0700 Subject: [PATCH 19/30] path2: Replace the path module outright Remove the old path. Rename path2 to path. Update all clients for the new path. Also make some miscellaneous changes to the Path APIs to help the adoption process. --- src/compiletest/compiletest.rs | 36 +- src/compiletest/header.rs | 4 +- src/compiletest/runtest.rs | 134 +- src/libextra/fileinput.rs | 12 +- src/libextra/glob.rs | 83 +- src/libextra/tempfile.rs | 2 +- src/libextra/terminfo/searcher.rs | 42 +- src/libextra/test.rs | 12 +- src/libextra/workcache.rs | 17 +- src/librustc/back/link.rs | 73 +- src/librustc/back/rpath.rs | 110 +- src/librustc/driver/driver.rs | 30 +- src/librustc/metadata/creader.rs | 11 +- src/librustc/metadata/filesearch.rs | 65 +- src/librustc/metadata/loader.rs | 16 +- src/librustc/middle/trans/debuginfo.rs | 6 +- src/librustc/rustc.rs | 6 +- src/librustdoc/core.rs | 2 +- src/librustdoc/html/render.rs | 67 +- src/librustdoc/plugins.rs | 2 +- src/librustdoc/rustdoc.rs | 14 +- src/librusti/rusti.rs | 33 +- src/librustpkg/api.rs | 12 +- src/librustpkg/context.rs | 14 +- src/librustpkg/installed_packages.rs | 49 +- src/librustpkg/package_id.rs | 27 +- src/librustpkg/package_source.rs | 120 +- src/librustpkg/path_util.rs | 106 +- src/librustpkg/rustpkg.rs | 141 +- src/librustpkg/search.rs | 2 +- src/librustpkg/source_control.rs | 33 +- src/librustpkg/target.rs | 4 +- src/librustpkg/tests.rs | 757 +++++---- src/librustpkg/util.rs | 79 +- src/librustpkg/version.rs | 31 +- src/librustpkg/workcache_support.rs | 15 +- src/librustpkg/workspace.rs | 23 +- src/libstd/io.rs | 35 +- src/libstd/os.rs | 274 +-- src/libstd/path.rs | 1507 ----------------- src/libstd/{path2 => path}/mod.rs | 385 +++-- src/libstd/{path2 => path}/posix.rs | 412 ++++- src/libstd/{path2 => path}/windows.rs | 451 ++++- src/libstd/prelude.rs | 5 +- src/libstd/rt/io/file.rs | 46 +- src/libstd/rt/io/support.rs | 4 +- src/libstd/rt/test.rs | 8 +- src/libstd/rt/uv/file.rs | 12 +- src/libstd/rt/uv/uvio.rs | 14 +- src/libstd/run.rs | 8 +- src/libstd/std.rs | 1 - src/libstd/unstable/dynamic_lib.rs | 2 +- src/libsyntax/ext/source_util.rs | 16 +- src/libsyntax/parse/mod.rs | 3 +- src/libsyntax/parse/parser.rs | 37 +- src/test/bench/core-std.rs | 4 +- src/test/bench/shootout-fasta.rs | 2 +- src/test/bench/shootout-k-nucleotide-pipes.rs | 4 +- src/test/run-pass/glob-std.rs | 6 +- src/test/run-pass/issue-3424.rs | 2 +- src/test/run-pass/rename-directory.rs | 14 +- src/test/run-pass/stat.rs | 4 +- src/test/run-pass/tempfile.rs | 50 +- 63 files changed, 2504 insertions(+), 2992 deletions(-) delete mode 100644 src/libstd/path.rs rename src/libstd/{path2 => path}/mod.rs (70%) rename src/libstd/{path2 => path}/posix.rs (76%) rename src/libstd/{path2 => path}/windows.rs (85%) diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index b5057013328d3..cc99f0d67d234 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -102,15 +102,15 @@ pub fn parse_config(args: ~[~str]) -> config { } fn opt_path(m: &getopts::Matches, nm: &str) -> Path { - Path(m.opt_str(nm).unwrap()) + Path::from_str(m.opt_str(nm).unwrap()) } config { compile_lib_path: matches.opt_str("compile-lib-path").unwrap(), run_lib_path: matches.opt_str("run-lib-path").unwrap(), rustc_path: opt_path(matches, "rustc-path"), - clang_path: matches.opt_str("clang-path").map(|s| Path(s)), - llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path(s)), + clang_path: matches.opt_str("clang-path").map(|s| Path::from_str(s)), + llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::from_str(s)), src_base: opt_path(matches, "src-base"), build_base: opt_path(matches, "build-base"), aux_base: opt_path(matches, "aux-base"), @@ -123,10 +123,10 @@ pub fn parse_config(args: ~[~str]) -> config { } else { None }, - logfile: matches.opt_str("logfile").map(|s| Path(s)), - save_metrics: matches.opt_str("save-metrics").map(|s| Path(s)), + logfile: matches.opt_str("logfile").map(|s| Path::from_str(s)), + save_metrics: matches.opt_str("save-metrics").map(|s| Path::from_str(s)), ratchet_metrics: - matches.opt_str("ratchet-metrics").map(|s| Path(s)), + matches.opt_str("ratchet-metrics").map(|s| Path::from_str(s)), ratchet_noise_percent: matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::(s)), runtool: matches.opt_str("runtool"), @@ -155,9 +155,9 @@ pub fn log_config(config: &config) { logv(c, format!("configuration:")); logv(c, format!("compile_lib_path: {}", config.compile_lib_path)); logv(c, format!("run_lib_path: {}", config.run_lib_path)); - logv(c, format!("rustc_path: {}", config.rustc_path.to_str())); - logv(c, format!("src_base: {}", config.src_base.to_str())); - logv(c, format!("build_base: {}", config.build_base.to_str())); + logv(c, format!("rustc_path: {}", config.rustc_path.display())); + logv(c, format!("src_base: {}", config.src_base.display())); + logv(c, format!("build_base: {}", config.build_base.display())); logv(c, format!("stage_id: {}", config.stage_id)); logv(c, format!("mode: {}", mode_str(config.mode))); logv(c, format!("run_ignored: {}", config.run_ignored)); @@ -245,12 +245,12 @@ pub fn test_opts(config: &config) -> test::TestOpts { pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] { debug2!("making tests from {}", - config.src_base.to_str()); + config.src_base.display()); let mut tests = ~[]; let dirs = os::list_dir_path(&config.src_base); for file in dirs.iter() { let file = file.clone(); - debug2!("inspecting file {}", file.to_str()); + debug2!("inspecting file {}", file.display()); if is_test(config, &file) { let t = do make_test(config, &file) { match config.mode { @@ -272,7 +272,7 @@ pub fn is_test(config: &config, testfile: &Path) -> bool { _ => ~[~".rc", ~".rs"] }; let invalid_prefixes = ~[~".", ~"#", ~"~"]; - let name = testfile.filename().unwrap(); + let name = testfile.filename_str().unwrap(); let mut valid = false; @@ -303,9 +303,9 @@ pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName { // Try to elide redundant long paths fn shorten(path: &Path) -> ~str { - let filename = path.filename(); - let p = path.pop(); - let dir = p.filename(); + let filename = path.filename_str(); + let p = path.dir_path(); + let dir = p.filename_str(); format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or("")) } @@ -317,13 +317,15 @@ pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName { pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn { use std::cell::Cell; let config = Cell::new((*config).clone()); - let testfile = Cell::new(testfile.to_str()); + // FIXME (#9639): This needs to handle non-utf8 paths + let testfile = Cell::new(testfile.as_str().unwrap().to_owned()); test::DynTestFn(|| { runtest::run(config.take(), testfile.take()) }) } pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn { use std::cell::Cell; let config = Cell::new((*config).clone()); - let testfile = Cell::new(testfile.to_str()); + // FIXME (#9639): This needs to handle non-utf8 paths + let testfile = Cell::new(testfile.as_str().unwrap().to_owned()); test::DynMetricFn(|mm| { runtest::run_metrics(config.take(), testfile.take(), mm) }) } diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 730df66af23e6..bf68371c7d2ef 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -161,10 +161,10 @@ fn parse_exec_env(line: &str) -> Option<(~str, ~str)> { fn parse_pp_exact(line: &str, testfile: &Path) -> Option { match parse_name_value_directive(line, ~"pp-exact") { - Some(s) => Some(Path(s)), + Some(s) => Some(Path::from_str(s)), None => { if parse_name_directive(line, "pp-exact") { - Some(testfile.file_path()) + testfile.file_path() } else { None } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 2d55fa775d7d7..e1c4c34a5f496 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -62,8 +62,8 @@ pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) { // We're going to be dumping a lot of info. Start on a new line. io::stdout().write_str("\n\n"); } - let testfile = Path(testfile); - debug2!("running {}", testfile.to_str()); + let testfile = Path::from_str(testfile); + debug2!("running {}", testfile.display()); let props = load_props(&testfile); debug2!("loaded props"); match config.mode { @@ -189,7 +189,7 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) { let mut expected = match props.pp_exact { Some(ref file) => { - let filepath = testfile.dir_path().push_rel(file); + let filepath = testfile.dir_path().join_path(file); io::read_whole_file_str(&filepath).unwrap() } None => { srcs[srcs.len() - 2u].clone() } @@ -221,7 +221,8 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) { fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs { let args = ~[~"-", ~"--pretty", ~"normal"]; - return ProcArgs {prog: config.rustc_path.to_str(), args: args}; + // FIXME (#9639): This needs to handle non-utf8 paths + return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args}; } fn compare_source(expected: &str, actual: &str) { @@ -251,14 +252,17 @@ actual:\n\ } fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs { + let aux_dir = aux_output_dir_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths let mut args = ~[~"-", ~"--no-trans", ~"--lib", - ~"-L", config.build_base.to_str(), + ~"-L", config.build_base.as_str().unwrap().to_owned(), ~"-L", - aux_output_dir_name(config, testfile).to_str()]; + aux_dir.as_str().unwrap().to_owned()]; args.push_all_move(split_maybe_args(&config.rustcflags)); args.push_all_move(split_maybe_args(&props.compile_flags)); - return ProcArgs {prog: config.rustc_path.to_str(), args: args}; + // FIXME (#9639): This needs to handle non-utf8 paths + return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args}; } } @@ -294,9 +298,11 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) { #[cfg(unix)] fn debugger() -> ~str { ~"gdb" } let debugger_script = make_out_name(config, testfile, "debugger.script"); + let exe_file = make_exe_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths let debugger_opts = ~[~"-quiet", ~"-batch", ~"-nx", - ~"-command=" + debugger_script.to_str(), - make_exe_name(config, testfile).to_str()]; + ~"-command=" + debugger_script.as_str().unwrap().to_owned(), + exe_file.as_str().unwrap().to_owned()]; let ProcArgs = ProcArgs {prog: debugger(), args: debugger_opts}; ProcRes = compose_and_run(config, testfile, ProcArgs, ~[], "", None); if ProcRes.status != 0 { @@ -328,7 +334,9 @@ fn check_error_patterns(props: &TestProps, testfile: &Path, ProcRes: &ProcRes) { if props.error_patterns.is_empty() { - fatal(~"no error pattern specified in " + testfile.to_str()); + do testfile.with_display_str |s| { + fatal(~"no error pattern specified in " + s); + } } if ProcRes.status == 0 { @@ -378,7 +386,7 @@ fn check_expected_errors(expected_errors: ~[errors::ExpectedError], } let prefixes = expected_errors.iter().map(|ee| { - format!("{}:{}:", testfile.to_str(), ee.line) + format!("{}:{}:", testfile.display(), ee.line) }).collect::<~[~str]>(); fn to_lower( s : &str ) -> ~str { @@ -538,7 +546,9 @@ fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes { fn compile_test_(config: &config, props: &TestProps, testfile: &Path, extra_args: &[~str]) -> ProcRes { - let link_args = ~[~"-L", aux_output_dir_name(config, testfile).to_str()]; + let aux_dir = aux_output_dir_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths + let link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()]; let args = make_compile_args(config, props, link_args + extra_args, make_exe_name, testfile); compose_and_run_compiler(config, props, testfile, args, None) @@ -579,11 +589,12 @@ fn compose_and_run_compiler( ensure_dir(&aux_output_dir_name(config, testfile)); } - let extra_link_args = ~[~"-L", - aux_output_dir_name(config, testfile).to_str()]; + let aux_dir = aux_output_dir_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths + let extra_link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()]; for rel_ab in props.aux_builds.iter() { - let abs_ab = config.aux_base.push_rel(&Path(*rel_ab)); + let abs_ab = config.aux_base.join_str(*rel_ab); let aux_args = make_compile_args(config, props, ~[~"--lib"] + extra_link_args, |a,b| make_lib_name(a, b, testfile), &abs_ab); @@ -592,7 +603,7 @@ fn compose_and_run_compiler( if auxres.status != 0 { fatal_ProcRes( format!("auxiliary build of {} failed to compile: ", - abs_ab.to_str()), + abs_ab.display()), &auxres); } @@ -615,7 +626,7 @@ fn compose_and_run_compiler( fn ensure_dir(path: &Path) { if os::path_is_dir(path) { return; } if !os::make_dir(path, 0x1c0i32) { - fail2!("can't make dir {}", path.to_str()); + fail2!("can't make dir {}", path.display()); } } @@ -631,24 +642,28 @@ fn compose_and_run(config: &config, testfile: &Path, fn make_compile_args(config: &config, props: &TestProps, extras: ~[~str], xform: &fn(&config, (&Path)) -> Path, testfile: &Path) -> ProcArgs { - let mut args = ~[testfile.to_str(), - ~"-o", xform(config, testfile).to_str(), - ~"-L", config.build_base.to_str()] + let xform_file = xform(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths + let mut args = ~[testfile.as_str().unwrap().to_owned(), + ~"-o", xform_file.as_str().unwrap().to_owned(), + ~"-L", config.build_base.as_str().unwrap().to_owned()] + extras; args.push_all_move(split_maybe_args(&config.rustcflags)); args.push_all_move(split_maybe_args(&props.compile_flags)); - return ProcArgs {prog: config.rustc_path.to_str(), args: args}; + return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args}; } fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { // what we return here is not particularly important, as it // happens; rustc ignores everything except for the directory. let auxname = output_testname(auxfile); - aux_output_dir_name(config, testfile).push_rel(&auxname) + aux_output_dir_name(config, testfile).join_path(&auxname) } fn make_exe_name(config: &config, testfile: &Path) -> Path { - Path(output_base_name(config, testfile).to_str() + os::EXE_SUFFIX) + let mut f = output_base_name(config, testfile); + f.add_extension_str(os::EXE_EXTENSION); + f } fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) -> @@ -656,7 +671,9 @@ fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) -> // If we've got another tool to run under (valgrind), // then split apart its command let mut args = split_maybe_args(&config.runtool); - args.push(make_exe_name(config, testfile).to_str()); + let exe_file = make_exe_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths + args.push(exe_file.as_str().unwrap().to_owned()); let prog = args.shift(); return ProcArgs {prog: prog, args: args}; } @@ -725,21 +742,23 @@ fn dump_output_file(config: &config, testfile: &Path, } fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path { - output_base_name(config, testfile).with_filetype(extension) + output_base_name(config, testfile).with_extension_str(extension) } fn aux_output_dir_name(config: &config, testfile: &Path) -> Path { - Path(output_base_name(config, testfile).to_str() + ".libaux") + let mut f = output_base_name(config, testfile); + f.add_extension_str("libaux"); + f } fn output_testname(testfile: &Path) -> Path { - Path(testfile.filestem().unwrap()) + Path::from_vec(testfile.filestem().unwrap()) } fn output_base_name(config: &config, testfile: &Path) -> Path { config.build_base - .push_rel(&output_testname(testfile)) - .with_filetype(config.stage_id) + .join_path(&output_testname(testfile)) + .with_extension_str(config.stage_id) } fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) { @@ -875,20 +894,19 @@ fn _dummy_exec_compiled_test(config: &config, props: &TestProps, } fn _arm_push_aux_shared_library(config: &config, testfile: &Path) { - let tstr = aux_output_dir_name(config, testfile).to_str(); + let tdir = aux_output_dir_name(config, testfile); - let dirs = os::list_dir_path(&Path(tstr)); + let dirs = os::list_dir_path(&tdir); for file in dirs.iter() { - - if (file.filetype() == Some(".so")) { - + if file.extension_str() == Some("so") { + // FIXME (#9639): This needs to handle non-utf8 paths let copy_result = procsrv::run("", config.adb_path, - [~"push", file.to_str(), config.adb_test_dir.clone()], + [~"push", file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()], ~[(~"",~"")], Some(~"")); if config.verbose { io::stdout().write_str(format!("push ({}) {} {} {}", - config.target, file.to_str(), + config.target, file.display(), copy_result.out, copy_result.err)); } } @@ -898,7 +916,7 @@ fn _arm_push_aux_shared_library(config: &config, testfile: &Path) { // codegen tests (vs. clang) fn make_o_name(config: &config, testfile: &Path) -> Path { - output_base_name(config, testfile).with_filetype("o") + output_base_name(config, testfile).with_extension_str("o") } fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path { @@ -906,13 +924,15 @@ fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path { (*p).clone() } else { let stem = p.filestem().unwrap(); - p.with_filestem(stem + "-" + suffix) + p.with_filename(stem + bytes!("-") + suffix.as_bytes()) } } fn compile_test_and_save_bitcode(config: &config, props: &TestProps, testfile: &Path) -> ProcRes { - let link_args = ~[~"-L", aux_output_dir_name(config, testfile).to_str()]; + let aux_dir = aux_output_dir_name(config, testfile); + // FIXME (#9639): This needs to handle non-utf8 paths + let link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()]; let llvm_args = ~[~"-c", ~"--lib", ~"--save-temps"]; let args = make_compile_args(config, props, link_args + llvm_args, @@ -922,14 +942,16 @@ fn compile_test_and_save_bitcode(config: &config, props: &TestProps, fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, testfile: &Path) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_filetype("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang"); + let testcc = testfile.with_extension_str("cc"); let ProcArgs = ProcArgs { - prog: config.clang_path.get_ref().to_str(), + // FIXME (#9639): This needs to handle non-utf8 paths + prog: config.clang_path.get_ref().as_str().unwrap().to_owned(), args: ~[~"-c", ~"-emit-llvm", - ~"-o", bitcodefile.to_str(), - testfile.with_filetype("cc").to_str() ] + ~"-o", bitcodefile.as_str().unwrap().to_owned(), + testcc.as_str().unwrap().to_owned() ] }; compose_and_run(config, testfile, ProcArgs, ~[], "", None) } @@ -937,35 +959,39 @@ fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, fn extract_function_from_bitcode(config: &config, _props: &TestProps, fname: &str, testfile: &Path, suffix: &str) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_filetype("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix); let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract"); + let prog = config.llvm_bin_path.get_ref().join_str("llvm-extract"); let ProcArgs = ProcArgs { - prog: config.llvm_bin_path.get_ref().push("llvm-extract").to_str(), - args: ~[~"-func=" + fname, - ~"-o=" + extracted_bc.to_str(), - bitcodefile.to_str() ] + // FIXME (#9639): This needs to handle non-utf8 paths + prog: prog.as_str().unwrap().to_owned(), + args: ~["-func=" + fname, + "-o=" + extracted_bc.as_str().unwrap(), + bitcodefile.as_str().unwrap().to_owned() ] }; compose_and_run(config, testfile, ProcArgs, ~[], "", None) } fn disassemble_extract(config: &config, _props: &TestProps, testfile: &Path, suffix: &str) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_filetype("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix); let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract"); - let extracted_ll = extracted_bc.with_filetype("ll"); + let extracted_ll = extracted_bc.with_extension_str("ll"); + let prog = config.llvm_bin_path.get_ref().join_str("llvm-dis"); let ProcArgs = ProcArgs { - prog: config.llvm_bin_path.get_ref().push("llvm-dis").to_str(), - args: ~[~"-o=" + extracted_ll.to_str(), - extracted_bc.to_str() ] + // FIXME (#9639): This needs to handle non-utf8 paths + prog: prog.as_str().unwrap().to_owned(), + args: ~["-o=" + extracted_ll.as_str().unwrap(), + extracted_bc.as_str().unwrap().to_owned() ] }; compose_and_run(config, testfile, ProcArgs, ~[], "", None) } fn count_extracted_lines(p: &Path) -> uint { - let x = io::read_whole_file_str(&p.with_filetype("ll")).unwrap(); + let x = io::read_whole_file_str(&p.with_extension_str("ll")).unwrap(); x.line_iter().len() } diff --git a/src/libextra/fileinput.rs b/src/libextra/fileinput.rs index bf3fe4b39b277..08d1222ff4666 100644 --- a/src/libextra/fileinput.rs +++ b/src/libextra/fileinput.rs @@ -362,7 +362,7 @@ pub fn make_path_option_vec(vec: &[~str], stdin_hyphen : bool) -> ~[Option if stdin_hyphen && "-" == *str { None } else { - Some(Path(*str)) + Some(Path::from_str(*str)) } }).collect() } @@ -435,14 +435,14 @@ mod test { fn test_make_path_option_vec() { let strs = [~"some/path", ~"some/other/path"]; - let paths = ~[Some(Path("some/path")), - Some(Path("some/other/path"))]; + let paths = ~[Some(Path::from_str("some/path")), + Some(Path::from_str("some/other/path"))]; assert_eq!(make_path_option_vec(strs, true), paths.clone()); assert_eq!(make_path_option_vec(strs, false), paths); assert_eq!(make_path_option_vec([~"-"], true), ~[None]); - assert_eq!(make_path_option_vec([~"-"], false), ~[Some(Path("-"))]); + assert_eq!(make_path_option_vec([~"-"], false), ~[Some(Path::from_str("-"))]); } #[test] @@ -567,9 +567,9 @@ mod test { #[test] fn test_no_trailing_newline() { let f1 = - Some(Path("tmp/lib-fileinput-test-no-trailing-newline-1.tmp")); + Some(Path::from_str("tmp/lib-fileinput-test-no-trailing-newline-1.tmp")); let f2 = - Some(Path("tmp/lib-fileinput-test-no-trailing-newline-2.tmp")); + Some(Path::from_str("tmp/lib-fileinput-test-no-trailing-newline-2.tmp")); { let mut wr = file::open(f1.get_ref(), io::CreateOrTruncate, diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index 112ea14218933..e398f41c467b0 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -35,7 +35,7 @@ pub struct GlobIterator { priv root: Path, priv dir_patterns: ~[Pattern], priv options: MatchOptions, - priv todo: ~[Path] + priv todo: ~[(Path,uint)] } /** @@ -80,18 +80,32 @@ pub fn glob(pattern: &str) -> GlobIterator { * Paths are yielded in alphabetical order, as absolute paths. */ pub fn glob_with(pattern: &str, options: MatchOptions) -> GlobIterator { + #[cfg(windows)] + use is_sep = std::path::windows::is_sep2; + #[cfg(not(windows))] + fn is_sep(c: char) -> bool { c <= '\x7F' && ::std::path::posix::is_sep(&(c as u8)) } + #[cfg(windows)] + fn check_windows_verbatim(p: &Path) -> bool { p.is_verbatim() } + #[cfg(not(windows))] + fn check_windows_verbatim(_: &Path) -> bool { false } + + // calculate root this way to handle volume-relative Windows paths correctly + let mut root = os::getcwd(); + let pat_root = Path::from_str(pattern).root_path(); + if pat_root.is_some() { + if check_windows_verbatim(pat_root.get_ref()) { + // XXX: How do we want to handle verbatim paths? I'm inclined to return nothing, + // since we can't very well find all UNC shares with a 1-letter server name. + return GlobIterator { root: root, dir_patterns: ~[], options: options, todo: ~[] }; + } + root.push_path(pat_root.get_ref()); + } - // note that this relies on the glob meta characters not - // having any special meaning in actual pathnames - let path = Path(pattern); - let dir_patterns = path.components.map(|s| Pattern::new(*s)); + let root_len = pat_root.map_move_default(0u, |p| p.as_vec().len()); + let dir_patterns = pattern.slice_from(root_len.min(&pattern.len())) + .split_terminator_iter(is_sep).map(|s| Pattern::new(s)).to_owned_vec(); - let root = if path.is_absolute() { - Path {components: ~[], .. path} // preserve windows path host/device - } else { - os::getcwd() - }; - let todo = list_dir_sorted(&root); + let todo = list_dir_sorted(&root).move_iter().map(|x|(x,0u)).to_owned_vec(); GlobIterator { root: root, @@ -109,18 +123,24 @@ impl Iterator for GlobIterator { return None; } - let path = self.todo.pop(); - let pattern_index = path.components.len() - self.root.components.len() - 1; - let ref pattern = self.dir_patterns[pattern_index]; - - if pattern.matches_with(*path.components.last(), self.options) { + let (path,idx) = self.todo.pop(); + let ref pattern = self.dir_patterns[idx]; - if pattern_index == self.dir_patterns.len() - 1 { + if pattern.matches_with(match path.filename_str() { + // this ugly match needs to go here to avoid a borrowck error + None => { + // FIXME (#9639): How do we handle non-utf8 filenames? Ignore them for now + // Ideally we'd still match them against a * + loop; + } + Some(x) => x + }, self.options) { + if idx == self.dir_patterns.len() - 1 { // it is not possible for a pattern to match a directory *AND* its children // so we don't need to check the children return Some(path); } else { - self.todo.push_all(list_dir_sorted(&path)); + self.todo.extend(&mut list_dir_sorted(&path).move_iter().map(|x|(x,idx+1))); } } } @@ -130,7 +150,7 @@ impl Iterator for GlobIterator { fn list_dir_sorted(path: &Path) -> ~[Path] { let mut children = os::list_dir_path(path); - sort::quick_sort(children, |p1, p2| p2.components.last() <= p1.components.last()); + sort::quick_sort(children, |p1, p2| p2.filename().unwrap() <= p1.filename().unwrap()); children } @@ -285,7 +305,10 @@ impl Pattern { * using the default match options (i.e. `MatchOptions::new()`). */ pub fn matches_path(&self, path: &Path) -> bool { - self.matches(path.to_str()) + // FIXME (#9639): This needs to handle non-utf8 paths + do path.as_str().map_move_default(false) |s| { + self.matches(s) + } } /** @@ -300,7 +323,10 @@ impl Pattern { * using the specified match options. */ pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool { - self.matches_with(path.to_str(), options) + // FIXME (#9639): This needs to handle non-utf8 paths + do path.as_str().map_move_default(false) |s| { + self.matches_with(s, options) + } } fn matches_from(&self, @@ -436,7 +462,7 @@ fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptio /// A helper function to determine if two chars are (possibly case-insensitively) equal. fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { - if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) { + if cfg!(windows) && path::windows::is_sep2(a) && path::windows::is_sep2(b) { true } else if !case_sensitive && a.is_ascii() && b.is_ascii() { // FIXME: work with non-ascii chars properly (issue #1347) @@ -449,9 +475,9 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { /// A helper function to determine if a char is a path separator on the current platform. fn is_sep(c: char) -> bool { if cfg!(windows) { - path::windows::is_sep(c) + path::windows::is_sep2(c) } else { - path::posix::is_sep(c) + c <= '\x7F' && path::posix::is_sep(&(c as u8)) } } @@ -522,8 +548,9 @@ mod test { assert!(glob("//").next().is_none()); // check windows absolute paths with host/device components - let root_with_device = (Path {components: ~[], .. os::getcwd()}).to_str() + "*"; - assert!(glob(root_with_device).next().is_some()); + let root_with_device = os::getcwd().root_path().unwrap().join_str("*"); + // FIXME (#9639): This needs to handle non-utf8 paths + assert!(glob(root_with_device.as_str().unwrap()).next().is_some()); } #[test] @@ -745,9 +772,9 @@ mod test { #[test] fn test_matches_path() { - // on windows, (Path("a/b").to_str() == "a\\b"), so this + // on windows, (Path::from_str("a/b").as_str().unwrap() == "a\\b"), so this // tests that / and \ are considered equivalent on windows - assert!(Pattern::new("a/b").matches_path(&Path("a/b"))); + assert!(Pattern::new("a/b").matches_path(&Path::from_str("a/b"))); } } diff --git a/src/libextra/tempfile.rs b/src/libextra/tempfile.rs index 60084faad9856..1226318878767 100644 --- a/src/libextra/tempfile.rs +++ b/src/libextra/tempfile.rs @@ -35,7 +35,7 @@ impl TempDir { let mut r = rand::rng(); for _ in range(0u, 1000) { - let p = tmpdir.push(r.gen_ascii_str(16) + suffix); + let p = tmpdir.join_str(r.gen_ascii_str(16) + suffix); if os::make_dir(&p, 0x1c0) { // 700 return Some(TempDir { path: Some(p) }); } diff --git a/src/libextra/terminfo/searcher.rs b/src/libextra/terminfo/searcher.rs index 5c7efdb298f27..cd4e487d70fe4 100644 --- a/src/libextra/terminfo/searcher.rs +++ b/src/libextra/terminfo/searcher.rs @@ -14,10 +14,9 @@ use std::{os, str}; use std::os::getenv; use std::io::{file_reader, Reader}; -use path = std::path::Path; /// Return path to database entry for `term` -pub fn get_dbpath_for_term(term: &str) -> Option<~path> { +pub fn get_dbpath_for_term(term: &str) -> Option<~Path> { if term.len() == 0 { return None; } @@ -29,25 +28,26 @@ pub fn get_dbpath_for_term(term: &str) -> Option<~path> { // Find search directory match getenv("TERMINFO") { - Some(dir) => dirs_to_search.push(path(dir)), + Some(dir) => dirs_to_search.push(Path::from_str(dir)), None => { if homedir.is_some() { - dirs_to_search.push(homedir.unwrap().push(".terminfo")); // ncurses compatability + // ncurses compatability; + dirs_to_search.push(homedir.unwrap().join_str(".terminfo")) } match getenv("TERMINFO_DIRS") { Some(dirs) => for i in dirs.split_iter(':') { if i == "" { - dirs_to_search.push(path("/usr/share/terminfo")); + dirs_to_search.push(Path::from_str("/usr/share/terminfo")); } else { - dirs_to_search.push(path(i.to_owned())); + dirs_to_search.push(Path::from_str(i.to_owned())); } }, // Found nothing, use the default paths // /usr/share/terminfo is the de facto location, but it seems // Ubuntu puts it in /lib/terminfo None => { - dirs_to_search.push(path("/usr/share/terminfo")); - dirs_to_search.push(path("/lib/terminfo")); + dirs_to_search.push(Path::from_str("/usr/share/terminfo")); + dirs_to_search.push(Path::from_str("/lib/terminfo")); } } } @@ -55,14 +55,18 @@ pub fn get_dbpath_for_term(term: &str) -> Option<~path> { // Look for the terminal in all of the search directories for p in dirs_to_search.iter() { - let newp = ~p.push_many(&[str::from_char(first_char), term.to_owned()]); - if os::path_exists(p) && os::path_exists(newp) { - return Some(newp); - } - // on some installations the dir is named after the hex of the char (e.g. OS X) - let newp = ~p.push_many(&[format!("{:x}", first_char as uint), term.to_owned()]); - if os::path_exists(p) && os::path_exists(newp) { - return Some(newp); + if os::path_exists(p) { + let f = str::from_char(first_char); + let newp = p.join_many_str([f.as_slice(), term]); + if os::path_exists(&newp) { + return Some(~newp); + } + // on some installations the dir is named after the hex of the char (e.g. OS X) + let f = format!("{:x}", first_char as uint); + let newp = p.join_many_str([f.as_slice(), term]); + if os::path_exists(&newp) { + return Some(~newp); + } } } None @@ -82,7 +86,11 @@ fn test_get_dbpath_for_term() { // woefully inadequate test coverage // note: current tests won't work with non-standard terminfo hierarchies (e.g. OS X's) use std::os::{setenv, unsetenv}; - fn x(t: &str) -> ~str { get_dbpath_for_term(t).expect("no terminfo entry found").to_str() }; + // FIXME (#9639): This needs to handle non-utf8 paths + fn x(t: &str) -> ~str { + let p = get_dbpath_for_term(t).expect("no terminfo entry found"); + p.as_str().unwrap().to_owned() + }; assert!(x("screen") == ~"/usr/share/terminfo/s/screen"); assert!(get_dbpath_for_term("") == None); setenv("TERMINFO_DIRS", ":"); diff --git a/src/libextra/test.rs b/src/libextra/test.rs index a352a2e467808..20690283dd5a1 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -271,20 +271,20 @@ pub fn parse_opts(args: &[~str]) -> Option { let run_ignored = matches.opt_present("ignored"); let logfile = matches.opt_str("logfile"); - let logfile = logfile.map(|s| Path(s)); + let logfile = logfile.map(|s| Path::from_str(s)); let run_benchmarks = matches.opt_present("bench"); let run_tests = ! run_benchmarks || matches.opt_present("test"); let ratchet_metrics = matches.opt_str("ratchet-metrics"); - let ratchet_metrics = ratchet_metrics.map(|s| Path(s)); + let ratchet_metrics = ratchet_metrics.map(|s| Path::from_str(s)); let ratchet_noise_percent = matches.opt_str("ratchet-noise-percent"); let ratchet_noise_percent = ratchet_noise_percent.map(|s| from_str::(s).unwrap()); let save_metrics = matches.opt_str("save-metrics"); - let save_metrics = save_metrics.map(|s| Path(s)); + let save_metrics = save_metrics.map(|s| Path::from_str(s)); let test_shard = matches.opt_str("test-shard"); let test_shard = opt_shard(test_shard); @@ -547,7 +547,7 @@ impl ConsoleTestState { let ratchet_success = match *ratchet_metrics { None => true, Some(ref pth) => { - self.out.write_str(format!("\nusing metrics ratchet: {}\n", pth.to_str())); + self.out.write_str(format!("\nusing metrics ratchet: {}\n", pth.display())); match ratchet_pct { None => (), Some(pct) => @@ -659,7 +659,7 @@ pub fn run_tests_console(opts: &TestOpts, None => (), Some(ref pth) => { st.metrics.save(pth); - st.out.write_str(format!("\nmetrics saved to: {}", pth.to_str())); + st.out.write_str(format!("\nmetrics saved to: {}", pth.display())); } } return st.write_run_finish(&opts.ratchet_metrics, opts.ratchet_noise_percent); @@ -1440,7 +1440,7 @@ mod tests { pub fn ratchet_test() { let dpth = TempDir::new("test-ratchet").expect("missing test for ratchet"); - let pth = dpth.path().push("ratchet.json"); + let pth = dpth.path().join_str("ratchet.json"); let mut m1 = MetricMap::new(); m1.insert_metric("runtime", 1000.0, 2.0); diff --git a/src/libextra/workcache.rs b/src/libextra/workcache.rs index 32a2d83d814e1..ea943cdb01b97 100644 --- a/src/libextra/workcache.rs +++ b/src/libextra/workcache.rs @@ -184,11 +184,11 @@ impl Database { let f = io::file_reader(&self.db_filename); match f { Err(e) => fail2!("Couldn't load workcache database {}: {}", - self.db_filename.to_str(), e.to_str()), + self.db_filename.display(), e.to_str()), Ok(r) => match json::from_reader(r) { Err(e) => fail2!("Couldn't parse workcache database (from file {}): {}", - self.db_filename.to_str(), e.to_str()), + self.db_filename.display(), e.to_str()), Ok(r) => { let mut decoder = json::Decoder(r); self.db_cache = Decodable::decode(&mut decoder); @@ -498,7 +498,7 @@ fn test() { // Create a path to a new file 'filename' in the directory in which // this test is running. fn make_path(filename: ~str) -> Path { - let pth = os::self_exe_path().expect("workcache::test failed").pop().push(filename); + let pth = os::self_exe_path().expect("workcache::test failed").with_filename_str(filename); if os::path_exists(&pth) { os::remove_file(&pth); } @@ -522,15 +522,20 @@ fn test() { let subcx = cx.clone(); let pth = pth.clone(); - prep.declare_input("file", pth.to_str(), digest_file(&pth)); + // FIXME (#9639): This needs to handle non-utf8 paths + prep.declare_input("file", pth.as_str().unwrap(), digest_file(&pth)); do prep.exec |_exe| { let out = make_path(~"foo.o"); - run::process_status("gcc", [pth.to_str(), ~"-o", out.to_str()]); + // FIXME (#9639): This needs to handle non-utf8 paths + run::process_status("gcc", [pth.as_str().unwrap().to_owned(), + ~"-o", + out.as_str().unwrap().to_owned()]); let _proof_of_concept = subcx.prep("subfn"); // Could run sub-rules inside here. - out.to_str() + // FIXME (#9639): This needs to handle non-utf8 paths + out.as_str().unwrap().to_owned() } }; diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index c19b5a83315d9..d5cd78f54f716 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -70,7 +70,7 @@ pub fn WriteOutputFile( Target: lib::llvm::TargetMachineRef, PM: lib::llvm::PassManagerRef, M: ModuleRef, - Output: &str, + Output: &Path, FileType: lib::llvm::FileType) { unsafe { do Output.with_c_str |Output| { @@ -129,15 +129,13 @@ pub mod jit { let cstore = sess.cstore; let r = cstore::get_used_crate_files(cstore); for cratepath in r.iter() { - let path = cratepath.to_str(); + debug2!("linking: {}", cratepath.display()); - debug2!("linking: {}", path); - - do path.with_c_str |buf_t| { + do cratepath.with_c_str |buf_t| { if !llvm::LLVMRustLoadCrate(manager, buf_t) { llvm_err(sess, ~"Could not link"); } - debug2!("linked: {}", path); + debug2!("linked: {}", cratepath.display()); } } @@ -251,7 +249,7 @@ pub mod write { llvm::LLVMInitializeMipsAsmParser(); if sess.opts.save_temps { - do output.with_filetype("no-opt.bc").with_c_str |buf| { + do output.with_extension_str("no-opt.bc").with_c_str |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } @@ -319,7 +317,7 @@ pub mod write { llvm::LLVMDisposePassManager(mpm); if sess.opts.save_temps { - do output.with_filetype("bc").with_c_str |buf| { + do output.with_extension_str("bc").with_c_str |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } @@ -350,12 +348,10 @@ pub mod write { } } output_type_assembly => { - WriteOutputFile(sess, tm, cpm, llmod, output.to_str(), - lib::llvm::AssemblyFile); + WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::AssemblyFile); } output_type_exe | output_type_object => { - WriteOutputFile(sess, tm, cpm, llmod, output.to_str(), - lib::llvm::ObjectFile); + WriteOutputFile(sess, tm, cpm, llmod, output, lib::llvm::ObjectFile); } } @@ -375,10 +371,11 @@ pub mod write { pub fn run_assembler(sess: Session, assembly: &Path, object: &Path) { let cc_prog = super::get_cc_prog(sess); + // FIXME (#9639): This needs to handle non-utf8 paths let cc_args = ~[ ~"-c", - ~"-o", object.to_str(), - assembly.to_str()]; + ~"-o", object.as_str().unwrap().to_owned(), + assembly.as_str().unwrap().to_owned()]; let prog = run::process_output(cc_prog, cc_args); @@ -612,11 +609,12 @@ pub fn build_link_meta(sess: Session, _ => { // to_managed could go away if there was a version of // filestem that returned an @str + // FIXME (#9639): Non-utf8 filenames will give a misleading error let name = session::expect(sess, - output.filestem(), + output.filestem_str(), || format!("output file name `{}` doesn't\ appear to have a stem", - output.to_str())).to_managed(); + output.display())).to_managed(); if name.is_empty() { sess.fatal("missing crate link meta `name`, and the \ inferred name is blank"); @@ -919,15 +917,16 @@ pub fn link_binary(sess: Session, let long_libname = output_dll_filename(sess.targ_cfg.os, lm); debug2!("link_meta.name: {}", lm.name); debug2!("long_libname: {}", long_libname); - debug2!("out_filename: {}", out_filename.to_str()); - debug2!("dirname(out_filename): {}", out_filename.dir_path().to_str()); + debug2!("out_filename: {}", out_filename.display()); + let out_dirname = out_filename.dir_path(); + debug2!("dirname(out_filename): {}", out_dirname.display()); - out_filename.dir_path().push(long_libname) + out_filename.with_filename_str(long_libname) } else { out_filename.clone() }; - debug2!("output: {}", output.to_str()); + debug2!("output: {}", output.display()); let cc_args = link_args(sess, obj_filename, out_filename, lm); debug2!("{} link args: {}", cc_prog, cc_args.connect(" ")); if (sess.opts.debugging_opts & session::print_link_args) != 0 { @@ -947,14 +946,15 @@ pub fn link_binary(sess: Session, // Clean up on Darwin if sess.targ_cfg.os == session::OsMacos { - run::process_status("dsymutil", [output.to_str()]); + // FIXME (#9639): This needs to handle non-utf8 paths + run::process_status("dsymutil", [output.as_str().unwrap().to_owned()]); } // Remove the temporary object file if we aren't saving temps if !sess.opts.save_temps { if ! os::remove_file(obj_filename) { sess.warn(format!("failed to delete object file `{}`", - obj_filename.to_str())); + obj_filename.display())); } } } @@ -977,20 +977,23 @@ pub fn link_args(sess: Session, let output = if *sess.building_library { let long_libname = output_dll_filename(sess.targ_cfg.os, lm); - out_filename.dir_path().push(long_libname) + out_filename.with_filename_str(long_libname) } else { out_filename.clone() }; // The default library location, we need this to find the runtime. // The location of crates will be determined as needed. - let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str(); + // FIXME (#9639): This needs to handle non-utf8 paths + let lib_path = sess.filesearch.get_target_lib_path(); + let stage: ~str = ~"-L" + lib_path.as_str().unwrap(); let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args); + // FIXME (#9639): This needs to handle non-utf8 paths args.push_all([ - ~"-o", output.to_str(), - obj_filename.to_str()]); + ~"-o", output.as_str().unwrap().to_owned(), + obj_filename.as_str().unwrap().to_owned()]); let lib_cmd = match sess.targ_cfg.os { session::OsMacos => ~"-dynamiclib", @@ -1001,14 +1004,15 @@ pub fn link_args(sess: Session, let cstore = sess.cstore; let r = cstore::get_used_crate_files(cstore); + // FIXME (#9639): This needs to handle non-utf8 paths for cratepath in r.iter() { - if cratepath.filetype() == Some(".rlib") { - args.push(cratepath.to_str()); + if cratepath.extension_str() == Some("rlib") { + args.push(cratepath.as_str().unwrap().to_owned()); continue; } - let dir = cratepath.dirname(); + let dir = cratepath.dirname_str().unwrap(); if !dir.is_empty() { args.push("-L" + dir); } - let libarg = unlib(sess.targ_cfg, cratepath.filestem().unwrap().to_owned()); + let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap().to_owned()); args.push("-l" + libarg); } @@ -1032,12 +1036,14 @@ pub fn link_args(sess: Session, // forces to make sure that library can be found at runtime. for path in sess.opts.addl_lib_search_paths.iter() { - args.push("-L" + path.to_str()); + // FIXME (#9639): This needs to handle non-utf8 paths + args.push("-L" + path.as_str().unwrap().to_owned()); } let rustpath = filesearch::rust_path(); for path in rustpath.iter() { - args.push("-L" + path.to_str()); + // FIXME (#9639): This needs to handle non-utf8 paths + args.push("-L" + path.as_str().unwrap().to_owned()); } // The names of the extern libraries @@ -1050,8 +1056,9 @@ pub fn link_args(sess: Session, // On mac we need to tell the linker to let this library // be rpathed if sess.targ_cfg.os == session::OsMacos { + // FIXME (#9639): This needs to handle non-utf8 paths args.push("-Wl,-install_name,@rpath/" - + output.filename().unwrap()); + + output.filename_str().unwrap()); } } diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 60289e0ebe5a1..b3fb6be686da7 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -45,23 +45,26 @@ pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { let r = filesearch::relative_target_lib_path(sess.opts.target_triple); - sess.filesearch.sysroot().push_rel(&r).push(os::dll_filename("rustrt")) + let mut p = sess.filesearch.sysroot().join_path(&r); + p.push_str(os::dll_filename("rustrt")); + p } -pub fn rpaths_to_flags(rpaths: &[Path]) -> ~[~str] { - rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",rpath.to_str())).collect() +pub fn rpaths_to_flags(rpaths: &[~str]) -> ~[~str] { + // FIXME (#9639): This needs to handle non-utf8 paths + rpaths.iter().map(|rpath| format!("-Wl,-rpath,{}",*rpath)).collect() } fn get_rpaths(os: session::Os, sysroot: &Path, output: &Path, libs: &[Path], - target_triple: &str) -> ~[Path] { - debug2!("sysroot: {}", sysroot.to_str()); - debug2!("output: {}", output.to_str()); + target_triple: &str) -> ~[~str] { + debug2!("sysroot: {}", sysroot.display()); + debug2!("output: {}", output.display()); debug2!("libs:"); for libpath in libs.iter() { - debug2!(" {}", libpath.to_str()); + debug2!(" {}", libpath.display()); } debug2!("target_triple: {}", target_triple); @@ -77,10 +80,10 @@ fn get_rpaths(os: session::Os, // And a final backup rpath to the global library location. let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)]; - fn log_rpaths(desc: &str, rpaths: &[Path]) { + fn log_rpaths(desc: &str, rpaths: &[~str]) { debug2!("{} rpaths:", desc); for rpath in rpaths.iter() { - debug2!(" {}", rpath.to_str()); + debug2!(" {}", *rpath); } } @@ -99,14 +102,14 @@ fn get_rpaths(os: session::Os, fn get_rpaths_relative_to_output(os: session::Os, output: &Path, - libs: &[Path]) -> ~[Path] { + libs: &[Path]) -> ~[~str] { libs.iter().map(|a| get_rpath_relative_to_output(os, output, a)).collect() } pub fn get_rpath_relative_to_output(os: session::Os, output: &Path, lib: &Path) - -> Path { + -> ~str { use std::os; assert!(not_win32(os)); @@ -119,29 +122,43 @@ pub fn get_rpath_relative_to_output(os: session::Os, session::OsWin32 => unreachable!() }; - Path(prefix).push_rel(&os::make_absolute(output).get_relative_to(&os::make_absolute(lib))) + let mut lib = os::make_absolute(lib); + lib.pop(); + let mut output = os::make_absolute(output); + output.pop(); + let relative = lib.path_relative_from(&output); + let relative = relative.expect("could not create rpath relative to output"); + // FIXME (#9639): This needs to handle non-utf8 paths + prefix+"/"+relative.as_str().expect("non-utf8 component in path") } -fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] { +fn get_absolute_rpaths(libs: &[Path]) -> ~[~str] { libs.iter().map(|a| get_absolute_rpath(a)).collect() } -pub fn get_absolute_rpath(lib: &Path) -> Path { - os::make_absolute(lib).dir_path() +pub fn get_absolute_rpath(lib: &Path) -> ~str { + let mut p = os::make_absolute(lib); + p.pop(); + // FIXME (#9639): This needs to handle non-utf8 paths + p.as_str().expect("non-utf8 component in rpath").to_owned() } -pub fn get_install_prefix_rpath(target_triple: &str) -> Path { +pub fn get_install_prefix_rpath(target_triple: &str) -> ~str { let install_prefix = env!("CFG_PREFIX"); let tlib = filesearch::relative_target_lib_path(target_triple); - os::make_absolute(&Path(install_prefix).push_rel(&tlib)) + let mut path = Path::from_str(install_prefix); + path.push_path(&tlib); + let path = os::make_absolute(&path); + // FIXME (#9639): This needs to handle non-utf8 paths + path.as_str().expect("non-utf8 component in rpath").to_owned() } -pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] { +pub fn minimize_rpaths(rpaths: &[~str]) -> ~[~str] { let mut set = HashSet::new(); let mut minimized = ~[]; for rpath in rpaths.iter() { - if set.insert(rpath.to_str()) { + if set.insert(rpath.as_slice()) { minimized.push(rpath.clone()); } } @@ -162,43 +179,43 @@ mod test { #[test] fn test_rpaths_to_flags() { - let flags = rpaths_to_flags([Path("path1"), - Path("path2")]); + let flags = rpaths_to_flags([~"path1", ~"path2"]); assert_eq!(flags, ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]); } #[test] fn test_prefix_rpath() { let res = get_install_prefix_rpath("triple"); - let d = Path(env!("CFG_PREFIX")) - .push_rel(&Path("lib/rustc/triple/lib")); + let mut d = Path::from_str(env!("CFG_PREFIX")); + d.push_str("lib/rustc/triple/lib"); debug2!("test_prefix_path: {} vs. {}", res.to_str(), - d.to_str()); - assert!(res.to_str().ends_with(d.to_str())); + d.display()); + assert!(ends_with(res.as_bytes(), d.as_vec())); + fn ends_with(v: &[u8], needle: &[u8]) -> bool { + v.len() >= needle.len() && v.slice_from(v.len()-needle.len()) == needle + } } #[test] fn test_prefix_rpath_abs() { let res = get_install_prefix_rpath("triple"); - assert!(res.is_absolute); + assert!(Path::from_str(res).is_absolute()); } #[test] fn test_minimize1() { - let res = minimize_rpaths([Path("rpath1"), - Path("rpath2"), - Path("rpath1")]); - assert_eq!(res, ~[Path("rpath1"), Path("rpath2")]); + let res = minimize_rpaths([~"rpath1", ~"rpath2", ~"rpath1"]); + assert_eq!(res.as_slice(), [~"rpath1", ~"rpath2"]); } #[test] fn test_minimize2() { - let res = minimize_rpaths([Path("1a"), Path("2"), Path("2"), - Path("1a"), Path("4a"),Path("1a"), - Path("2"), Path("3"), Path("4a"), - Path("3")]); - assert_eq!(res, ~[Path("1a"), Path("2"), Path("4a"), Path("3")]); + let res = minimize_rpaths([~"1a", ~"2", ~"2", + ~"1a", ~"4a", ~"1a", + ~"2", ~"3", ~"4a", + ~"3"]); + assert_eq!(res.as_slice(), [~"1a", ~"2", ~"4a", ~"3"]); } #[test] @@ -207,8 +224,8 @@ mod test { fn test_rpath_relative() { let o = session::OsLinux; let res = get_rpath_relative_to_output(o, - &Path("bin/rustc"), &Path("lib/libstd.so")); - assert_eq!(res.to_str(), ~"$ORIGIN/../lib"); + &Path::from_str("bin/rustc"), &Path::from_str("lib/libstd.so")); + assert_eq!(res.as_slice(), "$ORIGIN/../lib"); } #[test] @@ -216,8 +233,8 @@ mod test { fn test_rpath_relative() { let o = session::OsFreebsd; let res = get_rpath_relative_to_output(o, - &Path("bin/rustc"), &Path("lib/libstd.so")); - assert_eq!(res.to_str(), ~"$ORIGIN/../lib"); + &Path::from_str("bin/rustc"), &Path::from_str("lib/libstd.so")); + assert_eq!(res.as_slice(), "$ORIGIN/../lib"); } #[test] @@ -225,18 +242,19 @@ mod test { fn test_rpath_relative() { let o = session::OsMacos; let res = get_rpath_relative_to_output(o, - &Path("bin/rustc"), - &Path("lib/libstd.so")); - assert_eq!(res.to_str(), ~"@executable_path/../lib"); + &Path::from_str("bin/rustc"), + &Path::from_str("lib/libstd.so")); + assert_eq!(res.as_slice(), "@executable_path/../lib"); } #[test] fn test_get_absolute_rpath() { - let res = get_absolute_rpath(&Path("lib/libstd.so")); + let res = get_absolute_rpath(&Path::from_str("lib/libstd.so")); + let lib = os::make_absolute(&Path::from_str("lib")); debug2!("test_get_absolute_rpath: {} vs. {}", - res.to_str(), - os::make_absolute(&Path("lib")).to_str()); + res.to_str(), lib.display()); - assert_eq!(res, os::make_absolute(&Path("lib"))); + // FIXME (#9639): This needs to handle non-utf8 paths + assert_eq!(res.as_slice(), lib.as_str().expect("non-utf8 component in path")); } } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 0c896007fc3ba..fc5ff163d7498 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -58,7 +58,8 @@ pub fn anon_src() -> @str { @"" } pub fn source_name(input: &input) -> @str { match *input { - file_input(ref ifile) => ifile.to_str().to_managed(), + // FIXME (#9639): This needs to handle non-utf8 paths + file_input(ref ifile) => ifile.as_str().unwrap().to_managed(), str_input(_) => anon_src() } } @@ -352,7 +353,7 @@ pub fn phase_5_run_llvm_passes(sess: Session, (sess.opts.output_type == link::output_type_object || sess.opts.output_type == link::output_type_exe) { let output_type = link::output_type_assembly; - let asm_filename = outputs.obj_filename.with_filetype("s"); + let asm_filename = outputs.obj_filename.with_extension_str("s"); time(sess.time_passes(), "LLVM passes", (), |_| link::write::run_passes(sess, @@ -721,7 +722,7 @@ pub fn build_session_options(binary: @str, } else if matches.opt_present("emit-llvm") { link::output_type_bitcode } else { link::output_type_exe }; - let sysroot_opt = matches.opt_str("sysroot").map(|m| @Path(m)); + let sysroot_opt = matches.opt_str("sysroot").map(|m| @Path::from_str(m)); let target = matches.opt_str("target").unwrap_or(host_triple()); let target_cpu = matches.opt_str("target-cpu").unwrap_or(~"generic"); let target_feature = matches.opt_str("target-feature").unwrap_or(~""); @@ -754,7 +755,7 @@ pub fn build_session_options(binary: @str, let statik = debugging_opts & session::statik != 0; - let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path(*s)); + let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path::from_str(*s)); let linker = matches.opt_str("linker"); let linker_args = matches.opt_strs("link-args").flat_map( |a| { a.split_iter(' ').map(|arg| arg.to_owned()).collect() @@ -985,7 +986,8 @@ pub fn build_output_filenames(input: &input, }; let mut stem = match *input { - file_input(ref ifile) => (*ifile).filestem().unwrap().to_managed(), + // FIXME (#9639): This needs to handle non-utf8 paths + file_input(ref ifile) => (*ifile).filestem_str().unwrap().to_managed(), str_input(_) => @"rust_out" }; @@ -1003,20 +1005,24 @@ pub fn build_output_filenames(input: &input, } if *sess.building_library { - out_path = dirpath.push(os::dll_filename(stem)); - obj_path = dirpath.push(stem).with_filetype(obj_suffix); + out_path = dirpath.join_str(os::dll_filename(stem)); + obj_path = { + let mut p = dirpath.join_str(stem); + p.set_extension_str(obj_suffix); + p + }; } else { - out_path = dirpath.push(stem); - obj_path = dirpath.push(stem).with_filetype(obj_suffix); + out_path = dirpath.join_str(stem); + obj_path = out_path.with_extension_str(obj_suffix); } } Some(ref out_file) => { - out_path = (*out_file).clone(); + out_path = out_file.clone(); obj_path = if stop_after_codegen { - (*out_file).clone() + out_file.clone() } else { - (*out_file).with_filetype(obj_suffix) + out_file.with_extension_str(obj_suffix) }; if *sess.building_library { diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 6df083aca4ff3..8ece290293b39 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -143,14 +143,15 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) { let meta_items = match path_opt { None => meta_items.clone(), Some((p, _path_str_style)) => { - let p_path = Path(p); - match p_path.filestem() { + let p_path = Path::from_str(p); + match p_path.filestem_str() { + None|Some("") => + e.diag.span_bug(i.span, "Bad package path in `extern mod` item"), Some(s) => vec::append( ~[attr::mk_name_value_item_str(@"package_id", p), attr::mk_name_value_item_str(@"name", s.to_managed())], - *meta_items), - None => e.diag.span_bug(i.span, "Bad package path in `extern mod` item") + *meta_items) } } }; @@ -274,7 +275,7 @@ fn resolve_crate(e: @mut Env, }; let (lident, ldata) = loader::load_library_crate(&load_ctxt); - let cfilename = Path(lident); + let cfilename = Path::from_str(lident); let cdata = ldata; let attrs = decoder::get_crate_attributes(cdata); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 6761445b74e22..4e3daa7c185da 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -24,10 +24,10 @@ pub enum FileMatch { FileMatches, FileDoesntMatch } pub type pick<'self> = &'self fn(path: &Path) -> FileMatch; pub fn pick_file(file: Path, path: &Path) -> Option { - if path.file_path() == file { - option::Some((*path).clone()) + if path.file_path() == Some(file) { + Some(path.clone()) } else { - option::None + None } } @@ -60,29 +60,29 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, FileMatches => found = true, FileDoesntMatch => () } - visited_dirs.insert(path.to_str()); + visited_dirs.insert(path.as_vec().to_owned()); } debug2!("filesearch: searching target lib path"); let tlib_path = make_target_lib_path(self.sysroot, self.target_triple); - if !visited_dirs.contains(&tlib_path.to_str()) { + if !visited_dirs.contains_equiv(&tlib_path.as_vec()) { match f(&tlib_path) { FileMatches => found = true, FileDoesntMatch => () } } - visited_dirs.insert(tlib_path.to_str()); + visited_dirs.insert(tlib_path.as_vec().to_owned()); // Try RUST_PATH if !found { let rustpath = rust_path(); for path in rustpath.iter() { let tlib_path = make_rustpkg_target_lib_path(path, self.target_triple); - debug2!("is {} in visited_dirs? {:?}", tlib_path.to_str(), - visited_dirs.contains(&tlib_path.to_str())); + debug2!("is {} in visited_dirs? {:?}", tlib_path.display(), + visited_dirs.contains_equiv(&tlib_path.as_vec().to_owned())); - if !visited_dirs.contains(&tlib_path.to_str()) { - visited_dirs.insert(tlib_path.to_str()); + if !visited_dirs.contains_equiv(&tlib_path.as_vec()) { + visited_dirs.insert(tlib_path.as_vec().to_owned()); // Don't keep searching the RUST_PATH if one match turns up -- // if we did, we'd get a "multiple matching crates" error match f(&tlib_path) { @@ -99,12 +99,14 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, make_target_lib_path(self.sysroot, self.target_triple) } fn get_target_lib_file_path(&self, file: &Path) -> Path { - self.get_target_lib_path().push_rel(file) + let mut p = self.get_target_lib_path(); + p.push_path(file); + p } } let sysroot = get_sysroot(maybe_sysroot); - debug2!("using sysroot = {}", sysroot.to_str()); + debug2!("using sysroot = {}", sysroot.display()); @FileSearchImpl { sysroot: sysroot, addl_lib_search_paths: addl_lib_search_paths, @@ -114,19 +116,19 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, pub fn search(filesearch: @FileSearch, pick: pick) { do filesearch.for_each_lib_search_path() |lib_search_path| { - debug2!("searching {}", lib_search_path.to_str()); + debug2!("searching {}", lib_search_path.display()); let r = os::list_dir_path(lib_search_path); let mut rslt = FileDoesntMatch; for path in r.iter() { - debug2!("testing {}", path.to_str()); + debug2!("testing {}", path.display()); let maybe_picked = pick(path); match maybe_picked { FileMatches => { - debug2!("picked {}", path.to_str()); + debug2!("picked {}", path.display()); rslt = FileMatches; } FileDoesntMatch => { - debug2!("rejected {}", path.to_str()); + debug2!("rejected {}", path.display()); } } } @@ -135,24 +137,30 @@ pub fn search(filesearch: @FileSearch, pick: pick) { } pub fn relative_target_lib_path(target_triple: &str) -> Path { - Path(libdir()).push_many([~"rustc", - target_triple.to_owned(), - libdir()]) + let dir = libdir(); + let mut p = Path::from_str(dir); + assert!(p.is_relative()); + p.push_str("rustc"); + p.push_str(target_triple); + p.push_str(dir); + p } fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> Path { - sysroot.push_rel(&relative_target_lib_path(target_triple)) + sysroot.join_path(&relative_target_lib_path(target_triple)) } fn make_rustpkg_target_lib_path(dir: &Path, target_triple: &str) -> Path { - dir.push_rel(&Path(libdir()).push(target_triple.to_owned())) + let mut p = dir.join_str(libdir()); + p.push_str(target_triple); + p } pub fn get_or_default_sysroot() -> Path { match os::self_exe_path() { - option::Some(ref p) => (*p).pop(), + option::Some(p) => { let mut p = p; p.pop(); p } option::None => fail2!("can't determine value for sysroot") } } @@ -184,13 +192,13 @@ pub fn rust_path() -> ~[Path] { Some(env_path) => { let env_path_components: ~[&str] = env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect(); - env_path_components.map(|&s| Path(s)) + env_path_components.map(|&s| Path::from_str(s)) } None => ~[] }; let cwd = os::getcwd(); // now add in default entries - let cwd_dot_rust = cwd.push(".rust"); + let cwd_dot_rust = cwd.join_str(".rust"); if !env_rust_path.contains(&cwd_dot_rust) { env_rust_path.push(cwd_dot_rust); } @@ -198,13 +206,14 @@ pub fn rust_path() -> ~[Path] { env_rust_path.push(cwd.clone()); } do cwd.each_parent() |p| { - if !env_rust_path.contains(&p.push(".rust")) { + if !env_rust_path.contains(&p.join_str(".rust")) { push_if_exists(&mut env_rust_path, p); } - } + true + }; let h = os::homedir(); for h in h.iter() { - if !env_rust_path.contains(&h.push(".rust")) { + if !env_rust_path.contains(&h.join_str(".rust")) { push_if_exists(&mut env_rust_path, h); } } @@ -214,7 +223,7 @@ pub fn rust_path() -> ~[Path] { /// Adds p/.rust into vec, only if it exists fn push_if_exists(vec: &mut ~[Path], p: &Path) { - let maybe_dir = p.push(".rust"); + let maybe_dir = p.join_str(".rust"); if os::path_exists(&maybe_dir) { vec.push(maybe_dir); } diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 4d69a2ffce85d..593a02c95082d 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -93,25 +93,27 @@ fn find_library_crate_aux( let prefix = format!("{}{}-", prefix, crate_name); let mut matches = ~[]; filesearch::search(filesearch, |path| -> FileMatch { - let path_str = path.filename(); + // FIXME (#9639): This needs to handle non-utf8 paths + let path_str = path.filename_str(); match path_str { None => FileDoesntMatch, Some(path_str) => if path_str.starts_with(prefix) && path_str.ends_with(suffix) { - debug2!("{} is a candidate", path.to_str()); + debug2!("{} is a candidate", path.display()); match get_metadata_section(cx.os, path) { Some(cvec) => if !crate_matches(cvec, cx.metas, cx.hash) { debug2!("skipping {}, metadata doesn't match", - path.to_str()); + path.display()); FileDoesntMatch } else { - debug2!("found {} with matching metadata", path.to_str()); - matches.push((path.to_str(), cvec)); + debug2!("found {} with matching metadata", path.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + matches.push((path.as_str().unwrap().to_owned(), cvec)); FileMatches }, _ => { - debug2!("could not load metadata for {}", path.to_str()); + debug2!("could not load metadata for {}", path.display()); FileDoesntMatch } } @@ -273,7 +275,7 @@ pub fn list_file_metadata(intr: @ident_interner, match get_metadata_section(os, path) { option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out), option::None => { - out.write_str(format!("could not find metadata in {}.\n", path.to_str())) + out.write_str(format!("could not find metadata in {}.\n", path.display())) } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index a982a4767fdcf..fb3f215e50624 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -859,7 +859,8 @@ fn compile_unit_metadata(cx: @mut CrateContext) { debug2!("compile_unit_metadata: {:?}", crate_name); - let work_dir = cx.sess.working_dir.to_str(); + // FIXME (#9639): This needs to handle non-utf8 paths + let work_dir = cx.sess.working_dir.as_str().unwrap(); let producer = format!("rustc version {}", env!("CFG_VERSION")); do crate_name.with_c_str |crate_name| { @@ -969,7 +970,8 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile { debug2!("file_metadata: {}", full_path); - let work_dir = cx.sess.working_dir.to_str(); + // FIXME (#9639): This needs to handle non-utf8 paths + let work_dir = cx.sess.working_dir.as_str().unwrap(); let file_name = if full_path.starts_with(work_dir) { full_path.slice(work_dir.len() + 1u, full_path.len()) diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index aa1ec26d42648..460ac45cdba06 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -250,7 +250,7 @@ pub fn run_compiler(args: &[~str], demitter: @diagnostic::Emitter) { let src = str::from_utf8(io::stdin().read_whole_stream()); str_input(src.to_managed()) } else { - file_input(Path(ifile)) + file_input(Path::from_str(ifile)) } } _ => early_error(demitter, "multiple input filenames provided") @@ -258,8 +258,8 @@ pub fn run_compiler(args: &[~str], demitter: @diagnostic::Emitter) { let sopts = build_session_options(binary, matches, demitter); let sess = build_session(sopts, demitter); - let odir = matches.opt_str("out-dir").map(|o| Path(o)); - let ofile = matches.opt_str("o").map(|o| Path(o)); + let odir = matches.opt_str("out-dir").map(|o| Path::from_str(o)); + let ofile = matches.opt_str("o").map(|o| Path::from_str(o)); let cfg = build_configuration(sess); let pretty = do matches.opt_default("pretty", "normal").map |a| { parse_pretty(sess, a) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7537366014e05..1b7fe9f15cb2e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -39,7 +39,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext { let sessopts = @driver::session::options { binary: @"rustdoc", - maybe_sysroot: Some(@os::self_exe_path().unwrap().pop()), + maybe_sysroot: Some(@os::self_exe_path().unwrap().dir_path()), addl_lib_search_paths: @mut libs, .. (*rustc::driver::session::basic_options()).clone() }; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index bb78b5aabb5f9..9347a1775d48b 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -47,7 +47,6 @@ use std::rt::io::Reader; use std::str; use std::task; use std::unstable::finally::Finally; -use std::util; use std::vec; use extra::arc::RWArc; @@ -256,16 +255,16 @@ pub fn run(mut crate: clean::Crate, dst: Path) { crate = cache.fold_crate(crate); // Add all the static files - let dst = cx.dst.push(crate.name); + let mut dst = cx.dst.join_str(crate.name); mkdir(&dst); - write(dst.push("jquery.js"), include_str!("static/jquery-2.0.3.min.js")); - write(dst.push("main.js"), include_str!("static/main.js")); - write(dst.push("main.css"), include_str!("static/main.css")); - write(dst.push("normalize.css"), include_str!("static/normalize.css")); + write(dst.join_str("jquery.js"), include_str!("static/jquery-2.0.3.min.js")); + write(dst.join_str("main.js"), include_str!("static/main.js")); + write(dst.join_str("main.css"), include_str!("static/main.css")); + write(dst.join_str("normalize.css"), include_str!("static/normalize.css")); // Publish the search index { - let dst = dst.push("search-index.js"); + dst.push_str("search-index.js"); let mut w = BufferedWriter::new(dst.open_writer(io::CreateOrTruncate)); let w = &mut w as &mut io::Writer; write!(w, "var searchIndex = ["); @@ -293,9 +292,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) { // Render all source files (this may turn into a giant no-op) { info2!("emitting source files"); - let dst = cx.dst.push("src"); + let dst = cx.dst.join_str("src"); mkdir(&dst); - let dst = dst.push(crate.name); + let dst = dst.join_str(crate.name); mkdir(&dst); let mut folder = SourceCollector { dst: dst, @@ -325,7 +324,7 @@ fn write(dst: Path, contents: &str) { fn mkdir(path: &Path) { do io::io_error::cond.trap(|err| { error2!("Couldn't create directory `{}`: {}", - path.to_str(), err.desc); + path.display(), err.desc); fail2!() }).inside { if !path.is_dir() { @@ -335,18 +334,18 @@ fn mkdir(path: &Path) { } /// Takes a path to a source file and cleans the path to it. This canonicalizes -/// things like "." and ".." to components which preserve the "top down" -/// hierarchy of a static HTML tree. -fn clean_srcpath(src: &str, f: &fn(&str)) { - let p = Path(src); - for c in p.components.iter() { - if "." == *c { - continue - } - if ".." == *c { - f("up"); - } else { - f(c.as_slice()) +/// things like ".." to components which preserve the "top down" hierarchy of a +/// static HTML tree. +// FIXME (#9639): The closure should deal with &[u8] instead of &str +fn clean_srcpath(src: &[u8], f: &fn(&str)) { + let p = Path::from_vec(src); + if p.as_vec() != bytes!(".") { + for c in p.str_component_iter().map(|x|x.unwrap()) { + if ".." == c { + f("up"); + } else { + f(c.as_slice()) + } } } } @@ -355,7 +354,7 @@ fn clean_srcpath(src: &str, f: &fn(&str)) { /// rendering in to the specified source destination. fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { // See if there's documentation generated into the local directory - let local_location = dst.push(e.name); + let local_location = dst.join_str(e.name); if local_location.is_dir() { return Local; } @@ -414,7 +413,7 @@ impl<'self> DocFolder for SourceCollector<'self> { impl<'self> SourceCollector<'self> { /// Renders the given filename into its corresponding HTML source file. fn emit_source(&mut self, filename: &str) -> bool { - let p = Path(filename); + let p = Path::from_str(filename); // Read the contents of the file let mut contents = ~[]; @@ -445,17 +444,17 @@ impl<'self> SourceCollector<'self> { // Create the intermediate directories let mut cur = self.dst.clone(); let mut root_path = ~"../../"; - do clean_srcpath(p.pop().to_str()) |component| { - cur = cur.push(component); + do clean_srcpath(p.dirname()) |component| { + cur.push_str(component); mkdir(&cur); root_path.push_str("../"); } - let dst = cur.push(*p.components.last() + ".html"); - let w = dst.open_writer(io::CreateOrTruncate); + cur.push(p.filename().expect("source has no filename") + bytes!(".html")); + let w = cur.open_writer(io::CreateOrTruncate); let mut w = BufferedWriter::new(w); - let title = format!("{} -- source", *dst.components.last()); + let title = cur.with_filename_display_str(|s| format!("{} -- source", s.unwrap())); let page = layout::Page { title: title, ty: "source", @@ -661,8 +660,8 @@ impl Context { if s.len() == 0 { fail2!("what {:?}", self); } - let next = self.dst.push(s); - let prev = util::replace(&mut self.dst, next); + let prev = self.dst.clone(); + self.dst.push_str(s); self.root_path.push_str("../"); self.current.push(s); @@ -809,7 +808,7 @@ impl Context { let item = Cell::new(item); do self.recurse(name) |this| { let item = item.take(); - let dst = this.dst.push("index.html"); + let dst = this.dst.join_str("index.html"); let writer = dst.open_writer(io::CreateOrTruncate); render(writer.unwrap(), this, &item, false); @@ -827,7 +826,7 @@ impl Context { // Things which don't have names (like impls) don't get special // pages dedicated to them. _ if item.name.is_some() => { - let dst = self.dst.push(item_path(&item)); + let dst = self.dst.join_str(item_path(&item)); let writer = dst.open_writer(io::CreateOrTruncate); render(writer.unwrap(), self, &item, true); } @@ -881,7 +880,7 @@ impl<'self> fmt::Default for Item<'self> { if it.cx.include_sources { let mut path = ~[]; - do clean_srcpath(it.item.source.filename) |component| { + do clean_srcpath(it.item.source.filename.as_bytes()) |component| { path.push(component.to_owned()); } let href = if it.item.source.loline == it.item.source.hiline { diff --git a/src/librustdoc/plugins.rs b/src/librustdoc/plugins.rs index 4bad87088a129..12128402f74f0 100644 --- a/src/librustdoc/plugins.rs +++ b/src/librustdoc/plugins.rs @@ -41,7 +41,7 @@ impl PluginManager { /// platform. On windows, it turns into name.dll, on OS X, name.dylib, and /// elsewhere, libname.so. pub fn load_plugin(&mut self, name: ~str) { - let x = self.prefix.push(libname(name)); + let x = self.prefix.join_str(libname(name)); let lib_result = dl::DynamicLibrary::open(Some(&x)); let lib = lib_result.unwrap(); let plugin = unsafe { lib.symbol("rustdoc_plugin_entrypoint") }.unwrap(); diff --git a/src/librustdoc/rustdoc.rs b/src/librustdoc/rustdoc.rs index a724ff3bf74d8..470ae7f9dfe00 100644 --- a/src/librustdoc/rustdoc.rs +++ b/src/librustdoc/rustdoc.rs @@ -134,13 +134,13 @@ pub fn main_args(args: &[~str]) -> int { info2!("going to format"); let started = time::precise_time_ns(); - let output = matches.opt_str("o").map(|s| Path(s)); + let output = matches.opt_str("o").map(|s| Path::from_str(s)); match matches.opt_str("w") { Some(~"html") | None => { - html::render::run(crate, output.unwrap_or(Path("doc"))) + html::render::run(crate, output.unwrap_or(Path::from_str("doc"))) } Some(~"json") => { - json_output(crate, res, output.unwrap_or(Path("doc.json"))) + json_output(crate, res, output.unwrap_or(Path::from_str("doc.json"))) } Some(s) => { println!("unknown output format: {}", s); @@ -188,8 +188,8 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { let mut plugins = matches.opt_strs("plugins"); // First, parse the crate and extract all relevant information. - let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s))); - let cr = Cell::new(Path(cratefile)); + let libs = Cell::new(matches.opt_strs("L").map(|s| Path::from_str(*s))); + let cr = Cell::new(Path::from_str(cratefile)); info2!("starting to run rustc"); let crate = do std::task::try { let cr = cr.take(); @@ -230,7 +230,7 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { // Load all plugins/passes into a PluginManager let path = matches.opt_str("plugin-path").unwrap_or(~"/tmp/rustdoc_ng/plugins"); - let mut pm = plugins::PluginManager::new(Path(path)); + let mut pm = plugins::PluginManager::new(Path::from_str(path)); for pass in passes.iter() { let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) { Some(i) => PASSES[i].n1(), @@ -254,7 +254,7 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { /// This input format purely deserializes the json output file. No passes are /// run over the deserialized output. fn json_input(input: &str) -> Result { - let input = match ::std::io::file_reader(&Path(input)) { + let input = match ::std::io::file_reader(&Path::from_str(input)) { Ok(i) => i, Err(s) => return Err(s), }; diff --git a/src/librusti/rusti.rs b/src/librusti/rusti.rs index b7f13f3f6b6ef..b511d17ec7778 100644 --- a/src/librusti/rusti.rs +++ b/src/librusti/rusti.rs @@ -142,7 +142,7 @@ fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str], let options = @session::options { crate_type: session::unknown_crate, binary: binary, - addl_lib_search_paths: @mut lib_search_paths.map(|p| Path(*p)), + addl_lib_search_paths: @mut lib_search_paths.map(|p| Path::from_str(*p)), jit: true, .. (*session::basic_options()).clone() }; @@ -315,8 +315,20 @@ fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str], // because it already exists and is newer than the source file, or // None if there were compile errors. fn compile_crate(src_filename: ~str, binary: ~str) -> Option { + fn has_prefix(v: &[u8], pref: &[u8]) -> bool { + v.len() >= pref.len() && v.slice_to(pref.len()) == pref + } + fn has_extension(v: &[u8], ext: Option<&[u8]>) -> bool { + match ext { + None => true, + Some(ext) => { + v.len() > ext.len() && v[v.len()-ext.len()-1] == '.' as u8 && + v.slice_from(v.len()-ext.len()) == ext + } + } + } match do task::try { - let src_path = Path(src_filename); + let src_path = Path::from_str(src_filename); let binary = binary.to_managed(); let options = @session::options { binary: binary, @@ -334,7 +346,7 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option { // If the library already exists and is newer than the source // file, skip compilation and return None. let mut should_compile = true; - let dir = os::list_dir_path(&Path(outputs.out_filename.dirname())); + let dir = os::list_dir_path(&outputs.out_filename.dir_path()); let maybe_lib_path = do dir.iter().find |file| { // The actual file's name has a hash value and version // number in it which is unknown at this time, so looking @@ -342,9 +354,9 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option { // instead we guess which file is the library by matching // the prefix and suffix of out_filename to files in the // directory. - let file_str = file.filename().unwrap(); - file_str.starts_with(outputs.out_filename.filestem().unwrap()) - && file_str.ends_with(outputs.out_filename.filetype().unwrap()) + let file_vec = file.filename().unwrap(); + has_prefix(file_vec, outputs.out_filename.filestem().unwrap()) && + has_extension(file_vec, outputs.out_filename.extension()) }; match maybe_lib_path { Some(lib_path) => { @@ -429,11 +441,12 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer, } } for crate in loaded_crates.iter() { - let crate_path = Path(*crate); - let crate_dir = crate_path.dirname(); + let crate_path = Path::from_str(*crate); + // FIXME (#9639): This needs to handle non-utf8 paths + let crate_dir = crate_path.dirname_str().unwrap(); repl.program.record_extern(format!("extern mod {};", *crate)); - if !repl.lib_search_paths.iter().any(|x| x == &crate_dir) { - repl.lib_search_paths.push(crate_dir); + if !repl.lib_search_paths.iter().any(|x| crate_dir == *x) { + repl.lib_search_paths.push(crate_dir.to_owned()); } } if loaded_crates.is_empty() { diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 390a09d4aa33e..6e0908d2a4f65 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -43,18 +43,18 @@ pub fn new_default_context(c: workcache::Context, p: Path) -> BuildContext { } fn file_is_fresh(path: &str, in_hash: &str) -> bool { - let path = Path(path); + let path = Path::from_str(path); os::path_exists(&path) && in_hash == digest_file_with_date(&path) } fn binary_is_fresh(path: &str, in_hash: &str) -> bool { - let path = Path(path); + let path = Path::from_str(path); os::path_exists(&path) && in_hash == digest_only_date(&path) } pub fn new_workcache_context(p: &Path) -> workcache::Context { - let db_file = p.push("rustpkg_db.json"); // ??? probably wrong - debug2!("Workcache database file: {}", db_file.to_str()); + let db_file = p.join_str("rustpkg_db.json"); // ??? probably wrong + debug2!("Workcache database file: {}", db_file.display()); let db = RWArc::new(Database::new(db_file)); let lg = RWArc::new(Logger::new()); let cfg = Arc::new(TreeMap::new()); @@ -73,7 +73,7 @@ pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version, source_workspace: root.clone(), build_in_destination: false, destination_workspace: root.clone(), - start_dir: root.push("src").push(name), + start_dir: root.join_many_str(["src", name.as_slice()]), id: PkgId{ version: version, ..PkgId::new(name)}, // n.b. This assumes the package only has one crate libs: ~[mk_crate(lib)], @@ -91,7 +91,7 @@ pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, source_workspace: root.clone(), build_in_destination: false, destination_workspace: root.clone(), - start_dir: root.push("src").push(name), + start_dir: root.join_many_str(["src", name.as_slice()]), id: PkgId{ version: version, ..PkgId::new(name)}, libs: ~[], // n.b. This assumes the package only has one crate diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs index 3b3cfe45b7a36..41b04c389ae05 100644 --- a/src/librustpkg/context.rs +++ b/src/librustpkg/context.rs @@ -123,7 +123,7 @@ impl Context { /// Debugging pub fn sysroot_str(&self) -> ~str { - self.sysroot.to_str() + self.sysroot.as_str().unwrap().to_owned() } // Hack so that rustpkg can run either out of a rustc target dir, @@ -132,7 +132,11 @@ impl Context { if !in_target(&self.sysroot) { self.sysroot.clone() } else { - self.sysroot.pop().pop().pop() + let mut p = self.sysroot.clone(); + p.pop(); + p.pop(); + p.pop(); + p } } @@ -150,8 +154,10 @@ impl Context { /// rustpkg from a Rust target directory. This is part of a /// kludgy hack used to adjust the sysroot. pub fn in_target(sysroot: &Path) -> bool { - debug2!("Checking whether {} is in target", sysroot.to_str()); - os::path_is_dir(&sysroot.pop().pop().push("rustc")) + debug2!("Checking whether {} is in target", sysroot.display()); + let mut p = sysroot.dir_path(); + p.set_filename_str("rustc"); + os::path_is_dir(&p) } impl RustcFlags { diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs index a3b807d1fc5b3..995f484a4e64f 100644 --- a/src/librustpkg/installed_packages.rs +++ b/src/librustpkg/installed_packages.rs @@ -17,27 +17,33 @@ use std::os; pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { let workspaces = rust_path(); for p in workspaces.iter() { - let binfiles = os::list_dir(&p.push("bin")); + let binfiles = os::list_dir(&p.join_str("bin")); for exec in binfiles.iter() { - let p = Path(*exec); - let exec_path = p.filestem(); - do exec_path.iter().advance |s| { - f(&PkgId::new(*s)) - }; + // FIXME (#9639): This needs to handle non-utf8 paths + match exec.filestem_str() { + None => (), + Some(exec_path) => { + if !f(&PkgId::new(exec_path)) { + return false; + } + } + } } - let libfiles = os::list_dir(&p.push("lib")); + let libfiles = os::list_dir(&p.join_str("lib")); for lib in libfiles.iter() { - let lib = Path(*lib); - debug2!("Full name: {}", lib.to_str()); - match has_library(&lib) { + debug2!("Full name: {}", lib.display()); + match has_library(lib) { Some(basename) => { + let parent = p.join_str("lib"); debug2!("parent = {}, child = {}", - p.push("lib").to_str(), lib.to_str()); - let rel_p = p.push("lib/").get_relative_to(&lib); - debug2!("Rel: {}", rel_p.to_str()); - let rel_path = rel_p.push(basename).to_str(); - debug2!("Rel name: {}", rel_path); - f(&PkgId::new(rel_path)); + parent.display(), lib.display()); + let rel_p = lib.path_relative_from(&parent).unwrap(); + debug2!("Rel: {}", rel_p.display()); + let rel_path = rel_p.join_str(basename); + do rel_path.with_display_str |s| { + debug2!("Rel name: {}", s); + f(&PkgId::new(s)); + } } None => () } @@ -48,10 +54,9 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { pub fn has_library(p: &Path) -> Option<~str> { let files = os::list_dir(p); - for q in files.iter() { - let as_path = Path(*q); - if as_path.filetype() == Some(os::consts::DLL_SUFFIX) { - let stuff : &str = as_path.filestem().expect("has_library: weird path"); + for path in files.iter() { + if path.extension_str() == Some(os::consts::DLL_EXTENSION) { + let stuff : &str = path.filestem_str().expect("has_library: weird path"); let mut stuff2 = stuff.split_str_iter(&"-"); let stuff3: ~[&str] = stuff2.collect(); // argh @@ -67,8 +72,10 @@ pub fn package_is_installed(p: &PkgId) -> bool { do list_installed_packages() |installed| { if installed == p { is_installed = true; + false + } else { + true } - false }; is_installed } diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index c06e197787c3e..bd22f1da913a9 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -59,14 +59,14 @@ impl PkgId { } }; - let path = Path(s); - if path.is_absolute { + let path = Path::from_str(s); + if !path.is_relative() { return cond.raise((path, ~"absolute pkgid")); } - if path.components.len() < 1 { + if path.filename().is_none() { return cond.raise((path, ~"0-length pkgid")); } - let short_name = path.filestem().expect(format!("Strange path! {}", s)); + let short_name = path.filestem_str().expect(format!("Strange path! {}", s)); let version = match given_version { Some(v) => v, @@ -87,9 +87,11 @@ impl PkgId { } pub fn hash(&self) -> ~str { - format!("{}-{}-{}", self.path.to_str(), - hash(self.path.to_str() + self.version.to_str()), - self.version.to_str()) + // FIXME (#9639): hash should take a &[u8] so we can hash the real path + do self.path.with_display_str |s| { + let vers = self.version.to_str(); + format!("{}-{}-{}", s, hash(s + vers), vers) + } } pub fn short_name_with_version(&self) -> ~str { @@ -98,7 +100,7 @@ impl PkgId { /// True if the ID has multiple components pub fn is_complex(&self) -> bool { - self.short_name != self.path.to_str() + self.short_name.as_bytes() != self.path.as_vec() } pub fn prefixes_iter(&self) -> Prefixes { @@ -115,7 +117,7 @@ impl PkgId { pub fn prefixes_iter(p: &Path) -> Prefixes { Prefixes { - components: p.components().to_owned(), + components: p.str_component_iter().map(|x|x.unwrap().to_owned()).to_owned_vec(), remaining: ~[] } } @@ -133,9 +135,10 @@ impl Iterator<(Path, Path)> for Prefixes { } else { let last = self.components.pop(); - self.remaining.push(last); + self.remaining.unshift(last); // converting to str and then back is a little unfortunate - Some((Path(self.components.to_str()), Path(self.remaining.to_str()))) + Some((Path::from_str(self.components.connect("/")), + Path::from_str(self.remaining.connect("/")))) } } } @@ -143,7 +146,7 @@ impl Iterator<(Path, Path)> for Prefixes { impl ToStr for PkgId { fn to_str(&self) -> ~str { // should probably use the filestem and not the whole path - format!("{}-{}", self.path.to_str(), self.version.to_str()) + format!("{}-{}", self.path.as_str().unwrap(), self.version.to_str()) } } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 4ffc57d7512b1..a924694cca5b2 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -58,9 +58,9 @@ impl ToStr for PkgSrc { fn to_str(&self) -> ~str { format!("Package ID {} in start dir {} [workspaces = {} -> {}]", self.id.to_str(), - self.start_dir.to_str(), - self.source_workspace.to_str(), - self.destination_workspace.to_str()) + self.start_dir.display(), + self.source_workspace.display(), + self.destination_workspace.display()) } } condition! { @@ -79,8 +79,8 @@ impl PkgSrc { debug2!("Checking package source for package ID {}, \ workspace = {} -> {}, use_rust_path_hack = {:?}", id.to_str(), - source_workspace.to_str(), - destination_workspace.to_str(), + source_workspace.display(), + destination_workspace.display(), use_rust_path_hack); let mut destination_workspace = destination_workspace.clone(); @@ -94,22 +94,27 @@ impl PkgSrc { } else { // We search for sources under both src/ and build/ , because build/ is where // automatically-checked-out sources go. - let result = source_workspace.push("src").push_rel(&id.path.pop()).push(format!("{}-{}", - id.short_name, id.version.to_str())); + let mut result = source_workspace.join_str("src"); + result.push_path(&id.path.dir_path()); + result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); + to_try.push(result); + let mut result = source_workspace.join_str("src"); + result.push_path(&id.path); to_try.push(result); - to_try.push(source_workspace.push("src").push_rel(&id.path)); - let result = build_dir.push("src").push_rel(&id.path.pop()).push(format!("{}-{}", - id.short_name, id.version.to_str())); + let mut result = build_dir.join_str("src"); + result.push_path(&id.path.dir_path()); + result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result.clone()); output_names.push(result); - let other_result = build_dir.push("src").push_rel(&id.path); + let mut other_result = build_dir.join_str("src"); + other_result.push_path(&id.path); to_try.push(other_result.clone()); output_names.push(other_result); } - debug2!("Checking dirs: {:?}", to_try.map(|s| s.to_str()).connect(":")); + debug2!("Checking dirs: {:?}", to_try.map(|p| p.to_display_str()).connect(":")); let path = to_try.iter().find(|&d| os::path_exists(d)); @@ -123,14 +128,14 @@ impl PkgSrc { // See if any of the prefixes of this package ID form a valid package ID // That is, is this a package ID that points into the middle of a workspace? for (prefix, suffix) in id.prefixes_iter() { - let package_id = PkgId::new(prefix.to_str()); - let path = build_dir.push_rel(&package_id.path); - debug2!("in loop: checking if {} is a directory", path.to_str()); + let package_id = PkgId::new(prefix.as_str().unwrap()); + let path = build_dir.join_path(&package_id.path); + debug2!("in loop: checking if {} is a directory", path.display()); if os::path_is_dir(&path) { let ps = PkgSrc::new(source_workspace, destination_workspace, use_rust_path_hack, - PkgId::new(prefix.to_str())); + package_id); match ps { PkgSrc { source_workspace: source, @@ -141,7 +146,7 @@ impl PkgSrc { source_workspace: source.clone(), build_in_destination: build_in_destination, destination_workspace: destination, - start_dir: start.push_rel(&suffix), + start_dir: start.join_path(&suffix), id: id, libs: ~[], mains: ~[], @@ -159,7 +164,7 @@ impl PkgSrc { // Ok, no prefixes work, so try fetching from git let mut ok_d = None; for w in output_names.iter() { - debug2!("Calling fetch_git on {}", w.to_str()); + debug2!("Calling fetch_git on {}", w.display()); let target_dir_opt = PkgSrc::fetch_git(w, &id); for p in target_dir_opt.iter() { ok_d = Some(p.clone()); @@ -209,9 +214,9 @@ impl PkgSrc { } }; debug2!("3. build_in_destination = {:?}", build_in_destination); - debug2!("source: {} dest: {}", source_workspace.to_str(), destination_workspace.to_str()); + debug2!("source: {} dest: {}", source_workspace.display(), destination_workspace.display()); - debug2!("For package id {}, returning {}", id.to_str(), dir.to_str()); + debug2!("For package id {}, returning {}", id.to_str(), dir.display()); if !os::path_is_dir(&dir) { cond.raise((id.clone(), ~"supplied path for package dir is a \ @@ -240,8 +245,8 @@ impl PkgSrc { use conditions::git_checkout_failed::cond; debug2!("Checking whether {} (path = {}) exists locally. Cwd = {}, does it? {:?}", - pkgid.to_str(), pkgid.path.to_str(), - os::getcwd().to_str(), + pkgid.to_str(), pkgid.path.display(), + os::getcwd().display(), os::path_exists(&pkgid.path)); match safe_git_clone(&pkgid.path, &pkgid.version, local) { @@ -250,14 +255,14 @@ impl PkgSrc { Some(local.clone()) } DirToUse(clone_target) => { - if pkgid.path.components().len() < 2 { + if pkgid.path.component_iter().nth(1).is_none() { // If a non-URL, don't bother trying to fetch return None; } - let url = format!("https://{}", pkgid.path.to_str()); + let url = format!("https://{}", pkgid.path.as_str().unwrap()); debug2!("Fetching package: git clone {} {} [version={}]", - url, clone_target.to_str(), pkgid.version.to_str()); + url, clone_target.display(), pkgid.version.to_str()); let mut failed = false; @@ -273,7 +278,7 @@ impl PkgSrc { // Move clone_target to local. // First, create all ancestor directories. - let moved = make_dir_rwx_recursive(&local.pop()) + let moved = make_dir_rwx_recursive(&local.dir_path()) && os::rename_file(&clone_target, local); if moved { Some(local.clone()) } else { None } @@ -284,28 +289,31 @@ impl PkgSrc { // If a file named "pkg.rs" in the start directory exists, // return the path for it. Otherwise, None pub fn package_script_option(&self) -> Option { - let maybe_path = self.start_dir.push("pkg.rs"); - debug2!("package_script_option: checking whether {} exists", maybe_path.to_str()); + let maybe_path = self.start_dir.join_str("pkg.rs"); + debug2!("package_script_option: checking whether {} exists", maybe_path.display()); if os::path_exists(&maybe_path) { Some(maybe_path) - } - else { + } else { None } } /// True if the given path's stem is self's pkg ID's stem fn stem_matches(&self, p: &Path) -> bool { - p.filestem().map_default(false, |p| { p == self.id.short_name.as_slice() }) + p.filestem().map_default(false, |p| { p == self.id.short_name.as_bytes() }) } pub fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { - assert!(p.components.len() > prefix); - let mut sub = Path(""); - for c in p.components.slice(prefix, p.components.len()).iter() { - sub = sub.push(*c); + let mut it = p.component_iter().peekable(); + if prefix > 0 { + it.nth(prefix-1); // skip elements + } + assert!(it.peek().is_some()); + let mut sub = Path::from_str("."); + for c in it { + sub.push(c); } - debug2!("Will compile crate {}", sub.to_str()); + debug2!("Will compile crate {}", sub.display()); cs.push(Crate::new(&sub)); } @@ -318,10 +326,10 @@ impl PkgSrc { pub fn find_crates_with_filter(&mut self, filter: &fn(&str) -> bool) { use conditions::missing_pkg_files::cond; - let prefix = self.start_dir.components.len(); + let prefix = self.start_dir.component_iter().len(); debug2!("Matching against {}", self.id.short_name); do os::walk_dir(&self.start_dir) |pth| { - let maybe_known_crate_set = match pth.filename() { + let maybe_known_crate_set = match pth.filename_str() { Some(filename) if filter(filename) => match filename { "lib.rs" => Some(&mut self.libs), "main.rs" => Some(&mut self.mains), @@ -349,7 +357,7 @@ impl PkgSrc { } debug2!("In {}, found {} libs, {} mains, {} tests, {} benchs", - self.start_dir.to_str(), + self.start_dir.display(), self.libs.len(), self.mains.len(), self.tests.len(), @@ -362,18 +370,17 @@ impl PkgSrc { cfgs: &[~str], what: OutputType) { for crate in crates.iter() { - let path = self.start_dir.push_rel(&crate.file).normalize(); - debug2!("build_crates: compiling {}", path.to_str()); - let path_str = path.to_str(); + let path = self.start_dir.join_path(&crate.file); + debug2!("build_crates: compiling {}", path.display()); let cfgs = crate.cfgs + cfgs; do ctx.workcache_context.with_prep(crate_tag(&path)) |prep| { - debug2!("Building crate {}, declaring it as an input", path.to_str()); - prep.declare_input("file", path.to_str(), + debug2!("Building crate {}, declaring it as an input", path.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + prep.declare_input("file", path.as_str().unwrap(), workcache_support::digest_file_with_date(&path)); let subpath = path.clone(); let subcfgs = cfgs.clone(); - let subpath_str = path_str.clone(); let subcx = ctx.clone(); let id = self.id.clone(); let sub_dir = self.build_workspace().clone(); @@ -387,9 +394,14 @@ impl PkgSrc { sub_flags, subcfgs, false, - what).to_str(); - debug2!("Result of compiling {} was {}", subpath_str, result); - result + what); + // XXX: result is an Option. The following code did not take that + // into account. I'm not sure if the workcache really likes seeing the + // output as "Some(\"path\")". But I don't know what to do about it. + // FIXME (#9639): This needs to handle non-utf8 paths + let result = result.map(|p|p.as_str().unwrap()); + debug2!("Result of compiling {} was {}", subpath.display(), result.to_str()); + result.to_str() } }; } @@ -403,10 +415,10 @@ impl PkgSrc { debug2!("In declare inputs, self = {}", self.to_str()); for cs in to_do.iter() { for c in cs.iter() { - let path = self.start_dir.push_rel(&c.file).normalize(); - debug2!("Declaring input: {}", path.to_str()); - prep.declare_input("file", - path.to_str(), + let path = self.start_dir.join_path(&c.file); + debug2!("Declaring input: {}", path.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + prep.declare_input("file", path.as_str().unwrap(), workcache_support::digest_file_with_date(&path.clone())); } } @@ -422,7 +434,7 @@ impl PkgSrc { let tests = self.tests.clone(); let benchs = self.benchs.clone(); debug2!("Building libs in {}, destination = {}", - self.source_workspace.to_str(), self.build_workspace().to_str()); + self.source_workspace.display(), self.build_workspace().display()); self.build_crates(build_context, libs, cfgs, Lib); debug2!("Building mains"); self.build_crates(build_context, mains, cfgs, Main); @@ -447,7 +459,7 @@ impl PkgSrc { let crate_sets = [&self.libs, &self.mains, &self.tests, &self.benchs]; for crate_set in crate_sets.iter() { for c in crate_set.iter() { - debug2!("Built crate: {}", c.file.to_str()) + debug2!("Built crate: {}", c.file.display()) } } } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index fbb2255ad1ccb..34b387fc9a73d 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -53,7 +53,7 @@ pub fn make_dir_rwx_recursive(p: &Path) -> bool { os::mkdir_recursive(p, U_RWX) /// True if there's a directory in with /// pkgid's short name pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { - workspace_contains_package_id_(pkgid, workspace, |p| { p.push("src") }).is_some() + workspace_contains_package_id_(pkgid, workspace, |p| p.join_str("src")).is_some() } pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, @@ -68,10 +68,9 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, let mut found = None; do os::walk_dir(&src_dir) |p| { if os::path_is_dir(p) { - if *p == src_dir.push_rel(&pkgid.path) || { - let pf = p.filename(); - do pf.iter().any |pf| { - let g = pf.to_str(); + if *p == src_dir.join_path(&pkgid.path) || { + let pf = p.filename_str(); + do pf.iter().any |&g| { match split_version_general(g, '-') { None => false, Some((ref might_match, ref vers)) => { @@ -89,9 +88,9 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, }; if found.is_some() { - debug2!("Found {} in {}", pkgid.to_str(), workspace.to_str()); + debug2!("Found {} in {}", pkgid.to_str(), workspace.display()); } else { - debug2!("Didn't find {} in {}", pkgid.to_str(), workspace.to_str()); + debug2!("Didn't find {} in {}", pkgid.to_str(), workspace.display()); } found } @@ -99,20 +98,24 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, /// Return the target-specific build subdirectory, pushed onto `base`; /// doesn't check that it exists or create it pub fn target_build_dir(workspace: &Path) -> Path { - workspace.push("build").push(host_triple()) + let mut dir = workspace.join_str("build"); + dir.push_str(host_triple()); + dir } /// Return the target-specific lib subdirectory, pushed onto `base`; /// doesn't check that it exists or create it fn target_lib_dir(workspace: &Path) -> Path { - workspace.push("lib").push(host_triple()) + let mut dir = workspace.join_str("lib"); + dir.push_str(host_triple()); + dir } /// Return the bin subdirectory, pushed onto `base`; /// doesn't check that it exists or create it /// note: this isn't target-specific fn target_bin_dir(workspace: &Path) -> Path { - workspace.push("bin") + workspace.join_str("bin") } /// Figure out what the executable name for in 's build @@ -121,12 +124,12 @@ pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option< let mut result = target_build_dir(workspace); result = mk_output_path(Main, Build, pkgid, result); debug2!("built_executable_in_workspace: checking whether {} exists", - result.to_str()); + result.display()); if os::path_exists(&result) { Some(result) } else { - debug2!("built_executable_in_workspace: {} does not exist", result.to_str()); + debug2!("built_executable_in_workspace: {} does not exist", result.display()); None } } @@ -148,12 +151,12 @@ fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Opt // should use a target-specific subdirectory result = mk_output_path(what, Build, pkgid, result); debug2!("output_in_workspace: checking whether {} exists", - result.to_str()); + result.display()); if os::path_exists(&result) { Some(result) } else { - error2!("output_in_workspace: {} does not exist", result.to_str()); + error2!("output_in_workspace: {} does not exist", result.display()); None } } @@ -167,7 +170,8 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option Option { // This could break once we're handling multiple versions better -- I should add a test for it - match pkg_path.filename() { + // FIXME (#9639): This needs to handle non-utf8 paths + match pkg_path.filename_str() { None => None, Some(short_name) => library_in_workspace(pkg_path, short_name, @@ -189,10 +193,10 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target, // contents debug2!("short_name = {} where = {:?} workspace = {} \ - prefix = {}", short_name, where, workspace.to_str(), prefix); + prefix = {}", short_name, where, workspace.display(), prefix); let dir_to_search = match where { - Build => target_build_dir(workspace).push_rel(path), + Build => target_build_dir(workspace).join_path(path), Install => target_lib_dir(workspace) }; @@ -201,28 +205,24 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target, // rustc doesn't use target-specific subdirectories pub fn system_library(sysroot: &Path, lib_name: &str) -> Option { - library_in(lib_name, &NoVersion, &sysroot.push("lib")) + library_in(lib_name, &NoVersion, &sysroot.join_str("lib")) } fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Option { - debug2!("Listing directory {}", dir_to_search.to_str()); + debug2!("Listing directory {}", dir_to_search.display()); let dir_contents = os::list_dir(dir_to_search); debug2!("dir has {:?} entries", dir_contents.len()); let lib_prefix = format!("{}{}", os::consts::DLL_PREFIX, short_name); - let lib_filetype = os::consts::DLL_SUFFIX; + let lib_filetype = os::consts::DLL_EXTENSION; debug2!("lib_prefix = {} and lib_filetype = {}", lib_prefix, lib_filetype); // Find a filename that matches the pattern: // (lib_prefix)-hash-(version)(lib_suffix) - let paths = do dir_contents.iter().map |p| { - Path((*p).clone()) - }; - - let mut libraries = do paths.filter |p| { - let extension = p.filetype(); - debug2!("p = {}, p's extension is {:?}", p.to_str(), extension); + let mut libraries = do dir_contents.iter().filter |p| { + let extension = p.extension_str(); + debug2!("p = {}, p's extension is {:?}", p.display(), extension); match extension { None => false, Some(ref s) => lib_filetype == *s @@ -233,7 +233,7 @@ fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Opti for p_path in libraries { // Find a filename that matches the pattern: (lib_prefix)-hash-(version)(lib_suffix) // and remember what the hash was - let mut f_name = match p_path.filestem() { + let mut f_name = match p_path.filestem_str() { Some(s) => s, None => continue }; // Already checked the filetype above @@ -267,14 +267,14 @@ fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Opti if result_filename.is_none() { debug2!("warning: library_in_workspace didn't find a library in {} for {}", - dir_to_search.to_str(), short_name); + dir_to_search.display(), short_name); } // Return the filename that matches, which we now know exists // (if result_filename != None) let abs_path = do result_filename.map |result_filename| { - let absolute_path = dir_to_search.push_rel(&result_filename); - debug2!("result_filename = {}", absolute_path.to_str()); + let absolute_path = dir_to_search.join_path(&result_filename); + debug2!("result_filename = {}", absolute_path.display()); absolute_path }; @@ -297,7 +297,7 @@ pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { if !os::path_is_dir(workspace) { cond.raise(((*workspace).clone(), format!("Workspace supplied to target_library_in_workspace \ - is not a directory! {}", workspace.to_str()))); + is not a directory! {}", workspace.display()))); } target_file_in_workspace(pkgid, workspace, Lib, Install) } @@ -329,14 +329,14 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, // Artifacts in the build directory live in a package-ID-specific subdirectory, // but installed ones don't. let result = match (where, what) { - (Build, _) => target_build_dir(workspace).push_rel(&pkgid.path), - (Install, Lib) => target_lib_dir(workspace), + (Build, _) => target_build_dir(workspace).join_path(&pkgid.path), + (Install, Lib) => target_lib_dir(workspace), (Install, _) => target_bin_dir(workspace) }; if !os::path_exists(&result) && !mkdir_recursive(&result, U_RWX) { cond.raise((result.clone(), format!("target_file_in_workspace couldn't \ create the {} dir (pkgid={}, workspace={}, what={:?}, where={:?}", - subdir, pkgid.to_str(), workspace.to_str(), what, where))); + subdir, pkgid.to_str(), workspace.display(), what, where))); } mk_output_path(what, where, pkgid, result) } @@ -347,8 +347,8 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { use conditions::bad_path::cond; let mut result = target_build_dir(workspace); - result = result.push_rel(&pkgid.path); - debug2!("Creating build dir {} for package id {}", result.to_str(), + result.push_path(&pkgid.path); + debug2!("Creating build dir {} for package id {}", result.display(), pkgid.to_str()); if os::path_exists(&result) || os::mkdir_recursive(&result, U_RWX) { result @@ -370,16 +370,16 @@ pub fn mk_output_path(what: OutputType, where: Target, // If we're installing, it just goes under ... Install => workspace, // and if we're just building, it goes in a package-specific subdir - Build => workspace.push_rel(&pkg_id.path) + Build => workspace.join_path(&pkg_id.path) }; debug2!("[{:?}:{:?}] mk_output_path: short_name = {}, path = {}", what, where, if what == Lib { short_name_with_version.clone() } else { pkg_id.short_name.clone() }, - dir.to_str()); + dir.display()); let mut output_path = match what { // this code is duplicated from elsewhere; fix this - Lib => dir.push(os::dll_filename(short_name_with_version)), + Lib => dir.join_str(os::dll_filename(short_name_with_version)), // executable names *aren't* versioned - _ => dir.push(format!("{}{}{}", pkg_id.short_name, + _ => dir.join_str(format!("{}{}{}", pkg_id.short_name, match what { Test => "test", Bench => "bench", @@ -388,9 +388,9 @@ pub fn mk_output_path(what: OutputType, where: Target, os::EXE_SUFFIX)) }; if !output_path.is_absolute() { - output_path = os::getcwd().push_rel(&output_path).normalize(); + output_path = os::getcwd().join_path(&output_path); } - debug2!("mk_output_path: returning {}", output_path.to_str()); + debug2!("mk_output_path: returning {}", output_path.display()); output_path } @@ -409,14 +409,14 @@ pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) { } if !did_something { warn(format!("Warning: there don't seem to be any files for {} installed in {}", - pkgid.to_str(), workspace.to_str())); + pkgid.to_str(), workspace.display())); } } fn dir_has_file(dir: &Path, file: &str) -> bool { assert!(dir.is_absolute()); - os::path_exists(&dir.push(file)) + os::path_exists(&dir.join_str(file)) } pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option { @@ -425,15 +425,15 @@ pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option { // Require that the parent directory match the package ID // Note that this only matches if the package ID being searched for // has a name that's a single component - if dir.is_parent_of(&p.path) || dir.is_parent_of(&versionize(&p.path, &p.version)) { - debug2!("In find_dir_using_rust_path_hack: checking dir {}", dir.to_str()); + if dir.ends_with_path(&p.path) || dir.ends_with_path(&versionize(&p.path, &p.version)) { + debug2!("In find_dir_using_rust_path_hack: checking dir {}", dir.display()); if dir_has_file(dir, "lib.rs") || dir_has_file(dir, "main.rs") || dir_has_file(dir, "test.rs") || dir_has_file(dir, "bench.rs") { - debug2!("Did find id {} in dir {}", p.to_str(), dir.to_str()); + debug2!("Did find id {} in dir {}", p.to_str(), dir.display()); return Some(dir.clone()); } } - debug2!("Didn't find id {} in dir {}", p.to_str(), dir.to_str()) + debug2!("Didn't find id {} in dir {}", p.to_str(), dir.display()) } None } @@ -449,8 +449,12 @@ pub fn user_set_rust_path() -> bool { /// Append the version string onto the end of the path's filename pub fn versionize(p: &Path, v: &Version) -> Path { - let q = p.file_path().to_str(); - p.with_filename(format!("{}-{}", q, v.to_str())) + let q = p.file_path().expect("path is a directory"); + let mut q = q.as_vec().to_owned(); + q.push('-' as u8); + let vs = v.to_str(); + q.push_all(vs.as_bytes()); + p.with_filename(q) } diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 63195112747ac..47a65753cc957 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -105,7 +105,7 @@ impl<'self> PkgScript<'self> { let binary = os::args()[0].to_managed(); // Build the rustc session data structures to pass // to the compiler - debug2!("pkgscript parse: {}", sysroot.to_str()); + debug2!("pkgscript parse: {}", sysroot.display()); let options = @session::options { binary: binary, maybe_sysroot: Some(sysroot), @@ -141,31 +141,36 @@ impl<'self> PkgScript<'self> { sysroot: &Path) -> (~[~str], ExitCode) { let sess = self.sess; - debug2!("Working directory = {}", self.build_dir.to_str()); + debug2!("Working directory = {}", self.build_dir.display()); // Collect together any user-defined commands in the package script let crate = util::ready_crate(sess, self.crate.take_unwrap()); debug2!("Building output filenames with script name {}", driver::source_name(&driver::file_input(self.input.clone()))); - let exe = self.build_dir.push(~"pkg" + util::exe_suffix()); + let exe = self.build_dir.join_str("pkg" + util::exe_suffix()); util::compile_crate_from_input(&self.input, exec, Nothing, &self.build_dir, sess, crate); - debug2!("Running program: {} {} {}", exe.to_str(), - sysroot.to_str(), "install"); + debug2!("Running program: {} {} {}", exe.display(), + sysroot.display(), "install"); // Discover the output - exec.discover_output("binary", exe.to_str(), digest_only_date(&exe)); + // FIXME (#9639): This needs to handle non-utf8 paths + exec.discover_output("binary", exe.as_str().unwrap(), digest_only_date(&exe)); // FIXME #7401 should support commands besides `install` - let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]); + // FIXME (#9639): This needs to handle non-utf8 paths + let status = run::process_status(exe.as_str().unwrap(), + [sysroot.as_str().unwrap().to_owned(), ~"install"]); if status != 0 { return (~[], status); } else { debug2!("Running program (configs): {} {} {}", - exe.to_str(), sysroot.to_str(), "configs"); - let output = run::process_output(exe.to_str(), [sysroot.to_str(), ~"configs"]); + exe.display(), sysroot.display(), "configs"); + // FIXME (#9639): This needs to handle non-utf8 paths + let output = run::process_output(exe.as_str().unwrap(), + [sysroot.as_str().unwrap().to_owned(), ~"configs"]); // Run the configs() function to get the configs let cfgs = str::from_utf8_slice(output.output).word_iter() .map(|w| w.to_owned()).collect(); @@ -208,7 +213,8 @@ impl CtxMethods for BuildContext { match cwd_to_workspace() { None if self.context.use_rust_path_hack => { let cwd = os::getcwd(); - let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]); + // FIXME (#9639): This needs to handle non-utf8 paths + let pkgid = PkgId::new(cwd.filename_str().unwrap()); let mut pkg_src = PkgSrc::new(cwd, default_workspace(), true, pkgid); self.build(&mut pkg_src, what); match pkg_src { @@ -237,13 +243,12 @@ impl CtxMethods for BuildContext { let mut dest_ws = default_workspace(); do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| { debug2!("found pkg {} in workspace {}, trying to build", - pkgid.to_str(), workspace.to_str()); + pkgid.to_str(), workspace.display()); dest_ws = determine_destination(os::getcwd(), self.context.use_rust_path_hack, workspace); - let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(), - false, pkgid.clone()); - self.build(&mut pkg_src, what); + let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); + dest_ws = Some(self.build(&mut pkg_src, what)); true }; // n.b. If this builds multiple packages, it only returns the workspace for @@ -290,8 +295,9 @@ impl CtxMethods for BuildContext { match cwd_to_workspace() { None if self.context.use_rust_path_hack => { let cwd = os::getcwd(); + // FIXME (#9639): This needs to handle non-utf8 paths let inferred_pkgid = - PkgId::new(cwd.components[cwd.components.len() - 1]); + PkgId::new(cwd.filename_str().unwrap()); self.install(PkgSrc::new(cwd, default_workspace(), true, inferred_pkgid), &Everything); } @@ -331,7 +337,9 @@ impl CtxMethods for BuildContext { "list" => { io::println("Installed packages:"); do installed_packages::list_installed_packages |pkg_id| { - println(pkg_id.path.to_str()); + do pkg_id.path.with_display_str |s| { + println(s); + } true }; } @@ -379,7 +387,7 @@ impl CtxMethods for BuildContext { do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| { path_util::uninstall_package_from(workspace, &pkgid); note(format!("Uninstalled package {} (was installed in {})", - pkgid.to_str(), workspace.to_str())); + pkgid.to_str(), workspace.display())); true }; } @@ -407,23 +415,25 @@ impl CtxMethods for BuildContext { let pkgid = pkg_src.id.clone(); debug2!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \ - pkgid = {} pkgsrc start_dir = {}", workspace.to_str(), - in_rust_path(&workspace), is_git_dir(&workspace.push_rel(&pkgid.path)), - pkgid.to_str(), pkg_src.start_dir.to_str()); + pkgid = {} pkgsrc start_dir = {}", workspace.display(), + in_rust_path(&workspace), is_git_dir(&workspace.join_path(&pkgid.path)), + pkgid.to_str(), pkg_src.start_dir.display()); // If workspace isn't in the RUST_PATH, and it's a git repo, // then clone it into the first entry in RUST_PATH, and repeat - if !in_rust_path(&workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) { - let out_dir = default_workspace().push("src").push_rel(&pkgid.path); - let git_result = source_control::safe_git_clone(&workspace.push_rel(&pkgid.path), + if !in_rust_path(&workspace) && is_git_dir(&workspace.join_path(&pkgid.path)) { + let mut out_dir = default_workspace().join_str("src"); + out_dir.push_path(&pkgid.path); + let git_result = source_control::safe_git_clone(&workspace.join_path(&pkgid.path), &pkgid.version, &out_dir); match git_result { CheckedOutSources => make_read_only(&out_dir), - _ => cond.raise((pkgid.path.to_str(), out_dir.clone())) + // FIXME (#9639): This needs to handle non-utf8 paths + _ => cond.raise((pkgid.path.as_str().unwrap(), out_dir.clone())) }; let default_ws = default_workspace(); - debug2!("Calling build recursively with {:?} and {:?}", default_ws.to_str(), + debug2!("Calling build recursively with {:?} and {:?}", default_ws.display(), pkgid.to_str()); return self.build(&mut PkgSrc::new(default_ws.clone(), default_ws, @@ -439,8 +449,10 @@ impl CtxMethods for BuildContext { let cfgs = match pkg_src.package_script_option() { Some(package_script_path) => { let sysroot = self.sysroot_to_use(); + // FIXME (#9639): This needs to handle non-utf8 paths + let pkg_script_path_str = package_script_path.as_str().unwrap(); let (cfgs, hook_result) = - do self.workcache_context.with_prep(package_script_path.to_str()) |prep| { + do self.workcache_context.with_prep(pkg_script_path_str) |prep| { let sub_sysroot = sysroot.clone(); let package_script_path_clone = package_script_path.clone(); let sub_ws = workspace.clone(); @@ -476,13 +488,13 @@ impl CtxMethods for BuildContext { // Find crates inside the workspace &Everything => pkg_src.find_crates(), // Find only tests - &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path(s)) }), + &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::from_str(s)) }), // Don't infer any crates -- just build the one that was requested &JustOne(ref p) => { // We expect that p is relative to the package source's start directory, // so check that assumption - debug2!("JustOne: p = {}", p.to_str()); - assert!(os::path_exists(&pkg_src.start_dir.push_rel(p))); + debug2!("JustOne: p = {}", p.display()); + assert!(os::path_exists(&pkg_src.start_dir.join_path(p))); if is_lib(p) { PkgSrc::push_crate(&mut pkg_src.libs, 0, p); } else if is_main(p) { @@ -492,7 +504,7 @@ impl CtxMethods for BuildContext { } else if is_bench(p) { PkgSrc::push_crate(&mut pkg_src.benchs, 0, p); } else { - warn(format!("Not building any crates for dependency {}", p.to_str())); + warn(format!("Not building any crates for dependency {}", p.display())); return; } } @@ -509,10 +521,10 @@ impl CtxMethods for BuildContext { let dir = build_pkg_id_in_workspace(id, workspace); note(format!("Cleaning package {} (removing directory {})", - id.to_str(), dir.to_str())); + id.to_str(), dir.display())); if os::path_exists(&dir) { os::remove_dir_recursive(&dir); - note(format!("Removed directory {}", dir.to_str())); + note(format!("Removed directory {}", dir.display())); } note(format!("Cleaned package {}", id.to_str())); @@ -541,21 +553,22 @@ impl CtxMethods for BuildContext { debug2!("In declare inputs for {}", id.to_str()); for cs in to_do.iter() { for c in cs.iter() { - let path = pkg_src.start_dir.push_rel(&c.file).normalize(); - debug2!("Recording input: {}", path.to_str()); - inputs.push((~"file", path.to_str())); + let path = pkg_src.start_dir.join_path(&c.file); + debug2!("Recording input: {}", path.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + inputs.push((~"file", path.as_str().unwrap().to_owned())); } } let result = self.install_no_build(pkg_src.build_workspace(), &pkg_src.destination_workspace, - &id).map(|s| Path(*s)); + &id).map(|s| Path::from_str(*s)); debug2!("install: id = {}, about to call discover_outputs, {:?}", - id.to_str(), result.to_str()); + id.to_str(), result.map(|p| p.to_display_str())); installed_files = installed_files + result; note(format!("Installed package {} to {}", id.to_str(), - pkg_src.destination_workspace.to_str())); + pkg_src.destination_workspace.display())); (installed_files, inputs) } @@ -578,18 +591,20 @@ impl CtxMethods for BuildContext { debug2!("target_exec = {} target_lib = {:?} \ maybe_executable = {:?} maybe_library = {:?}", - target_exec.to_str(), target_lib, + target_exec.display(), target_lib, maybe_executable, maybe_library); do self.workcache_context.with_prep(id.install_tag()) |prep| { for ee in maybe_executable.iter() { + // FIXME (#9639): This needs to handle non-utf8 paths prep.declare_input("binary", - ee.to_str(), + ee.as_str().unwrap(), workcache_support::digest_only_date(ee)); } for ll in maybe_library.iter() { + // FIXME (#9639): This needs to handle non-utf8 paths prep.declare_input("binary", - ll.to_str(), + ll.as_str().unwrap(), workcache_support::digest_only_date(ll)); } let subex = maybe_executable.clone(); @@ -601,31 +616,32 @@ impl CtxMethods for BuildContext { let mut outputs = ~[]; for exec in subex.iter() { - debug2!("Copying: {} -> {}", exec.to_str(), sub_target_ex.to_str()); + debug2!("Copying: {} -> {}", exec.display(), sub_target_ex.display()); if !(os::mkdir_recursive(&sub_target_ex.dir_path(), U_RWX) && os::copy_file(exec, &sub_target_ex)) { cond.raise(((*exec).clone(), sub_target_ex.clone())); } + // FIXME (#9639): This needs to handle non-utf8 paths exe_thing.discover_output("binary", - sub_target_ex.to_str(), + sub_target_ex.as_str().unwrap(), workcache_support::digest_only_date(&sub_target_ex)); - outputs.push(sub_target_ex.to_str()); + outputs.push(sub_target_ex.as_str().unwrap().to_owned()); } for lib in sublib.iter() { - let target_lib = sub_target_lib + let mut target_lib = sub_target_lib .clone().expect(format!("I built {} but apparently \ - didn't install it!", lib.to_str())); + didn't install it!", lib.display())); let target_lib = target_lib - .pop().push(lib.filename().expect("weird target lib")); + .set_filename(lib.filename().expect("weird target lib")); if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && os::copy_file(lib, &target_lib)) { cond.raise(((*lib).clone(), target_lib.clone())); } - debug2!("3. discovering output {}", target_lib.to_str()); + debug2!("3. discovering output {}", target_lib.display()); exe_thing.discover_output("binary", - target_lib.to_str(), + target_lib.as_str().unwrap(), workcache_support::digest_only_date(&target_lib)); - outputs.push(target_lib.to_str()); + outputs.push(target_lib.as_str().unwrap().to_owned()); } outputs } @@ -639,23 +655,24 @@ impl CtxMethods for BuildContext { fn test(&self, pkgid: &PkgId, workspace: &Path) { match built_test_in_workspace(pkgid, workspace) { Some(test_exec) => { - debug2!("test: test_exec = {}", test_exec.to_str()); - let status = run::process_status(test_exec.to_str(), [~"--test"]); + debug2!("test: test_exec = {}", test_exec.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]); os::set_exit_status(status); } None => { error(format!("Internal error: test executable for package ID {} in workspace {} \ wasn't built! Please report this as a bug.", - pkgid.to_str(), workspace.to_str())); + pkgid.to_str(), workspace.display())); } } } fn init(&self) { - os::mkdir_recursive(&Path("src"), U_RWX); - os::mkdir_recursive(&Path("lib"), U_RWX); - os::mkdir_recursive(&Path("bin"), U_RWX); - os::mkdir_recursive(&Path("build"), U_RWX); + os::mkdir_recursive(&Path::from_str("src"), U_RWX); + os::mkdir_recursive(&Path::from_str("lib"), U_RWX); + os::mkdir_recursive(&Path::from_str("bin"), U_RWX); + os::mkdir_recursive(&Path::from_str("build"), U_RWX); } fn uninstall(&self, _id: &str, _vers: Option<~str>) { @@ -835,12 +852,13 @@ pub fn main_args(args: &[~str]) -> int { let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect(); remaining_args.shift(); let sroot = match supplied_sysroot { - Some(getopts::Val(s)) => Path(s), + Some(getopts::Val(s)) => Path::from_str(s), _ => filesearch::get_or_default_sysroot() }; - debug2!("Using sysroot: {}", sroot.to_str()); - debug2!("Will store workcache in {}", default_workspace().to_str()); + debug2!("Using sysroot: {}", sroot.display()); + let ws = default_workspace(); + debug2!("Will store workcache in {}", ws.display()); let rm_args = remaining_args.clone(); let sub_cmd = cmd.clone(); @@ -866,7 +884,8 @@ pub fn main_args(args: &[~str]) -> int { fn declare_package_script_dependency(prep: &mut workcache::Prep, pkg_src: &PkgSrc) { match pkg_src.package_script_option() { - Some(ref p) => prep.declare_input("file", p.to_str(), + // FIXME (#9639): This needs to handle non-utf8 paths + Some(ref p) => prep.declare_input("file", p.as_str().unwrap(), workcache_support::digest_file_with_date(p)), None => () } diff --git a/src/librustpkg/search.rs b/src/librustpkg/search.rs index f0042e1f8e2d7..080ba461f05fb 100644 --- a/src/librustpkg/search.rs +++ b/src/librustpkg/search.rs @@ -18,7 +18,7 @@ use version::Version; pub fn find_installed_library_in_rust_path(pkg_path: &Path, _version: &Version) -> Option { let rp = rust_path(); debug2!("find_installed_library_in_rust_path: looking for path {}", - pkg_path.to_str()); + pkg_path.display()); for p in rp.iter() { match installed_library_in_workspace(pkg_path, p) { Some(path) => return Some(path), diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs index 33b86e7cbc529..b910c07920508 100644 --- a/src/librustpkg/source_control.rs +++ b/src/librustpkg/source_control.rs @@ -24,14 +24,17 @@ use path_util::chmod_read_only; pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult { if os::path_exists(source) { debug2!("{} exists locally! Cloning it into {}", - source.to_str(), target.to_str()); + source.display(), target.display()); // Ok to use target here; we know it will succeed assert!(os::path_is_dir(source)); assert!(is_git_dir(source)); if !os::path_exists(target) { - debug2!("Running: git clone {} {}", source.to_str(), target.to_str()); - let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]); + debug2!("Running: git clone {} {}", source.display(), target.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + let outp = run::process_output("git", [~"clone", + source.as_str().unwrap().to_owned(), + target.as_str().unwrap().to_owned()]); if outp.status != 0 { io::println(str::from_utf8_owned(outp.output.clone())); io::println(str::from_utf8_owned(outp.error)); @@ -40,11 +43,13 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult else { match v { &ExactRevision(ref s) => { + let git_dir = target.join_str(".git"); debug2!("`Running: git --work-tree={} --git-dir={} checkout {}", - *s, target.to_str(), target.push(".git").to_str()); + *s, target.display(), git_dir.display()); + // FIXME (#9639: This needs to handle non-utf8 paths let outp = run::process_output("git", - [format!("--work-tree={}", target.to_str()), - format!("--git-dir={}", target.push(".git").to_str()), + [format!("--work-tree={}", target.as_str().unwrap().to_owned()), + format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()), ~"checkout", format!("{}", *s)]); if outp.status != 0 { io::println(str::from_utf8_owned(outp.output.clone())); @@ -59,11 +64,13 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult // Check that no version was specified. There's no reason to not handle the // case where a version was requested, but I haven't implemented it. assert!(*v == NoVersion); + let git_dir = target.join_str(".git"); debug2!("Running: git --work-tree={} --git-dir={} pull --no-edit {}", - target.to_str(), target.push(".git").to_str(), source.to_str()); - let args = [format!("--work-tree={}", target.to_str()), - format!("--git-dir={}", target.push(".git").to_str()), - ~"pull", ~"--no-edit", source.to_str()]; + target.display(), git_dir.display(), source.display()); + // FIXME (#9639: This needs to handle non-utf8 paths + let args = [format!("--work-tree={}", target.as_str().unwrap().to_owned()), + format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()), + ~"pull", ~"--no-edit", source.as_str().unwrap().to_owned()]; let outp = run::process_output("git", args); assert!(outp.status == 0); } @@ -73,7 +80,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult let scratch_dir = TempDir::new("rustpkg"); let clone_target = match scratch_dir { - Some(d) => d.unwrap().push("rustpkg_temp"), + Some(d) => d.unwrap().join_str("rustpkg_temp"), None => cond.raise(~"Failed to create temporary directory for fetching git sources") }; @@ -109,7 +116,7 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) { else { match v { &ExactRevision(ref s) | &Tagged(ref s) => { - let outp = process_output_in_cwd("git", [~"checkout", format!("{}", *s)], + let outp = process_output_in_cwd("git", [~"checkout", s.to_owned()], target); if outp.status != 0 { debug2!("{}", str::from_utf8_owned(outp.output.clone())); @@ -129,5 +136,5 @@ fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput } pub fn is_git_dir(p: &Path) -> bool { - os::path_is_dir(&p.push(".git")) + os::path_is_dir(&p.join_str(".git")) } diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index 664f6807227d8..9f371a865356b 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -50,13 +50,13 @@ pub fn is_bench(p: &Path) -> bool { fn file_is(p: &Path, stem: &str) -> bool { match p.filestem() { - Some(s) if s == stem => true, + Some(s) if s == stem.as_bytes() => true, _ => false } } pub fn lib_name_of(p: &Path) -> Path { - p.push("lib.rs") + p.join_str("lib.rs") } pub static lib_crate_filename: &'static str = "lib.rs"; diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index d03243599ef59..de7ad9dc06b58 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -41,7 +41,7 @@ use util::datestamp; fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { let context = workcache::Context::new( - RWArc::new(Database::new(workspace.push("rustpkg_db.json"))), + RWArc::new(Database::new(workspace.join_str("rustpkg_db.json"))), RWArc::new(Logger::new()), Arc::new(TreeMap::new())); BuildContext { @@ -59,7 +59,7 @@ fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { fn fake_pkg() -> PkgId { let sn = ~"bogus"; PkgId { - path: Path(sn), + path: Path::from_str(sn), short_name: sn, version: NoVersion } @@ -67,7 +67,7 @@ fn fake_pkg() -> PkgId { fn git_repo_pkg() -> PkgId { PkgId { - path: Path("mockgithub.com/catamorphism/test-pkg"), + path: Path::from_str("mockgithub.com/catamorphism/test-pkg"), short_name: ~"test-pkg", version: NoVersion } @@ -75,7 +75,7 @@ fn git_repo_pkg() -> PkgId { fn git_repo_pkg_with_tag(a_tag: ~str) -> PkgId { PkgId { - path: Path("mockgithub.com/catamorphism/test-pkg"), + path: Path::from_str("mockgithub.com/catamorphism/test-pkg"), short_name: ~"test-pkg", version: Tagged(a_tag) } @@ -101,35 +101,36 @@ fn mk_empty_workspace(short_name: &Path, version: &Version, tag: &str) -> TempDi fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path { // include version number in directory name - let package_dir = workspace.push_many([~"src", format!("{}-{}", - short_name.to_str(), version.to_str())]); + // FIXME (#9639): This needs to handle non-utf8 paths + let package_dir = workspace.join_many_str([~"src", format!("{}-{}", + short_name.as_str().unwrap(), version.to_str())]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); package_dir } fn mk_temp_workspace(short_name: &Path, version: &Version) -> (TempDir, Path) { let workspace_dir = mk_empty_workspace(short_name, version, "temp_workspace"); - let package_dir = workspace_dir.path().push_many([~"src", - format!("{}-{}", - short_name.to_str(), - version.to_str())]); + let package_dir = workspace_dir.path().join_many_str([~"src", + format!("{}-{}", + short_name.to_str(), + version.to_str())]); - debug2!("Created {} and does it exist? {:?}", package_dir.to_str(), + debug2!("Created {} and does it exist? {:?}", package_dir.display(), os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files - debug2!("mk_workspace: creating {}", package_dir.to_str()); + debug2!("mk_workspace: creating {}", package_dir.display()); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - debug2!("Created {} and does it exist? {:?}", package_dir.to_str(), + debug2!("Created {} and does it exist? {:?}", package_dir.display(), os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files - writeFile(&package_dir.push("main.rs"), + writeFile(&package_dir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.push("lib.rs"), + writeFile(&package_dir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.push("test.rs"), + writeFile(&package_dir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.push("bench.rs"), + writeFile(&package_dir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); (workspace_dir, package_dir) } @@ -153,18 +154,18 @@ fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &st /// Should create an empty git repo in p, relative to the tmp dir, and return the new /// absolute path fn init_git_repo(p: &Path) -> TempDir { - assert!(!p.is_absolute()); + assert!(p.is_relative()); let tmp = TempDir::new("git_local").expect("couldn't create temp dir"); - let work_dir = tmp.path().push_rel(p); + let work_dir = tmp.path().join_path(p); let work_dir_for_opts = work_dir.clone(); assert!(os::mkdir_recursive(&work_dir, U_RWX)); - debug2!("Running: git init in {}", work_dir.to_str()); - let ws = work_dir.to_str(); + debug2!("Running: git init in {}", work_dir.display()); run_git([~"init"], None, &work_dir_for_opts, - format!("Couldn't initialize git repository in {}", ws)); + format!("Couldn't initialize git repository in {}", work_dir.display())); // Add stuff to the dir so that git tag succeeds - writeFile(&work_dir.push("README"), ""); - run_git([~"add", ~"README"], None, &work_dir_for_opts, format!("Couldn't add in {}", ws)); + writeFile(&work_dir.join_str("README"), ""); + run_git([~"add", ~"README"], None, &work_dir_for_opts, format!("Couldn't add in {}", + work_dir.display())); git_commit(&work_dir_for_opts, ~"whatever"); tmp } @@ -176,11 +177,11 @@ fn add_all_and_commit(repo: &Path) { fn git_commit(repo: &Path, msg: ~str) { run_git([~"commit", ~"--author=tester ", ~"-m", msg], - None, repo, format!("Couldn't commit in {}", repo.to_str())); + None, repo, format!("Couldn't commit in {}", repo.display())); } fn git_add_all(repo: &Path) { - run_git([~"add", ~"-A"], None, repo, format!("Couldn't add all files in {}", repo.to_str())); + run_git([~"add", ~"-A"], None, repo, format!("Couldn't add all files in {}", repo.display())); } fn add_git_tag(repo: &Path, tag: ~str) { @@ -188,7 +189,7 @@ fn add_git_tag(repo: &Path, tag: ~str) { git_add_all(repo); git_commit(repo, ~"whatever"); run_git([~"tag", tag.clone()], None, repo, - format!("Couldn't add git tag {} in {}", tag, repo.to_str())); + format!("Couldn't add git tag {} in {}", tag, repo.display())); } fn is_rwx(p: &Path) -> bool { @@ -215,24 +216,29 @@ fn is_read_only(p: &Path) -> bool { } } +fn ends_with(v: &[u8], needle: &[u8]) -> bool { + v.len() >= needle.len() && v.slice_from(v.len() - needle.len()) == needle +} + fn test_sysroot() -> Path { // Totally gross hack but it's just for test cases. // Infer the sysroot from the exe name and pray that it's right. // (Did I mention it was a gross hack?) - let self_path = os::self_exe_path().expect("Couldn't get self_exe path"); - self_path.pop() + let mut self_path = os::self_exe_path().expect("Couldn't get self_exe path"); + self_path.pop(); + self_path } // Returns the path to rustpkg fn rustpkg_exec() -> Path { // Ugh - let first_try = test_sysroot().push_many( + let first_try = test_sysroot().join_many_str( [~"lib", ~"rustc", host_triple(), ~"bin", ~"rustpkg"]); if is_executable(&first_try) { first_try } else { - let second_try = test_sysroot().push_many([~"bin", ~"rustpkg"]); + let second_try = test_sysroot().join_many_str(["bin", "rustpkg"]); if is_executable(&second_try) { second_try } @@ -275,12 +281,14 @@ enum ProcessResult { /// Returns the process's output. fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>) -> ProcessResult { - let cmd = rustpkg_exec().to_str(); + // FIXME (#9639): This needs to handle non-utf8 paths + let exec_path = rustpkg_exec(); + let cmd = exec_path.as_str().unwrap().to_owned(); let env_str = match env { Some(ref pairs) => pairs.map(|&(ref k, ref v)| { format!("{}={}", *k, *v) }).connect(","), None => ~"" }; - debug2!("{} cd {}; {} {}", env_str, cwd.to_str(), cmd, args.connect(" ")); + debug2!("{} cd {}; {} {}", env_str, cwd.display(), cmd, args.connect(" ")); assert!(os::path_is_dir(&*cwd)); let cwd = (*cwd).clone(); let mut prog = run::Process::new(cmd, args, run::ProcessOptions { @@ -314,27 +322,27 @@ to make sure the command succeeded fn create_local_package(pkgid: &PkgId) -> TempDir { let (workspace, parent_dir) = mk_temp_workspace(&pkgid.path, &pkgid.version); - debug2!("Created empty package dir for {}, returning {}", pkgid.to_str(), parent_dir.to_str()); + debug2!("Created empty package dir for {}, returning {}", pkgid.to_str(), parent_dir.display()); workspace } fn create_local_package_in(pkgid: &PkgId, pkgdir: &Path) -> Path { - let package_dir = pkgdir.push_many([~"src", pkgid.to_str()]); + let package_dir = pkgdir.join_many_str([~"src", pkgid.to_str()]); // Create main, lib, test, and bench files assert!(os::mkdir_recursive(&package_dir, U_RWX)); - debug2!("Created {} and does it exist? {:?}", package_dir.to_str(), + debug2!("Created {} and does it exist? {:?}", package_dir.display(), os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files - writeFile(&package_dir.push("main.rs"), + writeFile(&package_dir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.push("lib.rs"), + writeFile(&package_dir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.push("test.rs"), + writeFile(&package_dir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.push("bench.rs"), + writeFile(&package_dir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); package_dir } @@ -348,11 +356,11 @@ fn create_local_package_with_dep(pkgid: &PkgId, subord_pkgid: &PkgId) -> TempDir let package_dir = create_local_package(pkgid); create_local_package_in(subord_pkgid, package_dir.path()); // Write a main.rs file into pkgid that references subord_pkgid - writeFile(&package_dir.path().push_many([~"src", pkgid.to_str(), ~"main.rs"]), + writeFile(&package_dir.path().join_many_str([~"src", pkgid.to_str(), ~"main.rs"]), format!("extern mod {};\nfn main() \\{\\}", subord_pkgid.short_name)); // Write a lib.rs file into subord_pkgid that has something in it - writeFile(&package_dir.path().push_many([~"src", subord_pkgid.to_str(), ~"lib.rs"]), + writeFile(&package_dir.path().join_many_str([~"src", subord_pkgid.to_str(), ~"lib.rs"]), "pub fn f() {}"); package_dir } @@ -371,7 +379,7 @@ fn assert_lib_exists(repo: &Path, pkg_path: &Path, v: Version) { } fn lib_exists(repo: &Path, pkg_path: &Path, _v: Version) -> bool { // ??? version? - debug2!("assert_lib_exists: repo = {}, pkg_path = {}", repo.to_str(), pkg_path.to_str()); + debug2!("assert_lib_exists: repo = {}, pkg_path = {}", repo.display(), pkg_path.display()); let lib = installed_library_in_workspace(pkg_path, repo); debug2!("assert_lib_exists: checking whether {:?} exists", lib); lib.is_some() && { @@ -385,13 +393,13 @@ fn assert_executable_exists(repo: &Path, short_name: &str) { } fn executable_exists(repo: &Path, short_name: &str) -> bool { - debug2!("executable_exists: repo = {}, short_name = {}", repo.to_str(), short_name); + debug2!("executable_exists: repo = {}, short_name = {}", repo.display(), short_name); let exec = target_executable_in_workspace(&PkgId::new(short_name), repo); os::path_exists(&exec) && is_rwx(&exec) } fn test_executable_exists(repo: &Path, short_name: &str) -> bool { - debug2!("test_executable_exists: repo = {}, short_name = {}", repo.to_str(), short_name); + debug2!("test_executable_exists: repo = {}, short_name = {}", repo.display(), short_name); let exec = built_test_in_workspace(&PkgId::new(short_name), repo); do exec.map_default(false) |exec| { os::path_exists(&exec) && is_rwx(&exec) @@ -411,7 +419,7 @@ fn assert_built_executable_exists(repo: &Path, short_name: &str) { fn built_executable_exists(repo: &Path, short_name: &str) -> bool { debug2!("assert_built_executable_exists: repo = {}, short_name = {}", - repo.to_str(), short_name); + repo.display(), short_name); let exec = built_executable_in_workspace(&PkgId::new(short_name), repo); exec.is_some() && { let execname = exec.get_ref(); @@ -444,7 +452,7 @@ fn llvm_bitcode_file_exists(repo: &Path, short_name: &str) -> bool { } fn file_exists(repo: &Path, short_name: &str, extension: &str) -> bool { - os::path_exists(&target_build_dir(repo).push_many([short_name.to_owned(), + os::path_exists(&target_build_dir(repo).join_many_str([short_name.to_owned(), format!("{}.{}", short_name, extension)])) } @@ -453,7 +461,7 @@ fn assert_built_library_exists(repo: &Path, short_name: &str) { } fn built_library_exists(repo: &Path, short_name: &str) -> bool { - debug2!("assert_built_library_exists: repo = {}, short_name = {}", repo.to_str(), short_name); + debug2!("assert_built_library_exists: repo = {}, short_name = {}", repo.display(), short_name); let lib = built_library_in_workspace(&PkgId::new(short_name), repo); lib.is_some() && { let libname = lib.get_ref(); @@ -488,8 +496,8 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~ // assumes short_name and path are one and the same -- I should fix fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { debug2!("lib_output_file_name: given {} and short name {}", - workspace.to_str(), short_name); - library_in_workspace(&Path(short_name), + workspace.display(), short_name); + library_in_workspace(&Path::from_str(short_name), short_name, Build, workspace, @@ -498,17 +506,19 @@ fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { } fn output_file_name(workspace: &Path, short_name: ~str) -> Path { - target_build_dir(workspace).push(short_name).push(format!("{}{}", short_name, os::EXE_SUFFIX)) + target_build_dir(workspace).join_str(short_name).join_str(format!("{}{}", short_name, + os::EXE_SUFFIX)) } fn touch_source_file(workspace: &Path, pkgid: &PkgId) { use conditions::bad_path::cond; - let pkg_src_dir = workspace.push_many([~"src", pkgid.to_str()]); + let pkg_src_dir = workspace.join_many_str([~"src", pkgid.to_str()]); let contents = os::list_dir_path(&pkg_src_dir); for p in contents.iter() { - if p.filetype() == Some(".rs") { + if p.extension_str() == Some("rs") { // should be able to do this w/o a process - if run::process_output("touch", [p.to_str()]).status != 0 { + // FIXME (#9639): This needs to handle non-utf8 paths + if run::process_output("touch", [p.as_str().unwrap().to_owned()]).status != 0 { let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path")); } } @@ -518,10 +528,10 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) { /// Add a comment at the end fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { use conditions::bad_path::cond; - let pkg_src_dir = workspace.push_many([~"src", pkgid.to_str()]); + let pkg_src_dir = workspace.join_many_str([~"src", pkgid.to_str()]); let mut maybe_p = None; - let maybe_file = pkg_src_dir.push(filename); - debug2!("Trying to frob {} -- {}", pkg_src_dir.to_str(), filename); + let maybe_file = pkg_src_dir.join_str(filename); + debug2!("Trying to frob {} -- {}", pkg_src_dir.display(), filename); if os::path_exists(&maybe_file) { maybe_p = Some(maybe_file); } @@ -535,17 +545,17 @@ fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { } } None => fail2!("frob_source_file failed to find a source file in {}", - pkg_src_dir.to_str()) + pkg_src_dir.display()) } } #[test] fn test_make_dir_rwx() { let temp = &os::tmpdir(); - let dir = temp.push("quux"); + let dir = temp.join_str("quux"); assert!(!os::path_exists(&dir) || os::remove_dir_recursive(&dir)); - debug2!("Trying to make {}", dir.to_str()); + debug2!("Trying to make {}", dir.display()); assert!(make_dir_rwx(&dir)); assert!(os::path_is_dir(&dir)); assert!(is_rwx(&dir)); @@ -557,12 +567,12 @@ fn test_install_valid() { use path_util::installed_library_in_workspace; let sysroot = test_sysroot(); - debug2!("sysroot = {}", sysroot.to_str()); + debug2!("sysroot = {}", sysroot.display()); let temp_pkg_id = fake_pkg(); let (temp_workspace, _pkg_dir) = mk_temp_workspace(&temp_pkg_id.path, &NoVersion); let temp_workspace = temp_workspace.path(); let ctxt = fake_ctxt(sysroot, temp_workspace); - debug2!("temp_workspace = {}", temp_workspace.to_str()); + debug2!("temp_workspace = {}", temp_workspace.display()); // should have test, bench, lib, and main let src = PkgSrc::new(temp_workspace.clone(), temp_workspace.clone(), @@ -571,7 +581,7 @@ fn test_install_valid() { ctxt.install(src, &Everything); // Check that all files exist let exec = target_executable_in_workspace(&temp_pkg_id, temp_workspace); - debug2!("exec = {}", exec.to_str()); + debug2!("exec = {}", exec.display()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); @@ -583,7 +593,7 @@ fn test_install_valid() { // And that the test and bench executables aren't installed assert!(!os::path_exists(&target_test_in_workspace(&temp_pkg_id, temp_workspace))); let bench = target_bench_in_workspace(&temp_pkg_id, temp_workspace); - debug2!("bench = {}", bench.to_str()); + debug2!("bench = {}", bench.display()); assert!(!os::path_exists(&bench)); // Make sure the db isn't dirty, so that it doesn't try to save() @@ -619,29 +629,30 @@ fn test_install_git() { let temp_pkg_id = git_repo_pkg(); let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); - debug2!("repo = {}", repo.to_str()); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]); - debug2!("repo_subdir = {}", repo_subdir.to_str()); + debug2!("repo = {}", repo.display()); + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + debug2!("repo_subdir = {}", repo_subdir.display()); - writeFile(&repo_subdir.push("main.rs"), + writeFile(&repo_subdir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.push("lib.rs"), + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.push("test.rs"), + writeFile(&repo_subdir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.push("bench.rs"), + writeFile(&repo_subdir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files debug2!("test_install_git: calling rustpkg install {} in {}", - temp_pkg_id.path.to_str(), repo.to_str()); + temp_pkg_id.path.display(), repo.display()); // should have test, bench, lib, and main - command_line_test([~"install", temp_pkg_id.path.to_str()], repo); - let ws = repo.push(".rust"); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo); + let ws = repo.join_str(".rust"); // Check that all files exist - debug2!("Checking for files in {}", ws.to_str()); + debug2!("Checking for files in {}", ws.display()); let exec = target_executable_in_workspace(&temp_pkg_id, &ws); - debug2!("exec = {}", exec.to_str()); + debug2!("exec = {}", exec.display()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); let _built_lib = @@ -657,9 +668,9 @@ fn test_install_git() { // And that the test and bench executables aren't installed let test = target_test_in_workspace(&temp_pkg_id, &ws); assert!(!os::path_exists(&test)); - debug2!("test = {}", test.to_str()); + debug2!("test = {}", test.display()); let bench = target_bench_in_workspace(&temp_pkg_id, &ws); - debug2!("bench = {}", bench.to_str()); + debug2!("bench = {}", bench.display()); assert!(!os::path_exists(&bench)); } @@ -685,7 +696,7 @@ fn test_package_ids_must_be_relative_path_like() { PkgId::new("github.com/catamorphism/test-pkg").to_str()); do cond.trap(|(p, e)| { - assert!("" == p.to_str()); + assert!(p.filename().is_none()) assert!("0-length pkgid" == e); whatever.clone() }).inside { @@ -694,11 +705,14 @@ fn test_package_ids_must_be_relative_path_like() { } do cond.trap(|(p, e)| { - assert_eq!(p.to_str(), os::make_absolute(&Path("foo/bar/quux")).to_str()); + let abs = os::make_absolute(&Path::from_str("foo/bar/quux")); + assert_eq!(p, abs); assert!("absolute pkgid" == e); whatever.clone() }).inside { - let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str()); + let zp = os::make_absolute(&Path::from_str("foo/bar/quux")); + // FIXME (#9639): This needs to handle non-utf8 paths + let z = PkgId::new(zp.as_str().unwrap()); assert_eq!(~"foo-0.1", z.to_str()); } @@ -707,17 +721,17 @@ fn test_package_ids_must_be_relative_path_like() { #[test] fn test_package_version() { let local_path = "mockgithub.com/catamorphism/test_pkg_version"; - let repo = init_git_repo(&Path(local_path)); + let repo = init_git_repo(&Path::from_str(local_path)); let repo = repo.path(); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test_pkg_version"]); - debug2!("Writing files in: {}", repo_subdir.to_str()); - writeFile(&repo_subdir.push("main.rs"), + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test_pkg_version"]); + debug2!("Writing files in: {}", repo_subdir.display()); + writeFile(&repo_subdir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.push("lib.rs"), + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.push("test.rs"), + writeFile(&repo_subdir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.push("bench.rs"), + writeFile(&repo_subdir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&repo_subdir, ~"0.4"); @@ -726,59 +740,64 @@ fn test_package_version() { // This should look at the prefix, clone into a workspace, then build. command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg_version"], repo); - let ws = repo.push(".rust"); + let ws = repo.join_str(".rust"); // we can still match on the filename to make sure it contains the 0.4 version assert!(match built_library_in_workspace(&temp_pkg_id, &ws) { - Some(p) => p.to_str().ends_with(format!("0.4{}", os::consts::DLL_SUFFIX)), + Some(p) => { + let suffix = format!("0.4{}", os::consts::DLL_SUFFIX); + ends_with(p.as_vec(), suffix.as_bytes()) + } None => false }); assert!(built_executable_in_workspace(&temp_pkg_id, &ws) - == Some(target_build_dir(&ws).push_many([~"mockgithub.com", - ~"catamorphism", - ~"test_pkg_version", - ~"test_pkg_version"]))); + == Some(target_build_dir(&ws).join_many_str(["mockgithub.com", + "catamorphism", + "test_pkg_version", + "test_pkg_version"]))); } #[test] fn test_package_request_version() { let local_path = "mockgithub.com/catamorphism/test_pkg_version"; - let repo = init_git_repo(&Path(local_path)); + let repo = init_git_repo(&Path::from_str(local_path)); let repo = repo.path(); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test_pkg_version"]); - debug2!("Writing files in: {}", repo_subdir.to_str()); - writeFile(&repo_subdir.push("main.rs"), + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test_pkg_version"]); + debug2!("Writing files in: {}", repo_subdir.display()); + writeFile(&repo_subdir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.push("lib.rs"), + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.push("test.rs"), + writeFile(&repo_subdir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.push("bench.rs"), + writeFile(&repo_subdir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); - writeFile(&repo_subdir.push("version-0.3-file.txt"), "hi"); + writeFile(&repo_subdir.join_str("version-0.3-file.txt"), "hi"); add_git_tag(&repo_subdir, ~"0.3"); - writeFile(&repo_subdir.push("version-0.4-file.txt"), "hello"); + writeFile(&repo_subdir.join_str("version-0.4-file.txt"), "hello"); add_git_tag(&repo_subdir, ~"0.4"); command_line_test([~"install", format!("{}\\#0.3", local_path)], repo); - assert!(match installed_library_in_workspace(&Path("test_pkg_version"), &repo.push(".rust")) { + assert!(match installed_library_in_workspace(&Path::from_str("test_pkg_version"), + &repo.join_str(".rust")) { Some(p) => { - debug2!("installed: {}", p.to_str()); - p.to_str().ends_with(format!("0.3{}", os::consts::DLL_SUFFIX)) + debug2!("installed: {}", p.display()); + let suffix = format!("0.3{}", os::consts::DLL_SUFFIX); + ends_with(p.as_vec(), suffix.as_bytes()) } None => false }); let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3"); - assert!(target_executable_in_workspace(&temp_pkg_id, &repo.push(".rust")) - == repo.push_many([~".rust", ~"bin", ~"test_pkg_version"])); + assert!(target_executable_in_workspace(&temp_pkg_id, &repo.join_str(".rust")) + == repo.join_many_str([".rust", "bin", "test_pkg_version"])); - let dir = target_build_dir(&repo.push(".rust")) - .push_rel(&Path("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); - debug2!("dir = {}", dir.to_str()); + let mut dir = target_build_dir(&repo.join_str(".rust")); + dir.push_path(&Path::from_str("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); + debug2!("dir = {}", dir.display()); assert!(os::path_is_dir(&dir)); - assert!(os::path_exists(&dir.push("version-0.3-file.txt"))); - assert!(!os::path_exists(&dir.push("version-0.4-file.txt"))); + assert!(os::path_exists(&dir.join_str("version-0.3-file.txt"))); + assert!(!os::path_exists(&dir.join_str("version-0.4-file.txt"))); } #[test] @@ -791,23 +810,23 @@ fn rustpkg_install_url_2() { #[test] fn rustpkg_library_target() { - let foo_repo = init_git_repo(&Path("foo")); + let foo_repo = init_git_repo(&Path::from_str("foo")); let foo_repo = foo_repo.path(); - let package_dir = foo_repo.push("foo"); + let package_dir = foo_repo.join_str("foo"); - debug2!("Writing files in: {}", package_dir.to_str()); - writeFile(&package_dir.push("main.rs"), + debug2!("Writing files in: {}", package_dir.display()); + writeFile(&package_dir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.push("lib.rs"), + writeFile(&package_dir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.push("test.rs"), + writeFile(&package_dir.join_str("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.push("bench.rs"), + writeFile(&package_dir.join_str("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&package_dir, ~"1.0"); command_line_test([~"install", ~"foo"], foo_repo); - assert_lib_exists(&foo_repo.push(".rust"), &Path("foo"), ExactRevision(~"1.0")); + assert_lib_exists(&foo_repo.join_str(".rust"), &Path::from_str("foo"), ExactRevision(~"1.0")); } #[test] @@ -822,29 +841,31 @@ fn rustpkg_local_pkg() { fn package_script_with_default_build() { let dir = create_local_package(&PkgId::new("fancy-lib")); let dir = dir.path(); - debug2!("dir = {}", dir.to_str()); - let source = test_sysroot().pop().pop().pop().push_many( - [~"src", ~"librustpkg", ~"testsuite", ~"pass", ~"src", ~"fancy-lib", ~"pkg.rs"]); - debug2!("package_script_with_default_build: {}", source.to_str()); + debug2!("dir = {}", dir.display()); + let mut source = test_sysroot().dir_path(); + source.pop(); source.pop(); + source.push_many_str( + ["src", "librustpkg", "testsuite", "pass", "src", "fancy-lib", "pkg.rs"]); + debug2!("package_script_with_default_build: {}", source.display()); if !os::copy_file(&source, - &dir.push_many([~"src", ~"fancy-lib-0.1", ~"pkg.rs"])) { + &dir.join_many_str(["src", "fancy-lib-0.1", "pkg.rs"])) { fail2!("Couldn't copy file"); } command_line_test([~"install", ~"fancy-lib"], dir); - assert_lib_exists(dir, &Path("fancy-lib"), NoVersion); - assert!(os::path_exists(&target_build_dir(dir).push_many([~"fancy-lib", ~"generated.rs"]))); + assert_lib_exists(dir, &Path::from_str("fancy-lib"), NoVersion); + assert!(os::path_exists(&target_build_dir(dir).join_many_str(["fancy-lib", "generated.rs"]))); } #[test] fn rustpkg_build_no_arg() { let tmp = TempDir::new("rustpkg_build_no_arg").expect("rustpkg_build_no_arg failed"); - let tmp = tmp.path().push(".rust"); - let package_dir = tmp.push_many([~"src", ~"foo"]); + let tmp = tmp.path().join_str(".rust"); + let package_dir = tmp.join_many_str(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.push("main.rs"), + writeFile(&package_dir.join_str("main.rs"), "fn main() { let _x = (); }"); - debug2!("build_no_arg: dir = {}", package_dir.to_str()); + debug2!("build_no_arg: dir = {}", package_dir.display()); command_line_test([~"build"], &package_dir); assert_built_executable_exists(&tmp, "foo"); } @@ -852,26 +873,26 @@ fn rustpkg_build_no_arg() { #[test] fn rustpkg_install_no_arg() { let tmp = TempDir::new("rustpkg_install_no_arg").expect("rustpkg_install_no_arg failed"); - let tmp = tmp.path().push(".rust"); - let package_dir = tmp.push_many([~"src", ~"foo"]); + let tmp = tmp.path().join_str(".rust"); + let package_dir = tmp.join_many_str(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.push("lib.rs"), + writeFile(&package_dir.join_str("lib.rs"), "fn main() { let _x = (); }"); - debug2!("install_no_arg: dir = {}", package_dir.to_str()); + debug2!("install_no_arg: dir = {}", package_dir.display()); command_line_test([~"install"], &package_dir); - assert_lib_exists(&tmp, &Path("foo"), NoVersion); + assert_lib_exists(&tmp, &Path::from_str("foo"), NoVersion); } #[test] fn rustpkg_clean_no_arg() { let tmp = TempDir::new("rustpkg_clean_no_arg").expect("rustpkg_clean_no_arg failed"); - let tmp = tmp.path().push(".rust"); - let package_dir = tmp.push_many([~"src", ~"foo"]); + let tmp = tmp.path().join_str(".rust"); + let package_dir = tmp.join_many_str(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.push("main.rs"), + writeFile(&package_dir.join_str("main.rs"), "fn main() { let _x = (); }"); - debug2!("clean_no_arg: dir = {}", package_dir.to_str()); + debug2!("clean_no_arg: dir = {}", package_dir.display()); command_line_test([~"build"], &package_dir); assert_built_executable_exists(&tmp, "foo"); command_line_test([~"clean"], &package_dir); @@ -882,16 +903,17 @@ fn rustpkg_clean_no_arg() { #[test] fn rust_path_test() { let dir_for_path = TempDir::new("more_rust").expect("rust_path_test failed"); - let dir = mk_workspace(dir_for_path.path(), &Path("foo"), &NoVersion); - debug2!("dir = {}", dir.to_str()); - writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }"); + let dir = mk_workspace(dir_for_path.path(), &Path::from_str("foo"), &NoVersion); + debug2!("dir = {}", dir.display()); + writeFile(&dir.join_str("main.rs"), "fn main() { let _x = (); }"); let cwd = os::getcwd(); - debug2!("cwd = {}", cwd.to_str()); + debug2!("cwd = {}", cwd.display()); // use command_line_test_with_env + // FIXME (#9639): This needs to handle non-utf8 paths command_line_test_with_env([~"install", ~"foo"], &cwd, - Some(~[(~"RUST_PATH", dir_for_path.path().to_str())])); + Some(~[(~"RUST_PATH", dir_for_path.path().as_str().unwrap())])); assert_executable_exists(dir_for_path.path(), "foo"); } @@ -899,21 +921,21 @@ fn rust_path_test() { #[ignore] // FIXME(#9184) tests can't change the cwd (other tests are sad then) fn rust_path_contents() { let dir = TempDir::new("rust_path").expect("rust_path_contents failed"); - let abc = &dir.path().push_many([~"A", ~"B", ~"C"]); - assert!(os::mkdir_recursive(&abc.push(".rust"), U_RWX)); - assert!(os::mkdir_recursive(&abc.pop().push(".rust"), U_RWX)); - assert!(os::mkdir_recursive(&abc.pop().pop().push(".rust"), U_RWX)); + let abc = &dir.path().join_many_str(["A", "B", "C"]); + assert!(os::mkdir_recursive(&abc.join_str(".rust"), U_RWX)); + assert!(os::mkdir_recursive(&abc.with_filename(".rust"), U_RWX)); + assert!(os::mkdir_recursive(&abc.dir_path().with_filename(".rust"), U_RWX)); assert!(os::change_dir(abc)); let p = rust_path(); - let cwd = os::getcwd().push(".rust"); - let parent = cwd.pop().pop().push(".rust"); - let grandparent = cwd.pop().pop().pop().push(".rust"); + let cwd = os::getcwd().join_str(".rust"); + let parent = cwd.dir_path().with_filename_str(".rust"); + let grandparent = cwd.dir_path().dir_path().with_filename_str(".rust"); assert!(p.contains(&cwd)); assert!(p.contains(&parent)); assert!(p.contains(&grandparent)); for a_path in p.iter() { - assert!(!a_path.components.is_empty()); + assert!(a_path.filename().is_some()); } } @@ -921,9 +943,9 @@ fn rust_path_contents() { fn rust_path_parse() { os::setenv("RUST_PATH", "/a/b/c:/d/e/f:/g/h/i"); let paths = rust_path(); - assert!(paths.contains(&Path("/g/h/i"))); - assert!(paths.contains(&Path("/d/e/f"))); - assert!(paths.contains(&Path("/a/b/c"))); + assert!(paths.contains(&Path::from_str("/g/h/i"))); + assert!(paths.contains(&Path::from_str("/d/e/f"))); + assert!(paths.contains(&Path::from_str("/a/b/c"))); os::unsetenv("RUST_PATH"); } @@ -940,7 +962,8 @@ fn test_list() { // list doesn't output very much right now... command_line_test([~"install", ~"foo"], dir); - let env_arg = ~[(~"RUST_PATH", dir.to_str())]; + // FIXME (#9639): This needs to handle non-utf8 paths + let env_arg = ~[(~"RUST_PATH", dir.as_str().unwrap().to_owned())]; let list_output = command_line_test_output_with_env([~"list"], env_arg.clone()); assert!(list_output.iter().any(|x| x.starts_with("foo"))); @@ -966,7 +989,8 @@ fn install_remove() { create_local_package_in(&foo, dir); create_local_package_in(&bar, dir); create_local_package_in(&quux, dir); - let rust_path_to_use = ~[(~"RUST_PATH", dir.to_str())]; + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path_to_use = ~[(~"RUST_PATH", dir.as_str().unwrap().to_owned())]; command_line_test([~"install", ~"foo"], dir); command_line_test([~"install", ~"bar"], dir); command_line_test([~"install", ~"quux"], dir); @@ -996,12 +1020,12 @@ fn install_check_duplicates() { let mut contents = ~[]; let check_dups = |p: &PkgId| { if contents.contains(p) { - fail2!("package {} appears in `list` output more than once", p.path.to_str()); + fail2!("package {} appears in `list` output more than once", p.path.display()); } else { contents.push((*p).clone()); } - false + true }; list_installed_packages(check_dups); } @@ -1059,7 +1083,7 @@ fn do_rebuild_dep_dates_change() { touch_source_file(workspace, &dep_id); command_line_test([~"build", ~"foo"], workspace); let new_bar_date = datestamp(&bar_lib_name); - debug2!("Datestamp on {} is {:?}", bar_lib_name.to_str(), new_bar_date); + debug2!("Datestamp on {} is {:?}", bar_lib_name.display(), new_bar_date); assert!(new_bar_date > bar_date); } @@ -1120,21 +1144,23 @@ fn test_non_numeric_tag() { let temp_pkg_id = git_repo_pkg(); let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]); - writeFile(&repo_subdir.push("foo"), "foo"); - writeFile(&repo_subdir.push("lib.rs"), + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + writeFile(&repo_subdir.join_str("foo"), "foo"); + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"testbranch"); - writeFile(&repo_subdir.push("testbranch_only"), "hello"); + writeFile(&repo_subdir.join_str("testbranch_only"), "hello"); add_git_tag(&repo_subdir, ~"another_tag"); - writeFile(&repo_subdir.push("not_on_testbranch_only"), "bye bye"); + writeFile(&repo_subdir.join_str("not_on_testbranch_only"), "bye bye"); add_all_and_commit(&repo_subdir); - command_line_test([~"install", format!("{}\\#testbranch", temp_pkg_id.path.to_str())], repo); - let file1 = repo.push_many(["mockgithub.com", "catamorphism", - "test-pkg", "testbranch_only"]); - let file2 = repo.push_many(["mockgithub.com", "catamorphism", "test-pkg", - "master_only"]); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([~"install", format!("{}\\#testbranch", + temp_pkg_id.path.as_str().unwrap())], repo); + let file1 = repo.join_many_str(["mockgithub.com", "catamorphism", + "test-pkg", "testbranch_only"]); + let file2 = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg", + "master_only"]); assert!(os::path_exists(&file1)); assert!(!os::path_exists(&file2)); } @@ -1143,12 +1169,13 @@ fn test_non_numeric_tag() { fn test_extern_mod() { let dir = TempDir::new("test_extern_mod").expect("test_extern_mod"); let dir = dir.path(); - let main_file = dir.push("main.rs"); + let main_file = dir.join_str("main.rs"); let lib_depend_dir = TempDir::new("foo").expect("test_extern_mod"); let lib_depend_dir = lib_depend_dir.path(); - let aux_dir = lib_depend_dir.push_many(["src", "mockgithub.com", "catamorphism", "test_pkg"]); + let aux_dir = lib_depend_dir.join_many_str(["src", "mockgithub.com", "catamorphism", + "test_pkg"]); assert!(os::mkdir_recursive(&aux_dir, U_RWX)); - let aux_pkg_file = aux_dir.push("lib.rs"); + let aux_pkg_file = aux_dir.join_str("lib.rs"); writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() { assert!(true); } }\n"); assert!(os::path_exists(&aux_pkg_file)); @@ -1159,15 +1186,19 @@ fn test_extern_mod() { command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg"], lib_depend_dir); - let exec_file = dir.push("out"); + let exec_file = dir.join_str("out"); // Be sure to extend the existing environment - let env = Some([(~"RUST_PATH", lib_depend_dir.to_str())] + os::env()); + // FIXME (#9639): This needs to handle non-utf8 paths + let env = Some([(~"RUST_PATH", lib_depend_dir.as_str().unwrap().to_owned())] + os::env()); let rustpkg_exec = rustpkg_exec(); - let rustc = rustpkg_exec.with_filename("rustc"); - - let mut prog = run::Process::new(rustc.to_str(), [main_file.to_str(), - ~"--sysroot", test_sysroot().to_str(), - ~"-o", exec_file.to_str()], + let rustc = rustpkg_exec.with_filename_str("rustc"); + + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + let mut prog = run::Process::new(rustc.as_str().unwrap(), + [main_file.as_str().unwrap().to_owned(), + ~"--sysroot", test_sys.as_str().unwrap().to_owned(), + ~"-o", exec_file.as_str().unwrap().to_owned()], run::ProcessOptions { env: env, dir: Some(dir), @@ -1188,12 +1219,12 @@ fn test_extern_mod() { fn test_extern_mod_simpler() { let dir = TempDir::new("test_extern_mod_simpler").expect("test_extern_mod_simpler"); let dir = dir.path(); - let main_file = dir.push("main.rs"); + let main_file = dir.join_str("main.rs"); let lib_depend_dir = TempDir::new("foo").expect("test_extern_mod_simpler"); let lib_depend_dir = lib_depend_dir.path(); - let aux_dir = lib_depend_dir.push_many(["src", "rust-awesomeness"]); + let aux_dir = lib_depend_dir.join_many_str(["src", "rust-awesomeness"]); assert!(os::mkdir_recursive(&aux_dir, U_RWX)); - let aux_pkg_file = aux_dir.push("lib.rs"); + let aux_pkg_file = aux_dir.join_str("lib.rs"); writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() { assert!(true); } }\n"); assert!(os::path_exists(&aux_pkg_file)); @@ -1204,21 +1235,25 @@ fn test_extern_mod_simpler() { command_line_test([~"install", ~"rust-awesomeness"], lib_depend_dir); - let exec_file = dir.push("out"); + let exec_file = dir.join_str("out"); // Be sure to extend the existing environment - let env = Some([(~"RUST_PATH", lib_depend_dir.to_str())] + os::env()); + // FIXME (#9639): This needs to handle non-utf8 paths + let env = Some([(~"RUST_PATH", lib_depend_dir.as_str().unwrap().to_owned())] + os::env()); let rustpkg_exec = rustpkg_exec(); - let rustc = rustpkg_exec.with_filename("rustc"); + let rustc = rustpkg_exec.with_filename_str("rustc"); + let test_sys = test_sysroot(); debug2!("RUST_PATH={} {} {} \n --sysroot {} -o {}", - lib_depend_dir.to_str(), - rustc.to_str(), - main_file.to_str(), - test_sysroot().to_str(), - exec_file.to_str()); - - let mut prog = run::Process::new(rustc.to_str(), [main_file.to_str(), - ~"--sysroot", test_sysroot().to_str(), - ~"-o", exec_file.to_str()], + lib_depend_dir.display(), + rustc.display(), + main_file.display(), + test_sys.display(), + exec_file.display()); + + // FIXME (#9639): This needs to handle non-utf8 paths + let mut prog = run::Process::new(rustc.as_str().unwrap(), + [main_file.as_str().unwrap().to_owned(), + ~"--sysroot", test_sys.as_str().unwrap().to_owned(), + ~"-o", exec_file.as_str().unwrap().to_owned()], run::ProcessOptions { env: env, dir: Some(dir), @@ -1240,11 +1275,11 @@ fn test_import_rustpkg() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.push_many([~"src", ~"foo-0.1", ~"pkg.rs"]), + writeFile(&workspace.join_many_str(["src", "foo-0.1", "pkg.rs"]), "extern mod rustpkg; fn main() {}"); command_line_test([~"build", ~"foo"], workspace); - debug2!("workspace = {}", workspace.to_str()); - assert!(os::path_exists(&target_build_dir(workspace).push("foo").push(format!("pkg{}", + debug2!("workspace = {}", workspace.display()); + assert!(os::path_exists(&target_build_dir(workspace).join_str("foo").join_str(format!("pkg{}", os::EXE_SUFFIX)))); } @@ -1253,11 +1288,11 @@ fn test_macro_pkg_script() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.push_many([~"src", ~"foo-0.1", ~"pkg.rs"]), + writeFile(&workspace.join_many_str(["src", "foo-0.1", "pkg.rs"]), "extern mod rustpkg; fn main() { debug2!(\"Hi\"); }"); command_line_test([~"build", ~"foo"], workspace); - debug2!("workspace = {}", workspace.to_str()); - assert!(os::path_exists(&target_build_dir(workspace).push("foo").push(format!("pkg{}", + debug2!("workspace = {}", workspace.display()); + assert!(os::path_exists(&target_build_dir(workspace).join_str("foo").join_str(format!("pkg{}", os::EXE_SUFFIX)))); } @@ -1267,14 +1302,16 @@ fn multiple_workspaces() { // Copy the exact same package into directory B and install it // Set the RUST_PATH to A:B // Make a third package that uses foo, make sure we can build/install it - let (a_loc, _pkg_dir) = mk_temp_workspace(&Path("foo"), &NoVersion); - let (b_loc, _pkg_dir) = mk_temp_workspace(&Path("foo"), &NoVersion); + let (a_loc, _pkg_dir) = mk_temp_workspace(&Path::from_str("foo"), &NoVersion); + let (b_loc, _pkg_dir) = mk_temp_workspace(&Path::from_str("foo"), &NoVersion); let (a_loc, b_loc) = (a_loc.path(), b_loc.path()); - debug2!("Trying to install foo in {}", a_loc.to_str()); + debug2!("Trying to install foo in {}", a_loc.display()); command_line_test([~"install", ~"foo"], a_loc); - debug2!("Trying to install foo in {}", b_loc.to_str()); + debug2!("Trying to install foo in {}", b_loc.display()); command_line_test([~"install", ~"foo"], b_loc); - let env = Some(~[(~"RUST_PATH", format!("{}:{}", a_loc.to_str(), b_loc.to_str()))]); + // FIXME (#9639): This needs to handle non-utf8 paths + let env = Some(~[(~"RUST_PATH", format!("{}:{}", a_loc.as_str().unwrap(), + b_loc.as_str().unwrap()))]); let c_loc = create_local_package_with_dep(&PkgId::new("bar"), &PkgId::new("foo")); command_line_test_with_env([~"install", ~"bar"], c_loc.path(), env); } @@ -1291,19 +1328,19 @@ fn rust_path_hack_test(hack_flag: bool) { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", - dest_workspace.to_str(), - workspace.push_many(["src", "foo-0.1"]).to_str()))]); + dest_workspace.as_str().unwrap(), + foo_path.as_str().unwrap()))]); command_line_test_with_env(~[~"install"] + if hack_flag { ~[~"--rust-path-hack"] } else { ~[] } + ~[~"foo"], dest_workspace, rust_path); - assert_lib_exists(dest_workspace, &Path("foo"), NoVersion); + assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); assert_executable_exists(dest_workspace, "foo"); assert_built_library_exists(dest_workspace, "foo"); assert_built_executable_exists(dest_workspace, "foo"); - assert!(!lib_exists(workspace, &Path("foo"), NoVersion)); + assert!(!lib_exists(workspace, &Path::from_str("foo"), NoVersion)); assert!(!executable_exists(workspace, "foo")); assert!(!built_library_exists(workspace, "foo")); assert!(!built_executable_exists(workspace, "foo")); @@ -1332,18 +1369,19 @@ fn test_rust_path_can_contain_package_dirs_without_flag() { fn rust_path_hack_cwd() { // Same as rust_path_hack_test, but the CWD is the dir to build out of let cwd = TempDir::new("foo").expect("rust_path_hack_cwd"); - let cwd = cwd.path().push("foo"); + let cwd = cwd.path().join_str("foo"); assert!(os::mkdir_recursive(&cwd, U_RWX)); - writeFile(&cwd.push("lib.rs"), "pub fn f() { }"); + writeFile(&cwd.join_str("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack", ~"foo"], &cwd, rust_path); - debug2!("Checking that foo exists in {}", dest_workspace.to_str()); - assert_lib_exists(dest_workspace, &Path("foo"), NoVersion); + debug2!("Checking that foo exists in {}", dest_workspace.display()); + assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); assert_built_library_exists(dest_workspace, "foo"); - assert!(!lib_exists(&cwd, &Path("foo"), NoVersion)); + assert!(!lib_exists(&cwd, &Path::from_str("foo"), NoVersion)); assert!(!built_library_exists(&cwd, "foo")); } @@ -1351,19 +1389,20 @@ fn rust_path_hack_cwd() { fn rust_path_hack_multi_path() { // Same as rust_path_hack_test, but with a more complex package ID let cwd = TempDir::new("pkg_files").expect("rust_path_hack_cwd"); - let subdir = cwd.path().push_many([~"foo", ~"bar", ~"quux"]); + let subdir = cwd.path().join_many_str(["foo", "bar", "quux"]); assert!(os::mkdir_recursive(&subdir, U_RWX)); - writeFile(&subdir.push("lib.rs"), "pub fn f() { }"); + writeFile(&subdir.join_str("lib.rs"), "pub fn f() { }"); let name = ~"foo/bar/quux"; - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack", name.clone()], &subdir, rust_path); - debug2!("Checking that {} exists in {}", name, dest_workspace.to_str()); - assert_lib_exists(dest_workspace, &Path("quux"), NoVersion); + debug2!("Checking that {} exists in {}", name, dest_workspace.display()); + assert_lib_exists(dest_workspace, &Path::from_str("quux"), NoVersion); assert_built_library_exists(dest_workspace, name); - assert!(!lib_exists(&subdir, &Path("quux"), NoVersion)); + assert!(!lib_exists(&subdir, &Path::from_str("quux"), NoVersion)); assert!(!built_library_exists(&subdir, name)); } @@ -1372,18 +1411,19 @@ fn rust_path_hack_install_no_arg() { // Same as rust_path_hack_cwd, but making rustpkg infer the pkg id let cwd = TempDir::new("pkg_files").expect("rust_path_hack_install_no_arg"); let cwd = cwd.path(); - let source_dir = cwd.push("foo"); + let source_dir = cwd.join_str("foo"); assert!(make_dir_rwx(&source_dir)); - writeFile(&source_dir.push("lib.rs"), "pub fn f() { }"); + writeFile(&source_dir.join_str("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack"], &source_dir, rust_path); - debug2!("Checking that foo exists in {}", dest_workspace.to_str()); - assert_lib_exists(dest_workspace, &Path("foo"), NoVersion); + debug2!("Checking that foo exists in {}", dest_workspace.display()); + assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); assert_built_library_exists(dest_workspace, "foo"); - assert!(!lib_exists(&source_dir, &Path("foo"), NoVersion)); + assert!(!lib_exists(&source_dir, &Path::from_str("foo"), NoVersion)); assert!(!built_library_exists(cwd, "foo")); } @@ -1391,15 +1431,16 @@ fn rust_path_hack_install_no_arg() { fn rust_path_hack_build_no_arg() { // Same as rust_path_hack_install_no_arg, but building instead of installing let cwd = TempDir::new("pkg_files").expect("rust_path_hack_build_no_arg"); - let source_dir = cwd.path().push("foo"); + let source_dir = cwd.path().join_str("foo"); assert!(make_dir_rwx(&source_dir)); - writeFile(&source_dir.push("lib.rs"), "pub fn f() { }"); + writeFile(&source_dir.join_str("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - let rust_path = Some(~[(~"RUST_PATH", dest_workspace.to_str())]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"build", ~"--rust-path-hack"], &source_dir, rust_path); - debug2!("Checking that foo exists in {}", dest_workspace.to_str()); + debug2!("Checking that foo exists in {}", dest_workspace.display()); assert_built_library_exists(dest_workspace, "foo"); assert!(!built_library_exists(&source_dir, "foo")); } @@ -1408,16 +1449,18 @@ fn rust_path_hack_build_no_arg() { fn rust_path_install_target() { let dir_for_path = TempDir::new( "source_workspace").expect("rust_path_install_target failed"); - let dir = mk_workspace(dir_for_path.path(), &Path("foo"), &NoVersion); - debug2!("dir = {}", dir.to_str()); - writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }"); + let mut dir = mk_workspace(dir_for_path.path(), &Path::from_str("foo"), &NoVersion); + debug2!("dir = {}", dir.display()); + writeFile(&dir.join_str("main.rs"), "fn main() { let _x = (); }"); let dir_to_install_to = TempDir::new( "dest_workspace").expect("rust_path_install_target failed"); let dir_to_install_to = dir_to_install_to.path(); - let dir = dir.pop().pop(); + dir.pop(); dir.pop(); - let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dir_to_install_to.to_str(), - dir.to_str()))]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", + dir_to_install_to.as_str().unwrap(), + dir.as_str().unwrap()))]); let cwd = os::getcwd(); command_line_test_with_env([~"install", ~"foo"], &cwd, @@ -1433,8 +1476,10 @@ fn sysroot_flag() { let workspace = create_local_package(&p_id); let workspace = workspace.path(); // no-op sysroot setting; I'm not sure how else to test this + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths command_line_test([~"--sysroot", - test_sysroot().to_str(), + test_sys.as_str().unwrap().to_owned(), ~"build", ~"foo"], workspace); @@ -1446,7 +1491,9 @@ fn compile_flag_build() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"--no-link", ~"foo"], @@ -1461,7 +1508,9 @@ fn compile_flag_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"install", ~"--no-link", ~"foo"], @@ -1479,7 +1528,9 @@ fn notrans_flag_build() { ~"--pretty", ~"-S"]; for flag in flags_to_test.iter() { - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", flag.clone(), ~"foo"], @@ -1501,14 +1552,16 @@ fn notrans_flag_fail() { let flags_to_test = [~"--no-trans", ~"--parse-only", ~"--pretty", ~"-S"]; for flag in flags_to_test.iter() { - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"install", flag.clone(), ~"foo"], workspace, None, BAD_FLAG_CODE); assert!(!built_executable_exists(workspace, "foo")); assert!(!object_file_exists(workspace, "foo")); - assert!(!lib_exists(workspace, &Path("foo"), NoVersion)); + assert!(!lib_exists(workspace, &Path::from_str("foo"), NoVersion)); } } @@ -1517,7 +1570,9 @@ fn dash_S() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sysroot().as_str().unwrap().to_owned(), ~"build", ~"-S", ~"foo"], @@ -1532,7 +1587,9 @@ fn dash_S_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"install", ~"-S", ~"foo"], @@ -1548,9 +1605,11 @@ fn test_cfg_build() { let workspace = create_local_package(&p_id); let workspace = workspace.path(); // If the cfg flag gets messed up, this won't compile - writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]), + writeFile(&workspace.join_many_str(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"--cfg", ~"quux", @@ -1564,9 +1623,11 @@ fn test_cfg_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]), + writeFile(&workspace.join_many_str(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); - match command_line_test_partial([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + match command_line_test_partial([test_sys.as_str().unwrap().to_owned(), ~"build", ~"foo"], workspace) { @@ -1581,7 +1642,9 @@ fn test_emit_llvm_S_build() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"-S", ~"--emit-llvm", ~"foo"], @@ -1597,7 +1660,9 @@ fn test_emit_llvm_S_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"install", ~"-S", ~"--emit-llvm", ~"foo"], @@ -1615,7 +1680,9 @@ fn test_emit_llvm_build() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"--emit-llvm", ~"foo"], @@ -1632,7 +1699,9 @@ fn test_emit_llvm_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"install", ~"--emit-llvm", ~"foo"], @@ -1659,7 +1728,9 @@ fn test_linker_build() { let sess = build_session(options, @diagnostic::DefaultEmitter as @diagnostic::Emitter); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"install", ~"--linker", get_cc_prog(sess), @@ -1681,7 +1752,9 @@ fn test_build_install_flags_fail() { ~[~"-Z", ~"--time-passes"]]; let cwd = os::getcwd(); for flag in forbidden.iter() { - command_line_test_expect_fail([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_expect_fail([test_sys.as_str().unwrap().to_owned(), ~"list"] + *flag, &cwd, None, BAD_FLAG_CODE); } } @@ -1691,7 +1764,9 @@ fn test_optimized_build() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"-O", ~"foo"], @@ -1705,23 +1780,23 @@ fn pkgid_pointing_to_subdir() { // rustpkg should recognize that and treat the part after some_repo/ as a subdir let workspace = TempDir::new("parent_repo").expect("Couldn't create temp dir"); let workspace = workspace.path(); - assert!(os::mkdir_recursive(&workspace.push_many([~"src", ~"mockgithub.com", - ~"mozilla", ~"some_repo"]), U_RWX)); + assert!(os::mkdir_recursive(&workspace.join_many_str(["src", "mockgithub.com", + "mozilla", "some_repo"]), U_RWX)); - let foo_dir = workspace.push_many([~"src", ~"mockgithub.com", ~"mozilla", ~"some_repo", - ~"extras", ~"foo"]); - let bar_dir = workspace.push_many([~"src", ~"mockgithub.com", ~"mozilla", ~"some_repo", - ~"extras", ~"bar"]); + let foo_dir = workspace.join_many_str(["src", "mockgithub.com", "mozilla", "some_repo", + "extras", "foo"]); + let bar_dir = workspace.join_many_str(["src", "mockgithub.com", "mozilla", "some_repo", + "extras", "bar"]); assert!(os::mkdir_recursive(&foo_dir, U_RWX)); assert!(os::mkdir_recursive(&bar_dir, U_RWX)); - writeFile(&foo_dir.push("lib.rs"), "pub fn f() {}"); - writeFile(&bar_dir.push("lib.rs"), "pub fn g() {}"); + writeFile(&foo_dir.join_str("lib.rs"), "pub fn f() {}"); + writeFile(&bar_dir.join_str("lib.rs"), "pub fn g() {}"); - debug2!("Creating a file in {}", workspace.to_str()); - let testpkg_dir = workspace.push_many([~"src", ~"testpkg-0.1"]); + debug2!("Creating a file in {}", workspace.display()); + let testpkg_dir = workspace.join_many_str(["src", "testpkg-0.1"]); assert!(os::mkdir_recursive(&testpkg_dir, U_RWX)); - writeFile(&testpkg_dir.push("main.rs"), + writeFile(&testpkg_dir.join_str("main.rs"), "extern mod foo = \"mockgithub.com/mozilla/some_repo/extras/foo\";\n extern mod bar = \"mockgithub.com/mozilla/some_repo/extras/bar\";\n use foo::f; use bar::g; \n @@ -1738,22 +1813,23 @@ fn test_recursive_deps() { let c_id = PkgId::new("c"); let b_workspace = create_local_package_with_dep(&b_id, &c_id); let b_workspace = b_workspace.path(); - writeFile(&b_workspace.push("src").push("c-0.1").push("lib.rs"), + writeFile(&b_workspace.join_many_str(["src", "c-0.1", "lib.rs"])), "pub fn g() {}"); let a_workspace = create_local_package(&a_id); let a_workspace = a_workspace.path(); - writeFile(&a_workspace.push("src").push("a-0.1").push("main.rs"), + writeFile(&a_workspace.join_many_str(["src", "a-0.1", "main.rs"]), "extern mod b; use b::f; fn main() { f(); }"); - writeFile(&b_workspace.push("src").push("b-0.1").push("lib.rs"), + writeFile(&b_workspace.join_many_str(["src", "b-0.1", "lib.rs"]), "extern mod c; use c::g; pub fn f() { g(); }"); - let environment = Some(~[(~"RUST_PATH", b_workspace.to_str())]); - debug2!("RUST_PATH={}", b_workspace.to_str()); + // FIXME (#9639): This needs to handle non-utf8 paths + let environment = Some(~[(~"RUST_PATH", b_workspace.as_str().unwrap().to_owned())]); + debug2!("RUST_PATH={}", b_workspace.display()); command_line_test_with_env([~"install", ~"a"], a_workspace, environment); - assert_lib_exists(a_workspace, &Path("a"), NoVersion); - assert_lib_exists(b_workspace, &Path("b"), NoVersion); - assert_lib_exists(b_workspace, &Path("c"), NoVersion); + assert_lib_exists(a_workspace, &Path::from_str("a"), NoVersion); + assert_lib_exists(b_workspace, &Path::from_str("b"), NoVersion); + assert_lib_exists(b_workspace, &Path::from_str("c"), NoVersion); } #[test] @@ -1761,13 +1837,16 @@ fn test_install_to_rust_path() { let p_id = PkgId::new("foo"); let second_workspace = create_local_package(&p_id); let second_workspace = second_workspace.path(); - let first_workspace = mk_empty_workspace(&Path("p"), &NoVersion, "dest"); + let first_workspace = mk_empty_workspace(&Path::from_str("p"), &NoVersion, "dest"); let first_workspace = first_workspace.path(); + // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", - format!("{}:{}", first_workspace.to_str(), - second_workspace.to_str()))]); - debug2!("RUST_PATH={}:{}", first_workspace.to_str(), second_workspace.to_str()); - command_line_test_with_env([test_sysroot().to_str(), + format!("{}:{}", first_workspace.as_str().unwrap(), + second_workspace.as_str().unwrap()))]); + debug2!("RUST_PATH={}:{}", first_workspace.display(), second_workspace.display()); + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test_with_env([test_sys.as_str().unwrap().to_owned(), ~"install", ~"foo"], &os::getcwd(), rust_path); @@ -1782,13 +1861,15 @@ fn test_target_specific_build_dir() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"foo"], workspace); assert!(os::path_is_dir(&target_build_dir(workspace))); assert!(built_executable_exists(workspace, "foo")); - assert!(os::list_dir(&workspace.push("build")).len() == 1); + assert!(os::list_dir(&workspace.join_str("build")).len() == 1); } #[test] @@ -1796,14 +1877,16 @@ fn test_target_specific_install_dir() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - command_line_test([test_sysroot().to_str(), + let test_sys = test_sysroot(); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"install", ~"foo"], workspace); - assert!(os::path_is_dir(&workspace.push("lib").push(host_triple()))); - assert_lib_exists(workspace, &Path("foo"), NoVersion); - assert!(os::list_dir(&workspace.push("lib")).len() == 1); - assert!(os::path_is_dir(&workspace.push("bin"))); + assert!(os::path_is_dir(&workspace.join_many_str(["lib", host_triple()]))); + assert_lib_exists(workspace, &Path::from_str("foo"), NoVersion); + assert!(os::list_dir(&workspace.join_str("lib")).len() == 1); + assert!(os::path_is_dir(&workspace.join_str("bin"))); assert_executable_exists(workspace, "foo"); } @@ -1813,10 +1896,10 @@ fn test_dependencies_terminate() { let b_id = PkgId::new("b"); let workspace = create_local_package(&b_id); let workspace = workspace.path(); - let b_dir = workspace.push_many([~"src", ~"b-0.1"]); - let b_subdir = b_dir.push("test"); + let b_dir = workspace.join_many_str(["src", "b-0.1"]); + let b_subdir = b_dir.join_str("test"); assert!(os::mkdir_recursive(&b_subdir, U_RWX)); - writeFile(&b_subdir.push("test.rs"), + writeFile(&b_subdir.join_str("test.rs"), "extern mod b; use b::f; #[test] fn g() { f() }"); command_line_test([~"install", ~"b"], workspace); } @@ -1877,14 +1960,16 @@ fn correct_package_name_with_rust_path_hack() { let bar_id = PkgId::new("bar"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - writeFile(&dest_workspace.push_many(["src", "bar-0.1", "main.rs"]), + writeFile(&dest_workspace.join_many_str(["src", "bar-0.1", "main.rs"]), "extern mod blat; fn main() { let _x = (); }"); - let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dest_workspace.to_str(), - foo_workspace.push_many(["src", "foo-0.1"]).to_str()))]); + let foo_path = foo_workspace.join_many_str(["src", "foo-0.1"]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dest_workspace.as_str().unwrap(), + foo_path.as_str().unwrap()))]); // bar doesn't exist, but we want to make sure rustpkg doesn't think foo is bar command_line_test_expect_fail([~"install", ~"--rust-path-hack", ~"bar"], // FIXME #3408: Should be NONEXISTENT_PACKAGE_CODE @@ -1904,7 +1989,7 @@ fn test_rustpkg_test_creates_exec() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - writeFile(&foo_workspace.push_many(["src", "foo-0.1", "test.rs"]), + writeFile(&foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]), "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -1928,7 +2013,7 @@ fn test_rebuild_when_needed() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let test_crate = foo_workspace.push_many(["src", "foo-0.1", "test.rs"]); + let test_crate = foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]); writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -1948,7 +2033,7 @@ fn test_no_rebuilding() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let test_crate = foo_workspace.push_many(["src", "foo-0.1", "test.rs"]); + let test_crate = foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]); writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -1969,23 +2054,24 @@ fn test_installed_read_only() { let temp_pkg_id = git_repo_pkg(); let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); - debug2!("repo = {}", repo.to_str()); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]); - debug2!("repo_subdir = {}", repo_subdir.to_str()); + debug2!("repo = {}", repo.display()); + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + debug2!("repo_subdir = {}", repo_subdir.display()); - writeFile(&repo_subdir.push("main.rs"), + writeFile(&repo_subdir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.push("lib.rs"), + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files - command_line_test([~"install", temp_pkg_id.path.to_str()], repo); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo); - let ws = repo.push(".rust"); + let ws = repo.join_str(".rust"); // Check that all files exist - debug2!("Checking for files in {}", ws.to_str()); + debug2!("Checking for files in {}", ws.display()); let exec = target_executable_in_workspace(&temp_pkg_id, &ws); - debug2!("exec = {}", exec.to_str()); + debug2!("exec = {}", exec.display()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); let built_lib = @@ -1995,8 +2081,8 @@ fn test_installed_read_only() { assert!(is_rwx(&built_lib)); // Make sure sources are (a) under "build" and (b) read-only - let src1 = target_build_dir(&ws).push_many([~"src", temp_pkg_id.to_str(), ~"main.rs"]); - let src2 = target_build_dir(&ws).push_many([~"src", temp_pkg_id.to_str(), ~"lib.rs"]); + let src1 = target_build_dir(&ws).join_many_str(["src", temp_pkg_id.to_str(), "main.rs"]); + let src2 = target_build_dir(&ws).join_many_str(["src", temp_pkg_id.to_str(), "lib.rs"]); assert!(os::path_exists(&src1)); assert!(os::path_exists(&src2)); assert!(is_read_only(&src1)); @@ -2008,29 +2094,30 @@ fn test_installed_local_changes() { let temp_pkg_id = git_repo_pkg(); let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); - debug2!("repo = {}", repo.to_str()); - let repo_subdir = repo.push_many([~"mockgithub.com", ~"catamorphism", ~"test-pkg"]); - debug2!("repo_subdir = {}", repo_subdir.to_str()); - assert!(os::mkdir_recursive(&repo.push_many([".rust", "src"]), U_RWX)); + debug2!("repo = {}", repo.display()); + let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + debug2!("repo_subdir = {}", repo_subdir.display()); + assert!(os::mkdir_recursive(&repo.join_many_str([".rust", "src"]), U_RWX)); - writeFile(&repo_subdir.push("main.rs"), + writeFile(&repo_subdir.join_str("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.push("lib.rs"), + writeFile(&repo_subdir.join_str("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files - command_line_test([~"install", temp_pkg_id.path.to_str()], repo); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo); // We installed the dependency. // Now start a new workspace and clone it into it let hacking_workspace = mk_emptier_workspace("hacking_workspace"); let hacking_workspace = hacking_workspace.path(); - let target_dir = hacking_workspace.push_many([~"src", - ~"mockgithub.com", - ~"catamorphism", - ~"test-pkg-0.1"]); - debug2!("---- git clone {} {}", repo_subdir.to_str(), target_dir.to_str()); + let target_dir = hacking_workspace.join_many_str(["src", + "mockgithub.com", + "catamorphism", + "test-pkg-0.1"]); + debug2!("---- git clone {} {}", repo_subdir.display(), target_dir.display()); let c_res = safe_git_clone(&repo_subdir, &NoVersion, &target_dir); @@ -2040,19 +2127,21 @@ fn test_installed_local_changes() { }; // Make a local change to it - writeFile(&target_dir.push("lib.rs"), + writeFile(&target_dir.join_str("lib.rs"), "pub fn g() { let _x = (); }"); // Finally, make *another* package that uses it let importer_pkg_id = fake_pkg(); let main_subdir = create_local_package_in(&importer_pkg_id, hacking_workspace); - writeFile(&main_subdir.push("main.rs"), + writeFile(&main_subdir.join_str("main.rs"), "extern mod test = \"mockgithub.com/catamorphism/test-pkg\"; \ use test::g; fn main() { g(); }"); // And make sure we can build it - command_line_test([~"build", importer_pkg_id.path.to_str()], hacking_workspace); + // FIXME (#9639): This needs to handle non-utf8 paths + command_line_test([~"build", importer_pkg_id.path.as_str().unwrap().to_owned()], + hacking_workspace); } #[test] @@ -2060,8 +2149,10 @@ fn test_7402() { let dir = create_local_package(&PkgId::new("foo")); let dest_workspace = TempDir::new("more_rust").expect("test_7402"); let dest_workspace = dest_workspace.path(); + // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", - format!("{}:{}", dest_workspace.to_str(), dir.path().to_str()))]); + format!("{}:{}", dest_workspace.as_str().unwrap(), + dir.path().as_str().unwrap()))]); let cwd = os::getcwd(); command_line_test_with_env([~"install", ~"foo"], &cwd, rust_path); assert_executable_exists(dest_workspace, "foo"); @@ -2072,7 +2163,7 @@ fn test_compile_error() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let main_crate = foo_workspace.push_many(["src", "foo-0.1", "main.rs"]); + let main_crate = foo_workspace.join_many_str(["src", "foo-0.1", "main.rs"]); // Write something bogus writeFile(&main_crate, "pub fn main() { if 42 != ~\"the answer\" { fail!(); } }"); let result = command_line_test_partial([~"build", ~"foo"], foo_workspace); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index d66dd847405cf..14de18734ed60 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -170,13 +170,14 @@ pub fn compile_input(context: &BuildContext, cfgs: &[~str], opt: bool, what: OutputType) -> Option { - assert!(in_file.components.len() > 1); - let input = driver::file_input((*in_file).clone()); - debug2!("compile_input: {} / {:?}", in_file.to_str(), what); + assert!(in_file.component_iter().nth(1).is_some()); + let input = driver::file_input(in_file.clone()); + debug2!("compile_input: {} / {:?}", in_file.display(), what); // tjc: by default, use the package ID name as the link name // not sure if we should support anything else - let out_dir = target_build_dir(workspace).push_rel(&pkg_id.path); + let mut out_dir = target_build_dir(workspace); + out_dir.push_path(&pkg_id.path); // Make the output directory if it doesn't exist already assert!(os::mkdir_recursive(&out_dir, U_RWX)); @@ -184,7 +185,8 @@ pub fn compile_input(context: &BuildContext, debug2!("flags: {}", flags.connect(" ")); debug2!("cfgs: {}", cfgs.connect(" ")); - debug2!("compile_input's sysroot = {}", context.sysroot().to_str()); + let csysroot = context.sysroot(); + debug2!("compile_input's sysroot = {}", csysroot.display()); let crate_type = match what { Lib => lib_crate, @@ -209,10 +211,15 @@ pub fn compile_input(context: &BuildContext, context.sysroot() } else { - context.sysroot().pop().pop().pop() + let mut p = context.sysroot().clone(); + p.pop(); + p.pop(); + p.pop(); + p }; - debug2!("compile_input's sysroot = {}", context.sysroot().to_str()); - debug2!("sysroot_to_use = {}", sysroot_to_use.to_str()); + let csysroot = context.sysroot(); + debug2!("compile_input's sysroot = {}", csysroot.display()); + debug2!("sysroot_to_use = {}", sysroot_to_use.display()); let output_type = match context.compile_upto() { Assemble => link::output_type_assembly, @@ -260,7 +267,7 @@ pub fn compile_input(context: &BuildContext, find_and_install_dependencies(context, pkg_id, sess, exec, &crate, |p| { - debug2!("a dependency: {}", p.to_str()); + debug2!("a dependency: {}", p.display()); // Pass the directory containing a dependency // as an additional lib search path if !addl_lib_search_paths.contains(&p) { @@ -278,18 +285,19 @@ pub fn compile_input(context: &BuildContext, _ => pkg_id.short_name.to_managed() }; debug2!("Injecting link name: {}", name_to_use); + // FIXME (#9639): This needs to handle non-utf8 paths let link_options = ~[attr::mk_name_value_item_str(@"name", name_to_use), attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())] + ~[attr::mk_name_value_item_str(@"package_id", - pkg_id.path.to_str().to_managed())]; + pkg_id.path.as_str().unwrap().to_managed())]; debug2!("link options: {:?}", link_options); crate.attrs = ~[attr::mk_attr(attr::mk_list_item(@"link", link_options))]; } debug2!("calling compile_crate_from_input, workspace = {}, - building_library = {:?}", out_dir.to_str(), sess.building_library); + building_library = {:?}", out_dir.display(), sess.building_library); let result = compile_crate_from_input(in_file, exec, context.compile_upto(), @@ -303,11 +311,12 @@ pub fn compile_input(context: &BuildContext, else { result }; - debug2!("About to discover output {}", discovered_output.to_str()); for p in discovered_output.iter() { + debug2!("About to discover output {}", p.display()); if os::path_exists(p) { - debug2!("4. discovering output {}", p.to_str()); - exec.discover_output("binary", p.to_str(), digest_only_date(p)); + debug2!("4. discovering output {}", p.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + exec.discover_output("binary", p.as_str().unwrap(), digest_only_date(p)); } // Nothing to do if it doesn't exist -- that could happen if we had the // -S or -emit-llvm flags, etc. @@ -330,21 +339,21 @@ pub fn compile_crate_from_input(input: &Path, // given crate: ast::Crate) -> Option { debug2!("Calling build_output_filenames with {}, building library? {:?}", - out_dir.to_str(), sess.building_library); + out_dir.display(), sess.building_library); // bad copy - debug2!("out_dir = {}", out_dir.to_str()); + debug2!("out_dir = {}", out_dir.display()); let outputs = driver::build_output_filenames(&driver::file_input(input.clone()), &Some(out_dir.clone()), &None, crate.attrs, sess); debug2!("Outputs are out_filename: {} and obj_filename: {} and output type = {:?}", - outputs.out_filename.to_str(), - outputs.obj_filename.to_str(), + outputs.out_filename.display(), + outputs.obj_filename.display(), sess.opts.output_type); debug2!("additional libraries:"); for lib in sess.opts.addl_lib_search_paths.iter() { - debug2!("an additional library: {}", lib.to_str()); + debug2!("an additional library: {}", lib.display()); } let analysis = driver::phase_3_run_analysis_passes(sess, &crate); if driver::stop_after_phase_3(sess) { return None; } @@ -359,9 +368,10 @@ pub fn compile_crate_from_input(input: &Path, driver::phase_6_link_output(sess, &translation, outputs); // Register dependency on the source file - exec.discover_input("file", input.to_str(), digest_file_with_date(input)); + // FIXME (#9639): This needs to handle non-utf8 paths + exec.discover_input("file", input.as_str().unwrap(), digest_file_with_date(input)); - debug2!("Built {}, date = {:?}", outputs.out_filename.to_str(), + debug2!("Built {}, date = {:?}", outputs.out_filename.display(), datestamp(&outputs.out_filename)); Some(outputs.out_filename) @@ -383,7 +393,7 @@ pub fn compile_crate(ctxt: &BuildContext, crate: &Path, workspace: &Path, flags: &[~str], cfgs: &[~str], opt: bool, what: OutputType) -> Option { - debug2!("compile_crate: crate={}, workspace={}", crate.to_str(), workspace.to_str()); + debug2!("compile_crate: crate={}, workspace={}", crate.display(), workspace.display()); debug2!("compile_crate: short_name = {}, flags =...", pkg_id.to_str()); for fl in flags.iter() { debug2!("+++ {}", *fl); @@ -414,15 +424,16 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { // Check standard Rust library path first match system_library(&self.context.sysroot(), lib_name) { Some(ref installed_path) => { - debug2!("It exists: {}", installed_path.to_str()); + debug2!("It exists: {}", installed_path.display()); // Say that [path for c] has a discovered dependency on // installed_path // For binary files, we only hash the datestamp, not the contents. // I'm not sure what the right thing is. // Now we know that this crate has a discovered dependency on // installed_path + // FIXME (#9639): This needs to handle non-utf8 paths self.exec.discover_input("binary", - installed_path.to_str(), + installed_path.as_str().unwrap(), digest_only_date(installed_path)); } None => { @@ -456,7 +467,8 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { self.context.context.use_rust_path_hack, pkg_id); let (outputs_disc, inputs_disc) = - self.context.install(pkg_src, &JustOne(Path(lib_crate_filename))); + self.context.install(pkg_src, + &JustOne(Path::from_str(lib_crate_filename))); debug2!("Installed {}, returned {:?} dependencies and \ {:?} transitive dependencies", lib_name, outputs_disc.len(), inputs_disc.len()); @@ -465,24 +477,27 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { // It must have installed *something*... assert!(!outputs_disc.is_empty()); for dep in outputs_disc.iter() { - debug2!("Discovering a binary input: {}", dep.to_str()); + debug2!("Discovering a binary input: {}", dep.display()); + // FIXME (#9639): This needs to handle non-utf8 paths self.exec.discover_input("binary", - dep.to_str(), + dep.as_str().unwrap(), digest_only_date(dep)); // Also, add an additional search path - debug2!("Installed {} into {}", dep.to_str(), dep.pop().to_str()); - (self.save)(dep.pop()); + debug2!("Installed {} into {}", dep.display(), + dep.dir_path().display()); + (self.save)(dep.dir_path()); } for &(ref what, ref dep) in inputs_disc.iter() { if *what == ~"file" { self.exec.discover_input(*what, *dep, - digest_file_with_date(&Path(*dep))); + digest_file_with_date( + &Path::from_str(*dep))); } else if *what == ~"binary" { self.exec.discover_input(*what, *dep, - digest_only_date(&Path(*dep))); + digest_only_date(&Path::from_str(*dep))); } else { fail2!("Bad kind: {}", *what); @@ -559,7 +574,7 @@ fn debug_flags() -> ~[~str] { ~[] } /// Returns the last-modified date as an Option pub fn datestamp(p: &Path) -> Option { - debug2!("Scrutinizing datestamp for {} - does it exist? {:?}", p.to_str(), os::path_exists(p)); + debug2!("Scrutinizing datestamp for {} - does it exist? {:?}", p.display(), os::path_exists(p)); let out = p.stat().map(|stat| stat.st_mtime); debug2!("Date = {:?}", out); out.map(|t| { t as libc::time_t }) diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs index 17b3cad0c2c3c..d0c76498c6264 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -98,15 +98,16 @@ pub fn parse_vers(vers: ~str) -> result::Result { pub fn try_getting_local_version(local_path: &Path) -> Option { let rustpath = rust_path(); for rp in rustpath.iter() { - let local_path = rp.push_rel(local_path); - let git_dir = local_path.push(".git"); + let local_path = rp.join_path(local_path); + let git_dir = local_path.join_str(".git"); if !os::path_is_dir(&git_dir) { continue; } + // FIXME (#9639): This needs to handle non-utf8 paths let outp = run::process_output("git", - [format!("--git-dir={}", git_dir.to_str()), ~"tag", ~"-l"]); + ["--git-dir=" + git_dir.as_str().unwrap(), ~"tag", ~"-l"]); - debug2!("git --git-dir={} tag -l ~~~> {:?}", git_dir.to_str(), outp.status); + debug2!("git --git-dir={} tag -l ~~~> {:?}", git_dir.display(), outp.status); if outp.status != 0 { continue; @@ -136,21 +137,23 @@ pub fn try_getting_version(remote_path: &Path) -> Option { let tmp_dir = tmp_dir.expect("try_getting_version: couldn't create temp dir"); let tmp_dir = tmp_dir.path(); debug2!("(to get version) executing \\{git clone https://{} {}\\}", - remote_path.to_str(), - tmp_dir.to_str()); - let outp = run::process_output("git", [~"clone", - format!("https://{}", - remote_path.to_str()), - tmp_dir.to_str()]); + remote_path.display(), + tmp_dir.display()); + // FIXME (#9639): This needs to handle non-utf8 paths + let outp = run::process_output("git", [~"clone", format!("https://{}", + remote_path.as_str().unwrap()), + tmp_dir.as_str().unwrap().to_owned()]); if outp.status == 0 { debug2!("Cloned it... ( {}, {} )", str::from_utf8(outp.output), str::from_utf8(outp.error)); let mut output = None; + let git_dir = tmp_dir.join_str(".git"); debug2!("(getting version, now getting tags) executing \\{git --git-dir={} tag -l\\}", - tmp_dir.push(".git").to_str()); + git_dir.display()); + // FIXME (#9639): This needs to handle non-utf8 paths let outp = run::process_output("git", - [format!("--git-dir={}", tmp_dir.push(".git").to_str()), + ["--git-dir=" + git_dir.as_str().unwrap(), ~"tag", ~"-l"]); let output_text = str::from_utf8(outp.output); debug2!("Full output: ( {} ) [{:?}]", output_text, outp.status); @@ -203,8 +206,8 @@ pub fn try_parsing_version(s: &str) -> Option { /// Just an approximation fn is_url_like(p: &Path) -> bool { - let str = p.to_str(); - str.split_iter('/').len() > 2 + // check if there are more than 2 /-separated components + p.as_vec().split_iter(|b| *b == '/' as u8).nth(2).is_some() } /// If s is of the form foo#bar, where bar is a valid version diff --git a/src/librustpkg/workcache_support.rs b/src/librustpkg/workcache_support.rs index 8af24ff4c3889..34404ad625c78 100644 --- a/src/librustpkg/workcache_support.rs +++ b/src/librustpkg/workcache_support.rs @@ -30,7 +30,12 @@ pub fn digest_file_with_date(path: &Path) -> ~str { (*sha).input_str(st.st_mtime.to_str()); (*sha).result_str() } - Err(e) => cond.raise((path.clone(), format!("Couldn't read file: {}", e))).to_str() + Err(e) => { + let path = cond.raise((path.clone(), format!("Couldn't read file: {}", e))); + // FIXME (#9639): This needs to handle non-utf8 paths + // XXX: I'm pretty sure this is the wrong return value + path.as_str().unwrap().to_owned() + } } } @@ -51,13 +56,15 @@ pub fn digest_only_date(path: &Path) -> ~str { pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) { debug2!("Discovering {:?} outputs", outputs.len()); for p in outputs.iter() { - debug2!("Discovering output! {}", p.to_str()); + debug2!("Discovering output! {}", p.display()); // For now, assume that all discovered outputs are binaries - e.discover_output("binary", p.to_str(), digest_only_date(p)); + // FIXME (#9639): This needs to handle non-utf8 paths + e.discover_output("binary", p.as_str().unwrap(), digest_only_date(p)); } } /// Returns the function name for building a crate pub fn crate_tag(p: &Path) -> ~str { - p.to_str() // implicitly, it's "build(p)"... + // FIXME (#9639): This needs to handle non-utf8 paths + p.as_str().unwrap().to_owned() // implicitly, it's "build(p)"... } diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index d5dab15c02ac6..15f9760a6379f 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -10,7 +10,7 @@ // rustpkg utilities having to do with workspaces -use std::{os,util}; +use std::os; use std::path::Path; use context::Context; use path_util::{workspace_contains_package_id, find_dir_using_rust_path_hack, default_workspace}; @@ -26,8 +26,8 @@ pub fn each_pkg_parent_workspace(cx: &Context, pkgid: &PkgId, action: &fn(&Path) // tjc: make this a condition fail2!("Package {} not found in any of \ the following workspaces: {}", - pkgid.path.to_str(), - rust_path().to_str()); + pkgid.path.display(), + rust_path().map(|p| p.to_display_str()).to_str()); } for ws in workspaces.iter() { if action(ws) { @@ -52,7 +52,7 @@ pub fn pkg_parent_workspaces(cx: &Context, pkgid: &PkgId) -> ~[Path] { } pub fn is_workspace(p: &Path) -> bool { - os::path_is_dir(&p.push("src")) + os::path_is_dir(&p.join_str("src")) } /// Construct a workspace and package-ID name based on the current directory. @@ -60,16 +60,13 @@ pub fn is_workspace(p: &Path) -> bool { pub fn cwd_to_workspace() -> Option<(Path, PkgId)> { let cwd = os::getcwd(); for path in rust_path().move_iter() { - let srcpath = path.push("src"); + let srcpath = path.join_str("src"); if srcpath.is_ancestor_of(&cwd) { - // I'd love to use srcpath.get_relative_to(cwd) but it behaves wrong - // I'd say broken, but it has tests enforcing the wrong behavior. - // instead, just hack up the components vec - let mut pkgid = cwd; - pkgid.is_absolute = false; - let comps = util::replace(&mut pkgid.components, ~[]); - pkgid.components = comps.move_iter().skip(srcpath.components.len()).collect(); - return Some((path, PkgId::new(pkgid.components.connect("/")))) + let rel = cwd.path_relative_from(&srcpath); + let rel_s = rel.and_then_ref(|p|p.as_str()); + if rel_s.is_some() { + return Some((path, PkgId::new(rel_s.unwrap()))); + } } } None diff --git a/src/libstd/io.rs b/src/libstd/io.rs index 7160496f4abad..434d781805bf8 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -60,12 +60,11 @@ use num; use ops::Drop; use option::{Some, None}; use os; -use path::Path; +use path::{Path,GenericPath}; use ptr; use result::{Result, Ok, Err}; use str::{StrSlice, OwnedStr}; use str; -use to_str::ToStr; use uint; use vec::{MutableVector, ImmutableVector, OwnedVector, OwnedCopyableVector, CopyableVector}; use vec; @@ -1069,7 +1068,9 @@ pub fn file_reader(path: &Path) -> Result<@Reader, ~str> { }; if f as uint == 0u { - Err(~"error opening " + path.to_str()) + do path.with_display_str |p| { + Err(~"error opening " + p) + } } else { Ok(FILE_reader(f, true)) } @@ -1335,7 +1336,7 @@ pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) } }; if fd < (0 as c_int) { - Err(format!("error opening {}: {}", path.to_str(), os::last_os_error())) + Err(format!("error opening {}: {}", path.display(), os::last_os_error())) } else { Ok(fd_writer(fd, true)) } @@ -1752,7 +1753,7 @@ pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> { if str::is_utf8(bytes) { Ok(str::from_utf8(bytes)) } else { - Err(file.to_str() + " is not UTF-8") + Err(file.to_display_str() + " is not UTF-8") } } } @@ -1892,8 +1893,8 @@ mod tests { #[test] fn test_simple() { - let tmpfile = &Path("tmp/lib-io-test-simple.tmp"); - debug2!("{:?}", tmpfile); + let tmpfile = &Path::from_str("tmp/lib-io-test-simple.tmp"); + debug2!("{}", tmpfile.display()); let frood: ~str = ~"A hoopy frood who really knows where his towel is."; debug2!("{}", frood.clone()); @@ -1910,7 +1911,7 @@ mod tests { #[test] fn test_each_byte_each_char_file() { // Issue #5056 -- shouldn't include trailing EOF. - let path = Path("tmp/lib-io-test-each-byte-each-char-file.tmp"); + let path = Path::from_str("tmp/lib-io-test-each-byte-each-char-file.tmp"); { // create empty, enough to reproduce a problem @@ -2010,7 +2011,7 @@ mod tests { #[test] fn file_reader_not_exist() { - match io::file_reader(&Path("not a file")) { + match io::file_reader(&Path::from_str("not a file")) { Err(e) => { assert_eq!(e, ~"error opening not a file"); } @@ -2021,7 +2022,7 @@ mod tests { #[test] #[should_fail] fn test_read_buffer_too_small() { - let path = &Path("tmp/lib-io-test-read-buffer-too-small.tmp"); + let path = &Path::from_str("tmp/lib-io-test-read-buffer-too-small.tmp"); // ensure the file exists io::file_writer(path, [io::Create]).unwrap(); @@ -2032,7 +2033,7 @@ mod tests { #[test] fn test_read_buffer_big_enough() { - let path = &Path("tmp/lib-io-test-read-buffer-big-enough.tmp"); + let path = &Path::from_str("tmp/lib-io-test-read-buffer-big-enough.tmp"); // ensure the file exists io::file_writer(path, [io::Create]).unwrap(); @@ -2043,7 +2044,7 @@ mod tests { #[test] fn test_write_empty() { - let file = io::file_writer(&Path("tmp/lib-io-test-write-empty.tmp"), + let file = io::file_writer(&Path::from_str("tmp/lib-io-test-write-empty.tmp"), [io::Create]).unwrap(); file.write([]); } @@ -2075,7 +2076,7 @@ mod tests { #[test] fn test_read_write_le() { - let path = Path("tmp/lib-io-test-read-write-le.tmp"); + let path = Path::from_str("tmp/lib-io-test-read-write-le.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file @@ -2097,7 +2098,7 @@ mod tests { #[test] fn test_read_write_be() { - let path = Path("tmp/lib-io-test-read-write-be.tmp"); + let path = Path::from_str("tmp/lib-io-test-read-write-be.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file @@ -2119,7 +2120,7 @@ mod tests { #[test] fn test_read_be_int_n() { - let path = Path("tmp/lib-io-test-read-be-int-n.tmp"); + let path = Path::from_str("tmp/lib-io-test-read-be-int-n.tmp"); let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; // write the ints to the file @@ -2143,7 +2144,7 @@ mod tests { #[test] fn test_read_f32() { - let path = Path("tmp/lib-io-test-read-f32.tmp"); + let path = Path::from_str("tmp/lib-io-test-read-f32.tmp"); //big-endian floating-point 8.1250 let buf = ~[0x41, 0x02, 0x00, 0x00]; @@ -2161,7 +2162,7 @@ mod tests { #[test] fn test_read_write_f32() { - let path = Path("tmp/lib-io-test-read-write-f32.tmp"); + let path = Path::from_str("tmp/lib-io-test-read-write-f32.tmp"); let f:f32 = 8.1250; { diff --git a/src/libstd/os.rs b/src/libstd/os.rs index b7921d7527b38..bfde1a86771ed 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -28,7 +28,7 @@ #[allow(missing_doc)]; -use c_str::ToCStr; +use c_str::{CString, ToCStr}; use clone::Clone; use container::Container; use io; @@ -78,22 +78,7 @@ pub fn getcwd() -> Path { fail2!() } - Path(str::raw::from_c_str(buf as *c_char)) - } - } -} - -// FIXME: move these to str perhaps? #2620 - -pub fn fill_charp_buf(f: &fn(*mut c_char, size_t) -> bool) -> Option<~str> { - let mut buf = [0 as c_char, .. TMPBUF_SZ]; - do buf.as_mut_buf |b, sz| { - if f(b, sz as size_t) { - unsafe { - Some(str::raw::from_c_str(b as *c_char)) - } - } else { - None + GenericPath::from_c_str(CString::new(buf as *c_char, false)) } } } @@ -451,70 +436,89 @@ pub fn dll_filename(base: &str) -> ~str { pub fn self_exe_path() -> Option { #[cfg(target_os = "freebsd")] - fn load_self() -> Option<~str> { + fn load_self() -> Option<~[u8]> { #[fixed_stack_segment]; #[inline(never)]; unsafe { use libc::funcs::bsd44::*; use libc::consts::os::extra::*; - do fill_charp_buf() |buf, sz| { - let mib = ~[CTL_KERN as c_int, - KERN_PROC as c_int, - KERN_PROC_PATHNAME as c_int, -1 as c_int]; - let mut sz = sz; + let mib = ~[CTL_KERN as c_int, + KERN_PROC as c_int, + KERN_PROC_PATHNAME as c_int, -1 as c_int]; + let mut sz: size_t = 0; + let err = sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint, + ptr::mut_null(), &mut sz, ptr::null(), 0u as size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + let mut v: ~[u8] = vec::with_capacity(sz as uint); + let err = do v.as_mut_buf |buf,_| { sysctl(vec::raw::to_ptr(mib), mib.len() as ::libc::c_uint, - buf as *mut c_void, &mut sz, ptr::null(), - 0u as size_t) == (0 as c_int) - } + buf as *mut c_void, &mut sz, ptr::null(), 0u as size_t) + }; + if err != 0 { return None; } + if sz == 0 { return None; } + vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL + Some(v) } } #[cfg(target_os = "linux")] #[cfg(target_os = "android")] - fn load_self() -> Option<~str> { + fn load_self() -> Option<~[u8]> { #[fixed_stack_segment]; #[inline(never)]; unsafe { use libc::funcs::posix01::unistd::readlink; - let mut path = [0 as c_char, .. TMPBUF_SZ]; - - do path.as_mut_buf |buf, len| { - let len = do "/proc/self/exe".with_c_str |proc_self_buf| { - readlink(proc_self_buf, buf, len as size_t) as uint - }; + let mut path: ~[u8] = vec::with_capacity(TMPBUF_SZ); - if len == -1 { - None - } else { - Some(str::raw::from_buf_len(buf as *u8, len)) + let len = do path.as_mut_buf |buf, _| { + do "/proc/self/exe".with_c_str |proc_self_buf| { + readlink(proc_self_buf, buf as *mut c_char, TMPBUF_SZ as size_t) as uint } + }; + if len == -1 { + None + } else { + vec::raw::set_len(&mut path, len as uint); + Some(path) } } } #[cfg(target_os = "macos")] - fn load_self() -> Option<~str> { + fn load_self() -> Option<~[u8]> { #[fixed_stack_segment]; #[inline(never)]; unsafe { - do fill_charp_buf() |buf, sz| { - let mut sz = sz as u32; - libc::funcs::extra::_NSGetExecutablePath( - buf, &mut sz) == (0 as c_int) - } + use libc::funcs::extra::_NSGetExecutablePath; + let mut sz: u32 = 0; + _NSGetExecutablePath(ptr::mut_null(), &mut sz); + if sz == 0 { return None; } + let mut v: ~[u8] = vec::with_capacity(sz as uint); + let err = do v.as_mut_buf |buf,_| { + _NSGetExecutablePath(buf as *mut i8, &mut sz) + }; + if err != 0 { return None; } + vec::raw::set_len(&mut v, sz as uint - 1); // chop off trailing NUL + Some(v) } } #[cfg(windows)] - fn load_self() -> Option<~str> { + fn load_self() -> Option<~[u8]> { #[fixed_stack_segment]; #[inline(never)]; unsafe { use os::win32::fill_utf16_buf_and_decode; do fill_utf16_buf_and_decode() |buf, sz| { libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) - } + }.map_move(|s| s.into_bytes()) } } - load_self().map(|path| Path(path).dir_path()) + load_self().and_then(|path| Path::from_vec_opt(path).map(|p| p.dir_path())) +} + + +/** + * Returns the path to the user's home directory, if known. } @@ -532,13 +536,10 @@ pub fn self_exe_path() -> Option { * Otherwise, homedir returns option::none. */ pub fn homedir() -> Option { + // FIXME (#7188): getenv needs a ~[u8] variant return match getenv("HOME") { - Some(ref p) => if !p.is_empty() { - Some(Path(*p)) - } else { - secondary() - }, - None => secondary() + Some(ref p) if !p.is_empty() => Path::from_str_opt(*p), + _ => secondary() }; #[cfg(unix)] @@ -550,7 +551,7 @@ pub fn homedir() -> Option { fn secondary() -> Option { do getenv("USERPROFILE").and_then |p| { if !p.is_empty() { - Some(Path(p)) + Path::from_str_opt(p) } else { None } @@ -579,7 +580,7 @@ pub fn tmpdir() -> Path { if x.is_empty() { None } else { - Some(Path(x)) + Path::from_str_opt(x) }, _ => None } @@ -588,9 +589,9 @@ pub fn tmpdir() -> Path { #[cfg(unix)] fn lookup() -> Path { if cfg!(target_os = "android") { - Path("/data/tmp") + Path::from_str("/data/tmp") } else { - getenv_nonempty("TMPDIR").unwrap_or(Path("/tmp")) + getenv_nonempty("TMPDIR").unwrap_or(Path::from_str("/tmp")) } } @@ -599,7 +600,7 @@ pub fn tmpdir() -> Path { getenv_nonempty("TMP").or( getenv_nonempty("TEMP").or( getenv_nonempty("USERPROFILE").or( - getenv_nonempty("WINDIR")))).unwrap_or(Path("C:\\Windows")) + getenv_nonempty("WINDIR")))).unwrap_or(Path::from_str("C:\\Windows")) } } @@ -607,7 +608,7 @@ pub fn tmpdir() -> Path { pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { let r = list_dir(p); r.iter().advance(|q| { - let path = &p.push(*q); + let path = &p.join_path(q); f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p))) }) } @@ -643,10 +644,12 @@ pub fn path_exists(p: &Path) -> bool { // querying; what it does depends on the process working directory, not just // the input paths. pub fn make_absolute(p: &Path) -> Path { - if p.is_absolute { - (*p).clone() + if p.is_absolute() { + p.clone() } else { - getcwd().push_many(p.components) + let mut ret = getcwd(); + ret.push_path(p); + ret } } @@ -661,7 +664,7 @@ pub fn make_dir(p: &Path, mode: c_int) -> bool { unsafe { use os::win32::as_utf16_p; // FIXME: turn mode into something useful? #2623 - do as_utf16_p(p.to_str()) |buf| { + do as_utf16_p(p.as_str().unwrap()) |buf| { libc::CreateDirectoryW(buf, ptr::mut_null()) != (0 as libc::BOOL) } @@ -690,38 +693,32 @@ pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { if path_is_dir(p) { return true; } - else if p.components.is_empty() { - return false; - } - else if p.components.len() == 1 { - // No parent directories to create - path_is_dir(p) || make_dir(p, mode) - } - else { - mkdir_recursive(&p.pop(), mode) && make_dir(p, mode) + let mut p_ = p.clone(); + if p_.pop().is_some() { + if !mkdir_recursive(&p_, mode) { + return false; + } } + return make_dir(p, mode); } /// Lists the contents of a directory -pub fn list_dir(p: &Path) -> ~[~str] { - if p.components.is_empty() && !p.is_absolute() { - // Not sure what the right behavior is here, but this - // prevents a bounds check failure later - return ~[]; - } +/// +/// Each resulting Path is a relative path with no directory component. +pub fn list_dir(p: &Path) -> ~[Path] { unsafe { #[cfg(target_os = "linux")] #[cfg(target_os = "android")] #[cfg(target_os = "freebsd")] #[cfg(target_os = "macos")] - unsafe fn get_list(p: &Path) -> ~[~str] { + unsafe fn get_list(p: &Path) -> ~[Path] { #[fixed_stack_segment]; #[inline(never)]; use libc::{dirent_t}; use libc::{opendir, readdir, closedir}; extern { fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; } - let mut strings = ~[]; + let mut paths = ~[]; debug2!("os::list_dir -- BEFORE OPENDIR"); let dir_ptr = do p.with_c_str |buf| { @@ -732,8 +729,8 @@ pub fn list_dir(p: &Path) -> ~[~str] { debug2!("os::list_dir -- opendir() SUCCESS"); let mut entry_ptr = readdir(dir_ptr); while (entry_ptr as uint != 0) { - strings.push(str::raw::from_c_str(rust_list_dir_val( - entry_ptr))); + let cstr = CString::new(rust_list_dir_val(entry_ptr), false); + paths.push(GenericPath::from_c_str(cstr)); entry_ptr = readdir(dir_ptr); } closedir(dir_ptr); @@ -741,11 +738,11 @@ pub fn list_dir(p: &Path) -> ~[~str] { else { debug2!("os::list_dir -- opendir() FAILURE"); } - debug2!("os::list_dir -- AFTER -- \\#: {}", strings.len()); - strings + debug2!("os::list_dir -- AFTER -- \\#: {}", paths.len()); + paths } #[cfg(windows)] - unsafe fn get_list(p: &Path) -> ~[~str] { + unsafe fn get_list(p: &Path) -> ~[Path] { #[fixed_stack_segment]; #[inline(never)]; use libc::consts::os::extra::INVALID_HANDLE_VALUE; use libc::{wcslen, free}; @@ -765,9 +762,9 @@ pub fn list_dir(p: &Path) -> ~[~str] { fn rust_list_dir_wfd_size() -> libc::size_t; fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; } - fn star(p: &Path) -> Path { p.push("*") } - do as_utf16_p(star(p).to_str()) |path_ptr| { - let mut strings = ~[]; + let star = p.join_str("*"); + do as_utf16_p(star.as_str().unwrap()) |path_ptr| { + let mut paths = ~[]; let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); if find_handle as libc::c_int != INVALID_HANDLE_VALUE { @@ -781,18 +778,18 @@ pub fn list_dir(p: &Path) -> ~[~str] { let fp_vec = vec::from_buf( fp_buf, wcslen(fp_buf) as uint); let fp_str = str::from_utf16(fp_vec); - strings.push(fp_str); + paths.push(Path::from_str(fp_str)); } more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); } FindClose(find_handle); free(wfd_ptr) } - strings + paths } } - do get_list(p).move_iter().filter |filename| { - "." != *filename && ".." != *filename + do get_list(p).move_iter().filter |path| { + path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") }.collect() } } @@ -803,7 +800,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { * This version prepends each entry with the directory. */ pub fn list_dir_path(p: &Path) -> ~[Path] { - list_dir(p).map(|f| p.push(*f)) + list_dir(p).map(|f| p.join_path(f)) } /// Removes a directory at the specified path, after removing @@ -838,7 +835,7 @@ pub fn remove_dir(p: &Path) -> bool { #[fixed_stack_segment]; #[inline(never)]; unsafe { use os::win32::as_utf16_p; - return do as_utf16_p(p.to_str()) |buf| { + return do as_utf16_p(p.as_str().unwrap()) |buf| { libc::RemoveDirectoryW(buf) != (0 as libc::BOOL) }; } @@ -865,7 +862,7 @@ pub fn change_dir(p: &Path) -> bool { #[fixed_stack_segment]; #[inline(never)]; unsafe { use os::win32::as_utf16_p; - return do as_utf16_p(p.to_str()) |buf| { + return do as_utf16_p(p.as_str().unwrap()) |buf| { libc::SetCurrentDirectoryW(buf) != (0 as libc::BOOL) }; } @@ -891,8 +888,8 @@ pub fn copy_file(from: &Path, to: &Path) -> bool { #[fixed_stack_segment]; #[inline(never)]; unsafe { use os::win32::as_utf16_p; - return do as_utf16_p(from.to_str()) |fromp| { - do as_utf16_p(to.to_str()) |top| { + return do as_utf16_p(from.as_str().unwrap()) |fromp| { + do as_utf16_p(to.as_str().unwrap()) |top| { libc::CopyFileW(fromp, top, (0 as libc::BOOL)) != (0 as libc::BOOL) } @@ -968,7 +965,7 @@ pub fn remove_file(p: &Path) -> bool { #[fixed_stack_segment]; #[inline(never)]; unsafe { use os::win32::as_utf16_p; - return do as_utf16_p(p.to_str()) |buf| { + return do as_utf16_p(p.as_str().unwrap()) |buf| { libc::DeleteFileW(buf) != (0 as libc::BOOL) }; } @@ -1657,35 +1654,45 @@ pub mod consts { pub static SYSNAME: &'static str = "macos"; pub static DLL_PREFIX: &'static str = "lib"; pub static DLL_SUFFIX: &'static str = ".dylib"; + pub static DLL_EXTENSION: &'static str = "dylib"; pub static EXE_SUFFIX: &'static str = ""; + pub static EXE_EXTENSION: &'static str = ""; } pub mod freebsd { pub static SYSNAME: &'static str = "freebsd"; pub static DLL_PREFIX: &'static str = "lib"; pub static DLL_SUFFIX: &'static str = ".so"; + pub static DLL_EXTENSION: &'static str = "so"; pub static EXE_SUFFIX: &'static str = ""; + pub static EXE_EXTENSION: &'static str = ""; } pub mod linux { pub static SYSNAME: &'static str = "linux"; pub static DLL_PREFIX: &'static str = "lib"; pub static DLL_SUFFIX: &'static str = ".so"; + pub static DLL_EXTENSION: &'static str = "so"; pub static EXE_SUFFIX: &'static str = ""; + pub static EXE_EXTENSION: &'static str = ""; } pub mod android { pub static SYSNAME: &'static str = "android"; pub static DLL_PREFIX: &'static str = "lib"; pub static DLL_SUFFIX: &'static str = ".so"; + pub static DLL_EXTENSION: &'static str = "so"; pub static EXE_SUFFIX: &'static str = ""; + pub static EXE_EXTENSION: &'static str = ""; } pub mod win32 { pub static SYSNAME: &'static str = "win32"; pub static DLL_PREFIX: &'static str = ""; pub static DLL_SUFFIX: &'static str = ".dll"; + pub static DLL_EXTENSION: &'static str = "dll"; pub static EXE_SUFFIX: &'static str = ".exe"; + pub static EXE_EXTENSION: &'static str = "exe"; } @@ -1790,7 +1797,7 @@ mod tests { debug2!("{:?}", path.clone()); // Hard to test this function - assert!(path.is_absolute); + assert!(path.is_absolute()); } #[test] @@ -1823,12 +1830,13 @@ mod tests { #[test] fn test() { - assert!((!Path("test-path").is_absolute)); + assert!((!Path::from_str("test-path").is_absolute())); - debug2!("Current working directory: {}", getcwd().to_str()); + let cwd = getcwd(); + debug2!("Current working directory: {}", cwd.display()); - debug2!("{:?}", make_absolute(&Path("test-path"))); - debug2!("{:?}", make_absolute(&Path("/usr/bin"))); + debug2!("{:?}", make_absolute(&Path::from_str("test-path"))); + debug2!("{:?}", make_absolute(&Path::from_str("/usr/bin"))); } #[test] @@ -1837,7 +1845,7 @@ mod tests { let oldhome = getenv("HOME"); setenv("HOME", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); setenv("HOME", ""); assert!(os::homedir().is_none()); @@ -1858,16 +1866,16 @@ mod tests { assert!(os::homedir().is_none()); setenv("HOME", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); setenv("HOME", ""); setenv("USERPROFILE", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); setenv("HOME", "/home/MountainView"); setenv("USERPROFILE", "/home/PaloAlto"); - assert_eq!(os::homedir(), Some(Path("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); for s in oldhome.iter() { setenv("HOME", *s) } for s in olduserprofile.iter() { setenv("USERPROFILE", *s) } @@ -1875,18 +1883,20 @@ mod tests { #[test] fn tmpdir() { - assert!(!os::tmpdir().to_str().is_empty()); + let p = os::tmpdir(); + let s = p.as_str(); + assert!(s.is_some() && s.unwrap() != "."); } // Issue #712 #[test] fn test_list_dir_no_invalid_memory_access() { - os::list_dir(&Path(".")); + os::list_dir(&Path::from_str(".")); } #[test] fn list_dir() { - let dirs = os::list_dir(&Path(".")); + let dirs = os::list_dir(&Path::from_str(".")); // Just assuming that we've got some contents in the current directory assert!(dirs.len() > 0u); @@ -1895,44 +1905,38 @@ mod tests { } } - #[test] - fn list_dir_empty_path() { - let dirs = os::list_dir(&Path("")); - assert!(dirs.is_empty()); - } - #[test] #[cfg(not(windows))] fn list_dir_root() { - let dirs = os::list_dir(&Path("/")); + let dirs = os::list_dir(&Path::from_str("/")); assert!(dirs.len() > 1); } #[test] #[cfg(windows)] fn list_dir_root() { - let dirs = os::list_dir(&Path("C:\\")); + let dirs = os::list_dir(&Path::from_str("C:\\")); assert!(dirs.len() > 1); } #[test] fn path_is_dir() { - assert!((os::path_is_dir(&Path(".")))); - assert!((!os::path_is_dir(&Path("test/stdtest/fs.rs")))); + assert!((os::path_is_dir(&Path::from_str(".")))); + assert!((!os::path_is_dir(&Path::from_str("test/stdtest/fs.rs")))); } #[test] fn path_exists() { - assert!((os::path_exists(&Path(".")))); - assert!((!os::path_exists(&Path( + assert!((os::path_exists(&Path::from_str(".")))); + assert!((!os::path_exists(&Path::from_str( "test/nonexistent-bogus-path")))); } #[test] fn copy_file_does_not_exist() { - assert!(!os::copy_file(&Path("test/nonexistent-bogus-path"), - &Path("test/other-bogus-path"))); - assert!(!os::path_exists(&Path("test/other-bogus-path"))); + assert!(!os::copy_file(&Path::from_str("test/nonexistent-bogus-path"), + &Path::from_str("test/other-bogus-path"))); + assert!(!os::path_exists(&Path::from_str("test/other-bogus-path"))); } #[test] @@ -1942,9 +1946,8 @@ mod tests { unsafe { let tempdir = getcwd(); // would like to use $TMPDIR, // doesn't seem to work on Linux - assert!((tempdir.to_str().len() > 0u)); - let input = tempdir.push("in.txt"); - let out = tempdir.push("out.txt"); + let input = tempdir.join_str("in.txt"); + let out = tempdir.join_str("out.txt"); /* Write the temp input file */ let ostream = do input.with_c_str |fromp| { @@ -1965,10 +1968,12 @@ mod tests { let in_mode = input.get_mode(); let rs = os::copy_file(&input, &out); if (!os::path_exists(&input)) { - fail2!("{} doesn't exist", input.to_str()); + fail2!("{} doesn't exist", input.display()); } assert!((rs)); - let rslt = run::process_status("diff", [input.to_str(), out.to_str()]); + // FIXME (#9639): This needs to handle non-utf8 paths + let rslt = run::process_status("diff", [input.as_str().unwrap().to_owned(), + out.as_str().unwrap().to_owned()]); assert_eq!(rslt, 0); assert_eq!(out.get_mode(), in_mode); assert!((remove_file(&input))); @@ -1978,16 +1983,10 @@ mod tests { #[test] fn recursive_mkdir_slash() { - let path = Path("/"); + let path = Path::from_str("/"); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); } - #[test] - fn recursive_mkdir_empty() { - let path = Path(""); - assert!(!os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - } - #[test] fn memory_map_rw() { use result::{Ok, Err}; @@ -2032,7 +2031,8 @@ mod tests { } } - let path = tmpdir().push("mmap_file.tmp"); + let mut path = tmpdir(); + path.push_str("mmap_file.tmp"); let size = MemoryMap::granularity() * 2; remove_file(&path); diff --git a/src/libstd/path.rs b/src/libstd/path.rs deleted file mode 100644 index ea157c6eea65b..0000000000000 --- a/src/libstd/path.rs +++ /dev/null @@ -1,1507 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -Cross-platform file path handling - -*/ - -#[allow(missing_doc)]; - -use c_str::ToCStr; -use c_str; -use clone::Clone; -use cmp::Eq; -use container::Container; -use iter::{Iterator, range}; -use libc; -use num; -use option::{None, Option, Some}; -use str::{OwnedStr, Str, StrSlice, StrVector}; -use to_str::ToStr; -use ascii::{AsciiCast, AsciiStr}; -use vec::{Vector, OwnedVector, ImmutableVector, OwnedCopyableVector}; - -#[cfg(windows)] -pub use Path = self::WindowsPath; -#[cfg(unix)] -pub use Path = self::PosixPath; - -#[deriving(Clone, Eq)] -pub struct WindowsPath { - host: Option<~str>, - device: Option<~str>, - is_absolute: bool, - components: ~[~str], -} - -pub fn WindowsPath(s: &str) -> WindowsPath { - GenericPath::from_str(s) -} - -#[deriving(Clone, Eq)] -pub struct PosixPath { - is_absolute: bool, - components: ~[~str], -} - -pub fn PosixPath(s: &str) -> PosixPath { - GenericPath::from_str(s) -} - -pub trait GenericPath : Clone + Eq + ToStr { - /// Converts a string to a path. - fn from_str(&str) -> Self; - - /// Returns the directory component of `self`, as a string. - fn dirname(&self) -> ~str { - let s = self.dir_path().to_str(); - match s.len() { - 0 => ~".", - _ => s, - } - } - - /// Returns the file component of `self`, as a string option. - /// Returns None if `self` names a directory. - fn filename<'a>(&'a self) -> Option<&'a str> { - match self.components().len() { - 0 => None, - n => Some(self.components()[n - 1].as_slice()), - } - } - - /// Returns the stem of the file component of `self`, as a string option. - /// The stem is the slice of a filename starting at 0 and ending just before - /// the last '.' in the name. - /// Returns None if `self` names a directory. - fn filestem<'a>(&'a self) -> Option<&'a str> { - match self.filename() { - None => None, - Some(ref f) => { - match f.rfind('.') { - Some(p) => Some(f.slice_to(p)), - None => Some((*f)), - } - } - } - } - - /// Returns the type of the file component of `self`, as a string option. - /// The file type is the slice of a filename starting just after the last - /// '.' in the name and ending at the last index in the filename. - /// Returns None if `self` names a directory. - fn filetype<'a>(&'a self) -> Option<&'a str> { - match self.filename() { - None => None, - Some(ref f) => { - match f.rfind('.') { - Some(p) if p < f.len() => Some(f.slice_from(p)), - _ => None, - } - } - } - } - - /// Returns a new path consisting of `self` with the parent directory component replaced - /// with the given string. - fn with_dirname(&self, (&str)) -> Self; - - /// Returns a new path consisting of `self` with the file component replaced - /// with the given string. - fn with_filename(&self, (&str)) -> Self; - - /// Returns a new path consisting of `self` with the file stem replaced - /// with the given string. - fn with_filestem(&self, s: &str) -> Self { - match self.filetype() { - None => self.with_filename(s), - Some(ref t) => self.with_filename(s.to_owned() + *t), - } - } - - /// Returns a new path consisting of `self` with the file type replaced - /// with the given string. - fn with_filetype(&self, t: &str) -> Self { - match (t.len(), self.filestem()) { - (0, None) => (*self).clone(), - (0, Some(ref s)) => self.with_filename(*s), - (_, None) => self.with_filename(format!(".{}", t)), - (_, Some(ref s)) => self.with_filename(format!("{}.{}", *s, t)), - } - } - - /// Returns the directory component of `self`, as a new path. - /// If `self` has no parent, returns `self`. - fn dir_path(&self) -> Self { - match self.components().len() { - 0 => (*self).clone(), - _ => self.pop(), - } - } - - /// Returns the file component of `self`, as a new path. - /// If `self` names a directory, returns the empty path. - fn file_path(&self) -> Self; - - /// Returns a new path whose parent directory is `self` and whose - /// file component is the given string. - fn push(&self, (&str)) -> Self; - - /// Returns a new path consisting of the given path, made relative to `self`. - fn push_rel(&self, other: &Self) -> Self { - assert!(!other.is_absolute()); - self.push_many(other.components()) - } - - /// Returns a new path consisting of the path given by the given vector - /// of strings, relative to `self`. - fn push_many(&self, (&[S])) -> Self; - - /// Identical to `dir_path` except in the case where `self` has only one - /// component. In this case, `pop` returns the empty path. - fn pop(&self) -> Self; - - /// The same as `push_rel`, except that the directory argument must not - /// contain directory separators in any of its components. - fn unsafe_join(&self, (&Self)) -> Self; - - /// On Unix, always returns `false`. On Windows, returns `true` iff `self`'s - /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4` - /// `lpt1` `lpt2` `lpt3` `prn` `nul`. - fn is_restricted(&self) -> bool; - - /// Returns a new path that names the same file as `self`, without containing - /// any '.', '..', or empty components. On Windows, uppercases the drive letter - /// as well. - fn normalize(&self) -> Self; - - /// Returns `true` if `self` is an absolute path. - fn is_absolute(&self) -> bool; - - /// True if `self` is an ancestor of `other`. - // See `test_is_ancestor_of` for examples. - fn is_ancestor_of(&self, other: &Self) -> bool { - debug2!("{} / {} {} {}", self.to_str(), other.to_str(), self.is_absolute(), - self.components().len()); - self == other || - (!other.components().is_empty() && - !(self.components().is_empty() && !self.is_absolute()) && - self.is_ancestor_of(&other.pop())) - } - - /// Finds the relative path from one file to another. - fn get_relative_to(&self, abs2: (&Self)) -> Self { - assert!(self.is_absolute()); - assert!(abs2.is_absolute()); - let abs1 = self.normalize(); - let abs2 = abs2.normalize(); - - let split1: &[~str] = abs1.components(); - let split2: &[~str] = abs2.components(); - let len1 = split1.len(); - let len2 = split2.len(); - assert!(len1 > 0); - assert!(len2 > 0); - - let max_common_path = num::min(len1, len2) - 1; - let mut start_idx = 0; - while start_idx < max_common_path - && split1[start_idx] == split2[start_idx] { - start_idx += 1; - } - - let mut path: ~[~str] = ~[]; - for _ in range(start_idx, len1 - 1) { path.push(~".."); }; - - path.push_all(split2.slice(start_idx, len2 - 1)); - - let mut result: Self = GenericPath::from_str("."); - if !path.is_empty() { - // Without this type hint, the typechecker doesn't seem to like it - let p: Self = GenericPath::from_str(""); - result = p.push_many(path); - }; - result - } - - - /// Returns `true` iff `child` is a suffix of `parent`. See the test - /// case for examples. - fn is_parent_of(&self, child: &Self) -> bool { - if !self.is_absolute() || child.is_absolute() - || self.components().len() < child.components().len() - || self.components().is_empty() { - return false; - } - let child_components = child.components().len(); - let parent_components = self.components().len(); - let to_drop = self.components().len() - child_components; - self.components().slice(to_drop, parent_components) == child.components() - } - - fn components<'a>(&'a self) -> &'a [~str]; -} - -#[cfg(target_os = "linux")] -#[cfg(target_os = "android")] -mod stat { - #[cfg(target_arch = "x86")] - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - __pad1: 0, - st_ino: 0, - st_mode: 0, - st_nlink: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - __pad2: 0, - st_size: 0, - st_blksize: 0, - st_blocks: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - __unused4: 0, - __unused5: 0, - } - } - } - - #[cfg(target_arch = "arm")] - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - __pad0: [0, ..4], - __st_ino: 0, - st_mode: 0, - st_nlink: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - __pad3: [0, ..4], - st_size: 0, - st_blksize: 0, - st_blocks: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - st_ino: 0 - } - } - } - - #[cfg(target_arch = "mips")] - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - st_pad1: [0, ..3], - st_ino: 0, - st_mode: 0, - st_nlink: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_pad2: [0, ..2], - st_size: 0, - st_pad3: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - st_blksize: 0, - st_blocks: 0, - st_pad5: [0, ..14], - } - } - } - - #[cfg(target_arch = "x86_64")] - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - st_ino: 0, - st_nlink: 0, - st_mode: 0, - st_uid: 0, - st_gid: 0, - __pad0: 0, - st_rdev: 0, - st_size: 0, - st_blksize: 0, - st_blocks: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - __unused: [0, 0, 0], - } - } - } -} - -#[cfg(target_os = "freebsd")] -mod stat { - #[cfg(target_arch = "x86_64")] - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - st_ino: 0, - st_mode: 0, - st_nlink: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - st_size: 0, - st_blocks: 0, - st_blksize: 0, - st_flags: 0, - st_gen: 0, - st_lspare: 0, - st_birthtime: 0, - st_birthtime_nsec: 0, - __unused: [0, 0], - } - } - } -} - -#[cfg(target_os = "macos")] -mod stat { - pub mod arch { - use libc; - - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - st_mode: 0, - st_nlink: 0, - st_ino: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_atime: 0, - st_atime_nsec: 0, - st_mtime: 0, - st_mtime_nsec: 0, - st_ctime: 0, - st_ctime_nsec: 0, - st_birthtime: 0, - st_birthtime_nsec: 0, - st_size: 0, - st_blocks: 0, - st_blksize: 0, - st_flags: 0, - st_gen: 0, - st_lspare: 0, - st_qspare: [0, 0], - } - } - } -} - -#[cfg(target_os = "win32")] -mod stat { - pub mod arch { - use libc; - pub fn default_stat() -> libc::stat { - libc::stat { - st_dev: 0, - st_ino: 0, - st_mode: 0, - st_nlink: 0, - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_size: 0, - st_atime: 0, - st_mtime: 0, - st_ctime: 0, - } - } - } -} - -#[cfg(target_os = "win32")] -impl WindowsPath { - pub fn stat(&self) -> Option { - #[fixed_stack_segment]; #[inline(never)]; - do self.with_c_str |buf| { - let mut st = stat::arch::default_stat(); - match unsafe { libc::stat(buf, &mut st) } { - 0 => Some(st), - _ => None, - } - } - } - - pub fn exists(&self) -> bool { - match self.stat() { - None => false, - Some(_) => true, - } - } - - pub fn get_size(&self) -> Option { - match self.stat() { - None => None, - Some(ref st) => Some(st.st_size as i64), - } - } - - pub fn get_mode(&self) -> Option { - match self.stat() { - None => None, - Some(ref st) => Some(st.st_mode as uint), - } - } -} - -#[cfg(not(target_os = "win32"))] -impl PosixPath { - pub fn stat(&self) -> Option { - #[fixed_stack_segment]; #[inline(never)]; - do self.with_c_str |buf| { - let mut st = stat::arch::default_stat(); - match unsafe { libc::stat(buf as *libc::c_char, &mut st) } { - 0 => Some(st), - _ => None, - } - } - } - - pub fn exists(&self) -> bool { - match self.stat() { - None => false, - Some(_) => true, - } - } - - pub fn get_size(&self) -> Option { - match self.stat() { - None => None, - Some(ref st) => Some(st.st_size as i64), - } - } - - pub fn get_mode(&self) -> Option { - match self.stat() { - None => None, - Some(ref st) => Some(st.st_mode as uint), - } - } - - /// Executes a function `f` on `self` as well as on all of its ancestors. - pub fn each_parent(&self, f: &fn(&Path)) { - if !self.components.is_empty() { - f(self); - self.pop().each_parent(f); - } - } - -} - -#[cfg(target_os = "freebsd")] -#[cfg(target_os = "linux")] -#[cfg(target_os = "macos")] -impl PosixPath { - pub fn get_atime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_atime as i64, - st.st_atime_nsec as int)) - } - } - } - - pub fn get_mtime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_mtime as i64, - st.st_mtime_nsec as int)) - } - } - } - - pub fn get_ctime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_ctime as i64, - st.st_ctime_nsec as int)) - } - } - } -} - -#[cfg(unix)] -impl PosixPath { - pub fn lstat(&self) -> Option { - #[fixed_stack_segment]; #[inline(never)]; - do self.with_c_str |buf| { - let mut st = stat::arch::default_stat(); - match unsafe { libc::lstat(buf, &mut st) } { - 0 => Some(st), - _ => None, - } - } - } -} - -#[cfg(target_os = "freebsd")] -#[cfg(target_os = "macos")] -impl PosixPath { - pub fn get_birthtime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_birthtime as i64, - st.st_birthtime_nsec as int)) - } - } - } -} - -#[cfg(target_os = "win32")] -impl WindowsPath { - pub fn get_atime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_atime as i64, 0)) - } - } - } - - pub fn get_mtime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_mtime as i64, 0)) - } - } - } - - pub fn get_ctime(&self) -> Option<(i64, int)> { - match self.stat() { - None => None, - Some(ref st) => { - Some((st.st_ctime as i64, 0)) - } - } - } - - /// Executes a function `f` on `self` as well as on all of its ancestors. - pub fn each_parent(&self, f: &fn(&Path)) { - if !self.components.is_empty() { - f(self); - self.pop().each_parent(f); - } - } -} - -impl ToStr for PosixPath { - fn to_str(&self) -> ~str { - let mut s = ~""; - if self.is_absolute { - s.push_str("/"); - } - s + self.components.connect("/") - } -} - -impl ToCStr for PosixPath { - fn to_c_str(&self) -> c_str::CString { - self.to_str().to_c_str() - } - - unsafe fn to_c_str_unchecked(&self) -> c_str::CString { - self.to_str().to_c_str_unchecked() - } -} - -impl GenericPath for PosixPath { - fn from_str(s: &str) -> PosixPath { - let components = s.split_iter('/') - .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())}) - .collect(); - let is_absolute = (s.len() != 0 && s[0] == '/' as u8); - PosixPath { - is_absolute: is_absolute, - components: components, - } - } - - fn with_dirname(&self, d: &str) -> PosixPath { - let dpath = PosixPath(d); - match self.filename() { - Some(ref f) => dpath.push(*f), - None => dpath, - } - } - - fn with_filename(&self, f: &str) -> PosixPath { - assert!(!f.iter().all(posix::is_sep)); - self.dir_path().push(f) - } - - fn file_path(&self) -> PosixPath { - let cs = match self.filename() { - None => ~[], - Some(ref f) => ~[(*f).to_owned()] - }; - PosixPath { - is_absolute: false, - components: cs, - } - } - - fn push(&self, s: &str) -> PosixPath { - let mut v = self.components.clone(); - for s in s.split_iter(posix::is_sep) { - if !s.is_empty() { - v.push(s.to_owned()) - } - } - PosixPath { - components: v, - ..(*self).clone() - } - } - - fn push_many(&self, cs: &[S]) -> PosixPath { - let mut v = self.components.clone(); - for e in cs.iter() { - for s in e.as_slice().split_iter(posix::is_sep) { - if !s.is_empty() { - v.push(s.to_owned()) - } - } - } - PosixPath { - is_absolute: self.is_absolute, - components: v, - } - } - - fn pop(&self) -> PosixPath { - let mut cs = self.components.clone(); - if cs.len() != 0 { - cs.pop(); - } - PosixPath { - is_absolute: self.is_absolute, - components: cs, - } //..self } - } - - fn unsafe_join(&self, other: &PosixPath) -> PosixPath { - if other.is_absolute { - PosixPath { - is_absolute: true, - components: other.components.clone(), - } - } else { - self.push_rel(other) - } - } - - fn is_restricted(&self) -> bool { - false - } - - fn normalize(&self) -> PosixPath { - PosixPath { - is_absolute: self.is_absolute, - components: normalize(self.components), - } // ..self } - } - - fn is_absolute(&self) -> bool { - self.is_absolute - } - - fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() } - -} - - -impl ToStr for WindowsPath { - fn to_str(&self) -> ~str { - let mut s = ~""; - match self.host { - Some(ref h) => { - s.push_str("\\\\"); - s.push_str(*h); - } - None => { } - } - match self.device { - Some(ref d) => { - s.push_str(*d); - s.push_str(":"); - } - None => { } - } - if self.is_absolute { - s.push_str("\\"); - } - s + self.components.connect("\\") - } -} - -impl c_str::ToCStr for WindowsPath { - fn to_c_str(&self) -> c_str::CString { - self.to_str().to_c_str() - } - - unsafe fn to_c_str_unchecked(&self) -> c_str::CString { - self.to_str().to_c_str_unchecked() - } -} - -impl GenericPath for WindowsPath { - fn from_str(s: &str) -> WindowsPath { - let host; - let device; - let rest; - - match ( - windows::extract_drive_prefix(s), - windows::extract_unc_prefix(s), - ) { - (Some((ref d, ref r)), _) => { - host = None; - device = Some((*d).clone()); - rest = (*r).clone(); - } - (None, Some((ref h, ref r))) => { - host = Some((*h).clone()); - device = None; - rest = (*r).clone(); - } - (None, None) => { - host = None; - device = None; - rest = s.to_owned(); - } - } - - let components = rest.split_iter(windows::is_sep) - .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())}) - .collect(); - - let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char)); - WindowsPath { - host: host, - device: device, - is_absolute: is_absolute, - components: components, - } - } - - fn with_dirname(&self, d: &str) -> WindowsPath { - let dpath = WindowsPath(d); - match self.filename() { - Some(ref f) => dpath.push(*f), - None => dpath, - } - } - - fn with_filename(&self, f: &str) -> WindowsPath { - assert!(! f.iter().all(windows::is_sep)); - self.dir_path().push(f) - } - - fn file_path(&self) -> WindowsPath { - WindowsPath { - host: None, - device: None, - is_absolute: false, - components: match self.filename() { - None => ~[], - Some(ref f) => ~[(*f).to_owned()], - } - } - } - - fn push(&self, s: &str) -> WindowsPath { - let mut v = self.components.clone(); - for s in s.split_iter(windows::is_sep) { - if !s.is_empty() { - v.push(s.to_owned()) - } - } - WindowsPath { components: v, ..(*self).clone() } - } - - fn push_many(&self, cs: &[S]) -> WindowsPath { - let mut v = self.components.clone(); - for e in cs.iter() { - for s in e.as_slice().split_iter(windows::is_sep) { - if !s.is_empty() { - v.push(s.to_owned()) - } - } - } - // tedious, but as-is, we can't use ..self - WindowsPath { - host: self.host.clone(), - device: self.device.clone(), - is_absolute: self.is_absolute, - components: v - } - } - - fn pop(&self) -> WindowsPath { - let mut cs = self.components.clone(); - if cs.len() != 0 { - cs.pop(); - } - WindowsPath { - host: self.host.clone(), - device: self.device.clone(), - is_absolute: self.is_absolute, - components: cs, - } - } - - fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath { - /* rhs not absolute is simple push */ - if !other.is_absolute { - return self.push_many(other.components); - } - - /* if rhs has a host set, then the whole thing wins */ - match other.host { - Some(ref host) => { - return WindowsPath { - host: Some((*host).clone()), - device: other.device.clone(), - is_absolute: true, - components: other.components.clone(), - }; - } - _ => {} - } - - /* if rhs has a device set, then a part wins */ - match other.device { - Some(ref device) => { - return WindowsPath { - host: None, - device: Some((*device).clone()), - is_absolute: true, - components: other.components.clone(), - }; - } - _ => {} - } - - /* fallback: host and device of lhs win, but the - whole path of the right */ - WindowsPath { - host: self.host.clone(), - device: self.device.clone(), - is_absolute: self.is_absolute || other.is_absolute, - components: other.components.clone(), - } - } - - fn is_restricted(&self) -> bool { - match self.filestem() { - Some(stem) => { - // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use - // to_ascii_move and to_str_move to not do a unnecessary copy. - match stem.to_ascii().to_lower().to_str_ascii() { - ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" | - ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true, - _ => false - } - }, - None => false - } - } - - fn normalize(&self) -> WindowsPath { - WindowsPath { - host: self.host.clone(), - device: match self.device { - None => None, - - // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use - // to_ascii_move and to_str_move to not do a unnecessary copy. - Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii()) - }, - is_absolute: self.is_absolute, - components: normalize(self.components) - } - } - - fn is_absolute(&self) -> bool { - self.is_absolute - } - - fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() } - -} - -pub fn normalize(components: &[~str]) -> ~[~str] { - let mut cs = ~[]; - for c in components.iter() { - if *c == ~"." && components.len() > 1 { continue; } - if *c == ~"" { continue; } - if *c == ~".." && cs.len() != 0 { - cs.pop(); - continue; - } - cs.push((*c).clone()); - } - cs -} - -// Various posix helpers. -pub mod posix { - - #[inline] - pub fn is_sep(u: char) -> bool { - u == '/' - } - -} - -// Various windows helpers. -pub mod windows { - use libc; - use option::{None, Option, Some}; - - #[inline] - pub fn is_sep(u: char) -> bool { - u == '/' || u == '\\' - } - - pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> { - if (s.len() > 1 && - (s[0] == '\\' as u8 || s[0] == '/' as u8) && - s[0] == s[1]) { - let mut i = 2; - while i < s.len() { - if is_sep(s[i] as char) { - let pre = s.slice(2, i).to_owned(); - let rest = s.slice(i, s.len()).to_owned(); - return Some((pre, rest)); - } - i += 1; - } - } - None - } - - pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> { - #[fixed_stack_segment]; #[inline(never)]; - - unsafe { - if (s.len() > 1 && - libc::isalpha(s[0] as libc::c_int) != 0 && - s[1] == ':' as u8) { - let rest = if s.len() == 2 { - ~"" - } else { - s.slice(2, s.len()).to_owned() - }; - return Some((s.slice(0,1).to_owned(), rest)); - } - None - } - } -} - -#[cfg(test)] -mod tests { - use option::{None, Some}; - use path::{PosixPath, WindowsPath, windows}; - - #[test] - fn test_double_slash_collapsing() { - let path = PosixPath("tmp/"); - let path = path.push("/hmm"); - let path = path.normalize(); - assert_eq!(~"tmp/hmm", path.to_str()); - - let path = WindowsPath("tmp/"); - let path = path.push("/hmm"); - let path = path.normalize(); - assert_eq!(~"tmp\\hmm", path.to_str()); - } - - #[test] - fn test_filetype_foo_bar() { - let wp = PosixPath("foo.bar"); - assert_eq!(wp.filetype(), Some(".bar")); - - let wp = WindowsPath("foo.bar"); - assert_eq!(wp.filetype(), Some(".bar")); - } - - #[test] - fn test_filetype_foo() { - let wp = PosixPath("foo"); - assert_eq!(wp.filetype(), None); - - let wp = WindowsPath("foo"); - assert_eq!(wp.filetype(), None); - } - - #[test] - fn test_posix_paths() { - fn t(wp: &PosixPath, s: &str) { - let ss = wp.to_str(); - let sss = s.to_owned(); - if (ss != sss) { - debug2!("got {}", ss); - debug2!("expected {}", sss); - assert_eq!(ss, sss); - } - } - - t(&(PosixPath("hi")), "hi"); - t(&(PosixPath("/lib")), "/lib"); - t(&(PosixPath("hi/there")), "hi/there"); - t(&(PosixPath("hi/there.txt")), "hi/there.txt"); - - t(&(PosixPath("hi/there.txt")), "hi/there.txt"); - t(&(PosixPath("hi/there.txt") - .with_filetype("")), "hi/there"); - - t(&(PosixPath("/a/b/c/there.txt") - .with_dirname("hi")), "hi/there.txt"); - - t(&(PosixPath("hi/there.txt") - .with_dirname(".")), "./there.txt"); - - t(&(PosixPath("a/b/c") - .push("..")), "a/b/c/.."); - - t(&(PosixPath("there.txt") - .with_filetype("o")), "there.o"); - - t(&(PosixPath("hi/there.txt") - .with_filetype("o")), "hi/there.o"); - - t(&(PosixPath("hi/there.txt") - .with_filetype("o") - .with_dirname("/usr/lib")), - "/usr/lib/there.o"); - - t(&(PosixPath("hi/there.txt") - .with_filetype("o") - .with_dirname("/usr/lib/")), - "/usr/lib/there.o"); - - t(&(PosixPath("hi/there.txt") - .with_filetype("o") - .with_dirname("/usr//lib//")), - "/usr/lib/there.o"); - - t(&(PosixPath("/usr/bin/rust") - .push_many([~"lib", ~"thingy.so"]) - .with_filestem("librustc")), - "/usr/bin/rust/lib/librustc.so"); - - } - - #[test] - fn test_posix_push_with_backslash() { - let a = PosixPath("/aaa/bbb"); - let b = a.push("x\\y"); // \ is not a file separator for posix paths - assert_eq!(a.components.len(), 2); - assert_eq!(b.components.len(), 3); - } - - #[test] - fn test_normalize() { - fn t(wp: &PosixPath, s: &str) { - let ss = wp.to_str(); - let sss = s.to_owned(); - if (ss != sss) { - debug2!("got {}", ss); - debug2!("expected {}", sss); - assert_eq!(ss, sss); - } - } - - t(&(PosixPath("hi/there.txt") - .with_dirname(".").normalize()), "there.txt"); - - t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()), - "a/foo.txt"); - - t(&(PosixPath("a/b/c") - .push("..").normalize()), "a/b"); - } - - #[test] - fn test_extract_unc_prefixes() { - assert!(windows::extract_unc_prefix("\\\\").is_none()); - assert!(windows::extract_unc_prefix("//").is_none()); - assert!(windows::extract_unc_prefix("\\\\hi").is_none()); - assert!(windows::extract_unc_prefix("//hi").is_none()); - assert!(windows::extract_unc_prefix("\\\\hi\\") == - Some((~"hi", ~"\\"))); - assert!(windows::extract_unc_prefix("//hi\\") == - Some((~"hi", ~"\\"))); - assert!(windows::extract_unc_prefix("\\\\hi\\there") == - Some((~"hi", ~"\\there"))); - assert!(windows::extract_unc_prefix("//hi/there") == - Some((~"hi", ~"/there"))); - assert!(windows::extract_unc_prefix( - "\\\\hi\\there\\friends.txt") == - Some((~"hi", ~"\\there\\friends.txt"))); - assert!(windows::extract_unc_prefix( - "//hi\\there\\friends.txt") == - Some((~"hi", ~"\\there\\friends.txt"))); - } - - #[test] - fn test_extract_drive_prefixes() { - assert!(windows::extract_drive_prefix("c").is_none()); - assert!(windows::extract_drive_prefix("c:") == - Some((~"c", ~""))); - assert!(windows::extract_drive_prefix("d:") == - Some((~"d", ~""))); - assert!(windows::extract_drive_prefix("z:") == - Some((~"z", ~""))); - assert!(windows::extract_drive_prefix("c:\\hi") == - Some((~"c", ~"\\hi"))); - assert!(windows::extract_drive_prefix("d:hi") == - Some((~"d", ~"hi"))); - assert!(windows::extract_drive_prefix("c:hi\\there.txt") == - Some((~"c", ~"hi\\there.txt"))); - assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") == - Some((~"c", ~"\\hi\\there.txt"))); - } - - #[test] - fn test_windows_paths() { - fn t(wp: &WindowsPath, s: &str) { - let ss = wp.to_str(); - let sss = s.to_owned(); - if (ss != sss) { - debug2!("got {}", ss); - debug2!("expected {}", sss); - assert_eq!(ss, sss); - } - } - - t(&(WindowsPath("hi")), "hi"); - t(&(WindowsPath("hi/there")), "hi\\there"); - t(&(WindowsPath("hi/there.txt")), "hi\\there.txt"); - - t(&(WindowsPath("there.txt") - .with_filetype("o")), "there.o"); - - t(&(WindowsPath("hi/there.txt") - .with_filetype("o")), "hi\\there.o"); - - t(&(WindowsPath("hi/there.txt") - .with_filetype("o") - .with_dirname("c:\\program files A")), - "c:\\program files A\\there.o"); - - t(&(WindowsPath("hi/there.txt") - .with_filetype("o") - .with_dirname("c:\\program files B\\")), - "c:\\program files B\\there.o"); - - t(&(WindowsPath("hi/there.txt") - .with_filetype("o") - .with_dirname("c:\\program files C\\/")), - "c:\\program files C\\there.o"); - - t(&(WindowsPath("c:\\program files (x86)\\rust") - .push_many([~"lib", ~"thingy.dll"]) - .with_filename("librustc.dll")), - "c:\\program files (x86)\\rust\\lib\\librustc.dll"); - - t(&(WindowsPath("\\\\computer\\share") - .unsafe_join(&WindowsPath("\\a"))), - "\\\\computer\\a"); - - t(&(WindowsPath("//computer/share") - .unsafe_join(&WindowsPath("\\a"))), - "\\\\computer\\a"); - - t(&(WindowsPath("//computer/share") - .unsafe_join(&WindowsPath("\\\\computer\\share"))), - "\\\\computer\\share"); - - t(&(WindowsPath("C:/whatever") - .unsafe_join(&WindowsPath("//computer/share/a/b"))), - "\\\\computer\\share\\a\\b"); - - t(&(WindowsPath("C:") - .unsafe_join(&WindowsPath("D:/foo"))), - "D:\\foo"); - - t(&(WindowsPath("C:") - .unsafe_join(&WindowsPath("B"))), - "C:B"); - - t(&(WindowsPath("C:") - .unsafe_join(&WindowsPath("/foo"))), - "C:\\foo"); - - t(&(WindowsPath("C:\\") - .unsafe_join(&WindowsPath("\\bar"))), - "C:\\bar"); - - t(&(WindowsPath("") - .unsafe_join(&WindowsPath(""))), - ""); - - t(&(WindowsPath("") - .unsafe_join(&WindowsPath("a"))), - "a"); - - t(&(WindowsPath("") - .unsafe_join(&WindowsPath("C:\\a"))), - "C:\\a"); - - t(&(WindowsPath("c:\\foo") - .normalize()), - "C:\\foo"); - } - - #[test] - fn test_windows_path_restrictions() { - assert_eq!(WindowsPath("hi").is_restricted(), false); - assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true); - assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true); - assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true); - } - - #[test] - fn test_is_ancestor_of() { - assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); - assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b"))); - assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d"))); - assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); - assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c"))); - assert!(!&PosixPath("/").is_ancestor_of(&PosixPath(""))); - assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath(""))); - assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c"))); - - assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); - assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b"))); - assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d"))); - assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); - assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); - assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath(""))); - assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath(""))); - assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); - - } - - #[test] - fn test_relative_to1() { - let p1 = PosixPath("/usr/bin/rustc"); - let p2 = PosixPath("/usr/lib/mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("../lib")); - - let p1 = WindowsPath("C:\\usr\\bin\\rustc"); - let p2 = WindowsPath("C:\\usr\\lib\\mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..\\lib")); - - } - - #[test] - fn test_relative_to2() { - let p1 = PosixPath("/usr/bin/rustc"); - let p2 = PosixPath("/usr/bin/../lib/mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("../lib")); - - let p1 = WindowsPath("C:\\usr\\bin\\rustc"); - let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..\\lib")); - } - - #[test] - fn test_relative_to3() { - let p1 = PosixPath("/usr/bin/whatever/rustc"); - let p2 = PosixPath("/usr/lib/whatever/mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("../../lib/whatever")); - - let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc"); - let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..\\..\\lib\\whatever")); - - } - - #[test] - fn test_relative_to4() { - let p1 = PosixPath("/usr/bin/whatever/../rustc"); - let p2 = PosixPath("/usr/lib/whatever/mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("../lib/whatever")); - - let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc"); - let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..\\lib\\whatever")); - - } - - #[test] - fn test_relative_to5() { - let p1 = PosixPath("/usr/bin/whatever/../rustc"); - let p2 = PosixPath("/usr/lib/whatever/../mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("../lib")); - - let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc"); - let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..\\lib")); - } - - #[test] - fn test_relative_to6() { - let p1 = PosixPath("/1"); - let p2 = PosixPath("/2/3"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("2")); - - let p1 = WindowsPath("C:\\1"); - let p2 = WindowsPath("C:\\2\\3"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("2")); - - } - - #[test] - fn test_relative_to7() { - let p1 = PosixPath("/1/2"); - let p2 = PosixPath("/3"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, PosixPath("..")); - - let p1 = WindowsPath("C:\\1\\2"); - let p2 = WindowsPath("C:\\3"); - let res = p1.get_relative_to(&p2); - assert_eq!(res, WindowsPath("..")); - - } - - #[test] - fn test_relative_to8() { - let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel( - &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so")); - let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel( - &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so")); - let res = p1.get_relative_to(&p2); - debug2!("test_relative_to8: {} vs. {}", - res.to_str(), - PosixPath(".").to_str()); - assert_eq!(res, PosixPath(".")); - - let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel( - &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so")); - let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel( - &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so")); - let res = p1.get_relative_to(&p2); - debug2!("test_relative_to8: {} vs. {}", - res.to_str(), - WindowsPath(".").to_str()); - assert_eq!(res, WindowsPath(".")); - - } - - - #[test] - fn test_is_parent_of() { - fn is_parent_of_pp(p: &PosixPath, q: &PosixPath) -> bool { - p.is_parent_of(q) - } - - assert!(is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("c/d/e"))); - assert!(!is_parent_of_pp(&PosixPath("a/b/c/d/e"), &PosixPath("c/d/e"))); - assert!(!is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("/c/d/e"))); - assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath(""))); - assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("a/b/c"))); - assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath(""))); - assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("a/b/c"))); - assert!(!is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("d/e/f"))); - - fn is_parent_of_wp(p: &WindowsPath, q: &WindowsPath) -> bool { - p.is_parent_of(q) - } - - let abcde = WindowsPath("C:\\a\\b\\c\\d\\e"); - let rel_abcde = WindowsPath("a\\b\\c\\d\\e"); - let cde = WindowsPath("c\\d\\e"); - let slashcde = WindowsPath("C:\\c\\d\\e"); - let empty = WindowsPath(""); - let abc = WindowsPath("C:\\a\\b\\c"); - let rel_abc = WindowsPath("a\\b\\c"); - let def = WindowsPath("d\\e\\f"); - - assert!(is_parent_of_wp(&abcde, &cde)); - assert!(!is_parent_of_wp(&rel_abcde, &cde)); - assert!(!is_parent_of_wp(&abcde, &slashcde)); - assert!(!is_parent_of_wp(&empty, &empty)); - assert!(!is_parent_of_wp(&empty, &rel_abc)); - assert!(is_parent_of_wp(&abc, &empty)); - assert!(is_parent_of_wp(&abc, &rel_abc)); - assert!(!is_parent_of_wp(&abc, &def)); - } - -} diff --git a/src/libstd/path2/mod.rs b/src/libstd/path/mod.rs similarity index 70% rename from src/libstd/path2/mod.rs rename to src/libstd/path/mod.rs index 38a48866e2248..db6bfada8c08f 100644 --- a/src/libstd/path2/mod.rs +++ b/src/libstd/path/mod.rs @@ -19,7 +19,7 @@ use option::{Option, None, Some}; use str; use str::{OwnedStr, Str, StrSlice}; use vec; -use vec::{CopyableVector, OwnedCopyableVector, OwnedVector}; +use vec::{CopyableVector, OwnedCopyableVector, OwnedVector, Vector}; use vec::{ImmutableEqVector, ImmutableVector}; /// Typedef for POSIX file paths. @@ -37,20 +37,31 @@ pub use Path = self::posix::Path; #[cfg(windows)] pub use Path = self::windows::Path; -/// Typedef for the POSIX path component iterator. -/// See `posix::ComponentIter` for more info. -pub use PosixComponentIter = self::posix::ComponentIter; - -/// Typedef for the Windows path component iterator. -/// See `windows::ComponentIter` for more info. -pub use WindowsComponentIter = self::windows::ComponentIter; - /// Typedef for the platform-native component iterator #[cfg(unix)] pub use ComponentIter = self::posix::ComponentIter; +/// Typedef for the platform-native reverse component iterator +#[cfg(unix)] +pub use RevComponentIter = self::posix::RevComponentIter; /// Typedef for the platform-native component iterator #[cfg(windows)] pub use ComponentIter = self::windows::ComponentIter; +/// Typedef for the platform-native reverse component iterator +#[cfg(windows)] +pub use RevComponentIter = self::windows::RevComponentIter; + +/// Typedef for the platform-native str component iterator +#[cfg(unix)] +pub use StrComponentIter = self::posix::StrComponentIter; +/// Typedef for the platform-native reverse str component iterator +#[cfg(unix)] +pub use RevStrComponentIter = self::posix::RevStrComponentIter; +/// Typedef for the platform-native str component iterator +#[cfg(windows)] +pub use StrComponentIter = self::windows::StrComponentIter; +/// Typedef for the platform-native reverse str component iterator +#[cfg(windows)] +pub use RevStrComponentIter = self::windows::RevStrComponentIter; pub mod posix; pub mod windows; @@ -128,7 +139,10 @@ pub trait GenericPath: Clone + GenericPathUnsafe { #[inline] fn from_c_str(path: CString) -> Self { // CStrings can't contain NULs - unsafe { GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) } + let v = path.as_bytes(); + // v is NUL-terminated. Strip it off + let v = v.slice_to(v.len()-1); + unsafe { GenericPathUnsafe::from_vec_unchecked(v) } } /// Returns the path as a string, if possible. @@ -146,7 +160,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If the path is not UTF-8, invalid sequences will be replaced with the unicode /// replacement char. This involves allocation. #[inline] - fn as_display_str(&self, f: &fn(&str) -> T) -> T { + fn with_display_str(&self, f: &fn(&str) -> T) -> T { match self.as_str() { Some(s) => f(s), None => { @@ -161,29 +175,37 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If the path is not UTF-8, invalid sequences will be replaced with the unicode /// replacement char. This involves allocation. /// - /// This is similar to `as_display_str()` except it will always allocate a new ~str. + /// This is similar to `with_display_str()` except it will always allocate a new ~str. fn to_display_str(&self) -> ~str { - // FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str - // This is a truly horrifically bad implementation, done as a functionality stopgap until - // we have a proper utf-8 decoder. I don't really want to write one here. - static REPLACEMENT_CHAR: char = '\uFFFD'; - - let mut v = self.as_vec(); - let mut s = str::with_capacity(v.len()); - while !v.is_empty() { - let w = str::utf8_char_width(v[0]); - if w == 0u { - s.push_char(REPLACEMENT_CHAR); - v = v.slice_from(1); - } else if v.len() < w || !str::is_utf8(v.slice_to(w)) { - s.push_char(REPLACEMENT_CHAR); - v = v.slice_from(1); - } else { - s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) }); - v = v.slice_from(w); + from_utf8_with_replacement(self.as_vec()) + } + + /// Provides the filename as a string + /// + /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode + /// replacement char. This involves allocation. + #[inline] + fn with_filename_display_str(&self, f: &fn(Option<&str>) -> T) -> T { + match self.filename_str() { + s@Some(_) => f(s), + None => { + let o = self.to_filename_display_str(); + f(o.map(|s|s.as_slice())) } } - s + } + + /// Returns the filename as a string + /// + /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode + /// replacement char. This involves allocation. + /// + /// This is similar to `to_filename_display_str` except it will always allocate a new ~str. + fn to_filename_display_str(&self) -> Option<~str> { + match self.filename() { + None => None, + Some(v) => Some(from_utf8_with_replacement(v)) + } } /// Returns an object that implements `fmt::Default` for printing paths @@ -211,51 +233,59 @@ pub trait GenericPath: Clone + GenericPathUnsafe { str::from_utf8_slice_opt(self.dirname()) } /// Returns the file component of `self`, as a byte vector. - /// If `self` represents the root of the file hierarchy, returns the empty vector. - /// If `self` is ".", returns the empty vector. - fn filename<'a>(&'a self) -> &'a [u8]; + /// If `self` represents the root of the file hierarchy, returns None. + /// If `self` is "." or "..", returns None. + fn filename<'a>(&'a self) -> Option<&'a [u8]>; /// Returns the file component of `self`, as a string, if possible. /// See `filename` for details. #[inline] fn filename_str<'a>(&'a self) -> Option<&'a str> { - str::from_utf8_slice_opt(self.filename()) + self.filename().and_then(str::from_utf8_slice_opt) } /// 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. - fn filestem<'a>(&'a self) -> &'a [u8] { - let name = self.filename(); - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => name, - Some(1) if name == bytes!("..") => name, - Some(pos) => name.slice_to(pos) + fn filestem<'a>(&'a self) -> Option<&'a [u8]> { + match self.filename() { + None => None, + Some(name) => Some({ + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => name, + Some(1) if name == bytes!("..") => name, + Some(pos) => name.slice_to(pos) + } + }) } } /// Returns the stem of the filename of `self`, as a string, if possible. /// See `filestem` for details. #[inline] fn filestem_str<'a>(&'a self) -> Option<&'a str> { - str::from_utf8_slice_opt(self.filestem()) + self.filestem().and_then(str::from_utf8_slice_opt) } /// Returns the extension of the filename of `self`, as an optional byte vector. /// The extension is the portion of the filename just after the last '.'. /// If there is no extension, None is returned. /// If the filename ends in '.', the empty vector is returned. fn extension<'a>(&'a self) -> Option<&'a [u8]> { - let name = self.filename(); - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => None, - Some(1) if name == bytes!("..") => None, - Some(pos) => Some(name.slice_from(pos+1)) + match self.filename() { + None => None, + Some(name) => { + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => None, + Some(1) if name == bytes!("..") => None, + Some(pos) => Some(name.slice_from(pos+1)) + } + } } } /// Returns the extension of the filename of `self`, as a string, if possible. /// See `extension` for details. #[inline] fn extension_str<'a>(&'a self) -> Option<&'a str> { - self.extension().and_then(|v| str::from_utf8_slice_opt(v)) + self.extension().and_then(str::from_utf8_slice_opt) } /// Replaces the directory portion of the path with the given byte vector. @@ -322,27 +352,30 @@ pub trait GenericPath: Clone + GenericPathUnsafe { fn set_filestem(&mut self, filestem: &[u8]) { // borrowck is being a pain here let val = { - let name = self.filename(); - if !name.is_empty() { - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => None, - Some(idx) => { - let mut v; - if contains_nul(filestem) { - let filestem = self::null_byte::cond.raise(filestem.to_owned()); - assert!(!contains_nul(filestem)); - v = vec::with_capacity(filestem.len() + name.len() - idx); - v.push_all(filestem); - } else { - v = vec::with_capacity(filestem.len() + name.len() - idx); - v.push_all(filestem); + match self.filename() { + None => None, + Some(name) => { + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => None, + Some(idx) => { + let mut v; + if contains_nul(filestem) { + let filestem = self::null_byte::cond.raise(filestem.to_owned()); + assert!(!contains_nul(filestem)); + v = filestem; + let n = v.len(); + v.reserve(n + name.len() - idx); + } else { + v = vec::with_capacity(filestem.len() + name.len() - idx); + v.push_all(filestem); + } + v.push_all(name.slice_from(idx)); + Some(v) } - v.push_all(name.slice_from(idx)); - Some(v) } } - } else { None } + } }; match val { None => self.set_filename(filestem), @@ -366,52 +399,56 @@ pub trait GenericPath: Clone + GenericPathUnsafe { fn set_extension(&mut self, extension: &[u8]) { // borrowck causes problems here too let val = { - let name = self.filename(); - if !name.is_empty() { - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => { - if extension.is_empty() { - None - } else { - let mut v; - if contains_nul(extension) { - let extension = self::null_byte::cond.raise(extension.to_owned()); - assert!(!contains_nul(extension)); - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); + match self.filename() { + None => None, + Some(name) => { + let dot = '.' as u8; + match name.rposition_elem(&dot) { + None | Some(0) => { + if extension.is_empty() { + None } else { - v = vec::with_capacity(name.len() + extension.len() + 1); - v.push_all(name); - v.push(dot); - v.push_all(extension); + let mut v; + if contains_nul(extension) { + let ext = extension.to_owned(); + let extension = self::null_byte::cond.raise(ext); + assert!(!contains_nul(extension)); + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } else { + v = vec::with_capacity(name.len() + extension.len() + 1); + v.push_all(name); + v.push(dot); + v.push_all(extension); + } + Some(v) } - Some(v) } - } - Some(idx) => { - if extension.is_empty() { - Some(name.slice_to(idx).to_owned()) - } else { - let mut v; - if contains_nul(extension) { - let extension = self::null_byte::cond.raise(extension.to_owned()); - assert!(!contains_nul(extension)); - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); + Some(idx) => { + if extension.is_empty() { + Some(name.slice_to(idx).to_owned()) } else { - v = vec::with_capacity(idx + extension.len() + 1); - v.push_all(name.slice_to(idx+1)); - v.push_all(extension); + let mut v; + if contains_nul(extension) { + let ext = extension.to_owned(); + let extension = self::null_byte::cond.raise(ext); + assert!(!contains_nul(extension)); + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } else { + v = vec::with_capacity(idx + extension.len() + 1); + v.push_all(name.slice_to(idx+1)); + v.push_all(extension); + } + Some(v) } - Some(v) } } } - } else { None } + } }; match val { None => (), @@ -424,6 +461,52 @@ pub trait GenericPath: Clone + GenericPathUnsafe { fn set_extension_str(&mut self, extension: &str) { self.set_extension(extension.as_bytes()) } + /// Adds the given extension (as a byte vector) to the file. + /// This does not remove any existing extension. + /// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`. + /// If `self` has no filename, this is a no-op. + /// If the given byte vector is [], this is a no-op. + /// + /// # Failure + /// + /// Raises the `null_byte` condition if the extension contains a NUL. + fn add_extension(&mut self, extension: &[u8]) { + if extension.is_empty() { return; } + // appease borrowck + let val = { + match self.filename() { + None => None, + Some(name) => { + let mut v; + if contains_nul(extension) { + let ext = extension.to_owned(); + let extension = self::null_byte::cond.raise(ext); + assert!(!contains_nul(extension)); + v = vec::with_capacity(name.len() + 1 + extension.len()); + v.push_all(name); + v.push('.' as u8); + v.push_all(extension); + } else { + v = vec::with_capacity(name.len() + 1 + extension.len()); + v.push_all(name); + v.push('.' as u8); + v.push_all(extension); + } + Some(v) + } + } + }; + match val { + None => (), + Some(v) => unsafe { self.set_filename_unchecked(v) } + } + } + /// Adds the given extension (as a string) to the file. + /// See `add_extension` for details. + #[inline] + fn add_extension_str(&mut self, extension: &str) { + self.add_extension(extension.as_bytes()) + } /// Returns a new Path constructed by replacing the dirname with the given byte vector. /// See `set_dirname` for details. @@ -516,12 +599,14 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If `self` represents the root of the filesystem hierarchy, returns None. fn file_path(&self) -> Option { // self.filename() returns a NUL-free vector - match self.filename() { - [] => None, - v => Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) - } + self.filename().map_move(|v| unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) } + /// Returns a Path that represents the filesystem root that `self` is rooted in. + /// + /// If `self` is not absolute, or vol-relative in the case of Windows, this returns None. + fn root_path(&self) -> Option; + /// Pushes a path (as a byte vector) onto `self`. /// If the argument represents an absolute path, it replaces `self`. /// @@ -554,6 +639,21 @@ pub trait GenericPath: Clone + GenericPathUnsafe { fn push_path(&mut self, path: &Self) { self.push(path.as_vec()) } + /// Pushes multiple paths (as byte vectors) onto `self`. + /// See `push` for details. + #[inline] + fn push_many>(&mut self, paths: &[V]) { + for p in paths.iter() { + self.push(p.as_slice()); + } + } + /// Pushes multiple paths (as strings) onto `self`. + #[inline] + fn push_many_str(&mut self, paths: &[S]) { + for p in paths.iter() { + self.push_str(p.as_slice()); + } + } /// Pops the last path component off of `self` and returns it. /// If `self` represents the root of the file hierarchy, None is returned. fn pop(&mut self) -> Option<~[u8]>; @@ -593,12 +693,36 @@ pub trait GenericPath: Clone + GenericPathUnsafe { p.push_path(path); p } + /// Returns a new Path constructed by joining `self` with the given paths (as byte vectors). + /// See `join` for details. + #[inline] + fn join_many>(&self, paths: &[V]) -> Self { + let mut p = self.clone(); + p.push_many(paths); + p + } + /// Returns a new Path constructed by joining `self` with the given paths (as strings). + /// See `join` for details. + #[inline] + fn join_many_str(&self, paths: &[S]) -> Self { + let mut p = self.clone(); + p.push_many_str(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. 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. + 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. @@ -707,6 +831,30 @@ fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) } +#[inline(always)] +fn from_utf8_with_replacement(mut v: &[u8]) -> ~str { + // FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str + // This is a truly horrifically bad implementation, done as a functionality stopgap until + // we have a proper utf-8 decoder. I don't really want to write one here. + static REPLACEMENT_CHAR: char = '\uFFFD'; + + let mut s = str::with_capacity(v.len()); + while !v.is_empty() { + let w = str::utf8_char_width(v[0]); + if w == 0u { + s.push_char(REPLACEMENT_CHAR); + v = v.slice_from(1); + } else if v.len() < w || !str::is_utf8(v.slice_to(w)) { + s.push_char(REPLACEMENT_CHAR); + v = v.slice_from(1); + } else { + s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) }); + v = v.slice_from(w); + } + } + s +} + // FIXME (#9537): libc::stat should derive Default #[cfg(target_os = "linux")] #[cfg(target_os = "android")] @@ -927,3 +1075,20 @@ mod stat { } } } + +#[cfg(test)] +mod tests { + use super::{GenericPath, PosixPath, WindowsPath}; + use c_str::ToCStr; + + #[test] + fn test_from_c_str() { + let input = "/foo/bar/baz"; + let path: PosixPath = GenericPath::from_c_str(input.to_c_str()); + assert_eq!(path.as_vec(), input.as_bytes()); + + let input = "\\foo\\bar\\baz"; + let path: WindowsPath = GenericPath::from_c_str(input.to_c_str()); + assert_eq!(path.as_str().unwrap(), input); + } +} diff --git a/src/libstd/path2/posix.rs b/src/libstd/path/posix.rs similarity index 76% rename from src/libstd/path2/posix.rs rename to src/libstd/path/posix.rs index 4aa2eb531e5c3..481387378fa6f 100644 --- a/src/libstd/path2/posix.rs +++ b/src/libstd/path/posix.rs @@ -15,22 +15,30 @@ use c_str::{CString, ToCStr}; use clone::Clone; use cmp::Eq; use from_str::FromStr; -use iter::{AdditiveIterator, Extendable, Iterator}; +use iter::{AdditiveIterator, Extendable, Iterator, Map}; use option::{Option, None, Some}; use str; use str::Str; use to_bytes::IterBytes; use util; use vec; -use vec::CopyableVector; -use vec::{Vector, VectorVector}; +use vec::{CopyableVector, RSplitIterator, SplitIterator, Vector, VectorVector}; use super::{GenericPath, GenericPathUnsafe}; #[cfg(not(target_os = "win32"))] use libc; -/// Iterator that yields successive components of a Path -pub type ComponentIter<'self> = vec::SplitIterator<'self, u8>; +/// Iterator that yields successive components of a Path as &[u8] +pub type ComponentIter<'self> = SplitIterator<'self, u8>; +/// Iterator that yields components of a Path in reverse as &[u8] +pub type RevComponentIter<'self> = RSplitIterator<'self, u8>; + +/// Iterator that yields successive components of a Path as Option<&str> +pub type StrComponentIter<'self> = Map<'self, &'self [u8], Option<&'self str>, + ComponentIter<'self>>; +/// Iterator that yields components of a Path in reverse as Option<&str> +pub type RevStrComponentIter<'self> = Map<'self, &'self [u8], Option<&'self str>, + RevComponentIter<'self>>; /// Represents a POSIX file path #[deriving(Clone, DeepClone)] @@ -187,12 +195,13 @@ impl GenericPath for Path { } } - fn filename<'a>(&'a self) -> &'a [u8] { + fn filename<'a>(&'a self) -> Option<&'a [u8]> { match self.sepidx { - None if bytes!(".") == self.repr || bytes!("..") == self.repr => &[], - None => self.repr.as_slice(), - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => &[], - Some(idx) => self.repr.slice_from(idx+1) + None if bytes!(".") == self.repr || bytes!("..") == self.repr => None, + None => Some(self.repr.as_slice()), + Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => None, + Some(0) if self.repr.slice_from(1).is_empty() => None, + Some(idx) => Some(self.repr.slice_from(idx+1)) } } @@ -219,6 +228,14 @@ impl GenericPath for Path { } } + fn root_path(&self) -> Option { + if self.is_absolute() { + Some(Path::from_str("/")) + } else { + None + } + } + #[inline] fn is_absolute(&self) -> bool { self.repr[0] == sep @@ -384,6 +401,48 @@ impl Path { } ret } + + /// Returns an iterator that yields each component of the path in reverse. + /// See component_iter() for details. + pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> { + let v = if self.repr[0] == sep { + self.repr.slice_from(1) + } else { self.repr.as_slice() }; + let mut ret = v.rsplit_iter(is_sep); + 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 component_iter() for details. + pub fn str_component_iter<'a>(&'a self) -> StrComponentIter<'a> { + self.component_iter().map(str::from_utf8_slice_opt) + } + + /// Returns an iterator that yields each component of the path in reverse as Option<&str>. + /// See component_iter() for details. + pub fn rev_str_component_iter<'a>(&'a self) -> RevStrComponentIter<'a> { + self.rev_component_iter().map(str::from_utf8_slice_opt) + } + + /// Returns whether the relative path `child` is a suffix of `self`. + pub fn ends_with_path(&self, child: &Path) -> bool { + if !child.is_relative() { return false; } + let mut selfit = self.rev_component_iter(); + let mut childit = child.rev_component_iter(); + 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 + } } // None result means the byte vector didn't need normalizing @@ -422,8 +481,8 @@ fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) } -static dot_static: &'static [u8] = &'static ['.' as u8]; -static dot_dot_static: &'static [u8] = &'static ['.' as u8, '.' as u8]; +static dot_static: &'static [u8] = bytes!("."); +static dot_dot_static: &'static [u8] = bytes!(".."); // Stat support #[cfg(not(target_os = "win32"))] @@ -524,7 +583,7 @@ impl Path { #[cfg(test)] mod tests { use super::*; - use option::{Some, None}; + use option::{Option, Some, None}; use iter::Iterator; use str; use vec::Vector; @@ -604,7 +663,7 @@ mod tests { #[test] fn test_null_byte() { - use path2::null_byte::cond; + use path::null_byte::cond; let mut handled = false; let mut p = do cond.trap(|v| { @@ -653,7 +712,7 @@ mod tests { #[test] fn test_null_byte_fail() { - use path2::null_byte::cond; + use path::null_byte::cond; use task; macro_rules! t( @@ -709,25 +768,47 @@ mod tests { assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); assert_eq!(Path::from_vec(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD"); assert_eq!(Path::from_vec(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar"); + assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).to_filename_display_str(), Some(~"bar")); + assert_eq!(Path::from_vec(b!("foo/", 0xff, "bar")).to_filename_display_str(), + Some(~"\uFFFDbar")); + assert_eq!(Path::from_vec(b!("/")).to_filename_display_str(), None); let mut called = false; - do Path::from_str("foo").as_display_str |s| { + do Path::from_str("foo").with_display_str |s| { assert_eq!(s, "foo"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("foo", 0x80)).as_display_str |s| { + do Path::from_vec(b!("foo", 0x80)).with_display_str |s| { assert_eq!(s, "foo\uFFFD"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("foo", 0xff, "bar")).as_display_str |s| { + do Path::from_vec(b!("foo", 0xff, "bar")).with_display_str |s| { assert_eq!(s, "foo\uFFFDbar"); called = true; }; assert!(called); + called = false; + do Path::from_vec(b!("foo", 0xff, "/bar")).with_filename_display_str |s| { + assert_eq!(s, Some("bar")); + called = true; + } + assert!(called); + called = false; + do Path::from_vec(b!("foo/", 0xff, "bar")).with_filename_display_str |s| { + assert_eq!(s, Some("\uFFFDbar")); + called = true; + } + assert!(called); + called = false; + do Path::from_vec(b!("/")).with_filename_display_str |s| { + assert!(s.is_none()); + called = true; + } + assert!(called); } #[test] @@ -774,20 +855,20 @@ mod tests { let path = Path::from_vec($path); assert_eq!(path.$op(), $exp); } - ) + ); ) - t!(v: b!("a/b/c"), filename, b!("c")); - t!(v: b!("a/b/c", 0xff), filename, b!("c", 0xff)); - t!(v: b!("a/b", 0xff, "/c"), filename, b!("c")); - t!(s: "a/b/c", filename, "c"); - t!(s: "/a/b/c", filename, "c"); - t!(s: "a", filename, "a"); - t!(s: "/a", filename, "a"); - t!(s: ".", filename, ""); - t!(s: "/", filename, ""); - t!(s: "..", filename, ""); - t!(s: "../..", filename, ""); + t!(v: b!("a/b/c"), filename, Some(b!("c"))); + t!(v: b!("a/b/c", 0xff), filename, Some(b!("c", 0xff))); + t!(v: b!("a/b", 0xff, "/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", 0xff), dirname, b!("a/b")); @@ -801,21 +882,21 @@ mod tests { t!(s: "..", dirname, ".."); t!(s: "../..", dirname, "../.."); - t!(v: b!("hi/there.txt"), filestem, b!("there")); - t!(v: b!("hi/there", 0x80, ".txt"), filestem, b!("there", 0x80)); - t!(v: b!("hi/there.t", 0x80, "xt"), filestem, b!("there")); - t!(s: "hi/there.txt", filestem, "there"); - t!(s: "hi/there", filestem, "there"); - t!(s: "there.txt", filestem, "there"); - t!(s: "there", filestem, "there"); - t!(s: ".", filestem, ""); - t!(s: "/", filestem, ""); - t!(s: "foo/.bar", filestem, ".bar"); - t!(s: ".bar", filestem, ".bar"); - t!(s: "..bar", filestem, "."); - t!(s: "hi/there..txt", filestem, "there."); - t!(s: "..", filestem, ""); - t!(s: "../..", filestem, ""); + t!(v: b!("hi/there.txt"), filestem, Some(b!("there"))); + t!(v: b!("hi/there", 0x80, ".txt"), filestem, Some(b!("there", 0x80))); + t!(v: b!("hi/there.t", 0x80, "xt"), 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", 0x80, ".txt"), extension, Some(b!("txt"))); @@ -878,6 +959,39 @@ mod tests { t!(s: "a/b", "../c", "a/c"); } + #[test] + fn test_push_many() { + use to_man = at_vec::to_managed_move; + + macro_rules! t( + (s: $path:expr, $push:expr, $exp:expr) => ( + { + let mut p = Path::from_str($path); + p.push_many_str($push); + assert_eq!(p.as_str(), Some($exp)); + } + ); + (v: $path:expr, $push:expr, $exp:expr) => ( + { + let mut p = Path::from_vec($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", ~"e"], "a/b/c/d/e"); + t!(s: "a/b/c", [@"d", @"e"], "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_owned(), b!("e").to_owned()], b!("a/b/c/d/e")); + t!(v: b!("a/b/c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())], + b!("a/b/c/d/e")); + } + #[test] fn test_pop() { macro_rules! t( @@ -926,6 +1040,12 @@ mod tests { assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar")); } + #[test] + fn test_root_path() { + assert_eq!(Path::from_vec(b!("a/b/c")).root_path(), None); + assert_eq!(Path::from_vec(b!("/a/b/c")).root_path(), Some(Path::from_str("/"))); + } + #[test] fn test_join() { t!(v: Path::from_vec(b!("a/b/c")).join(b!("..")), b!("a/b")); @@ -960,6 +1080,38 @@ mod tests { t!(s: "/", "a/b", "/a/b"); } + #[test] + fn test_join_many() { + use to_man = at_vec::to_managed_move; + + macro_rules! t( + (s: $path:expr, $join:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let res = path.join_many_str($join); + assert_eq!(res.as_str(), Some($exp)); + } + ); + (v: $path:expr, $join:expr, $exp:expr) => ( + { + let path = Path::from_vec($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", ~"e"], "a/b/c/d/e"); + t!(s: "a/b/c", [@"d", @"e"], "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_owned(), b!("e").to_owned()], b!("a/b/c/d/e")); + t!(v: b!("a/b/c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())], + b!("a/b/c/d/e")); + } + #[test] fn test_with_helpers() { t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); @@ -1121,16 +1273,58 @@ mod tests { t!(s: ".", set_extension_str, with_extension_str, "txt"); } + #[test] + fn test_add_extension() { + macro_rules! t( + (s: $path:expr, $ext:expr, $exp:expr) => ( + { + let mut path = Path::from_str($path); + path.add_extension_str($ext); + assert_eq!(path.as_str(), Some($exp)); + } + ); + (v: $path:expr, $ext:expr, $exp:expr) => ( + { + let mut path = Path::from_vec($path); + path.add_extension($ext); + assert_eq!(path.as_vec(), $exp); + } + ) + ) + + t!(s: "hi/there.txt", "foo", "hi/there.txt.foo"); + t!(s: "hi/there.", "foo", "hi/there..foo"); + t!(s: "hi/there", ".foo", "hi/there..foo"); + t!(s: "hi/there.txt", "", "hi/there.txt"); + t!(v: b!("hi/there.txt"), b!("foo"), b!("hi/there.txt.foo")); + t!(v: b!("hi/there"), b!("bar"), b!("hi/there.bar")); + t!(v: b!("/"), b!("foo"), b!("/")); + t!(v: b!("."), b!("foo"), b!(".")); + t!(v: b!("hi/there.", 0x80, "foo"), b!(0xff), b!("hi/there.", 0x80, "foo.", 0xff)); + } + #[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); + let filename = $filename; + assert!(path.filename_str() == filename, + "`%s`.filename_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`%s`.dirname_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`%s`.filestem_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`%s`.extension_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( @@ -1144,15 +1338,15 @@ mod tests { ) ) - t!(v: Path::from_vec(b!("a/b/c")), b!("c"), b!("a/b"), b!("c"), None); - t!(v: Path::from_vec(b!("a/b/", 0xff)), b!(0xff), b!("a/b"), b!(0xff), None); - t!(v: Path::from_vec(b!("hi/there.", 0xff)), b!("there.", 0xff), b!("hi"), - b!("there"), Some(b!(0xff))); + t!(v: Path::from_vec(b!("a/b/c")), Some(b!("c")), b!("a/b"), Some(b!("c")), None); + t!(v: Path::from_vec(b!("a/b/", 0xff)), Some(b!(0xff)), b!("a/b"), Some(b!(0xff)), None); + t!(v: Path::from_vec(b!("hi/there.", 0xff)), Some(b!("there.", 0xff)), b!("hi"), + Some(b!("there")), Some(b!(0xff))); t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); - t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); - t!(s: Path::from_str("/"), Some(""), Some("/"), Some(""), None); - t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None); - t!(s: Path::from_str("../.."), Some(""), Some("../.."), Some(""), None); + t!(s: Path::from_str("."), None, Some("."), None, None); + t!(s: Path::from_str("/"), None, Some("/"), None, None); + t!(s: Path::from_str(".."), None, Some(".."), None, None); + t!(s: Path::from_str("../.."), None, Some("../.."), None, None); t!(s: Path::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); t!(s: Path::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); @@ -1207,14 +1401,23 @@ mod tests { #[test] fn test_is_absolute() { - assert_eq!(Path::from_str("a/b/c").is_absolute(), false); - assert_eq!(Path::from_str("/a/b/c").is_absolute(), true); - assert_eq!(Path::from_str("a").is_absolute(), false); - assert_eq!(Path::from_str("/a").is_absolute(), true); - assert_eq!(Path::from_str(".").is_absolute(), false); - assert_eq!(Path::from_str("/").is_absolute(), true); - assert_eq!(Path::from_str("..").is_absolute(), false); - assert_eq!(Path::from_str("../..").is_absolute(), false); + macro_rules! t( + (s: $path:expr, $abs:expr, $rel:expr) => ( + { + let path = Path::from_str($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] @@ -1251,6 +1454,45 @@ mod tests { t!(s: "../..", "a/b", true); } + #[test] + fn test_ends_with_path() { + macro_rules! t( + (s: $path:expr, $child:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let child = Path::from_str($child); + assert_eq!(path.ends_with_path(&child), $exp); + } + ); + (v: $path:expr, $child:expr, $exp:expr) => ( + { + let path = Path::from_vec($path); + let child = Path::from_vec($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/", 0xff), b!(0xff), true); + t!(v: b!("a/b/", 0xff), b!("b/", 0xff), true); + } + #[test] fn test_path_relative_from() { macro_rules! t( @@ -1305,7 +1547,12 @@ mod tests { let comps = path.component_iter().to_owned_vec(); let exp: &[&str] = $exp; let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); - assert_eq!(comps, exps); + assert!(comps == exps, "component_iter: Expected %?, found %?", + comps, exps); + let comps = path.rev_component_iter().to_owned_vec(); + let exps = exps.move_rev_iter().to_owned_vec(); + assert!(comps == exps, "rev_component_iter: Expected %?, found %?", + comps, exps); } ); (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( @@ -1313,7 +1560,12 @@ mod tests { let path = Path::from_vec(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = [$(b!($($exp),*)),*]; - assert_eq!(comps.as_slice(), exp); + assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?", + comps.as_slice(), exp); + let comps = path.rev_component_iter().to_owned_vec(); + let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); + assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?", + comps.as_slice(), exp); } ) ) @@ -1334,6 +1586,32 @@ mod tests { t!(s: "../../foo", ["..", "..", "foo"]); } + #[test] + fn test_str_component_iter() { + macro_rules! t( + (v: [$($arg:expr),+], $exp:expr) => ( + { + let path = Path::from_vec(b!($($arg),+)); + let comps = path.str_component_iter().to_owned_vec(); + let exp: &[Option<&str>] = $exp; + assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); + let comps = path.rev_str_component_iter().to_owned_vec(); + let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); + assert!(comps.as_slice() == exp, + "rev_str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); + } + ) + ) + + t!(v: ["a/b/c"], [Some("a"), Some("b"), Some("c")]); + t!(v: ["/", 0xff, "/a/", 0x80], [None, Some("a"), None]); + t!(v: ["../../foo", 0xcd, "bar"], [Some(".."), Some(".."), None]); + // str_component_iter is a wrapper around component_iter, so no need to do + // the full set of tests + } + #[test] fn test_each_parent() { assert!(Path::from_str("/foo/bar").each_parent(|_| true)); diff --git a/src/libstd/path2/windows.rs b/src/libstd/path/windows.rs similarity index 85% rename from src/libstd/path2/windows.rs rename to src/libstd/path/windows.rs index 7cc7a5610bde5..50cbe698d0452 100644 --- a/src/libstd/path2/windows.rs +++ b/src/libstd/path/windows.rs @@ -15,10 +15,10 @@ use c_str::{CString, ToCStr}; use cast; use cmp::Eq; use from_str::FromStr; -use iter::{AdditiveIterator, Extendable, Iterator}; +use iter::{AdditiveIterator, DoubleEndedIterator, Extendable, Invert, Iterator, Map}; use option::{Option, Some, None}; use str; -use str::{OwnedStr, Str, StrVector}; +use str::{CharSplitIterator, OwnedStr, Str, StrVector}; use to_bytes::IterBytes; use util; use vec::Vector; @@ -27,8 +27,25 @@ use super::{GenericPath, GenericPathUnsafe}; #[cfg(target_os = "win32")] use libc; -/// Iterator that yields successive components of a Path -pub type ComponentIter<'self> = str::CharSplitIterator<'self, char>; +/// 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 StrComponentIter<'self> = Map<'self, &'self str, Option<&'self str>, + CharSplitIterator<'self, char>>; +/// Iterator that yields components of a Path in reverse 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 RevStrComponentIter<'self> = Invert, + CharSplitIterator<'self, char>>>; + +/// Iterator that yields successive components of a Path as &[u8] +pub type ComponentIter<'self> = Map<'self, Option<&'self str>, &'self [u8], + StrComponentIter<'self>>; +/// Iterator that yields components of a Path in reverse as &[u8] +pub type RevComponentIter<'self> = Map<'self, Option<&'self str>, &'self [u8], + RevStrComponentIter<'self>>; /// Represents a Windows path // Notes for Windows path impl: @@ -361,7 +378,7 @@ impl GenericPath for Path { } #[inline] - fn as_display_str(&self, f: &fn(&str) -> T) -> T { + fn with_display_str(&self, f: &fn(&str) -> T) -> T { f(self.repr.as_slice()) } @@ -400,33 +417,34 @@ impl GenericPath for Path { } #[inline] - fn filename<'a>(&'a self) -> &'a [u8] { - self.filename_str().unwrap().as_bytes() + fn filename<'a>(&'a self) -> Option<&'a [u8]> { + self.filename_str().map_move(|x| x.as_bytes()) } /// See `GenericPath::filename_str` for info. - /// Always returns a `Some` value. + /// Always returns a `Some` value if `filename` returns a `Some` value. fn filename_str<'a>(&'a self) -> Option<&'a str> { - Some(match self.sepidx_or_prefix_len() { - None if "." == self.repr || ".." == self.repr => "", - None => self.repr.as_slice(), - Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => "", - Some((_,idxa,end)) => self.repr.slice(idxa, end) - }) + match self.sepidx_or_prefix_len() { + None if "." == self.repr || ".." == self.repr => None, + None => Some(self.repr.as_slice()), + Some((_,idxa,end)) if self.repr.slice(idxa, end) == ".." => None, + Some((_,idxa,end)) if idxa == end => None, + Some((_,idxa,end)) => Some(self.repr.slice(idxa, end)) + } } /// See `GenericPath::filestem_str` for info. - /// Always returns a `Some` value. + /// 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 - Some(unsafe { cast::transmute(self.filestem()) }) + self.filestem().map_move(cast::transmute) } #[inline] fn extension_str<'a>(&'a self) -> Option<&'a str> { // extension() returns a byte vector that's guaranteed valid UTF-8 - self.extension().map_move(|v| unsafe { cast::transmute(v) }) + self.extension().map_move(cast::transmute) } fn dir_path(&self) -> Path { @@ -434,10 +452,7 @@ impl GenericPath for Path { } fn file_path(&self) -> Option { - match self.filename_str() { - None | Some("") => None, - Some(s) => Some(unsafe { GenericPathUnsafe::from_str_unchecked(s) }) - } + self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::from_str_unchecked(s) }) } #[inline] @@ -477,6 +492,21 @@ impl GenericPath for Path { } } + fn root_path(&self) -> Option { + if self.is_absolute() { + Some(Path::from_str(match self.prefix { + Some(VerbatimDiskPrefix)|Some(DiskPrefix) => { + self.repr.slice_to(self.prefix_len()+1) + } + _ => self.repr.slice_to(self.prefix_len()) + })) + } else if self.is_vol_relative() { + Some(Path::from_str(self.repr.slice_to(1))) + } else { + None + } + } + /// See `GenericPath::is_absolute` for info. /// /// A Windows Path is considered absolute only if it has a non-volume prefix, @@ -498,6 +528,11 @@ impl GenericPath for Path { } } + #[inline] + fn is_relative(&self) -> bool { + self.prefix.is_none() && !self.is_vol_relative() + } + fn is_ancestor_of(&self, other: &Path) -> bool { if !self.equiv_prefix(other) { false @@ -505,8 +540,8 @@ impl GenericPath for Path { self.is_vol_relative() != other.is_vol_relative() { false } else { - let mut ita = self.component_iter(); - let mut itb = other.component_iter(); + let mut ita = self.str_component_iter().map(|x|x.unwrap()); + let mut itb = other.str_component_iter().map(|x|x.unwrap()); if "." == self.repr { return itb.next() != Some(".."); } @@ -553,8 +588,8 @@ impl GenericPath for Path { None } } else { - let mut ita = self.component_iter(); - let mut itb = base.component_iter(); + let mut ita = self.str_component_iter().map(|x|x.unwrap()); + let mut itb = base.str_component_iter().map(|x|x.unwrap()); let mut comps = ~[]; let a_verb = self.is_verbatim(); @@ -669,13 +704,14 @@ impl Path { path } - /// Returns an iterator that yields each component of the path in turn. + /// 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 component_iter<'a>(&'a self) -> ComponentIter<'a> { + pub fn str_component_iter<'a>(&'a self) -> StrComponentIter<'a> { let s = match self.prefix { Some(_) => { let plen = self.prefix_len(); @@ -686,10 +722,52 @@ impl Path { None if self.repr[0] == sep as u8 => self.repr.slice_from(1), None => self.repr.as_slice() }; - let ret = s.split_terminator_iter(sep); + let ret = s.split_terminator_iter(sep).map(Some); ret } + /// Returns an iterator that yields each component of the path in reverse as an Option<&str> + /// See str_component_iter() for details. + pub fn rev_str_component_iter<'a>(&'a self) -> RevStrComponentIter<'a> { + self.str_component_iter().invert() + } + + /// Returns an iterator that yields each component of the path in turn as a &[u8]. + /// See str_component_iter() for details. + pub fn component_iter<'a>(&'a self) -> ComponentIter<'a> { + fn convert<'a>(x: Option<&'a str>) -> &'a [u8] { + #[inline]; + x.unwrap().as_bytes() + } + self.str_component_iter().map(convert) + } + + /// Returns an iterator that yields each component of the path in reverse as a &[u8]. + /// See str_component_iter() for details. + pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> { + fn convert<'a>(x: Option<&'a str>) -> &'a [u8] { + #[inline]; + x.unwrap().as_bytes() + } + self.rev_str_component_iter().map(convert) + } + + /// Returns whether the relative path `child` is a suffix of `self`. + pub fn ends_with_path(&self, child: &Path) -> bool { + if !child.is_relative() { return false; } + let mut selfit = self.str_component_iter().invert(); + let mut childit = child.str_component_iter().invert(); + 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 + } + /// 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. @@ -1096,8 +1174,8 @@ impl Path { /// Returns whether the represented file exists pub fn exists(&self) -> bool { match self.stat() { - None => None, - Some(st) => Some(st.st_size as i64) + None => false, + Some(_) => true } } @@ -1339,7 +1417,7 @@ mod tests { #[test] fn test_null_byte() { - use path2::null_byte::cond; + use path::null_byte::cond; let mut handled = false; let mut p = do cond.trap(|v| { @@ -1388,7 +1466,7 @@ mod tests { #[test] fn test_null_byte_fail() { - use path2::null_byte::cond; + use path::null_byte::cond; use task; macro_rules! t( @@ -1448,13 +1526,20 @@ mod tests { #[test] fn test_display_str() { assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); + assert_eq!(Path::from_vec(b!("\\")).to_filename_display_str(), None); let mut called = false; - do Path::from_str("foo").as_display_str |s| { + do Path::from_str("foo").with_display_str |s| { assert_eq!(s, "foo"); called = true; }; assert!(called); + called = false; + do Path::from_vec(b!("\\")).with_filename_display_str |s| { + assert!(s.is_none()); + called = true; + } + assert!(called); } #[test] @@ -1500,37 +1585,37 @@ mod tests { ) ) - t!(v: b!("a\\b\\c"), filename, b!("c")); + 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, ""); - t!(s: "\\", filename_str, ""); - t!(s: "..", filename_str, ""); - t!(s: "..\\..", filename_str, ""); + 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, ""); - t!(s: "C:", filename_str, ""); + 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, ""); + 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, ""); - t!(s: "\\\\?\\", filename_str, ""); + 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, ""); - t!(s: "\\\\?\\UNC\\", filename_str, ""); + 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, ""); - t!(s: "\\\\?\\C:", filename_str, ""); - t!(s: "\\\\?\\foo/bar", filename_str, ""); - t!(s: "\\\\?\\C:/foo", filename_str, ""); + 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, ""); - t!(s: "\\\\.\\foo/bar", filename_str, ""); + 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, ""); + t!(s: "\\\\.\\", filename_str, None, opt); t!(s: "\\\\?\\a\\b\\", filename_str, "b"); t!(v: b!("a\\b\\c"), dirname, b!("a\\b")); @@ -1564,19 +1649,19 @@ mod tests { t!(s: "\\\\.\\foo", dirname_str, "\\\\.\\foo"); t!(s: "\\\\?\\a\\b\\", dirname_str, "\\\\?\\a"); - t!(v: b!("hi\\there.txt"), filestem, b!("there")); + 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, ""); - t!(s: "\\", filestem_str, ""); + 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, ""); - t!(s: "..\\..", filestem_str, ""); + 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"))); @@ -1681,6 +1766,39 @@ mod tests { t!(s: "\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one } + #[test] + fn test_push_many() { + use to_man = at_vec::to_managed_move; + + macro_rules! t( + (s: $path:expr, $push:expr, $exp:expr) => ( + { + let mut p = Path::from_str($path); + p.push_many_str($push); + assert_eq!(p.as_str(), Some($exp)); + } + ); + (v: $path:expr, $push:expr, $exp:expr) => ( + { + let mut p = Path::from_vec($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", ~"e"], "a\\b\\c\\d\\e"); + t!(s: "a\\b\\c", [@"d", @"e"], "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_owned(), b!("e").to_owned()], b!("a\\b\\c\\d\\e")); + t!(v: b!("a\\b\\c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())], + b!("a\\b\\c\\d\\e")); + } + #[test] fn test_pop() { macro_rules! t( @@ -1753,6 +1871,20 @@ mod tests { t!(s: "\\\\?\\a\\b\\", "\\\\?\\a", Some("b")); } + #[test] + fn test_root_path() { + assert_eq!(Path::from_str("a\\b\\c").root_path(), None); + assert_eq!(Path::from_str("\\a\\b\\c").root_path(), Some(Path::from_str("\\"))); + assert_eq!(Path::from_str("C:a").root_path(), None); + assert_eq!(Path::from_str("C:\\a").root_path(), Some(Path::from_str("C:\\"))); + assert_eq!(Path::from_str("\\\\a\\b\\c").root_path(), Some(Path::from_str("\\\\a\\b"))); + assert_eq!(Path::from_str("\\\\?\\a\\b").root_path(), Some(Path::from_str("\\\\?\\a"))); + assert_eq!(Path::from_str("\\\\?\\C:\\a").root_path(), Some(Path::from_str("\\\\?\\C:\\"))); + assert_eq!(Path::from_str("\\\\?\\UNC\\a\\b\\c").root_path(), + Some(Path::from_str("\\\\?\\UNC\\a\\b"))); + assert_eq!(Path::from_str("\\\\.\\a\\b").root_path(), Some(Path::from_str("\\\\.\\a"))); + } + #[test] fn test_join() { t!(s: Path::from_str("a\\b\\c").join_str(".."), "a\\b"); @@ -1790,6 +1922,38 @@ mod tests { // the full set of prefix tests } + #[test] + fn test_join_many() { + use to_man = at_vec::to_managed_move; + + macro_rules! t( + (s: $path:expr, $join:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let res = path.join_many_str($join); + assert_eq!(res.as_str(), Some($exp)); + } + ); + (v: $path:expr, $join:expr, $exp:expr) => ( + { + let path = Path::from_vec($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", ~"e"], "a\\b\\c\\d\\e"); + t!(s: "a\\b\\c", [@"d", @"e"], "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_owned(), b!("e").to_owned()], b!("a\\b\\c\\d\\e")); + t!(v: b!("a\\b\\c"), [to_man(b!("d").to_owned()), to_man(b!("e").to_owned())], + b!("a\\b\\c\\d\\e")); + } + #[test] fn test_with_helpers() { macro_rules! t( @@ -2002,16 +2166,55 @@ mod tests { // will suffice. No need for the full set of prefix tests. } + #[test] + fn test_add_extension() { + macro_rules! t( + (s: $path:expr, $ext:expr, $exp:expr) => ( + { + let mut path = Path::from_str($path); + path.add_extension_str($ext); + assert_eq!(path.as_str(), Some($exp)); + } + ); + (v: $path:expr, $ext:expr, $exp:expr) => ( + { + let mut path = Path::from_vec($path); + path.add_extension($ext); + assert_eq!(path.as_vec(), $exp); + } + ) + ) + + t!(v: b!("hi\\there.txt"), b!("foo"), b!("hi\\there.txt.foo")); + t!(v: b!("hi\\there"), b!("bar"), b!("hi\\there.bar")); + t!(v: b!("\\"), b!("foo"), b!("\\")); + t!(v: b!("."), b!("foo"), b!(".")); + t!(s: "hi\\there.", "foo", "hi\\there..foo"); + t!(s: "hi\\there.txt", "", "hi\\there.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); + let filename = $filename; + assert!(path.filename_str() == filename, + "`%s`.filename_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`%s`.dirname_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`%s`.filestem_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`%s`.extension_str(): Expected `%?`, found `%?`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( @@ -2025,12 +2228,12 @@ mod tests { ) ) - t!(v: Path::from_vec(b!("a\\b\\c")), b!("c"), b!("a\\b"), b!("c"), None); + t!(v: Path::from_vec(b!("a\\b\\c")), Some(b!("c")), b!("a\\b"), Some(b!("c")), None); t!(s: Path::from_str("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); - t!(s: Path::from_str("."), Some(""), Some("."), Some(""), None); - t!(s: Path::from_str("\\"), Some(""), Some("\\"), Some(""), None); - t!(s: Path::from_str(".."), Some(""), Some(".."), Some(""), None); - t!(s: Path::from_str("..\\.."), Some(""), Some("..\\.."), Some(""), None); + t!(s: Path::from_str("."), None, Some("."), None, None); + t!(s: Path::from_str("\\"), None, Some("\\"), None, None); + t!(s: Path::from_str(".."), None, Some(".."), None, None); + t!(s: Path::from_str("..\\.."), None, Some("..\\.."), None, None); t!(s: Path::from_str("hi\\there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); t!(s: Path::from_str("hi\\there"), Some("there"), Some("hi"), Some("there"), None); @@ -2076,10 +2279,10 @@ mod tests { #[test] fn test_is_absolute() { macro_rules! t( - ($path:expr, $abs:expr, $vol:expr, $cwd:expr) => ( + ($path:expr, $abs:expr, $vol:expr, $cwd:expr, $rel:expr) => ( { let path = Path::from_str($path); - let (abs, vol, cwd) = ($abs, $vol, $cwd); + let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel); let b = path.is_absolute(); assert!(b == abs, "Path '%s'.is_absolute(): expected %?, found %?", path.as_str().unwrap(), abs, b); @@ -2089,25 +2292,28 @@ mod tests { let b = path.is_cwd_relative(); assert!(b == cwd, "Path '%s'.is_cwd_relative(): expected %?, found %?", path.as_str().unwrap(), cwd, b); + let b = path.is_relative(); + assert!(b == rel, "Path '%s'.is_relativf(): expected %?, found %?", + path.as_str().unwrap(), rel, b); } ) ) - t!("a\\b\\c", false, false, false); - t!("\\a\\b\\c", false, true, false); - t!("a", false, false, false); - t!("\\a", false, true, false); - t!(".", false, false, false); - t!("\\", false, true, false); - t!("..", false, false, false); - t!("..\\..", false, false, false); - t!("C:a\\b.txt", false, false, true); - t!("C:\\a\\b.txt", true, false, false); - t!("\\\\server\\share\\a\\b.txt", true, false, false); - t!("\\\\?\\a\\b\\c.txt", true, false, false); - t!("\\\\?\\C:\\a\\b.txt", true, false, false); - t!("\\\\?\\C:a\\b.txt", true, false, false); // NB: not equivalent to C:a\b.txt - t!("\\\\?\\UNC\\server\\share\\a\\b.txt", true, false, false); - t!("\\\\.\\a\\b", true, false, false); + 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] @@ -2213,6 +2419,38 @@ mod tests { 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::from_str($path); + let child = Path::from_str($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( @@ -2348,22 +2586,34 @@ mod tests { } #[test] - fn test_component_iter() { + fn test_str_component_iter() { macro_rules! t( (s: $path:expr, $exp:expr) => ( { let path = Path::from_str($path); - let comps = path.component_iter().to_owned_vec(); + let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; - assert_eq!(comps.as_slice(), exp); + assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); + let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec(); + let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); + assert!(comps.as_slice() == exp, + "rev_str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); } ); (v: [$($arg:expr),+], $exp:expr) => ( { let path = Path::from_vec(b!($($arg),+)); - let comps = path.component_iter().to_owned_vec(); + let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; - assert_eq!(comps.as_slice(), exp); + assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); + let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec(); + let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); + assert!(comps.as_slice() == exp, + "rev_str_component_iter: Expected %?, found %?", + comps.as_slice(), exp); } ) ) @@ -2408,6 +2658,29 @@ mod tests { t!(s: "\\\\.\\foo", []); } + #[test] + fn test_component_iter() { + macro_rules! t( + (s: $path:expr, $exp:expr) => ( + { + let path = Path::from_str($path); + let comps = path.component_iter().to_owned_vec(); + let exp: &[&[u8]] = $exp; + assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?", + comps.as_slice(), exp); + let comps = path.rev_component_iter().to_owned_vec(); + let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); + assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?", + comps.as_slice(), exp); + } + ) + ) + + t!(s: "a\\b\\c", [b!("a"), b!("b"), b!("c")]); + t!(s: ".", [b!(".")]); + // since this is really a wrapper around str_component_iter, those tests suffice + } + #[test] fn test_each_parent() { assert!(Path::from_str("/foo/bar").each_parent(|_| true)); diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 3da337add948c..24327e57f8295 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -60,10 +60,7 @@ pub use num::{Algebraic, Trigonometric, Exponential, Hyperbolic}; pub use num::{Integer, Fractional, Real, RealExt}; pub use num::{Bitwise, BitCount, Bounded}; pub use num::{Primitive, Int, Float, ToStrRadix, ToPrimitive, FromPrimitive}; -pub use path::GenericPath; -pub use path::Path; -pub use path::PosixPath; -pub use path::WindowsPath; +pub use path::{GenericPath, Path, PosixPath, WindowsPath}; pub use ptr::RawPtr; pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume}; pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr}; diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 3258c350cd084..9fbb897c2a7da 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -627,12 +627,13 @@ pub trait DirectoryInfo : FileSystemInfo { fn mkdir(&self) { match ignore_io_error(|| self.stat()) { Some(_) => { + let path = self.get_path(); io_error::cond.raise(IoError { kind: PathAlreadyExists, desc: "Path already exists", detail: Some(format!("{} already exists; can't mkdir it", - self.get_path().to_str())) + path.display())) }) }, None => mkdir(self.get_path()) @@ -655,24 +656,27 @@ pub trait DirectoryInfo : FileSystemInfo { match s.is_dir { true => rmdir(self.get_path()), false => { + let path = self.get_path(); let ioerr = IoError { kind: MismatchedFileTypeForOperation, desc: "Cannot do rmdir() on a non-directory", detail: Some(format!( "{} is a non-directory; can't rmdir it", - self.get_path().to_str())) + path.display())) }; io_error::cond.raise(ioerr); } } }, - None => + None => { + let path = self.get_path(); io_error::cond.raise(IoError { kind: PathDoesntExist, desc: "Path doesn't exist", detail: Some(format!("{} doesn't exist; can't rmdir it", - self.get_path().to_str())) + path.display())) }) + } } } @@ -699,7 +703,7 @@ mod test { fn file_test_io_smoke_test() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; - let filename = &Path("./tmp/file_rt_io_file_test.txt"); + let filename = &Path::from_str("./tmp/file_rt_io_file_test.txt"); { let mut write_stream = open(filename, Create, ReadWrite).unwrap(); write_stream.write(message.as_bytes()); @@ -721,7 +725,7 @@ mod test { #[test] fn file_test_io_invalid_path_opened_without_create_should_raise_condition() { do run_in_mt_newsched_task { - let filename = &Path("./tmp/file_that_does_not_exist.txt"); + let filename = &Path::from_str("./tmp/file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -736,7 +740,7 @@ mod test { #[test] fn file_test_iounlinking_invalid_path_should_raise_condition() { do run_in_mt_newsched_task { - let filename = &Path("./tmp/file_another_file_that_does_not_exist.txt"); + let filename = &Path::from_str("./tmp/file_another_file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -753,7 +757,7 @@ mod test { use str; let message = "ten-four"; let mut read_mem = [0, .. 8]; - let filename = &Path("./tmp/file_rt_io_file_test_positional.txt"); + let filename = &Path::from_str("./tmp/file_rt_io_file_test_positional.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(message.as_bytes()); @@ -784,7 +788,7 @@ mod test { let set_cursor = 4 as u64; let mut tell_pos_pre_read; let mut tell_pos_post_read; - let filename = &Path("./tmp/file_rt_io_file_test_seeking.txt"); + let filename = &Path::from_str("./tmp/file_rt_io_file_test_seeking.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(message.as_bytes()); @@ -813,7 +817,7 @@ mod test { let final_msg = "foo-the-bar!!"; let seek_idx = 3; let mut read_mem = [0, .. 13]; - let filename = &Path("./tmp/file_rt_io_file_test_seek_and_write.txt"); + let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_and_write.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(initial_msg.as_bytes()); @@ -839,7 +843,7 @@ mod test { let chunk_two = "asdf"; let chunk_three = "zxcv"; let mut read_mem = [0, .. 4]; - let filename = &Path("./tmp/file_rt_io_file_test_seek_shakedown.txt"); + let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_shakedown.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(initial_msg.as_bytes()); @@ -869,7 +873,7 @@ mod test { #[test] fn file_test_stat_is_correct_on_is_file() { do run_in_mt_newsched_task { - let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); + let filename = &Path::from_str("./tmp/file_stat_correct_on_is_file.txt"); { let mut fs = open(filename, Create, ReadWrite).unwrap(); let msg = "hw"; @@ -887,7 +891,7 @@ mod test { #[test] fn file_test_stat_is_correct_on_is_dir() { do run_in_mt_newsched_task { - let filename = &Path("./tmp/file_stat_correct_on_is_dir"); + let filename = &Path::from_str("./tmp/file_stat_correct_on_is_dir"); mkdir(filename); let stat_res = match stat(filename) { Some(s) => s, @@ -901,7 +905,7 @@ mod test { #[test] fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { do run_in_mt_newsched_task { - let dir = &Path("./tmp/fileinfo_false_on_dir"); + let dir = &Path::from_str("./tmp/fileinfo_false_on_dir"); mkdir(dir); assert!(dir.is_file() == false); rmdir(dir); @@ -911,7 +915,7 @@ mod test { #[test] fn file_test_fileinfo_check_exists_before_and_after_file_creation() { do run_in_mt_newsched_task { - let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt"); + let file = &Path::from_str("./tmp/fileinfo_check_exists_b_and_a.txt"); { let msg = "foo".as_bytes(); let mut w = file.open_writer(Create); @@ -926,7 +930,7 @@ mod test { #[test] fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { do run_in_mt_newsched_task { - let dir = &Path("./tmp/before_and_after_dir"); + let dir = &Path::from_str("./tmp/before_and_after_dir"); assert!(!dir.exists()); dir.mkdir(); assert!(dir.exists()); @@ -940,11 +944,11 @@ mod test { fn file_test_directoryinfo_readdir() { use str; do run_in_mt_newsched_task { - let dir = &Path("./tmp/di_readdir"); + let dir = &Path::from_str("./tmp/di_readdir"); dir.mkdir(); let prefix = "foo"; for n in range(0,3) { - let f = dir.push(format!("{}.txt", n)); + let f = dir.join_str(format!("{}.txt", n)); let mut w = f.open_writer(Create); let msg_str = (prefix + n.to_str().to_owned()).to_owned(); let msg = msg_str.as_bytes(); @@ -955,13 +959,13 @@ mod test { let mut mem = [0u8, .. 4]; for f in files.iter() { { - let n = f.filestem(); + let n = f.filestem_str(); let mut r = f.open_reader(Open); r.read(mem); let read_str = str::from_utf8(mem); let expected = match n { - Some(n) => prefix+n, - None => fail2!("really shouldn't happen..") + None|Some("") => fail2!("really shouldn't happen.."), + Some(n) => prefix+n }; assert!(expected == read_str); } diff --git a/src/libstd/rt/io/support.rs b/src/libstd/rt/io/support.rs index 59db8194963bb..a872423c255ae 100644 --- a/src/libstd/rt/io/support.rs +++ b/src/libstd/rt/io/support.rs @@ -22,7 +22,7 @@ impl<'self> PathLike for &'self str { impl PathLike for Path { fn path_as_str(&self, f: &fn(&str) -> T) -> T { - let s = self.to_str(); + let s = self.as_str().unwrap(); f(s) } } @@ -35,7 +35,7 @@ mod test { #[test] fn path_like_smoke_test() { let expected = if cfg!(unix) { "/home" } else { "C:\\" }; - let path = Path(expected); + let path = Path::from_str(expected); path.path_as_str(|p| assert!(p == expected)); path.path_as_str(|p| assert!(p == expected)); } diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs index b6611eee9e62d..1178bfdaa80fc 100644 --- a/src/libstd/rt/test.rs +++ b/src/libstd/rt/test.rs @@ -16,6 +16,7 @@ use container::Container; use iter::{Iterator, range}; use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr}; use vec::{OwnedVector, MutableVector, ImmutableVector}; +use path::GenericPath; use rt::sched::Scheduler; use unstable::{run_in_bare_thread}; use rt::thread::Thread; @@ -346,7 +347,6 @@ it is running in and assigns a port range based on it. fn base_port() -> uint { use os; use str::StrSlice; - use to_str::ToStr; use vec::ImmutableVector; let base = 9600u; @@ -363,12 +363,14 @@ fn base_port() -> uint { ("dist", base + range * 8) ]; - let path = os::getcwd().to_str(); + // FIXME (#9639): This needs to handle non-utf8 paths + let path = os::getcwd(); + let path_s = path.as_str().unwrap(); let mut final_base = base; for &(dir, base) in bases.iter() { - if path.contains(dir) { + if path_s.contains(dir) { final_base = base; break; } diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index dc5b512e56ea6..7756448adf829 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -391,7 +391,7 @@ mod test { let read_mem = vec::from_elem(read_buf_len, 0u8); let read_buf = slice_to_uv_buf(read_mem); let read_buf_ptr: *Buf = &read_buf; - let p = Path(path_str); + let p = Path::from_str(path_str); let open_req = FsRequest::new(); do open_req.open(&loop_, &p, create_flags as int, mode as int) |req, uverr| { @@ -405,7 +405,7 @@ mod test { assert!(uverr.is_none()); let loop_ = req.get_loop(); let open_req = FsRequest::new(); - do open_req.open(&loop_, &Path(path_str), read_flags as int,0) + do open_req.open(&loop_, &Path::from_str(path_str), read_flags as int,0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); @@ -431,7 +431,7 @@ mod test { assert!(uverr.is_none()); let loop_ = &req.get_loop(); let unlink_req = FsRequest::new(); - do unlink_req.unlink(loop_, &Path(path_str)) + do unlink_req.unlink(loop_, &Path::from_str(path_str)) |_,uverr| { assert!(uverr.is_none()); }; @@ -465,7 +465,7 @@ mod test { let write_buf = slice_to_uv_buf(write_val); // open/create let open_req = FsRequest::new(); - let result = open_req.open_sync(&loop_, &Path(path_str), + let result = open_req.open_sync(&loop_, &Path::from_str(path_str), create_flags as int, mode as int); assert!(result.is_ok()); let fd = result.unwrap(); @@ -479,7 +479,7 @@ mod test { assert!(result.is_ok()); // re-open let open_req = FsRequest::new(); - let result = open_req.open_sync(&loop_, &Path(path_str), + let result = open_req.open_sync(&loop_, &Path::from_str(path_str), read_flags as int,0); assert!(result.is_ok()); let len = 1028; @@ -503,7 +503,7 @@ mod test { assert!(result.is_ok()); // unlink let unlink_req = FsRequest::new(); - let result = unlink_req.unlink_sync(&loop_, &Path(path_str)); + let result = unlink_req.unlink_sync(&loop_, &Path::from_str(path_str)); assert!(result.is_ok()); } else { fail2!("nread was 0.. wudn't expectin' that."); } loop_.close(); diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 1de6042003cdc..a139c4e95eff4 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -34,7 +34,7 @@ use rt::uv::idle::IdleWatcher; use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs}; use rt::uv::addrinfo::GetAddrInfoRequest; use unstable::sync::Exclusive; -use path::Path; +use path::{GenericPath, Path}; use super::super::io::support::PathLike; use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, S_IRUSR, S_IWUSR, S_IRWXU}; @@ -631,7 +631,7 @@ impl IoFactory for UvIoFactory { None => { let stat = req.get_stat(); Ok(FileStat { - path: Path(path_str), + path: Path::from_str(path_str), is_file: stat.is_file(), is_dir: stat.is_dir(), size: stat.st_size, @@ -720,7 +720,9 @@ impl IoFactory for UvIoFactory { let rel_paths = req.get_paths(); let mut paths = ~[]; for r in rel_paths.iter() { - paths.push(Path(path_str+"/"+*r)); + let mut p = Path::from_str(path_str); + p.push_str(*r); + paths.push(p); } Ok(paths) }, @@ -2177,20 +2179,20 @@ fn file_test_uvio_full_simple_impl() { { let create_fm = Create; let create_fa = ReadWrite; - let mut fd = (*io).fs_open(&Path(path), create_fm, create_fa).unwrap(); + let mut fd = (*io).fs_open(&Path::from_str(path), create_fm, create_fa).unwrap(); let write_buf = write_val.as_bytes(); fd.write(write_buf); } { let ro_fm = Open; let ro_fa = Read; - let mut fd = (*io).fs_open(&Path(path), ro_fm, ro_fa).unwrap(); + let mut fd = (*io).fs_open(&Path::from_str(path), ro_fm, ro_fa).unwrap(); let mut read_vec = [0, .. 1028]; let nread = fd.read(read_vec).unwrap(); let read_val = str::from_utf8(read_vec.slice(0, nread as uint)); assert!(read_val == write_val.to_owned()); } - (*io).fs_unlink(&Path(path)); + (*io).fs_unlink(&Path::from_str(path)); } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 8712d01aae963..3f7ce3eae5864 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -578,8 +578,8 @@ mod tests { let mut prog = run_pwd(None); let output = str::from_utf8(prog.finish_with_output().output); - let parent_dir = os::getcwd().normalize(); - let child_dir = Path(output.trim()).normalize(); + let parent_dir = os::getcwd(); + let child_dir = Path::from_str(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); @@ -592,11 +592,11 @@ mod tests { fn test_change_working_directory() { // test changing to the parent of os::getcwd() because we know // the path exists (and os::getcwd() is not expected to be root) - let parent_dir = os::getcwd().dir_path().normalize(); + let parent_dir = os::getcwd().dir_path(); let mut prog = run_pwd(Some(&parent_dir)); let output = str::from_utf8(prog.finish_with_output().output); - let child_dir = Path(output.trim()).normalize(); + let child_dir = Path::from_str(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); diff --git a/src/libstd/std.rs b/src/libstd/std.rs index e8109124e57a0..73cb6a5645de3 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -177,7 +177,6 @@ pub mod libc; pub mod c_str; pub mod os; pub mod path; -pub mod path2; pub mod rand; pub mod run; pub mod sys; diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 62ff8c9fbc80a..a91d68366f33a 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -225,7 +225,7 @@ pub mod dl { pub unsafe fn open_external(filename: &path::Path) -> *libc::c_void { #[fixed_stack_segment]; #[inline(never)]; - do os::win32::as_utf16_p(filename.to_str()) |raw_name| { + do os::win32::as_utf16_p(filename.as_str().unwrap()) |raw_name| { LoadLibraryW(raw_name) } } diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index e76ade0dc3dae..ef09315a88777 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -81,7 +81,7 @@ pub fn expand_include(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) let file = get_single_str_from_tts(cx, sp, tts, "include!"); let p = parse::new_sub_parser_from_file( cx.parse_sess(), cx.cfg(), - &res_rel_file(cx, sp, &Path(file)), sp); + &res_rel_file(cx, sp, &Path::from_str(file)), sp); base::MRExpr(p.parse_expr()) } @@ -89,7 +89,7 @@ pub fn expand_include(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) pub fn expand_include_str(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult { let file = get_single_str_from_tts(cx, sp, tts, "include_str!"); - let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path(file))); + let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path::from_str(file))); match res { result::Ok(res) => { base::MRExpr(cx.expr_str(sp, res.to_managed())) @@ -103,7 +103,7 @@ pub fn expand_include_str(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) pub fn expand_include_bin(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult { let file = get_single_str_from_tts(cx, sp, tts, "include_bin!"); - match io::read_whole_file(&res_rel_file(cx, sp, &Path(file))) { + match io::read_whole_file(&res_rel_file(cx, sp, &Path::from_str(file))) { result::Ok(src) => { let u8_exprs: ~[@ast::Expr] = src.iter().map(|char| cx.expr_u8(sp, *char)).collect(); base::MRExpr(cx.expr_vec(sp, u8_exprs)) @@ -144,10 +144,12 @@ fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo { // isn't already) fn res_rel_file(cx: @ExtCtxt, sp: codemap::Span, arg: &Path) -> Path { // NB: relative paths are resolved relative to the compilation unit - if !arg.is_absolute { - let cu = Path(cx.codemap().span_to_filename(sp)); - cu.dir_path().push_many(arg.components) + if !arg.is_absolute() { + let mut cu = Path::from_str(cx.codemap().span_to_filename(sp)); + cu.pop(); + cu.push_path(arg); + cu } else { - (*arg).clone() + arg.clone() } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 67bcab319562e..05998d8021395 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -261,7 +261,8 @@ pub fn new_parser_from_tts(sess: @mut ParseSess, pub fn file_to_filemap(sess: @mut ParseSess, path: &Path, spanopt: Option) -> @FileMap { match io::read_whole_file_str(path) { - Ok(src) => string_to_filemap(sess, src.to_managed(), path.to_str().to_managed()), + // FIXME (#9639): This needs to handle non-utf8 paths + Ok(src) => string_to_filemap(sess, src.to_managed(), path.as_str().unwrap().to_managed()), Err(e) => { match spanopt { Some(span) => sess.span_diagnostic.span_fatal(span, e), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e7c579d2f19d8..ad565fd2ec4ac 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3992,27 +3992,20 @@ impl Parser { outer_attrs: &[ast::Attribute], id_sp: Span) -> (ast::item_, ~[ast::Attribute]) { - let prefix = Path(self.sess.cm.span_to_filename(*self.span)); - let prefix = prefix.dir_path(); + let mut prefix = Path::from_str(self.sess.cm.span_to_filename(*self.span)); + prefix.pop(); let mod_path_stack = &*self.mod_path_stack; - let mod_path = Path(".").push_many(*mod_path_stack); - let dir_path = prefix.push_many(mod_path.components); + let mod_path = Path::from_str(".").join_many_str(*mod_path_stack); + let dir_path = prefix.join_path(&mod_path); let file_path = match ::attr::first_attr_value_str_by_name( outer_attrs, "path") { - Some(d) => { - let path = Path(d); - if !path.is_absolute { - dir_path.push(d) - } else { - path - } - } + Some(d) => dir_path.join_str(d), None => { let mod_name = token::interner_get(id.name).to_owned(); let default_path_str = mod_name + ".rs"; let secondary_path_str = mod_name + "/mod.rs"; - let default_path = dir_path.push(default_path_str); - let secondary_path = dir_path.push(secondary_path_str); + let default_path = dir_path.join_str(default_path_str); + let secondary_path = dir_path.join_str(secondary_path_str); let default_exists = default_path.exists(); let secondary_exists = secondary_path.exists(); match (default_exists, secondary_exists) { @@ -4039,28 +4032,30 @@ impl Parser { path: Path, outer_attrs: ~[ast::Attribute], id_sp: Span) -> (ast::item_, ~[ast::Attribute]) { - let full_path = path.normalize(); - - let maybe_i = do self.sess.included_mod_stack.iter().position |p| { *p == full_path }; + let maybe_i = do self.sess.included_mod_stack.iter().position |p| { *p == path }; match maybe_i { Some(i) => { let stack = &self.sess.included_mod_stack; let mut err = ~"circular modules: "; for p in stack.slice(i, stack.len()).iter() { - err.push_str(p.to_str()); + do p.with_display_str |s| { + err.push_str(s); + } err.push_str(" -> "); } - err.push_str(full_path.to_str()); + do path.with_display_str |s| { + err.push_str(s); + } self.span_fatal(id_sp, err); } None => () } - self.sess.included_mod_stack.push(full_path.clone()); + self.sess.included_mod_stack.push(path.clone()); let p0 = new_sub_parser_from_file(self.sess, self.cfg.clone(), - &full_path, + &path, id_sp); let (inner, next) = p0.parse_inner_attrs_and_next(); let mod_attrs = vec::append(outer_attrs, inner); diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs index 5222c4f59b723..8a5cb016a738c 100644 --- a/src/test/bench/core-std.rs +++ b/src/test/bench/core-std.rs @@ -73,8 +73,8 @@ fn read_line() { use std::rt::io::file::FileInfo; use std::rt::io::buffered::BufferedReader; - let path = Path(env!("CFG_SRC_DIR")) - .push_rel(&Path("src/test/bench/shootout-k-nucleotide.data")); + let path = Path::from_str(env!("CFG_SRC_DIR")) + .join_str("src/test/bench/shootout-k-nucleotide.data"); for _ in range(0, 3) { let mut reader = BufferedReader::new(path.open_reader(Open).unwrap()); diff --git a/src/test/bench/shootout-fasta.rs b/src/test/bench/shootout-fasta.rs index 53da752fdc4fa..addb43c49952c 100644 --- a/src/test/bench/shootout-fasta.rs +++ b/src/test/bench/shootout-fasta.rs @@ -122,7 +122,7 @@ fn main() { }; let writer = if os::getenv("RUST_BENCH").is_some() { - io::file_writer(&Path("./shootout-fasta.data"), + io::file_writer(&Path::from_str("./shootout-fasta.data"), [io::Truncate, io::Create]).unwrap() } else { io::stdout() diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index 445b28b693cfc..58ec5d440718c 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -164,8 +164,8 @@ fn main() { let rdr = if os::getenv("RUST_BENCH").is_some() { // FIXME: Using this compile-time env variable is a crummy way to // get to this massive data set, but include_bin! chokes on it (#2598) - let path = Path(env!("CFG_SRC_DIR")) - .push_rel(&Path("src/test/bench/shootout-k-nucleotide.data")); + let path = Path::from_str(env!("CFG_SRC_DIR")) + .join_str("src/test/bench/shootout-k-nucleotide.data"); ~path.open_reader(Open).unwrap() as ~Reader } else { ~stdio::stdin() as ~Reader diff --git a/src/test/run-pass/glob-std.rs b/src/test/run-pass/glob-std.rs index 08843c28a0436..170645f4404b5 100644 --- a/src/test/run-pass/glob-std.rs +++ b/src/test/run-pass/glob-std.rs @@ -20,14 +20,14 @@ use std::{io, os, unstable}; pub fn main() { fn mk_file(path: &str, directory: bool) { if directory { - os::make_dir(&Path(path), 0xFFFF); + os::make_dir(&Path::from_str(path), 0xFFFF); } else { - io::mk_file_writer(&Path(path), [io::Create]); + io::mk_file_writer(&Path::from_str(path), [io::Create]); } } fn abs_path(path: &str) -> Path { - os::getcwd().push_many(Path(path).components) + os::getcwd().join_path(&Path::from_str(path)) } fn glob_vec(pattern: &str) -> ~[Path] { diff --git a/src/test/run-pass/issue-3424.rs b/src/test/run-pass/issue-3424.rs index a40d1cf1c6ef1..01d895d1bcf05 100644 --- a/src/test/run-pass/issue-3424.rs +++ b/src/test/run-pass/issue-3424.rs @@ -23,7 +23,7 @@ fn tester() { let loader: rsrc_loader = |_path| {result::Ok(~"more blah")}; - let path = path::Path("blah"); + let path = path::Path::from_str("blah"); assert!(loader(&path).is_ok()); } diff --git a/src/test/run-pass/rename-directory.rs b/src/test/run-pass/rename-directory.rs index 007ab381a5fc6..608624670fc37 100644 --- a/src/test/run-pass/rename-directory.rs +++ b/src/test/run-pass/rename-directory.rs @@ -25,12 +25,12 @@ fn rename_directory() { let tmpdir = TempDir::new("rename_directory").expect("rename_directory failed"); let tmpdir = tmpdir.path(); - let old_path = tmpdir.push_many(["foo", "bar", "baz"]); + let old_path = tmpdir.join_many_str(["foo", "bar", "baz"]); assert!(os::mkdir_recursive(&old_path, U_RWX)); - let test_file = &old_path.push("temp.txt"); + let test_file = &old_path.join_str("temp.txt"); /* Write the temp input file */ - let ostream = do test_file.to_str().with_c_str |fromp| { + let ostream = do test_file.with_c_str |fromp| { do "w+b".with_c_str |modebuf| { libc::fopen(fromp, modebuf) } @@ -46,11 +46,11 @@ fn rename_directory() { } assert_eq!(libc::fclose(ostream), (0u as libc::c_int)); - let new_path = tmpdir.push_many(["quux", "blat"]); + let new_path = tmpdir.join_many_str(["quux", "blat"]); assert!(os::mkdir_recursive(&new_path, U_RWX)); - assert!(os::rename_file(&old_path, &new_path.push("newdir"))); - assert!(os::path_is_dir(&new_path.push("newdir"))); - assert!(os::path_exists(&new_path.push_many(["newdir", "temp.txt"]))); + assert!(os::rename_file(&old_path, &new_path.join_str("newdir"))); + assert!(os::path_is_dir(&new_path.join_str("newdir"))); + assert!(os::path_exists(&new_path.join_many_str(["newdir", "temp.txt"]))); } } diff --git a/src/test/run-pass/stat.rs b/src/test/run-pass/stat.rs index 67b84d6c93c23..5cd62368aa222 100644 --- a/src/test/run-pass/stat.rs +++ b/src/test/run-pass/stat.rs @@ -18,8 +18,8 @@ use std::io; use std::os; pub fn main() { - let dir = tempfile::TempDir::new_in(&Path("."), "").unwrap(); - let path = dir.path().push("file"); + let dir = tempfile::TempDir::new_in(&Path::from_str("."), "").unwrap(); + let path = dir.path().join_str("file"); { match io::file_writer(&path, [io::Create, io::Truncate]) { diff --git a/src/test/run-pass/tempfile.rs b/src/test/run-pass/tempfile.rs index 2937a08b7b6cb..80e63396350b2 100644 --- a/src/test/run-pass/tempfile.rs +++ b/src/test/run-pass/tempfile.rs @@ -28,12 +28,15 @@ use std::cell::Cell; fn test_tempdir() { let path = { - let p = TempDir::new_in(&Path("."), "foobar").unwrap(); + let p = TempDir::new_in(&Path::from_str("."), "foobar").unwrap(); let p = p.path(); - assert!(p.to_str().ends_with("foobar")); + assert!(ends_with(p.as_vec(), bytes!("foobar"))); p.clone() }; assert!(!os::path_exists(&path)); + fn ends_with(v: &[u8], needle: &[u8]) -> bool { + v.len() >= needle.len() && v.slice_from(v.len()-needle.len()) == needle + } } fn test_rm_tempdir() { @@ -81,10 +84,10 @@ fn test_rm_tempdir() { // Ideally these would be in std::os but then core would need // to depend on std fn recursive_mkdir_rel() { - let path = Path("frob"); - debug2!("recursive_mkdir_rel: Making: {} in cwd {} [{:?}]", path.to_str(), - os::getcwd().to_str(), - os::path_exists(&path)); + let path = Path::from_str("frob"); + let cwd = os::getcwd(); + debug2!("recursive_mkdir_rel: Making: {} in cwd {} [{:?}]", path.display(), + cwd.display(), os::path_exists(&path)); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); assert!(os::path_is_dir(&path)); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); @@ -92,25 +95,26 @@ fn recursive_mkdir_rel() { } fn recursive_mkdir_dot() { - let dot = Path("."); + let dot = Path::from_str("."); assert!(os::mkdir_recursive(&dot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - let dotdot = Path(".."); + let dotdot = Path::from_str(".."); assert!(os::mkdir_recursive(&dotdot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); } fn recursive_mkdir_rel_2() { - let path = Path("./frob/baz"); - debug2!("recursive_mkdir_rel_2: Making: {} in cwd {} [{:?}]", path.to_str(), - os::getcwd().to_str(), os::path_exists(&path)); + let path = Path::from_str("./frob/baz"); + let cwd = os::getcwd(); + debug2!("recursive_mkdir_rel_2: Making: {} in cwd {} [{:?}]", path.display(), + cwd.display(), os::path_exists(&path)); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); assert!(os::path_is_dir(&path)); - assert!(os::path_is_dir(&path.pop())); - let path2 = Path("quux/blat"); - debug2!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.to_str(), - os::getcwd().to_str()); + assert!(os::path_is_dir(&path.dir_path())); + let path2 = Path::from_str("quux/blat"); + debug2!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(), + cwd.display()); assert!(os::mkdir_recursive(&path2, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); assert!(os::path_is_dir(&path2)); - assert!(os::path_is_dir(&path2.pop())); + assert!(os::path_is_dir(&path2.dir_path())); } // Ideally this would be in core, but needs TempFile @@ -120,17 +124,17 @@ pub fn test_rmdir_recursive_ok() { let tmpdir = TempDir::new("test").expect("test_rmdir_recursive_ok: \ couldn't create temp dir"); let tmpdir = tmpdir.path(); - let root = tmpdir.push("foo"); + let root = tmpdir.join_str("foo"); - debug2!("making {}", root.to_str()); + debug2!("making {}", root.display()); assert!(os::make_dir(&root, rwx)); - assert!(os::make_dir(&root.push("foo"), rwx)); - assert!(os::make_dir(&root.push("foo").push("bar"), rwx)); - assert!(os::make_dir(&root.push("foo").push("bar").push("blat"), rwx)); + assert!(os::make_dir(&root.join_str("foo"), rwx)); + assert!(os::make_dir(&root.join_str("foo").join_str("bar"), rwx)); + assert!(os::make_dir(&root.join_str("foo").join_str("bar").join_str("blat"), rwx)); assert!(os::remove_dir_recursive(&root)); assert!(!os::path_exists(&root)); - assert!(!os::path_exists(&root.push("bar"))); - assert!(!os::path_exists(&root.push("bar").push("blat"))); + assert!(!os::path_exists(&root.join_str("bar"))); + assert!(!os::path_exists(&root.join_str("bar").join_str("blat"))); } fn in_tmpdir(f: &fn()) { From b8af6539844a7edead3220f088248f8020793a0d Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 1 Oct 2013 23:41:59 -0700 Subject: [PATCH 20/30] path2: Update asserts for new format!() style --- src/libstd/path/mod.rs | 2 +- src/libstd/path/posix.rs | 22 ++++++++++--------- src/libstd/path/windows.rs | 43 ++++++++++++++++++++------------------ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index db6bfada8c08f..de8e4365f4a8a 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -1089,6 +1089,6 @@ mod tests { let input = "\\foo\\bar\\baz"; let path: WindowsPath = GenericPath::from_c_str(input.to_c_str()); - assert_eq!(path.as_str().unwrap(), input); + assert_eq!(path.as_str().unwrap(), input.as_slice()); } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 481387378fa6f..fb0069bbe01e1 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -1311,19 +1311,19 @@ mod tests { let path = $path; let filename = $filename; assert!(path.filename_str() == filename, - "`%s`.filename_str(): Expected `%?`, found `%?`", + "{}.filename_str(): Expected `{:?}`, found {:?}", path.as_str().unwrap(), filename, path.filename_str()); let dirname = $dirname; assert!(path.dirname_str() == dirname, - "`%s`.dirname_str(): Expected `%?`, found `%?`", + "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), dirname, path.dirname_str()); let filestem = $filestem; assert!(path.filestem_str() == filestem, - "`%s`.filestem_str(): Expected `%?`, found `%?`", + "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), filestem, path.filestem_str()); let ext = $ext; assert!(path.extension_str() == ext, - "`%s`.extension_str(): Expected `%?`, found `%?`", + "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), ext, path.extension_str()); } ); @@ -1547,11 +1547,11 @@ mod tests { let comps = path.component_iter().to_owned_vec(); let exp: &[&str] = $exp; let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); - assert!(comps == exps, "component_iter: Expected %?, found %?", + assert!(comps == exps, "component_iter: Expected {:?}, found {:?}", comps, exps); let comps = path.rev_component_iter().to_owned_vec(); let exps = exps.move_rev_iter().to_owned_vec(); - assert!(comps == exps, "rev_component_iter: Expected %?, found %?", + assert!(comps == exps, "rev_component_iter: Expected {:?}, found {:?}", comps, exps); } ); @@ -1560,11 +1560,12 @@ mod tests { let path = Path::from_vec(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = [$(b!($($exp),*)),*]; - assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, "component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); let comps = path.rev_component_iter().to_owned_vec(); let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); - assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, + "rev_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); } ) @@ -1594,12 +1595,13 @@ mod tests { let path = Path::from_vec(b!($($arg),+)); let comps = path.str_component_iter().to_owned_vec(); let exp: &[Option<&str>] = $exp; - assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, + "str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); let comps = path.rev_str_component_iter().to_owned_vec(); let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); assert!(comps.as_slice() == exp, - "rev_str_component_iter: Expected %?, found %?", + "rev_str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); } ) diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 50cbe698d0452..6dd7954de80d1 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1263,7 +1263,7 @@ mod tests { let exp = $exp; let res = parse_prefix(path); assert!(res == exp, - "parse_prefix(\"%s\"): expected %?, found %?", path, exp, res); + "parse_prefix(\"{}\"): expected {:?}, found {:?}", path, exp, res); } ) ) @@ -1809,11 +1809,11 @@ mod tests { let file = p.pop_str(); let left = $left; assert!(p.as_str() == Some(left), - "`%s`.pop() failed; expected remainder `%s`, found `%s`", + "`{}`.pop() failed; expected remainder `{}`, found `{}`", pstr, left, p.as_str().unwrap()); let right = $right; let res = file.map(|s| s.as_slice()); - assert!(res == right, "`%s`.pop() failed; expected `%?`, found `%?`", + assert!(res == right, "`{}`.pop() failed; expected `{:?}`, found `{:?}`", pstr, right, res); } ); @@ -1965,7 +1965,7 @@ mod tests { let res = path.$op(arg); let exp = $res; assert!(res.as_str() == Some(exp), - "`%s`.%s(\"%s\"): Expected `%s`, found `%s`", + "`{}`.{}(\"{}\"): Expected `{}`, found `{}`", pstr, stringify!($op), arg, exp, res.as_str().unwrap()); } ) @@ -2201,19 +2201,19 @@ mod tests { let path = $path; let filename = $filename; assert!(path.filename_str() == filename, - "`%s`.filename_str(): Expected `%?`, found `%?`", + "`{}`.filename_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), filename, path.filename_str()); let dirname = $dirname; assert!(path.dirname_str() == dirname, - "`%s`.dirname_str(): Expected `%?`, found `%?`", + "`{}`.dirname_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), dirname, path.dirname_str()); let filestem = $filestem; assert!(path.filestem_str() == filestem, - "`%s`.filestem_str(): Expected `%?`, found `%?`", + "`{}`.filestem_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), filestem, path.filestem_str()); let ext = $ext; assert!(path.extension_str() == ext, - "`%s`.extension_str(): Expected `%?`, found `%?`", + "`{}`.extension_str(): Expected `{:?}`, found `{:?}`", path.as_str().unwrap(), ext, path.extension_str()); } ); @@ -2284,16 +2284,16 @@ mod tests { let path = Path::from_str($path); let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel); let b = path.is_absolute(); - assert!(b == abs, "Path '%s'.is_absolute(): expected %?, found %?", + assert!(b == abs, "Path '{}'.is_absolute(): expected {:?}, found {:?}", path.as_str().unwrap(), abs, b); let b = path.is_vol_relative(); - assert!(b == vol, "Path '%s'.is_vol_relative(): expected %?, found %?", + assert!(b == vol, "Path '{}'.is_vol_relative(): expected {:?}, found {:?}", path.as_str().unwrap(), vol, b); let b = path.is_cwd_relative(); - assert!(b == cwd, "Path '%s'.is_cwd_relative(): expected %?, found %?", + assert!(b == cwd, "Path '{}'.is_cwd_relative(): expected {:?}, found {:?}", path.as_str().unwrap(), cwd, b); let b = path.is_relative(); - assert!(b == rel, "Path '%s'.is_relativf(): expected %?, found %?", + assert!(b == rel, "Path '{}'.is_relativf(): expected {:?}, found {:?}", path.as_str().unwrap(), rel, b); } ) @@ -2326,7 +2326,7 @@ mod tests { let exp = $exp; let res = path.is_ancestor_of(&dest); assert!(res == exp, - "`%s`.is_ancestor_of(`%s`): Expected %?, found %?", + "`{}`.is_ancestor_of(`{}`): Expected {:?}, found {:?}", path.as_str().unwrap(), dest.as_str().unwrap(), exp, res); } ) @@ -2461,7 +2461,7 @@ mod tests { let res = path.path_relative_from(&other); let exp = $exp; assert!(res.and_then_ref(|x| x.as_str()) == exp, - "`%s`.path_relative_from(`%s`): Expected %?, got %?", + "`{}`.path_relative_from(`{}`): Expected {:?}, got {:?}", path.as_str().unwrap(), other.as_str().unwrap(), exp, res.and_then_ref(|x| x.as_str())); } @@ -2593,12 +2593,13 @@ mod tests { let path = Path::from_str($path); let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; - assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, + "str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); assert!(comps.as_slice() == exp, - "rev_str_component_iter: Expected %?, found %?", + "rev_str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); } ); @@ -2607,12 +2608,13 @@ mod tests { let path = Path::from_vec(b!($($arg),+)); let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; - assert!(comps.as_slice() == exp, "str_component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, + "str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); let comps = path.rev_str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); assert!(comps.as_slice() == exp, - "rev_str_component_iter: Expected %?, found %?", + "rev_str_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); } ) @@ -2666,11 +2668,12 @@ mod tests { let path = Path::from_str($path); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = $exp; - assert!(comps.as_slice() == exp, "component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, "component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); let comps = path.rev_component_iter().to_owned_vec(); let exp = exp.rev_iter().map(|&x|x).to_owned_vec(); - assert!(comps.as_slice() == exp, "rev_component_iter: Expected %?, found %?", + assert!(comps.as_slice() == exp, + "rev_component_iter: Expected {:?}, found {:?}", comps.as_slice(), exp); } ) From e65d33e9ed3057a6afeaea9bf68519eb758f51ab Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 2 Oct 2013 20:26:28 -0700 Subject: [PATCH 21/30] path2: Update for loop -> continue --- src/libextra/glob.rs | 2 +- src/libstd/path/posix.rs | 2 +- src/libstd/path/windows.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index e398f41c467b0..51a4718394e37 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -131,7 +131,7 @@ impl Iterator for GlobIterator { None => { // FIXME (#9639): How do we handle non-utf8 filenames? Ignore them for now // Ideally we'd still match them against a * - loop; + continue; } Some(x) => x }, self.options) { diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index fb0069bbe01e1..b0b6d1ceea0db 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -253,7 +253,7 @@ impl GenericPath for Path { loop { match (ita.next(), itb.next()) { (None, _) => break, - (Some(a), Some(b)) if a == b => { loop }, + (Some(a), Some(b)) if a == b => { continue }, (Some(a), _) if a == bytes!("..") => { // if ita contains only .. components, it's an ancestor return ita.all(|x| x == bytes!("..")); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 6dd7954de80d1..620b7c375ff2f 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -548,7 +548,7 @@ impl GenericPath for Path { loop { match (ita.next(), itb.next()) { (None, _) => break, - (Some(a), Some(b)) if a == b => { loop }, + (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 == ".."); From eaec8a71322a59eb284cbd5a8d502b6da321df3c Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 2 Oct 2013 22:06:03 -0700 Subject: [PATCH 22/30] path2: Write a few paragraphs of module documentation --- src/libstd/path/mod.rs | 55 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index de8e4365f4a8a..41fe31daae9a5 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -8,7 +8,60 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Cross-platform file path handling (re-write) +/*! + +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 `::from_str()` or +`.component_iter()`. + +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, and methods +used should be restricted to those defined in `GenericPath`, and those methods +that are declared identically on both `PosixPath` and `WindowsPath`. + +Creation of a path is typically done with either `Path::from_str(some_str)` or +`Path::from_vec(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()`. + +When rendering a path to some form of display, 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 +let mut path = Path::from_str("/tmp/path"); +debug2!("path: {}", path.display()); +path.set_filename_str("foo"); +path.push_str("bar"); +debug2!("new path: {}", path.display()); +let b = std::os::path_exists(&path); +debug2!("path exists: {}", b); +``` + +*/ use container::Container; use c_str::CString; From ed539e14712539473c3e89604cb69e2307110772 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 2 Oct 2013 22:29:46 -0700 Subject: [PATCH 23/30] path2: Remove Path::normalize() There are no clients of this API, so just remove it. Update the module docstring to mention normalization. --- src/libstd/path/mod.rs | 4 ++++ src/libstd/path/posix.rs | 2 +- src/libstd/path/windows.rs | 7 ------- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 41fe31daae9a5..1ecb31a2a8797 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -43,6 +43,10 @@ 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::from_str("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 display, 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 diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index b0b6d1ceea0db..f39062e66cb33 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -349,7 +349,7 @@ impl Path { /// Returns a normalized byte vector representation of a path, by removing all empty /// components, and unnecessary . and .. components. - pub fn normalize+CopyableVector>(v: V) -> ~[u8] { + fn normalize+CopyableVector>(v: V) -> ~[u8] { // borrowck is being very picky let val = { let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep; diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 620b7c375ff2f..5dfe5b4f35a87 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -697,13 +697,6 @@ impl Path { Some(self.repr) } - /// Returns a normalized string representation of a path, by removing all empty - /// components, and unnecessary . and .. components. - pub fn normalize(s: S) -> ~str { - let (_, path) = Path::normalize_(s); - 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). From d6d9b926836b1f1c2b8b3fe4ab35dc63bec7ffcd Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 5 Oct 2013 19:49:32 -0700 Subject: [PATCH 24/30] path2: Adjust the API to remove all the _str mutation methods Add a new trait BytesContainer that is implemented for both byte vectors and strings. Convert Path::from_vec and ::from_str to one function, Path::new(). Remove all the _str-suffixed mutation methods (push, join, with_*, set_*) and modify the non-suffixed versions to use BytesContainer. --- src/compiletest/compiletest.rs | 12 +- src/compiletest/header.rs | 2 +- src/compiletest/runtest.rs | 32 +- src/libextra/fileinput.rs | 16 +- src/libextra/glob.rs | 8 +- src/libextra/tempfile.rs | 2 +- src/libextra/terminfo/searcher.rs | 16 +- src/libextra/test.rs | 8 +- src/libextra/workcache.rs | 2 +- src/librustc/back/link.rs | 8 +- src/librustc/back/rpath.rs | 22 +- src/librustc/driver/driver.rs | 18 +- src/librustc/metadata/creader.rs | 4 +- src/librustc/metadata/filesearch.rs | 22 +- src/librustc/rustc.rs | 6 +- src/librustdoc/html/render.rs | 30 +- src/librustdoc/plugins.rs | 2 +- src/librustdoc/rustdoc.rs | 14 +- src/librusti/rusti.rs | 6 +- src/librustpkg/api.rs | 10 +- src/librustpkg/context.rs | 2 +- src/librustpkg/installed_packages.rs | 8 +- src/librustpkg/package_id.rs | 6 +- src/librustpkg/package_source.rs | 17 +- src/librustpkg/path_util.rs | 20 +- src/librustpkg/rustpkg.rs | 18 +- src/librustpkg/source_control.rs | 8 +- src/librustpkg/target.rs | 2 +- src/librustpkg/tests.rs | 374 ++++---- src/librustpkg/util.rs | 8 +- src/librustpkg/version.rs | 4 +- src/librustpkg/workspace.rs | 4 +- src/libstd/io.rs | 24 +- src/libstd/os.rs | 62 +- src/libstd/path/mod.rs | 469 +++++----- src/libstd/path/posix.rs | 618 +++++++------ src/libstd/path/windows.rs | 848 ++++++++---------- src/libstd/rt/io/file.rs | 28 +- src/libstd/rt/io/support.rs | 2 +- src/libstd/rt/uv/file.rs | 12 +- src/libstd/rt/uv/uvio.rs | 13 +- src/libstd/run.rs | 4 +- src/libstd/unstable/dynamic_lib.rs | 2 +- src/libsyntax/ext/source_util.rs | 8 +- src/libsyntax/parse/parser.rs | 10 +- src/test/bench/core-std.rs | 4 +- src/test/bench/shootout-fasta.rs | 2 +- src/test/bench/shootout-k-nucleotide-pipes.rs | 4 +- src/test/run-pass/glob-std.rs | 6 +- src/test/run-pass/issue-3424.rs | 2 +- src/test/run-pass/rename-directory.rs | 12 +- src/test/run-pass/stat.rs | 4 +- src/test/run-pass/tempfile.rs | 24 +- 53 files changed, 1384 insertions(+), 1485 deletions(-) diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index cc99f0d67d234..362d2ba749d1c 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -102,15 +102,15 @@ pub fn parse_config(args: ~[~str]) -> config { } fn opt_path(m: &getopts::Matches, nm: &str) -> Path { - Path::from_str(m.opt_str(nm).unwrap()) + Path::new(m.opt_str(nm).unwrap()) } config { compile_lib_path: matches.opt_str("compile-lib-path").unwrap(), run_lib_path: matches.opt_str("run-lib-path").unwrap(), rustc_path: opt_path(matches, "rustc-path"), - clang_path: matches.opt_str("clang-path").map(|s| Path::from_str(s)), - llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::from_str(s)), + clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)), + llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)), src_base: opt_path(matches, "src-base"), build_base: opt_path(matches, "build-base"), aux_base: opt_path(matches, "aux-base"), @@ -123,10 +123,10 @@ pub fn parse_config(args: ~[~str]) -> config { } else { None }, - logfile: matches.opt_str("logfile").map(|s| Path::from_str(s)), - save_metrics: matches.opt_str("save-metrics").map(|s| Path::from_str(s)), + logfile: matches.opt_str("logfile").map(|s| Path::new(s)), + save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)), ratchet_metrics: - matches.opt_str("ratchet-metrics").map(|s| Path::from_str(s)), + matches.opt_str("ratchet-metrics").map(|s| Path::new(s)), ratchet_noise_percent: matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::(s)), runtool: matches.opt_str("runtool"), diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index bf68371c7d2ef..9cd3d3683cded 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -161,7 +161,7 @@ fn parse_exec_env(line: &str) -> Option<(~str, ~str)> { fn parse_pp_exact(line: &str, testfile: &Path) -> Option { match parse_name_value_directive(line, ~"pp-exact") { - Some(s) => Some(Path::from_str(s)), + Some(s) => Some(Path::new(s)), None => { if parse_name_directive(line, "pp-exact") { testfile.file_path() diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index e1c4c34a5f496..b5b81f7b8102c 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -62,7 +62,7 @@ pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) { // We're going to be dumping a lot of info. Start on a new line. io::stdout().write_str("\n\n"); } - let testfile = Path::from_str(testfile); + let testfile = Path::new(testfile); debug2!("running {}", testfile.display()); let props = load_props(&testfile); debug2!("loaded props"); @@ -594,7 +594,7 @@ fn compose_and_run_compiler( let extra_link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()]; for rel_ab in props.aux_builds.iter() { - let abs_ab = config.aux_base.join_str(*rel_ab); + let abs_ab = config.aux_base.join(rel_ab.as_slice()); let aux_args = make_compile_args(config, props, ~[~"--lib"] + extra_link_args, |a,b| make_lib_name(a, b, testfile), &abs_ab); @@ -662,7 +662,7 @@ fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { fn make_exe_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - f.add_extension_str(os::EXE_EXTENSION); + f.add_extension(os::EXE_EXTENSION); f } @@ -742,23 +742,23 @@ fn dump_output_file(config: &config, testfile: &Path, } fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path { - output_base_name(config, testfile).with_extension_str(extension) + output_base_name(config, testfile).with_extension(extension) } fn aux_output_dir_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - f.add_extension_str("libaux"); + f.add_extension("libaux"); f } fn output_testname(testfile: &Path) -> Path { - Path::from_vec(testfile.filestem().unwrap()) + Path::new(testfile.filestem().unwrap()) } fn output_base_name(config: &config, testfile: &Path) -> Path { config.build_base .join_path(&output_testname(testfile)) - .with_extension_str(config.stage_id) + .with_extension(config.stage_id.as_slice()) } fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) { @@ -916,7 +916,7 @@ fn _arm_push_aux_shared_library(config: &config, testfile: &Path) { // codegen tests (vs. clang) fn make_o_name(config: &config, testfile: &Path) -> Path { - output_base_name(config, testfile).with_extension_str("o") + output_base_name(config, testfile).with_extension("o") } fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path { @@ -942,9 +942,9 @@ fn compile_test_and_save_bitcode(config: &config, props: &TestProps, fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, testfile: &Path) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang"); - let testcc = testfile.with_extension_str("cc"); + let testcc = testfile.with_extension("cc"); let ProcArgs = ProcArgs { // FIXME (#9639): This needs to handle non-utf8 paths prog: config.clang_path.get_ref().as_str().unwrap().to_owned(), @@ -959,10 +959,10 @@ fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps, fn extract_function_from_bitcode(config: &config, _props: &TestProps, fname: &str, testfile: &Path, suffix: &str) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix); let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract"); - let prog = config.llvm_bin_path.get_ref().join_str("llvm-extract"); + let prog = config.llvm_bin_path.get_ref().join("llvm-extract"); let ProcArgs = ProcArgs { // FIXME (#9639): This needs to handle non-utf8 paths prog: prog.as_str().unwrap().to_owned(), @@ -975,11 +975,11 @@ fn extract_function_from_bitcode(config: &config, _props: &TestProps, fn disassemble_extract(config: &config, _props: &TestProps, testfile: &Path, suffix: &str) -> ProcRes { - let bitcodefile = output_base_name(config, testfile).with_extension_str("bc"); + let bitcodefile = output_base_name(config, testfile).with_extension("bc"); let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix); let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract"); - let extracted_ll = extracted_bc.with_extension_str("ll"); - let prog = config.llvm_bin_path.get_ref().join_str("llvm-dis"); + let extracted_ll = extracted_bc.with_extension("ll"); + let prog = config.llvm_bin_path.get_ref().join("llvm-dis"); let ProcArgs = ProcArgs { // FIXME (#9639): This needs to handle non-utf8 paths prog: prog.as_str().unwrap().to_owned(), @@ -991,7 +991,7 @@ fn disassemble_extract(config: &config, _props: &TestProps, fn count_extracted_lines(p: &Path) -> uint { - let x = io::read_whole_file_str(&p.with_extension_str("ll")).unwrap(); + let x = io::read_whole_file_str(&p.with_extension("ll")).unwrap(); x.line_iter().len() } diff --git a/src/libextra/fileinput.rs b/src/libextra/fileinput.rs index 08d1222ff4666..8f176d5ccea13 100644 --- a/src/libextra/fileinput.rs +++ b/src/libextra/fileinput.rs @@ -358,11 +358,11 @@ instance. `stdin_hyphen` controls whether `-` represents `stdin` or a literal `-`. */ pub fn make_path_option_vec(vec: &[~str], stdin_hyphen : bool) -> ~[Option] { - vec.iter().map(|str| { - if stdin_hyphen && "-" == *str { + vec.iter().map(|s| { + if stdin_hyphen && "-" == *s { None } else { - Some(Path::from_str(*str)) + Some(Path::new(s.as_slice())) } }).collect() } @@ -435,14 +435,14 @@ mod test { fn test_make_path_option_vec() { let strs = [~"some/path", ~"some/other/path"]; - let paths = ~[Some(Path::from_str("some/path")), - Some(Path::from_str("some/other/path"))]; + let paths = ~[Some(Path::new("some/path")), + Some(Path::new("some/other/path"))]; assert_eq!(make_path_option_vec(strs, true), paths.clone()); assert_eq!(make_path_option_vec(strs, false), paths); assert_eq!(make_path_option_vec([~"-"], true), ~[None]); - assert_eq!(make_path_option_vec([~"-"], false), ~[Some(Path::from_str("-"))]); + assert_eq!(make_path_option_vec([~"-"], false), ~[Some(Path::new("-"))]); } #[test] @@ -567,9 +567,9 @@ mod test { #[test] fn test_no_trailing_newline() { let f1 = - Some(Path::from_str("tmp/lib-fileinput-test-no-trailing-newline-1.tmp")); + Some(Path::new("tmp/lib-fileinput-test-no-trailing-newline-1.tmp")); let f2 = - Some(Path::from_str("tmp/lib-fileinput-test-no-trailing-newline-2.tmp")); + Some(Path::new("tmp/lib-fileinput-test-no-trailing-newline-2.tmp")); { let mut wr = file::open(f1.get_ref(), io::CreateOrTruncate, diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index 51a4718394e37..1a6c8e08e3b84 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -91,7 +91,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> GlobIterator { // calculate root this way to handle volume-relative Windows paths correctly let mut root = os::getcwd(); - let pat_root = Path::from_str(pattern).root_path(); + let pat_root = Path::new(pattern).root_path(); if pat_root.is_some() { if check_windows_verbatim(pat_root.get_ref()) { // XXX: How do we want to handle verbatim paths? I'm inclined to return nothing, @@ -548,7 +548,7 @@ mod test { assert!(glob("//").next().is_none()); // check windows absolute paths with host/device components - let root_with_device = os::getcwd().root_path().unwrap().join_str("*"); + let root_with_device = os::getcwd().root_path().unwrap().join("*"); // FIXME (#9639): This needs to handle non-utf8 paths assert!(glob(root_with_device.as_str().unwrap()).next().is_some()); } @@ -772,9 +772,9 @@ mod test { #[test] fn test_matches_path() { - // on windows, (Path::from_str("a/b").as_str().unwrap() == "a\\b"), so this + // on windows, (Path::new("a/b").as_str().unwrap() == "a\\b"), so this // tests that / and \ are considered equivalent on windows - assert!(Pattern::new("a/b").matches_path(&Path::from_str("a/b"))); + assert!(Pattern::new("a/b").matches_path(&Path::new("a/b"))); } } diff --git a/src/libextra/tempfile.rs b/src/libextra/tempfile.rs index 1226318878767..d8fa130916a46 100644 --- a/src/libextra/tempfile.rs +++ b/src/libextra/tempfile.rs @@ -35,7 +35,7 @@ impl TempDir { let mut r = rand::rng(); for _ in range(0u, 1000) { - let p = tmpdir.join_str(r.gen_ascii_str(16) + suffix); + let p = tmpdir.join(r.gen_ascii_str(16) + suffix); if os::make_dir(&p, 0x1c0) { // 700 return Some(TempDir { path: Some(p) }); } diff --git a/src/libextra/terminfo/searcher.rs b/src/libextra/terminfo/searcher.rs index cd4e487d70fe4..ea7d20e096df4 100644 --- a/src/libextra/terminfo/searcher.rs +++ b/src/libextra/terminfo/searcher.rs @@ -28,26 +28,26 @@ pub fn get_dbpath_for_term(term: &str) -> Option<~Path> { // Find search directory match getenv("TERMINFO") { - Some(dir) => dirs_to_search.push(Path::from_str(dir)), + Some(dir) => dirs_to_search.push(Path::new(dir)), None => { if homedir.is_some() { // ncurses compatability; - dirs_to_search.push(homedir.unwrap().join_str(".terminfo")) + dirs_to_search.push(homedir.unwrap().join(".terminfo")) } match getenv("TERMINFO_DIRS") { Some(dirs) => for i in dirs.split_iter(':') { if i == "" { - dirs_to_search.push(Path::from_str("/usr/share/terminfo")); + dirs_to_search.push(Path::new("/usr/share/terminfo")); } else { - dirs_to_search.push(Path::from_str(i.to_owned())); + dirs_to_search.push(Path::new(i.to_owned())); } }, // Found nothing, use the default paths // /usr/share/terminfo is the de facto location, but it seems // Ubuntu puts it in /lib/terminfo None => { - dirs_to_search.push(Path::from_str("/usr/share/terminfo")); - dirs_to_search.push(Path::from_str("/lib/terminfo")); + dirs_to_search.push(Path::new("/usr/share/terminfo")); + dirs_to_search.push(Path::new("/lib/terminfo")); } } } @@ -57,13 +57,13 @@ pub fn get_dbpath_for_term(term: &str) -> Option<~Path> { for p in dirs_to_search.iter() { if os::path_exists(p) { let f = str::from_char(first_char); - let newp = p.join_many_str([f.as_slice(), term]); + let newp = p.join_many([f.as_slice(), term]); if os::path_exists(&newp) { return Some(~newp); } // on some installations the dir is named after the hex of the char (e.g. OS X) let f = format!("{:x}", first_char as uint); - let newp = p.join_many_str([f.as_slice(), term]); + let newp = p.join_many([f.as_slice(), term]); if os::path_exists(&newp) { return Some(~newp); } diff --git a/src/libextra/test.rs b/src/libextra/test.rs index 20690283dd5a1..21fa9ed757442 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -271,20 +271,20 @@ pub fn parse_opts(args: &[~str]) -> Option { let run_ignored = matches.opt_present("ignored"); let logfile = matches.opt_str("logfile"); - let logfile = logfile.map(|s| Path::from_str(s)); + let logfile = logfile.map(|s| Path::new(s)); let run_benchmarks = matches.opt_present("bench"); let run_tests = ! run_benchmarks || matches.opt_present("test"); let ratchet_metrics = matches.opt_str("ratchet-metrics"); - let ratchet_metrics = ratchet_metrics.map(|s| Path::from_str(s)); + let ratchet_metrics = ratchet_metrics.map(|s| Path::new(s)); let ratchet_noise_percent = matches.opt_str("ratchet-noise-percent"); let ratchet_noise_percent = ratchet_noise_percent.map(|s| from_str::(s).unwrap()); let save_metrics = matches.opt_str("save-metrics"); - let save_metrics = save_metrics.map(|s| Path::from_str(s)); + let save_metrics = save_metrics.map(|s| Path::new(s)); let test_shard = matches.opt_str("test-shard"); let test_shard = opt_shard(test_shard); @@ -1440,7 +1440,7 @@ mod tests { pub fn ratchet_test() { let dpth = TempDir::new("test-ratchet").expect("missing test for ratchet"); - let pth = dpth.path().join_str("ratchet.json"); + let pth = dpth.path().join("ratchet.json"); let mut m1 = MetricMap::new(); m1.insert_metric("runtime", 1000.0, 2.0); diff --git a/src/libextra/workcache.rs b/src/libextra/workcache.rs index ea943cdb01b97..26309cf3b37ed 100644 --- a/src/libextra/workcache.rs +++ b/src/libextra/workcache.rs @@ -498,7 +498,7 @@ fn test() { // Create a path to a new file 'filename' in the directory in which // this test is running. fn make_path(filename: ~str) -> Path { - let pth = os::self_exe_path().expect("workcache::test failed").with_filename_str(filename); + let pth = os::self_exe_path().expect("workcache::test failed").with_filename(filename); if os::path_exists(&pth) { os::remove_file(&pth); } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index d5cd78f54f716..404efa25ff332 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -249,7 +249,7 @@ pub mod write { llvm::LLVMInitializeMipsAsmParser(); if sess.opts.save_temps { - do output.with_extension_str("no-opt.bc").with_c_str |buf| { + do output.with_extension("no-opt.bc").with_c_str |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } @@ -317,7 +317,7 @@ pub mod write { llvm::LLVMDisposePassManager(mpm); if sess.opts.save_temps { - do output.with_extension_str("bc").with_c_str |buf| { + do output.with_extension("bc").with_c_str |buf| { llvm::LLVMWriteBitcodeToFile(llmod, buf); } } @@ -921,7 +921,7 @@ pub fn link_binary(sess: Session, let out_dirname = out_filename.dir_path(); debug2!("dirname(out_filename): {}", out_dirname.display()); - out_filename.with_filename_str(long_libname) + out_filename.with_filename(long_libname) } else { out_filename.clone() }; @@ -977,7 +977,7 @@ pub fn link_args(sess: Session, let output = if *sess.building_library { let long_libname = output_dll_filename(sess.targ_cfg.os, lm); - out_filename.with_filename_str(long_libname) + out_filename.with_filename(long_libname) } else { out_filename.clone() }; diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index b3fb6be686da7..b7ae9c1ecb58e 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -46,7 +46,7 @@ pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { let r = filesearch::relative_target_lib_path(sess.opts.target_triple); let mut p = sess.filesearch.sysroot().join_path(&r); - p.push_str(os::dll_filename("rustrt")); + p.push(os::dll_filename("rustrt")); p } @@ -147,7 +147,7 @@ pub fn get_install_prefix_rpath(target_triple: &str) -> ~str { let install_prefix = env!("CFG_PREFIX"); let tlib = filesearch::relative_target_lib_path(target_triple); - let mut path = Path::from_str(install_prefix); + let mut path = Path::new(install_prefix); path.push_path(&tlib); let path = os::make_absolute(&path); // FIXME (#9639): This needs to handle non-utf8 paths @@ -186,8 +186,8 @@ mod test { #[test] fn test_prefix_rpath() { let res = get_install_prefix_rpath("triple"); - let mut d = Path::from_str(env!("CFG_PREFIX")); - d.push_str("lib/rustc/triple/lib"); + let mut d = Path::new(env!("CFG_PREFIX")); + d.push("lib/rustc/triple/lib"); debug2!("test_prefix_path: {} vs. {}", res.to_str(), d.display()); @@ -200,7 +200,7 @@ mod test { #[test] fn test_prefix_rpath_abs() { let res = get_install_prefix_rpath("triple"); - assert!(Path::from_str(res).is_absolute()); + assert!(Path::new(res).is_absolute()); } #[test] @@ -224,7 +224,7 @@ mod test { fn test_rpath_relative() { let o = session::OsLinux; let res = get_rpath_relative_to_output(o, - &Path::from_str("bin/rustc"), &Path::from_str("lib/libstd.so")); + &Path::new("bin/rustc"), &Path::new("lib/libstd.so")); assert_eq!(res.as_slice(), "$ORIGIN/../lib"); } @@ -233,7 +233,7 @@ mod test { fn test_rpath_relative() { let o = session::OsFreebsd; let res = get_rpath_relative_to_output(o, - &Path::from_str("bin/rustc"), &Path::from_str("lib/libstd.so")); + &Path::new("bin/rustc"), &Path::new("lib/libstd.so")); assert_eq!(res.as_slice(), "$ORIGIN/../lib"); } @@ -242,15 +242,15 @@ mod test { fn test_rpath_relative() { let o = session::OsMacos; let res = get_rpath_relative_to_output(o, - &Path::from_str("bin/rustc"), - &Path::from_str("lib/libstd.so")); + &Path::new("bin/rustc"), + &Path::new("lib/libstd.so")); assert_eq!(res.as_slice(), "@executable_path/../lib"); } #[test] fn test_get_absolute_rpath() { - let res = get_absolute_rpath(&Path::from_str("lib/libstd.so")); - let lib = os::make_absolute(&Path::from_str("lib")); + let res = get_absolute_rpath(&Path::new("lib/libstd.so")); + let lib = os::make_absolute(&Path::new("lib")); debug2!("test_get_absolute_rpath: {} vs. {}", res.to_str(), lib.display()); diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index fc5ff163d7498..f85b0dbcdc3e3 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -353,7 +353,7 @@ pub fn phase_5_run_llvm_passes(sess: Session, (sess.opts.output_type == link::output_type_object || sess.opts.output_type == link::output_type_exe) { let output_type = link::output_type_assembly; - let asm_filename = outputs.obj_filename.with_extension_str("s"); + let asm_filename = outputs.obj_filename.with_extension("s"); time(sess.time_passes(), "LLVM passes", (), |_| link::write::run_passes(sess, @@ -722,7 +722,7 @@ pub fn build_session_options(binary: @str, } else if matches.opt_present("emit-llvm") { link::output_type_bitcode } else { link::output_type_exe }; - let sysroot_opt = matches.opt_str("sysroot").map(|m| @Path::from_str(m)); + let sysroot_opt = matches.opt_str("sysroot").map(|m| @Path::new(m)); let target = matches.opt_str("target").unwrap_or(host_triple()); let target_cpu = matches.opt_str("target-cpu").unwrap_or(~"generic"); let target_feature = matches.opt_str("target-feature").unwrap_or(~""); @@ -755,7 +755,7 @@ pub fn build_session_options(binary: @str, let statik = debugging_opts & session::statik != 0; - let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path::from_str(*s)); + let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path::new(s.as_slice())); let linker = matches.opt_str("linker"); let linker_args = matches.opt_strs("link-args").flat_map( |a| { a.split_iter(' ').map(|arg| arg.to_owned()).collect() @@ -1005,15 +1005,15 @@ pub fn build_output_filenames(input: &input, } if *sess.building_library { - out_path = dirpath.join_str(os::dll_filename(stem)); + out_path = dirpath.join(os::dll_filename(stem)); obj_path = { - let mut p = dirpath.join_str(stem); - p.set_extension_str(obj_suffix); + let mut p = dirpath.join(stem); + p.set_extension(obj_suffix); p }; } else { - out_path = dirpath.join_str(stem); - obj_path = out_path.with_extension_str(obj_suffix); + out_path = dirpath.join(stem); + obj_path = out_path.with_extension(obj_suffix); } } @@ -1022,7 +1022,7 @@ pub fn build_output_filenames(input: &input, obj_path = if stop_after_codegen { out_file.clone() } else { - out_file.with_extension_str(obj_suffix) + out_file.with_extension(obj_suffix) }; if *sess.building_library { diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 8ece290293b39..9700f68383ad7 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -143,7 +143,7 @@ fn visit_view_item(e: @mut Env, i: &ast::view_item) { let meta_items = match path_opt { None => meta_items.clone(), Some((p, _path_str_style)) => { - let p_path = Path::from_str(p); + let p_path = Path::new(p); match p_path.filestem_str() { None|Some("") => e.diag.span_bug(i.span, "Bad package path in `extern mod` item"), @@ -275,7 +275,7 @@ fn resolve_crate(e: @mut Env, }; let (lident, ldata) = loader::load_library_crate(&load_ctxt); - let cfilename = Path::from_str(lident); + let cfilename = Path::new(lident); let cdata = ldata; let attrs = decoder::get_crate_attributes(cdata); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 4e3daa7c185da..6335df47d7388 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -138,11 +138,11 @@ pub fn search(filesearch: @FileSearch, pick: pick) { pub fn relative_target_lib_path(target_triple: &str) -> Path { let dir = libdir(); - let mut p = Path::from_str(dir); + let mut p = Path::new(dir.as_slice()); assert!(p.is_relative()); - p.push_str("rustc"); - p.push_str(target_triple); - p.push_str(dir); + p.push("rustc"); + p.push(target_triple); + p.push(dir); p } @@ -153,8 +153,8 @@ fn make_target_lib_path(sysroot: &Path, fn make_rustpkg_target_lib_path(dir: &Path, target_triple: &str) -> Path { - let mut p = dir.join_str(libdir()); - p.push_str(target_triple); + let mut p = dir.join(libdir()); + p.push(target_triple); p } @@ -192,13 +192,13 @@ pub fn rust_path() -> ~[Path] { Some(env_path) => { let env_path_components: ~[&str] = env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect(); - env_path_components.map(|&s| Path::from_str(s)) + env_path_components.map(|&s| Path::new(s)) } None => ~[] }; let cwd = os::getcwd(); // now add in default entries - let cwd_dot_rust = cwd.join_str(".rust"); + let cwd_dot_rust = cwd.join(".rust"); if !env_rust_path.contains(&cwd_dot_rust) { env_rust_path.push(cwd_dot_rust); } @@ -206,14 +206,14 @@ pub fn rust_path() -> ~[Path] { env_rust_path.push(cwd.clone()); } do cwd.each_parent() |p| { - if !env_rust_path.contains(&p.join_str(".rust")) { + if !env_rust_path.contains(&p.join(".rust")) { push_if_exists(&mut env_rust_path, p); } true }; let h = os::homedir(); for h in h.iter() { - if !env_rust_path.contains(&h.join_str(".rust")) { + if !env_rust_path.contains(&h.join(".rust")) { push_if_exists(&mut env_rust_path, h); } } @@ -223,7 +223,7 @@ pub fn rust_path() -> ~[Path] { /// Adds p/.rust into vec, only if it exists fn push_if_exists(vec: &mut ~[Path], p: &Path) { - let maybe_dir = p.join_str(".rust"); + let maybe_dir = p.join(".rust"); if os::path_exists(&maybe_dir) { vec.push(maybe_dir); } diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index 460ac45cdba06..1d9f37a2e877e 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -250,7 +250,7 @@ pub fn run_compiler(args: &[~str], demitter: @diagnostic::Emitter) { let src = str::from_utf8(io::stdin().read_whole_stream()); str_input(src.to_managed()) } else { - file_input(Path::from_str(ifile)) + file_input(Path::new(ifile)) } } _ => early_error(demitter, "multiple input filenames provided") @@ -258,8 +258,8 @@ pub fn run_compiler(args: &[~str], demitter: @diagnostic::Emitter) { let sopts = build_session_options(binary, matches, demitter); let sess = build_session(sopts, demitter); - let odir = matches.opt_str("out-dir").map(|o| Path::from_str(o)); - let ofile = matches.opt_str("o").map(|o| Path::from_str(o)); + let odir = matches.opt_str("out-dir").map(|o| Path::new(o)); + let ofile = matches.opt_str("o").map(|o| Path::new(o)); let cfg = build_configuration(sess); let pretty = do matches.opt_default("pretty", "normal").map |a| { parse_pretty(sess, a) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 9347a1775d48b..7ed424136be22 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -255,16 +255,16 @@ pub fn run(mut crate: clean::Crate, dst: Path) { crate = cache.fold_crate(crate); // Add all the static files - let mut dst = cx.dst.join_str(crate.name); + let mut dst = cx.dst.join(crate.name.as_slice()); mkdir(&dst); - write(dst.join_str("jquery.js"), include_str!("static/jquery-2.0.3.min.js")); - write(dst.join_str("main.js"), include_str!("static/main.js")); - write(dst.join_str("main.css"), include_str!("static/main.css")); - write(dst.join_str("normalize.css"), include_str!("static/normalize.css")); + write(dst.join("jquery.js"), include_str!("static/jquery-2.0.3.min.js")); + write(dst.join("main.js"), include_str!("static/main.js")); + write(dst.join("main.css"), include_str!("static/main.css")); + write(dst.join("normalize.css"), include_str!("static/normalize.css")); // Publish the search index { - dst.push_str("search-index.js"); + dst.push("search-index.js"); let mut w = BufferedWriter::new(dst.open_writer(io::CreateOrTruncate)); let w = &mut w as &mut io::Writer; write!(w, "var searchIndex = ["); @@ -292,9 +292,9 @@ pub fn run(mut crate: clean::Crate, dst: Path) { // Render all source files (this may turn into a giant no-op) { info2!("emitting source files"); - let dst = cx.dst.join_str("src"); + let dst = cx.dst.join("src"); mkdir(&dst); - let dst = dst.join_str(crate.name); + let dst = dst.join(crate.name.as_slice()); mkdir(&dst); let mut folder = SourceCollector { dst: dst, @@ -338,7 +338,7 @@ fn mkdir(path: &Path) { /// static HTML tree. // FIXME (#9639): The closure should deal with &[u8] instead of &str fn clean_srcpath(src: &[u8], f: &fn(&str)) { - let p = Path::from_vec(src); + let p = Path::new(src); if p.as_vec() != bytes!(".") { for c in p.str_component_iter().map(|x|x.unwrap()) { if ".." == c { @@ -354,7 +354,7 @@ fn clean_srcpath(src: &[u8], f: &fn(&str)) { /// rendering in to the specified source destination. fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { // See if there's documentation generated into the local directory - let local_location = dst.join_str(e.name); + let local_location = dst.join(e.name.as_slice()); if local_location.is_dir() { return Local; } @@ -413,7 +413,7 @@ impl<'self> DocFolder for SourceCollector<'self> { impl<'self> SourceCollector<'self> { /// Renders the given filename into its corresponding HTML source file. fn emit_source(&mut self, filename: &str) -> bool { - let p = Path::from_str(filename); + let p = Path::new(filename); // Read the contents of the file let mut contents = ~[]; @@ -445,7 +445,7 @@ impl<'self> SourceCollector<'self> { let mut cur = self.dst.clone(); let mut root_path = ~"../../"; do clean_srcpath(p.dirname()) |component| { - cur.push_str(component); + cur.push(component); mkdir(&cur); root_path.push_str("../"); } @@ -661,7 +661,7 @@ impl Context { fail2!("what {:?}", self); } let prev = self.dst.clone(); - self.dst.push_str(s); + self.dst.push(s.as_slice()); self.root_path.push_str("../"); self.current.push(s); @@ -808,7 +808,7 @@ impl Context { let item = Cell::new(item); do self.recurse(name) |this| { let item = item.take(); - let dst = this.dst.join_str("index.html"); + let dst = this.dst.join("index.html"); let writer = dst.open_writer(io::CreateOrTruncate); render(writer.unwrap(), this, &item, false); @@ -826,7 +826,7 @@ impl Context { // Things which don't have names (like impls) don't get special // pages dedicated to them. _ if item.name.is_some() => { - let dst = self.dst.join_str(item_path(&item)); + let dst = self.dst.join(item_path(&item)); let writer = dst.open_writer(io::CreateOrTruncate); render(writer.unwrap(), self, &item, true); } diff --git a/src/librustdoc/plugins.rs b/src/librustdoc/plugins.rs index 12128402f74f0..4efc68a265512 100644 --- a/src/librustdoc/plugins.rs +++ b/src/librustdoc/plugins.rs @@ -41,7 +41,7 @@ impl PluginManager { /// platform. On windows, it turns into name.dll, on OS X, name.dylib, and /// elsewhere, libname.so. pub fn load_plugin(&mut self, name: ~str) { - let x = self.prefix.join_str(libname(name)); + let x = self.prefix.join(libname(name)); let lib_result = dl::DynamicLibrary::open(Some(&x)); let lib = lib_result.unwrap(); let plugin = unsafe { lib.symbol("rustdoc_plugin_entrypoint") }.unwrap(); diff --git a/src/librustdoc/rustdoc.rs b/src/librustdoc/rustdoc.rs index 470ae7f9dfe00..6684410b58778 100644 --- a/src/librustdoc/rustdoc.rs +++ b/src/librustdoc/rustdoc.rs @@ -134,13 +134,13 @@ pub fn main_args(args: &[~str]) -> int { info2!("going to format"); let started = time::precise_time_ns(); - let output = matches.opt_str("o").map(|s| Path::from_str(s)); + let output = matches.opt_str("o").map(|s| Path::new(s)); match matches.opt_str("w") { Some(~"html") | None => { - html::render::run(crate, output.unwrap_or(Path::from_str("doc"))) + html::render::run(crate, output.unwrap_or(Path::new("doc"))) } Some(~"json") => { - json_output(crate, res, output.unwrap_or(Path::from_str("doc.json"))) + json_output(crate, res, output.unwrap_or(Path::new("doc.json"))) } Some(s) => { println!("unknown output format: {}", s); @@ -188,8 +188,8 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { let mut plugins = matches.opt_strs("plugins"); // First, parse the crate and extract all relevant information. - let libs = Cell::new(matches.opt_strs("L").map(|s| Path::from_str(*s))); - let cr = Cell::new(Path::from_str(cratefile)); + let libs = Cell::new(matches.opt_strs("L").map(|s| Path::new(s.as_slice()))); + let cr = Cell::new(Path::new(cratefile)); info2!("starting to run rustc"); let crate = do std::task::try { let cr = cr.take(); @@ -230,7 +230,7 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { // Load all plugins/passes into a PluginManager let path = matches.opt_str("plugin-path").unwrap_or(~"/tmp/rustdoc_ng/plugins"); - let mut pm = plugins::PluginManager::new(Path::from_str(path)); + let mut pm = plugins::PluginManager::new(Path::new(path)); for pass in passes.iter() { let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) { Some(i) => PASSES[i].n1(), @@ -254,7 +254,7 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { /// This input format purely deserializes the json output file. No passes are /// run over the deserialized output. fn json_input(input: &str) -> Result { - let input = match ::std::io::file_reader(&Path::from_str(input)) { + let input = match ::std::io::file_reader(&Path::new(input)) { Ok(i) => i, Err(s) => return Err(s), }; diff --git a/src/librusti/rusti.rs b/src/librusti/rusti.rs index b511d17ec7778..c025d9b10dd04 100644 --- a/src/librusti/rusti.rs +++ b/src/librusti/rusti.rs @@ -142,7 +142,7 @@ fn run(mut program: ~Program, binary: ~str, lib_search_paths: ~[~str], let options = @session::options { crate_type: session::unknown_crate, binary: binary, - addl_lib_search_paths: @mut lib_search_paths.map(|p| Path::from_str(*p)), + addl_lib_search_paths: @mut lib_search_paths.map(|p| Path::new(p.as_slice())), jit: true, .. (*session::basic_options()).clone() }; @@ -328,7 +328,7 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option { } } match do task::try { - let src_path = Path::from_str(src_filename); + let src_path = Path::new(src_filename.as_slice()); let binary = binary.to_managed(); let options = @session::options { binary: binary, @@ -441,7 +441,7 @@ fn run_cmd(repl: &mut Repl, _in: @io::Reader, _out: @io::Writer, } } for crate in loaded_crates.iter() { - let crate_path = Path::from_str(*crate); + let crate_path = Path::new(crate.as_slice()); // FIXME (#9639): This needs to handle non-utf8 paths let crate_dir = crate_path.dirname_str().unwrap(); repl.program.record_extern(format!("extern mod {};", *crate)); diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 6e0908d2a4f65..02a9640222909 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -43,17 +43,17 @@ pub fn new_default_context(c: workcache::Context, p: Path) -> BuildContext { } fn file_is_fresh(path: &str, in_hash: &str) -> bool { - let path = Path::from_str(path); + let path = Path::new(path); os::path_exists(&path) && in_hash == digest_file_with_date(&path) } fn binary_is_fresh(path: &str, in_hash: &str) -> bool { - let path = Path::from_str(path); + let path = Path::new(path); os::path_exists(&path) && in_hash == digest_only_date(&path) } pub fn new_workcache_context(p: &Path) -> workcache::Context { - let db_file = p.join_str("rustpkg_db.json"); // ??? probably wrong + let db_file = p.join("rustpkg_db.json"); // ??? probably wrong debug2!("Workcache database file: {}", db_file.display()); let db = RWArc::new(Database::new(db_file)); let lg = RWArc::new(Logger::new()); @@ -73,7 +73,7 @@ pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version, source_workspace: root.clone(), build_in_destination: false, destination_workspace: root.clone(), - start_dir: root.join_many_str(["src", name.as_slice()]), + start_dir: root.join_many(["src", name.as_slice()]), id: PkgId{ version: version, ..PkgId::new(name)}, // n.b. This assumes the package only has one crate libs: ~[mk_crate(lib)], @@ -91,7 +91,7 @@ pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, source_workspace: root.clone(), build_in_destination: false, destination_workspace: root.clone(), - start_dir: root.join_many_str(["src", name.as_slice()]), + start_dir: root.join_many(["src", name.as_slice()]), id: PkgId{ version: version, ..PkgId::new(name)}, libs: ~[], // n.b. This assumes the package only has one crate diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs index 41b04c389ae05..554019133b2d7 100644 --- a/src/librustpkg/context.rs +++ b/src/librustpkg/context.rs @@ -156,7 +156,7 @@ impl Context { pub fn in_target(sysroot: &Path) -> bool { debug2!("Checking whether {} is in target", sysroot.display()); let mut p = sysroot.dir_path(); - p.set_filename_str("rustc"); + p.set_filename("rustc"); os::path_is_dir(&p) } diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs index 995f484a4e64f..485cbfef2f602 100644 --- a/src/librustpkg/installed_packages.rs +++ b/src/librustpkg/installed_packages.rs @@ -17,7 +17,7 @@ use std::os; pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { let workspaces = rust_path(); for p in workspaces.iter() { - let binfiles = os::list_dir(&p.join_str("bin")); + let binfiles = os::list_dir(&p.join("bin")); for exec in binfiles.iter() { // FIXME (#9639): This needs to handle non-utf8 paths match exec.filestem_str() { @@ -29,17 +29,17 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { } } } - let libfiles = os::list_dir(&p.join_str("lib")); + let libfiles = os::list_dir(&p.join("lib")); for lib in libfiles.iter() { debug2!("Full name: {}", lib.display()); match has_library(lib) { Some(basename) => { - let parent = p.join_str("lib"); + let parent = p.join("lib"); debug2!("parent = {}, child = {}", parent.display(), lib.display()); let rel_p = lib.path_relative_from(&parent).unwrap(); debug2!("Rel: {}", rel_p.display()); - let rel_path = rel_p.join_str(basename); + let rel_path = rel_p.join(basename); do rel_path.with_display_str |s| { debug2!("Rel name: {}", s); f(&PkgId::new(s)); diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index bd22f1da913a9..38b922ec05d64 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -59,7 +59,7 @@ impl PkgId { } }; - let path = Path::from_str(s); + let path = Path::new(s); if !path.is_relative() { return cond.raise((path, ~"absolute pkgid")); } @@ -137,8 +137,8 @@ impl Iterator<(Path, Path)> for Prefixes { let last = self.components.pop(); self.remaining.unshift(last); // converting to str and then back is a little unfortunate - Some((Path::from_str(self.components.connect("/")), - Path::from_str(self.remaining.connect("/")))) + Some((Path::new(self.components.connect("/")), + Path::new(self.remaining.connect("/")))) } } } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index a924694cca5b2..4e0db8cac4f69 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -94,20 +94,20 @@ impl PkgSrc { } else { // We search for sources under both src/ and build/ , because build/ is where // automatically-checked-out sources go. - let mut result = source_workspace.join_str("src"); + let mut result = source_workspace.join("src"); result.push_path(&id.path.dir_path()); - result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); + result.push(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result); - let mut result = source_workspace.join_str("src"); + let mut result = source_workspace.join("src"); result.push_path(&id.path); to_try.push(result); - let mut result = build_dir.join_str("src"); + let mut result = build_dir.join("src"); result.push_path(&id.path.dir_path()); result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result.clone()); output_names.push(result); - let mut other_result = build_dir.join_str("src"); + let mut other_result = build_dir.join("src"); other_result.push_path(&id.path); to_try.push(other_result.clone()); output_names.push(other_result); @@ -129,7 +129,7 @@ impl PkgSrc { // That is, is this a package ID that points into the middle of a workspace? for (prefix, suffix) in id.prefixes_iter() { let package_id = PkgId::new(prefix.as_str().unwrap()); - let path = build_dir.join_path(&package_id.path); + let path = build_dir.join(&package_id.path); debug2!("in loop: checking if {} is a directory", path.display()); if os::path_is_dir(&path) { let ps = PkgSrc::new(source_workspace, @@ -260,6 +260,7 @@ impl PkgSrc { return None; } + // FIXME (#9639): This needs to handle non-utf8 paths let url = format!("https://{}", pkgid.path.as_str().unwrap()); debug2!("Fetching package: git clone {} {} [version={}]", url, clone_target.display(), pkgid.version.to_str()); @@ -289,7 +290,7 @@ impl PkgSrc { // If a file named "pkg.rs" in the start directory exists, // return the path for it. Otherwise, None pub fn package_script_option(&self) -> Option { - let maybe_path = self.start_dir.join_str("pkg.rs"); + let maybe_path = self.start_dir.join("pkg.rs"); debug2!("package_script_option: checking whether {} exists", maybe_path.display()); if os::path_exists(&maybe_path) { Some(maybe_path) @@ -309,7 +310,7 @@ impl PkgSrc { it.nth(prefix-1); // skip elements } assert!(it.peek().is_some()); - let mut sub = Path::from_str("."); + let mut sub = Path::new("."); for c in it { sub.push(c); } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 34b387fc9a73d..0f5f1470b6fe6 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -53,7 +53,7 @@ pub fn make_dir_rwx_recursive(p: &Path) -> bool { os::mkdir_recursive(p, U_RWX) /// True if there's a directory in with /// pkgid's short name pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { - workspace_contains_package_id_(pkgid, workspace, |p| p.join_str("src")).is_some() + workspace_contains_package_id_(pkgid, workspace, |p| p.join("src")).is_some() } pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, @@ -98,16 +98,16 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, /// Return the target-specific build subdirectory, pushed onto `base`; /// doesn't check that it exists or create it pub fn target_build_dir(workspace: &Path) -> Path { - let mut dir = workspace.join_str("build"); - dir.push_str(host_triple()); + let mut dir = workspace.join("build"); + dir.push(host_triple()); dir } /// Return the target-specific lib subdirectory, pushed onto `base`; /// doesn't check that it exists or create it fn target_lib_dir(workspace: &Path) -> Path { - let mut dir = workspace.join_str("lib"); - dir.push_str(host_triple()); + let mut dir = workspace.join("lib"); + dir.push(host_triple()); dir } @@ -115,7 +115,7 @@ fn target_lib_dir(workspace: &Path) -> Path { /// doesn't check that it exists or create it /// note: this isn't target-specific fn target_bin_dir(workspace: &Path) -> Path { - workspace.join_str("bin") + workspace.join("bin") } /// Figure out what the executable name for in 's build @@ -205,7 +205,7 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target, // rustc doesn't use target-specific subdirectories pub fn system_library(sysroot: &Path, lib_name: &str) -> Option { - library_in(lib_name, &NoVersion, &sysroot.join_str("lib")) + library_in(lib_name, &NoVersion, &sysroot.join("lib")) } fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Option { @@ -377,9 +377,9 @@ pub fn mk_output_path(what: OutputType, where: Target, dir.display()); let mut output_path = match what { // this code is duplicated from elsewhere; fix this - Lib => dir.join_str(os::dll_filename(short_name_with_version)), + Lib => dir.join(os::dll_filename(short_name_with_version)), // executable names *aren't* versioned - _ => dir.join_str(format!("{}{}{}", pkg_id.short_name, + _ => dir.join(format!("{}{}{}", pkg_id.short_name, match what { Test => "test", Bench => "bench", @@ -416,7 +416,7 @@ pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) { fn dir_has_file(dir: &Path, file: &str) -> bool { assert!(dir.is_absolute()); - os::path_exists(&dir.join_str(file)) + os::path_exists(&dir.join(file)) } pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option { diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 47a65753cc957..5ef8948c37772 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -146,7 +146,7 @@ impl<'self> PkgScript<'self> { let crate = util::ready_crate(sess, self.crate.take_unwrap()); debug2!("Building output filenames with script name {}", driver::source_name(&driver::file_input(self.input.clone()))); - let exe = self.build_dir.join_str("pkg" + util::exe_suffix()); + let exe = self.build_dir.join("pkg" + util::exe_suffix()); util::compile_crate_from_input(&self.input, exec, Nothing, @@ -422,7 +422,7 @@ impl CtxMethods for BuildContext { // If workspace isn't in the RUST_PATH, and it's a git repo, // then clone it into the first entry in RUST_PATH, and repeat if !in_rust_path(&workspace) && is_git_dir(&workspace.join_path(&pkgid.path)) { - let mut out_dir = default_workspace().join_str("src"); + let mut out_dir = default_workspace().join("src"); out_dir.push_path(&pkgid.path); let git_result = source_control::safe_git_clone(&workspace.join_path(&pkgid.path), &pkgid.version, @@ -488,7 +488,7 @@ impl CtxMethods for BuildContext { // Find crates inside the workspace &Everything => pkg_src.find_crates(), // Find only tests - &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::from_str(s)) }), + &Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::new(s)) }), // Don't infer any crates -- just build the one that was requested &JustOne(ref p) => { // We expect that p is relative to the package source's start directory, @@ -562,7 +562,7 @@ impl CtxMethods for BuildContext { let result = self.install_no_build(pkg_src.build_workspace(), &pkg_src.destination_workspace, - &id).map(|s| Path::from_str(*s)); + &id).map(|s| Path::new(*s)); debug2!("install: id = {}, about to call discover_outputs, {:?}", id.to_str(), result.map(|p| p.to_display_str())); installed_files = installed_files + result; @@ -669,10 +669,10 @@ impl CtxMethods for BuildContext { } fn init(&self) { - os::mkdir_recursive(&Path::from_str("src"), U_RWX); - os::mkdir_recursive(&Path::from_str("lib"), U_RWX); - os::mkdir_recursive(&Path::from_str("bin"), U_RWX); - os::mkdir_recursive(&Path::from_str("build"), U_RWX); + os::mkdir_recursive(&Path::new("src"), U_RWX); + os::mkdir_recursive(&Path::new("lib"), U_RWX); + os::mkdir_recursive(&Path::new("bin"), U_RWX); + os::mkdir_recursive(&Path::new("build"), U_RWX); } fn uninstall(&self, _id: &str, _vers: Option<~str>) { @@ -852,7 +852,7 @@ pub fn main_args(args: &[~str]) -> int { let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect(); remaining_args.shift(); let sroot = match supplied_sysroot { - Some(getopts::Val(s)) => Path::from_str(s), + Some(getopts::Val(s)) => Path::new(s), _ => filesearch::get_or_default_sysroot() }; diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs index b910c07920508..2fc925a8a6a93 100644 --- a/src/librustpkg/source_control.rs +++ b/src/librustpkg/source_control.rs @@ -43,7 +43,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult else { match v { &ExactRevision(ref s) => { - let git_dir = target.join_str(".git"); + let git_dir = target.join(".git"); debug2!("`Running: git --work-tree={} --git-dir={} checkout {}", *s, target.display(), git_dir.display()); // FIXME (#9639: This needs to handle non-utf8 paths @@ -64,7 +64,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult // Check that no version was specified. There's no reason to not handle the // case where a version was requested, but I haven't implemented it. assert!(*v == NoVersion); - let git_dir = target.join_str(".git"); + let git_dir = target.join(".git"); debug2!("Running: git --work-tree={} --git-dir={} pull --no-edit {}", target.display(), git_dir.display(), source.display()); // FIXME (#9639: This needs to handle non-utf8 paths @@ -80,7 +80,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult let scratch_dir = TempDir::new("rustpkg"); let clone_target = match scratch_dir { - Some(d) => d.unwrap().join_str("rustpkg_temp"), + Some(d) => d.unwrap().join("rustpkg_temp"), None => cond.raise(~"Failed to create temporary directory for fetching git sources") }; @@ -136,5 +136,5 @@ fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput } pub fn is_git_dir(p: &Path) -> bool { - os::path_is_dir(&p.join_str(".git")) + os::path_is_dir(&p.join(".git")) } diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index 9f371a865356b..b21641a5e53e3 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -56,7 +56,7 @@ fn file_is(p: &Path, stem: &str) -> bool { } pub fn lib_name_of(p: &Path) -> Path { - p.join_str("lib.rs") + p.join("lib.rs") } pub static lib_crate_filename: &'static str = "lib.rs"; diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index de7ad9dc06b58..cb40c3c00906a 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -41,7 +41,7 @@ use util::datestamp; fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { let context = workcache::Context::new( - RWArc::new(Database::new(workspace.join_str("rustpkg_db.json"))), + RWArc::new(Database::new(workspace.join("rustpkg_db.json"))), RWArc::new(Logger::new()), Arc::new(TreeMap::new())); BuildContext { @@ -59,7 +59,7 @@ fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { fn fake_pkg() -> PkgId { let sn = ~"bogus"; PkgId { - path: Path::from_str(sn), + path: Path::new(sn.as_slice()), short_name: sn, version: NoVersion } @@ -67,7 +67,7 @@ fn fake_pkg() -> PkgId { fn git_repo_pkg() -> PkgId { PkgId { - path: Path::from_str("mockgithub.com/catamorphism/test-pkg"), + path: Path::new("mockgithub.com/catamorphism/test-pkg"), short_name: ~"test-pkg", version: NoVersion } @@ -75,7 +75,7 @@ fn git_repo_pkg() -> PkgId { fn git_repo_pkg_with_tag(a_tag: ~str) -> PkgId { PkgId { - path: Path::from_str("mockgithub.com/catamorphism/test-pkg"), + path: Path::new("mockgithub.com/catamorphism/test-pkg"), short_name: ~"test-pkg", version: Tagged(a_tag) } @@ -102,18 +102,18 @@ fn mk_empty_workspace(short_name: &Path, version: &Version, tag: &str) -> TempDi fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path { // include version number in directory name // FIXME (#9639): This needs to handle non-utf8 paths - let package_dir = workspace.join_many_str([~"src", format!("{}-{}", - short_name.as_str().unwrap(), version.to_str())]); + let package_dir = workspace.join_many([~"src", format!("{}-{}", + short_name.as_str().unwrap(), version.to_str())]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); package_dir } fn mk_temp_workspace(short_name: &Path, version: &Version) -> (TempDir, Path) { let workspace_dir = mk_empty_workspace(short_name, version, "temp_workspace"); - let package_dir = workspace_dir.path().join_many_str([~"src", - format!("{}-{}", - short_name.to_str(), - version.to_str())]); + let package_dir = workspace_dir.path().join_many([~"src", + format!("{}-{}", + short_name.to_str(), + version.to_str())]); debug2!("Created {} and does it exist? {:?}", package_dir.display(), os::path_is_dir(&package_dir)); @@ -124,13 +124,13 @@ fn mk_temp_workspace(short_name: &Path, version: &Version) -> (TempDir, Path) { os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files - writeFile(&package_dir.join_str("main.rs"), + writeFile(&package_dir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.join_str("lib.rs"), + writeFile(&package_dir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.join_str("test.rs"), + writeFile(&package_dir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.join_str("bench.rs"), + writeFile(&package_dir.join("bench.rs"), "#[bench] pub fn f() { (); }"); (workspace_dir, package_dir) } @@ -163,7 +163,7 @@ fn init_git_repo(p: &Path) -> TempDir { run_git([~"init"], None, &work_dir_for_opts, format!("Couldn't initialize git repository in {}", work_dir.display())); // Add stuff to the dir so that git tag succeeds - writeFile(&work_dir.join_str("README"), ""); + writeFile(&work_dir.join("README"), ""); run_git([~"add", ~"README"], None, &work_dir_for_opts, format!("Couldn't add in {}", work_dir.display())); git_commit(&work_dir_for_opts, ~"whatever"); @@ -232,13 +232,13 @@ fn test_sysroot() -> Path { // Returns the path to rustpkg fn rustpkg_exec() -> Path { // Ugh - let first_try = test_sysroot().join_many_str( + let first_try = test_sysroot().join_many( [~"lib", ~"rustc", host_triple(), ~"bin", ~"rustpkg"]); if is_executable(&first_try) { first_try } else { - let second_try = test_sysroot().join_many_str(["bin", "rustpkg"]); + let second_try = test_sysroot().join_many(["bin", "rustpkg"]); if is_executable(&second_try) { second_try } @@ -328,7 +328,7 @@ fn create_local_package(pkgid: &PkgId) -> TempDir { fn create_local_package_in(pkgid: &PkgId, pkgdir: &Path) -> Path { - let package_dir = pkgdir.join_many_str([~"src", pkgid.to_str()]); + let package_dir = pkgdir.join_many([~"src", pkgid.to_str()]); // Create main, lib, test, and bench files assert!(os::mkdir_recursive(&package_dir, U_RWX)); @@ -336,13 +336,13 @@ fn create_local_package_in(pkgid: &PkgId, pkgdir: &Path) -> Path { os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files - writeFile(&package_dir.join_str("main.rs"), + writeFile(&package_dir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.join_str("lib.rs"), + writeFile(&package_dir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.join_str("test.rs"), + writeFile(&package_dir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.join_str("bench.rs"), + writeFile(&package_dir.join("bench.rs"), "#[bench] pub fn f() { (); }"); package_dir } @@ -356,11 +356,11 @@ fn create_local_package_with_dep(pkgid: &PkgId, subord_pkgid: &PkgId) -> TempDir let package_dir = create_local_package(pkgid); create_local_package_in(subord_pkgid, package_dir.path()); // Write a main.rs file into pkgid that references subord_pkgid - writeFile(&package_dir.path().join_many_str([~"src", pkgid.to_str(), ~"main.rs"]), + writeFile(&package_dir.path().join_many([~"src", pkgid.to_str(), ~"main.rs"]), format!("extern mod {};\nfn main() \\{\\}", subord_pkgid.short_name)); // Write a lib.rs file into subord_pkgid that has something in it - writeFile(&package_dir.path().join_many_str([~"src", subord_pkgid.to_str(), ~"lib.rs"]), + writeFile(&package_dir.path().join_many([~"src", subord_pkgid.to_str(), ~"lib.rs"]), "pub fn f() {}"); package_dir } @@ -452,7 +452,7 @@ fn llvm_bitcode_file_exists(repo: &Path, short_name: &str) -> bool { } fn file_exists(repo: &Path, short_name: &str, extension: &str) -> bool { - os::path_exists(&target_build_dir(repo).join_many_str([short_name.to_owned(), + os::path_exists(&target_build_dir(repo).join_many([short_name.to_owned(), format!("{}.{}", short_name, extension)])) } @@ -497,7 +497,7 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~ fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { debug2!("lib_output_file_name: given {} and short name {}", workspace.display(), short_name); - library_in_workspace(&Path::from_str(short_name), + library_in_workspace(&Path::new(short_name), short_name, Build, workspace, @@ -506,13 +506,13 @@ fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { } fn output_file_name(workspace: &Path, short_name: ~str) -> Path { - target_build_dir(workspace).join_str(short_name).join_str(format!("{}{}", short_name, - os::EXE_SUFFIX)) + target_build_dir(workspace).join(short_name.as_slice()).join(format!("{}{}", short_name, + os::EXE_SUFFIX)) } fn touch_source_file(workspace: &Path, pkgid: &PkgId) { use conditions::bad_path::cond; - let pkg_src_dir = workspace.join_many_str([~"src", pkgid.to_str()]); + let pkg_src_dir = workspace.join_many([~"src", pkgid.to_str()]); let contents = os::list_dir_path(&pkg_src_dir); for p in contents.iter() { if p.extension_str() == Some("rs") { @@ -528,9 +528,9 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) { /// Add a comment at the end fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { use conditions::bad_path::cond; - let pkg_src_dir = workspace.join_many_str([~"src", pkgid.to_str()]); + let pkg_src_dir = workspace.join_many([~"src", pkgid.to_str()]); let mut maybe_p = None; - let maybe_file = pkg_src_dir.join_str(filename); + let maybe_file = pkg_src_dir.join(filename); debug2!("Trying to frob {} -- {}", pkg_src_dir.display(), filename); if os::path_exists(&maybe_file) { maybe_p = Some(maybe_file); @@ -552,7 +552,7 @@ fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { #[test] fn test_make_dir_rwx() { let temp = &os::tmpdir(); - let dir = temp.join_str("quux"); + let dir = temp.join("quux"); assert!(!os::path_exists(&dir) || os::remove_dir_recursive(&dir)); debug2!("Trying to make {}", dir.display()); @@ -630,16 +630,16 @@ fn test_install_git() { let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); debug2!("repo = {}", repo.display()); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg"]); debug2!("repo_subdir = {}", repo_subdir.display()); - writeFile(&repo_subdir.join_str("main.rs"), + writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.join_str("lib.rs"), + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.join_str("test.rs"), + writeFile(&repo_subdir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.join_str("bench.rs"), + writeFile(&repo_subdir.join("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files @@ -648,7 +648,7 @@ fn test_install_git() { // should have test, bench, lib, and main // FIXME (#9639): This needs to handle non-utf8 paths command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo); - let ws = repo.join_str(".rust"); + let ws = repo.join(".rust"); // Check that all files exist debug2!("Checking for files in {}", ws.display()); let exec = target_executable_in_workspace(&temp_pkg_id, &ws); @@ -705,12 +705,12 @@ fn test_package_ids_must_be_relative_path_like() { } do cond.trap(|(p, e)| { - let abs = os::make_absolute(&Path::from_str("foo/bar/quux")); + let abs = os::make_absolute(&Path::new("foo/bar/quux")); assert_eq!(p, abs); assert!("absolute pkgid" == e); whatever.clone() }).inside { - let zp = os::make_absolute(&Path::from_str("foo/bar/quux")); + let zp = os::make_absolute(&Path::new("foo/bar/quux")); // FIXME (#9639): This needs to handle non-utf8 paths let z = PkgId::new(zp.as_str().unwrap()); assert_eq!(~"foo-0.1", z.to_str()); @@ -721,17 +721,17 @@ fn test_package_ids_must_be_relative_path_like() { #[test] fn test_package_version() { let local_path = "mockgithub.com/catamorphism/test_pkg_version"; - let repo = init_git_repo(&Path::from_str(local_path)); + let repo = init_git_repo(&Path::new(local_path)); let repo = repo.path(); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test_pkg_version"]); + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test_pkg_version"]); debug2!("Writing files in: {}", repo_subdir.display()); - writeFile(&repo_subdir.join_str("main.rs"), + writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.join_str("lib.rs"), + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.join_str("test.rs"), + writeFile(&repo_subdir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.join_str("bench.rs"), + writeFile(&repo_subdir.join("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&repo_subdir, ~"0.4"); @@ -740,7 +740,7 @@ fn test_package_version() { // This should look at the prefix, clone into a workspace, then build. command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg_version"], repo); - let ws = repo.join_str(".rust"); + let ws = repo.join(".rust"); // we can still match on the filename to make sure it contains the 0.4 version assert!(match built_library_in_workspace(&temp_pkg_id, &ws) { @@ -751,36 +751,36 @@ fn test_package_version() { None => false }); assert!(built_executable_in_workspace(&temp_pkg_id, &ws) - == Some(target_build_dir(&ws).join_many_str(["mockgithub.com", - "catamorphism", - "test_pkg_version", - "test_pkg_version"]))); + == Some(target_build_dir(&ws).join_many(["mockgithub.com", + "catamorphism", + "test_pkg_version", + "test_pkg_version"]))); } #[test] fn test_package_request_version() { let local_path = "mockgithub.com/catamorphism/test_pkg_version"; - let repo = init_git_repo(&Path::from_str(local_path)); + let repo = init_git_repo(&Path::new(local_path)); let repo = repo.path(); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test_pkg_version"]); + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test_pkg_version"]); debug2!("Writing files in: {}", repo_subdir.display()); - writeFile(&repo_subdir.join_str("main.rs"), + writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.join_str("lib.rs"), + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&repo_subdir.join_str("test.rs"), + writeFile(&repo_subdir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&repo_subdir.join_str("bench.rs"), + writeFile(&repo_subdir.join("bench.rs"), "#[bench] pub fn f() { (); }"); - writeFile(&repo_subdir.join_str("version-0.3-file.txt"), "hi"); + writeFile(&repo_subdir.join("version-0.3-file.txt"), "hi"); add_git_tag(&repo_subdir, ~"0.3"); - writeFile(&repo_subdir.join_str("version-0.4-file.txt"), "hello"); + writeFile(&repo_subdir.join("version-0.4-file.txt"), "hello"); add_git_tag(&repo_subdir, ~"0.4"); command_line_test([~"install", format!("{}\\#0.3", local_path)], repo); - assert!(match installed_library_in_workspace(&Path::from_str("test_pkg_version"), - &repo.join_str(".rust")) { + assert!(match installed_library_in_workspace(&Path::new("test_pkg_version"), + &repo.join(".rust")) { Some(p) => { debug2!("installed: {}", p.display()); let suffix = format!("0.3{}", os::consts::DLL_SUFFIX); @@ -789,15 +789,15 @@ fn test_package_request_version() { None => false }); let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3"); - assert!(target_executable_in_workspace(&temp_pkg_id, &repo.join_str(".rust")) - == repo.join_many_str([".rust", "bin", "test_pkg_version"])); + assert!(target_executable_in_workspace(&temp_pkg_id, &repo.join(".rust")) + == repo.join_many([".rust", "bin", "test_pkg_version"])); - let mut dir = target_build_dir(&repo.join_str(".rust")); - dir.push_path(&Path::from_str("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); + let mut dir = target_build_dir(&repo.join(".rust")); + dir.push_path(&Path::new("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); debug2!("dir = {}", dir.display()); assert!(os::path_is_dir(&dir)); - assert!(os::path_exists(&dir.join_str("version-0.3-file.txt"))); - assert!(!os::path_exists(&dir.join_str("version-0.4-file.txt"))); + assert!(os::path_exists(&dir.join("version-0.3-file.txt"))); + assert!(!os::path_exists(&dir.join("version-0.4-file.txt"))); } #[test] @@ -810,23 +810,23 @@ fn rustpkg_install_url_2() { #[test] fn rustpkg_library_target() { - let foo_repo = init_git_repo(&Path::from_str("foo")); + let foo_repo = init_git_repo(&Path::new("foo")); let foo_repo = foo_repo.path(); - let package_dir = foo_repo.join_str("foo"); + let package_dir = foo_repo.join("foo"); debug2!("Writing files in: {}", package_dir.display()); - writeFile(&package_dir.join_str("main.rs"), + writeFile(&package_dir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&package_dir.join_str("lib.rs"), + writeFile(&package_dir.join("lib.rs"), "pub fn f() { let _x = (); }"); - writeFile(&package_dir.join_str("test.rs"), + writeFile(&package_dir.join("test.rs"), "#[test] pub fn f() { (); }"); - writeFile(&package_dir.join_str("bench.rs"), + writeFile(&package_dir.join("bench.rs"), "#[bench] pub fn f() { (); }"); add_git_tag(&package_dir, ~"1.0"); command_line_test([~"install", ~"foo"], foo_repo); - assert_lib_exists(&foo_repo.join_str(".rust"), &Path::from_str("foo"), ExactRevision(~"1.0")); + assert_lib_exists(&foo_repo.join(".rust"), &Path::new("foo"), ExactRevision(~"1.0")); } #[test] @@ -844,26 +844,25 @@ fn package_script_with_default_build() { debug2!("dir = {}", dir.display()); let mut source = test_sysroot().dir_path(); source.pop(); source.pop(); - source.push_many_str( - ["src", "librustpkg", "testsuite", "pass", "src", "fancy-lib", "pkg.rs"]); + source.push_many(["src", "librustpkg", "testsuite", "pass", "src", "fancy-lib", "pkg.rs"]); debug2!("package_script_with_default_build: {}", source.display()); if !os::copy_file(&source, - &dir.join_many_str(["src", "fancy-lib-0.1", "pkg.rs"])) { + &dir.join_many(["src", "fancy-lib-0.1", "pkg.rs"])) { fail2!("Couldn't copy file"); } command_line_test([~"install", ~"fancy-lib"], dir); - assert_lib_exists(dir, &Path::from_str("fancy-lib"), NoVersion); - assert!(os::path_exists(&target_build_dir(dir).join_many_str(["fancy-lib", "generated.rs"]))); + assert_lib_exists(dir, &Path::new("fancy-lib"), NoVersion); + assert!(os::path_exists(&target_build_dir(dir).join_many(["fancy-lib", "generated.rs"]))); } #[test] fn rustpkg_build_no_arg() { let tmp = TempDir::new("rustpkg_build_no_arg").expect("rustpkg_build_no_arg failed"); - let tmp = tmp.path().join_str(".rust"); - let package_dir = tmp.join_many_str(["src", "foo"]); + let tmp = tmp.path().join(".rust"); + let package_dir = tmp.join_many(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.join_str("main.rs"), + writeFile(&package_dir.join("main.rs"), "fn main() { let _x = (); }"); debug2!("build_no_arg: dir = {}", package_dir.display()); command_line_test([~"build"], &package_dir); @@ -873,24 +872,24 @@ fn rustpkg_build_no_arg() { #[test] fn rustpkg_install_no_arg() { let tmp = TempDir::new("rustpkg_install_no_arg").expect("rustpkg_install_no_arg failed"); - let tmp = tmp.path().join_str(".rust"); - let package_dir = tmp.join_many_str(["src", "foo"]); + let tmp = tmp.path().join(".rust"); + let package_dir = tmp.join_many(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.join_str("lib.rs"), + writeFile(&package_dir.join("lib.rs"), "fn main() { let _x = (); }"); debug2!("install_no_arg: dir = {}", package_dir.display()); command_line_test([~"install"], &package_dir); - assert_lib_exists(&tmp, &Path::from_str("foo"), NoVersion); + assert_lib_exists(&tmp, &Path::new("foo"), NoVersion); } #[test] fn rustpkg_clean_no_arg() { let tmp = TempDir::new("rustpkg_clean_no_arg").expect("rustpkg_clean_no_arg failed"); - let tmp = tmp.path().join_str(".rust"); - let package_dir = tmp.join_many_str(["src", "foo"]); + let tmp = tmp.path().join(".rust"); + let package_dir = tmp.join_many(["src", "foo"]); assert!(os::mkdir_recursive(&package_dir, U_RWX)); - writeFile(&package_dir.join_str("main.rs"), + writeFile(&package_dir.join("main.rs"), "fn main() { let _x = (); }"); debug2!("clean_no_arg: dir = {}", package_dir.display()); command_line_test([~"build"], &package_dir); @@ -903,9 +902,9 @@ fn rustpkg_clean_no_arg() { #[test] fn rust_path_test() { let dir_for_path = TempDir::new("more_rust").expect("rust_path_test failed"); - let dir = mk_workspace(dir_for_path.path(), &Path::from_str("foo"), &NoVersion); + let dir = mk_workspace(dir_for_path.path(), &Path::new("foo"), &NoVersion); debug2!("dir = {}", dir.display()); - writeFile(&dir.join_str("main.rs"), "fn main() { let _x = (); }"); + writeFile(&dir.join("main.rs"), "fn main() { let _x = (); }"); let cwd = os::getcwd(); debug2!("cwd = {}", cwd.display()); @@ -921,16 +920,16 @@ fn rust_path_test() { #[ignore] // FIXME(#9184) tests can't change the cwd (other tests are sad then) fn rust_path_contents() { let dir = TempDir::new("rust_path").expect("rust_path_contents failed"); - let abc = &dir.path().join_many_str(["A", "B", "C"]); - assert!(os::mkdir_recursive(&abc.join_str(".rust"), U_RWX)); + let abc = &dir.path().join_many(["A", "B", "C"]); + assert!(os::mkdir_recursive(&abc.join(".rust"), U_RWX)); assert!(os::mkdir_recursive(&abc.with_filename(".rust"), U_RWX)); assert!(os::mkdir_recursive(&abc.dir_path().with_filename(".rust"), U_RWX)); assert!(os::change_dir(abc)); let p = rust_path(); - let cwd = os::getcwd().join_str(".rust"); - let parent = cwd.dir_path().with_filename_str(".rust"); - let grandparent = cwd.dir_path().dir_path().with_filename_str(".rust"); + let cwd = os::getcwd().join(".rust"); + let parent = cwd.dir_path().with_filename(".rust"); + let grandparent = cwd.dir_path().dir_path().with_filename(".rust"); assert!(p.contains(&cwd)); assert!(p.contains(&parent)); assert!(p.contains(&grandparent)); @@ -943,9 +942,9 @@ fn rust_path_contents() { fn rust_path_parse() { os::setenv("RUST_PATH", "/a/b/c:/d/e/f:/g/h/i"); let paths = rust_path(); - assert!(paths.contains(&Path::from_str("/g/h/i"))); - assert!(paths.contains(&Path::from_str("/d/e/f"))); - assert!(paths.contains(&Path::from_str("/a/b/c"))); + assert!(paths.contains(&Path::new("/g/h/i"))); + assert!(paths.contains(&Path::new("/d/e/f"))); + assert!(paths.contains(&Path::new("/a/b/c"))); os::unsetenv("RUST_PATH"); } @@ -1144,23 +1143,21 @@ fn test_non_numeric_tag() { let temp_pkg_id = git_repo_pkg(); let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); - writeFile(&repo_subdir.join_str("foo"), "foo"); - writeFile(&repo_subdir.join_str("lib.rs"), + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg"]); + writeFile(&repo_subdir.join("foo"), "foo"); + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"testbranch"); - writeFile(&repo_subdir.join_str("testbranch_only"), "hello"); + writeFile(&repo_subdir.join("testbranch_only"), "hello"); add_git_tag(&repo_subdir, ~"another_tag"); - writeFile(&repo_subdir.join_str("not_on_testbranch_only"), "bye bye"); + writeFile(&repo_subdir.join("not_on_testbranch_only"), "bye bye"); add_all_and_commit(&repo_subdir); // FIXME (#9639): This needs to handle non-utf8 paths command_line_test([~"install", format!("{}\\#testbranch", temp_pkg_id.path.as_str().unwrap())], repo); - let file1 = repo.join_many_str(["mockgithub.com", "catamorphism", - "test-pkg", "testbranch_only"]); - let file2 = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg", - "master_only"]); + let file1 = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg", "testbranch_only"]); + let file2 = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg", "master_only"]); assert!(os::path_exists(&file1)); assert!(!os::path_exists(&file2)); } @@ -1169,13 +1166,12 @@ fn test_non_numeric_tag() { fn test_extern_mod() { let dir = TempDir::new("test_extern_mod").expect("test_extern_mod"); let dir = dir.path(); - let main_file = dir.join_str("main.rs"); + let main_file = dir.join("main.rs"); let lib_depend_dir = TempDir::new("foo").expect("test_extern_mod"); let lib_depend_dir = lib_depend_dir.path(); - let aux_dir = lib_depend_dir.join_many_str(["src", "mockgithub.com", "catamorphism", - "test_pkg"]); + let aux_dir = lib_depend_dir.join_many(["src", "mockgithub.com", "catamorphism", "test_pkg"]); assert!(os::mkdir_recursive(&aux_dir, U_RWX)); - let aux_pkg_file = aux_dir.join_str("lib.rs"); + let aux_pkg_file = aux_dir.join("lib.rs"); writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() { assert!(true); } }\n"); assert!(os::path_exists(&aux_pkg_file)); @@ -1186,12 +1182,12 @@ fn test_extern_mod() { command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg"], lib_depend_dir); - let exec_file = dir.join_str("out"); + let exec_file = dir.join("out"); // Be sure to extend the existing environment // FIXME (#9639): This needs to handle non-utf8 paths let env = Some([(~"RUST_PATH", lib_depend_dir.as_str().unwrap().to_owned())] + os::env()); let rustpkg_exec = rustpkg_exec(); - let rustc = rustpkg_exec.with_filename_str("rustc"); + let rustc = rustpkg_exec.with_filename("rustc"); let test_sys = test_sysroot(); // FIXME (#9639): This needs to handle non-utf8 paths @@ -1219,12 +1215,12 @@ fn test_extern_mod() { fn test_extern_mod_simpler() { let dir = TempDir::new("test_extern_mod_simpler").expect("test_extern_mod_simpler"); let dir = dir.path(); - let main_file = dir.join_str("main.rs"); + let main_file = dir.join("main.rs"); let lib_depend_dir = TempDir::new("foo").expect("test_extern_mod_simpler"); let lib_depend_dir = lib_depend_dir.path(); - let aux_dir = lib_depend_dir.join_many_str(["src", "rust-awesomeness"]); + let aux_dir = lib_depend_dir.join_many(["src", "rust-awesomeness"]); assert!(os::mkdir_recursive(&aux_dir, U_RWX)); - let aux_pkg_file = aux_dir.join_str("lib.rs"); + let aux_pkg_file = aux_dir.join("lib.rs"); writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() { assert!(true); } }\n"); assert!(os::path_exists(&aux_pkg_file)); @@ -1235,12 +1231,12 @@ fn test_extern_mod_simpler() { command_line_test([~"install", ~"rust-awesomeness"], lib_depend_dir); - let exec_file = dir.join_str("out"); + let exec_file = dir.join("out"); // Be sure to extend the existing environment // FIXME (#9639): This needs to handle non-utf8 paths let env = Some([(~"RUST_PATH", lib_depend_dir.as_str().unwrap().to_owned())] + os::env()); let rustpkg_exec = rustpkg_exec(); - let rustc = rustpkg_exec.with_filename_str("rustc"); + let rustc = rustpkg_exec.with_filename("rustc"); let test_sys = test_sysroot(); debug2!("RUST_PATH={} {} {} \n --sysroot {} -o {}", lib_depend_dir.display(), @@ -1275,11 +1271,11 @@ fn test_import_rustpkg() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.join_many_str(["src", "foo-0.1", "pkg.rs"]), + writeFile(&workspace.join_many(["src", "foo-0.1", "pkg.rs"]), "extern mod rustpkg; fn main() {}"); command_line_test([~"build", ~"foo"], workspace); debug2!("workspace = {}", workspace.display()); - assert!(os::path_exists(&target_build_dir(workspace).join_str("foo").join_str(format!("pkg{}", + assert!(os::path_exists(&target_build_dir(workspace).join("foo").join(format!("pkg{}", os::EXE_SUFFIX)))); } @@ -1288,11 +1284,11 @@ fn test_macro_pkg_script() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.join_many_str(["src", "foo-0.1", "pkg.rs"]), + writeFile(&workspace.join_many(["src", "foo-0.1", "pkg.rs"]), "extern mod rustpkg; fn main() { debug2!(\"Hi\"); }"); command_line_test([~"build", ~"foo"], workspace); debug2!("workspace = {}", workspace.display()); - assert!(os::path_exists(&target_build_dir(workspace).join_str("foo").join_str(format!("pkg{}", + assert!(os::path_exists(&target_build_dir(workspace).join("foo").join(format!("pkg{}", os::EXE_SUFFIX)))); } @@ -1302,8 +1298,8 @@ fn multiple_workspaces() { // Copy the exact same package into directory B and install it // Set the RUST_PATH to A:B // Make a third package that uses foo, make sure we can build/install it - let (a_loc, _pkg_dir) = mk_temp_workspace(&Path::from_str("foo"), &NoVersion); - let (b_loc, _pkg_dir) = mk_temp_workspace(&Path::from_str("foo"), &NoVersion); + let (a_loc, _pkg_dir) = mk_temp_workspace(&Path::new("foo"), &NoVersion); + let (b_loc, _pkg_dir) = mk_temp_workspace(&Path::new("foo"), &NoVersion); let (a_loc, b_loc) = (a_loc.path(), b_loc.path()); debug2!("Trying to install foo in {}", a_loc.display()); command_line_test([~"install", ~"foo"], a_loc); @@ -1328,7 +1324,7 @@ fn rust_path_hack_test(hack_flag: bool) { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", @@ -1336,11 +1332,11 @@ fn rust_path_hack_test(hack_flag: bool) { foo_path.as_str().unwrap()))]); command_line_test_with_env(~[~"install"] + if hack_flag { ~[~"--rust-path-hack"] } else { ~[] } + ~[~"foo"], dest_workspace, rust_path); - assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); + assert_lib_exists(dest_workspace, &Path::new("foo"), NoVersion); assert_executable_exists(dest_workspace, "foo"); assert_built_library_exists(dest_workspace, "foo"); assert_built_executable_exists(dest_workspace, "foo"); - assert!(!lib_exists(workspace, &Path::from_str("foo"), NoVersion)); + assert!(!lib_exists(workspace, &Path::new("foo"), NoVersion)); assert!(!executable_exists(workspace, "foo")); assert!(!built_library_exists(workspace, "foo")); assert!(!built_executable_exists(workspace, "foo")); @@ -1369,19 +1365,19 @@ fn test_rust_path_can_contain_package_dirs_without_flag() { fn rust_path_hack_cwd() { // Same as rust_path_hack_test, but the CWD is the dir to build out of let cwd = TempDir::new("foo").expect("rust_path_hack_cwd"); - let cwd = cwd.path().join_str("foo"); + let cwd = cwd.path().join("foo"); assert!(os::mkdir_recursive(&cwd, U_RWX)); - writeFile(&cwd.join_str("lib.rs"), "pub fn f() { }"); + writeFile(&cwd.join("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack", ~"foo"], &cwd, rust_path); debug2!("Checking that foo exists in {}", dest_workspace.display()); - assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); + assert_lib_exists(dest_workspace, &Path::new("foo"), NoVersion); assert_built_library_exists(dest_workspace, "foo"); - assert!(!lib_exists(&cwd, &Path::from_str("foo"), NoVersion)); + assert!(!lib_exists(&cwd, &Path::new("foo"), NoVersion)); assert!(!built_library_exists(&cwd, "foo")); } @@ -1389,20 +1385,20 @@ fn rust_path_hack_cwd() { fn rust_path_hack_multi_path() { // Same as rust_path_hack_test, but with a more complex package ID let cwd = TempDir::new("pkg_files").expect("rust_path_hack_cwd"); - let subdir = cwd.path().join_many_str(["foo", "bar", "quux"]); + let subdir = cwd.path().join_many(["foo", "bar", "quux"]); assert!(os::mkdir_recursive(&subdir, U_RWX)); - writeFile(&subdir.join_str("lib.rs"), "pub fn f() { }"); + writeFile(&subdir.join("lib.rs"), "pub fn f() { }"); let name = ~"foo/bar/quux"; - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack", name.clone()], &subdir, rust_path); debug2!("Checking that {} exists in {}", name, dest_workspace.display()); - assert_lib_exists(dest_workspace, &Path::from_str("quux"), NoVersion); + assert_lib_exists(dest_workspace, &Path::new("quux"), NoVersion); assert_built_library_exists(dest_workspace, name); - assert!(!lib_exists(&subdir, &Path::from_str("quux"), NoVersion)); + assert!(!lib_exists(&subdir, &Path::new("quux"), NoVersion)); assert!(!built_library_exists(&subdir, name)); } @@ -1411,19 +1407,19 @@ fn rust_path_hack_install_no_arg() { // Same as rust_path_hack_cwd, but making rustpkg infer the pkg id let cwd = TempDir::new("pkg_files").expect("rust_path_hack_install_no_arg"); let cwd = cwd.path(); - let source_dir = cwd.join_str("foo"); + let source_dir = cwd.join("foo"); assert!(make_dir_rwx(&source_dir)); - writeFile(&source_dir.join_str("lib.rs"), "pub fn f() { }"); + writeFile(&source_dir.join("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); command_line_test_with_env([~"install", ~"--rust-path-hack"], &source_dir, rust_path); debug2!("Checking that foo exists in {}", dest_workspace.display()); - assert_lib_exists(dest_workspace, &Path::from_str("foo"), NoVersion); + assert_lib_exists(dest_workspace, &Path::new("foo"), NoVersion); assert_built_library_exists(dest_workspace, "foo"); - assert!(!lib_exists(&source_dir, &Path::from_str("foo"), NoVersion)); + assert!(!lib_exists(&source_dir, &Path::new("foo"), NoVersion)); assert!(!built_library_exists(cwd, "foo")); } @@ -1431,11 +1427,11 @@ fn rust_path_hack_install_no_arg() { fn rust_path_hack_build_no_arg() { // Same as rust_path_hack_install_no_arg, but building instead of installing let cwd = TempDir::new("pkg_files").expect("rust_path_hack_build_no_arg"); - let source_dir = cwd.path().join_str("foo"); + let source_dir = cwd.path().join("foo"); assert!(make_dir_rwx(&source_dir)); - writeFile(&source_dir.join_str("lib.rs"), "pub fn f() { }"); + writeFile(&source_dir.join("lib.rs"), "pub fn f() { }"); - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", dest_workspace.as_str().unwrap().to_owned())]); @@ -1449,9 +1445,9 @@ fn rust_path_hack_build_no_arg() { fn rust_path_install_target() { let dir_for_path = TempDir::new( "source_workspace").expect("rust_path_install_target failed"); - let mut dir = mk_workspace(dir_for_path.path(), &Path::from_str("foo"), &NoVersion); + let mut dir = mk_workspace(dir_for_path.path(), &Path::new("foo"), &NoVersion); debug2!("dir = {}", dir.display()); - writeFile(&dir.join_str("main.rs"), "fn main() { let _x = (); }"); + writeFile(&dir.join("main.rs"), "fn main() { let _x = (); }"); let dir_to_install_to = TempDir::new( "dest_workspace").expect("rust_path_install_target failed"); let dir_to_install_to = dir_to_install_to.path(); @@ -1561,7 +1557,7 @@ fn notrans_flag_fail() { workspace, None, BAD_FLAG_CODE); assert!(!built_executable_exists(workspace, "foo")); assert!(!object_file_exists(workspace, "foo")); - assert!(!lib_exists(workspace, &Path::from_str("foo"), NoVersion)); + assert!(!lib_exists(workspace, &Path::new("foo"), NoVersion)); } } @@ -1605,7 +1601,7 @@ fn test_cfg_build() { let workspace = create_local_package(&p_id); let workspace = workspace.path(); // If the cfg flag gets messed up, this won't compile - writeFile(&workspace.join_many_str(["src", "foo-0.1", "main.rs"]), + writeFile(&workspace.join_many(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); let test_sys = test_sysroot(); // FIXME (#9639): This needs to handle non-utf8 paths @@ -1623,7 +1619,7 @@ fn test_cfg_fail() { let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); let workspace = workspace.path(); - writeFile(&workspace.join_many_str(["src", "foo-0.1", "main.rs"]), + writeFile(&workspace.join_many(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); let test_sys = test_sysroot(); // FIXME (#9639): This needs to handle non-utf8 paths @@ -1780,23 +1776,23 @@ fn pkgid_pointing_to_subdir() { // rustpkg should recognize that and treat the part after some_repo/ as a subdir let workspace = TempDir::new("parent_repo").expect("Couldn't create temp dir"); let workspace = workspace.path(); - assert!(os::mkdir_recursive(&workspace.join_many_str(["src", "mockgithub.com", - "mozilla", "some_repo"]), U_RWX)); + assert!(os::mkdir_recursive(&workspace.join_many(["src", "mockgithub.com", + "mozilla", "some_repo"]), U_RWX)); - let foo_dir = workspace.join_many_str(["src", "mockgithub.com", "mozilla", "some_repo", - "extras", "foo"]); - let bar_dir = workspace.join_many_str(["src", "mockgithub.com", "mozilla", "some_repo", - "extras", "bar"]); + let foo_dir = workspace.join_many(["src", "mockgithub.com", "mozilla", "some_repo", + "extras", "foo"]); + let bar_dir = workspace.join_many(["src", "mockgithub.com", "mozilla", "some_repo", + "extras", "bar"]); assert!(os::mkdir_recursive(&foo_dir, U_RWX)); assert!(os::mkdir_recursive(&bar_dir, U_RWX)); - writeFile(&foo_dir.join_str("lib.rs"), "pub fn f() {}"); - writeFile(&bar_dir.join_str("lib.rs"), "pub fn g() {}"); + writeFile(&foo_dir.join("lib.rs"), "pub fn f() {}"); + writeFile(&bar_dir.join("lib.rs"), "pub fn g() {}"); debug2!("Creating a file in {}", workspace.display()); - let testpkg_dir = workspace.join_many_str(["src", "testpkg-0.1"]); + let testpkg_dir = workspace.join_many(["src", "testpkg-0.1"]); assert!(os::mkdir_recursive(&testpkg_dir, U_RWX)); - writeFile(&testpkg_dir.join_str("main.rs"), + writeFile(&testpkg_dir.join("main.rs"), "extern mod foo = \"mockgithub.com/mozilla/some_repo/extras/foo\";\n extern mod bar = \"mockgithub.com/mozilla/some_repo/extras/bar\";\n use foo::f; use bar::g; \n @@ -1813,13 +1809,13 @@ fn test_recursive_deps() { let c_id = PkgId::new("c"); let b_workspace = create_local_package_with_dep(&b_id, &c_id); let b_workspace = b_workspace.path(); - writeFile(&b_workspace.join_many_str(["src", "c-0.1", "lib.rs"])), + writeFile(&b_workspace.join_many(["src", "c-0.1", "lib.rs"])), "pub fn g() {}"); let a_workspace = create_local_package(&a_id); let a_workspace = a_workspace.path(); - writeFile(&a_workspace.join_many_str(["src", "a-0.1", "main.rs"]), + writeFile(&a_workspace.join_many(["src", "a-0.1", "main.rs"]), "extern mod b; use b::f; fn main() { f(); }"); - writeFile(&b_workspace.join_many_str(["src", "b-0.1", "lib.rs"]), + writeFile(&b_workspace.join_many(["src", "b-0.1", "lib.rs"]), "extern mod c; use c::g; pub fn f() { g(); }"); // FIXME (#9639): This needs to handle non-utf8 paths let environment = Some(~[(~"RUST_PATH", b_workspace.as_str().unwrap().to_owned())]); @@ -1827,9 +1823,9 @@ fn test_recursive_deps() { command_line_test_with_env([~"install", ~"a"], a_workspace, environment); - assert_lib_exists(a_workspace, &Path::from_str("a"), NoVersion); - assert_lib_exists(b_workspace, &Path::from_str("b"), NoVersion); - assert_lib_exists(b_workspace, &Path::from_str("c"), NoVersion); + assert_lib_exists(a_workspace, &Path::new("a"), NoVersion); + assert_lib_exists(b_workspace, &Path::new("b"), NoVersion); + assert_lib_exists(b_workspace, &Path::new("c"), NoVersion); } #[test] @@ -1837,7 +1833,7 @@ fn test_install_to_rust_path() { let p_id = PkgId::new("foo"); let second_workspace = create_local_package(&p_id); let second_workspace = second_workspace.path(); - let first_workspace = mk_empty_workspace(&Path::from_str("p"), &NoVersion, "dest"); + let first_workspace = mk_empty_workspace(&Path::new("p"), &NoVersion, "dest"); let first_workspace = first_workspace.path(); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", @@ -1869,7 +1865,7 @@ fn test_target_specific_build_dir() { workspace); assert!(os::path_is_dir(&target_build_dir(workspace))); assert!(built_executable_exists(workspace, "foo")); - assert!(os::list_dir(&workspace.join_str("build")).len() == 1); + assert!(os::list_dir(&workspace.join("build")).len() == 1); } #[test] @@ -1883,10 +1879,10 @@ fn test_target_specific_install_dir() { ~"install", ~"foo"], workspace); - assert!(os::path_is_dir(&workspace.join_many_str(["lib", host_triple()]))); - assert_lib_exists(workspace, &Path::from_str("foo"), NoVersion); - assert!(os::list_dir(&workspace.join_str("lib")).len() == 1); - assert!(os::path_is_dir(&workspace.join_str("bin"))); + assert!(os::path_is_dir(&workspace.join_many(["lib", host_triple()]))); + assert_lib_exists(workspace, &Path::new("foo"), NoVersion); + assert!(os::list_dir(&workspace.join("lib")).len() == 1); + assert!(os::path_is_dir(&workspace.join("bin"))); assert_executable_exists(workspace, "foo"); } @@ -1896,10 +1892,10 @@ fn test_dependencies_terminate() { let b_id = PkgId::new("b"); let workspace = create_local_package(&b_id); let workspace = workspace.path(); - let b_dir = workspace.join_many_str(["src", "b-0.1"]); - let b_subdir = b_dir.join_str("test"); + let b_dir = workspace.join_many(["src", "b-0.1"]); + let b_subdir = b_dir.join("test"); assert!(os::mkdir_recursive(&b_subdir, U_RWX)); - writeFile(&b_subdir.join_str("test.rs"), + writeFile(&b_subdir.join("test.rs"), "extern mod b; use b::f; #[test] fn g() { f() }"); command_line_test([~"install", ~"b"], workspace); } @@ -1960,13 +1956,13 @@ fn correct_package_name_with_rust_path_hack() { let bar_id = PkgId::new("bar"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let dest_workspace = mk_empty_workspace(&Path::from_str("bar"), &NoVersion, "dest_workspace"); + let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); - writeFile(&dest_workspace.join_many_str(["src", "bar-0.1", "main.rs"]), + writeFile(&dest_workspace.join_many(["src", "bar-0.1", "main.rs"]), "extern mod blat; fn main() { let _x = (); }"); - let foo_path = foo_workspace.join_many_str(["src", "foo-0.1"]); + let foo_path = foo_workspace.join_many(["src", "foo-0.1"]); // FIXME (#9639): This needs to handle non-utf8 paths let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dest_workspace.as_str().unwrap(), foo_path.as_str().unwrap()))]); @@ -1989,7 +1985,7 @@ fn test_rustpkg_test_creates_exec() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - writeFile(&foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]), + writeFile(&foo_workspace.join_many(["src", "foo-0.1", "test.rs"]), "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -2013,7 +2009,7 @@ fn test_rebuild_when_needed() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let test_crate = foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]); + let test_crate = foo_workspace.join_many(["src", "foo-0.1", "test.rs"]); writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -2033,7 +2029,7 @@ fn test_no_rebuilding() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let test_crate = foo_workspace.join_many_str(["src", "foo-0.1", "test.rs"]); + let test_crate = foo_workspace.join_many(["src", "foo-0.1", "test.rs"]); writeFile(&test_crate, "#[test] fn f() { assert!('a' == 'a'); }"); command_line_test([~"test", ~"foo"], foo_workspace); assert!(test_executable_exists(foo_workspace, "foo")); @@ -2058,16 +2054,16 @@ fn test_installed_read_only() { let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); debug2!("repo_subdir = {}", repo_subdir.display()); - writeFile(&repo_subdir.join_str("main.rs"), + writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.join_str("lib.rs"), + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files // FIXME (#9639): This needs to handle non-utf8 paths command_line_test([~"install", temp_pkg_id.path.as_str().unwrap().to_owned()], repo); - let ws = repo.join_str(".rust"); + let ws = repo.join(".rust"); // Check that all files exist debug2!("Checking for files in {}", ws.display()); let exec = target_executable_in_workspace(&temp_pkg_id, &ws); @@ -2099,9 +2095,9 @@ fn test_installed_local_changes() { debug2!("repo_subdir = {}", repo_subdir.display()); assert!(os::mkdir_recursive(&repo.join_many_str([".rust", "src"]), U_RWX)); - writeFile(&repo_subdir.join_str("main.rs"), + writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); - writeFile(&repo_subdir.join_str("lib.rs"), + writeFile(&repo_subdir.join("lib.rs"), "pub fn f() { let _x = (); }"); add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files @@ -2127,13 +2123,13 @@ fn test_installed_local_changes() { }; // Make a local change to it - writeFile(&target_dir.join_str("lib.rs"), + writeFile(&target_dir.join("lib.rs"), "pub fn g() { let _x = (); }"); // Finally, make *another* package that uses it let importer_pkg_id = fake_pkg(); let main_subdir = create_local_package_in(&importer_pkg_id, hacking_workspace); - writeFile(&main_subdir.join_str("main.rs"), + writeFile(&main_subdir.join("main.rs"), "extern mod test = \"mockgithub.com/catamorphism/test-pkg\"; \ use test::g; fn main() { g(); }"); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 14de18734ed60..c5e81e6dc4a2c 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -467,8 +467,7 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { self.context.context.use_rust_path_hack, pkg_id); let (outputs_disc, inputs_disc) = - self.context.install(pkg_src, - &JustOne(Path::from_str(lib_crate_filename))); + self.context.install(pkg_src, &JustOne(Path::new(lib_crate_filename))); debug2!("Installed {}, returned {:?} dependencies and \ {:?} transitive dependencies", lib_name, outputs_disc.len(), inputs_disc.len()); @@ -492,12 +491,13 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { self.exec.discover_input(*what, *dep, digest_file_with_date( - &Path::from_str(*dep))); + &Path::new(dep.as_slice()))); } else if *what == ~"binary" { self.exec.discover_input(*what, *dep, - digest_only_date(&Path::from_str(*dep))); + digest_only_date( + &Path::new(dep.as_slice()))); } else { fail2!("Bad kind: {}", *what); diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs index d0c76498c6264..21abeeb03e3a2 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -99,7 +99,7 @@ pub fn try_getting_local_version(local_path: &Path) -> Option { let rustpath = rust_path(); for rp in rustpath.iter() { let local_path = rp.join_path(local_path); - let git_dir = local_path.join_str(".git"); + let git_dir = local_path.join(".git"); if !os::path_is_dir(&git_dir) { continue; } @@ -148,7 +148,7 @@ pub fn try_getting_version(remote_path: &Path) -> Option { str::from_utf8(outp.output), str::from_utf8(outp.error)); let mut output = None; - let git_dir = tmp_dir.join_str(".git"); + let git_dir = tmp_dir.join(".git"); debug2!("(getting version, now getting tags) executing \\{git --git-dir={} tag -l\\}", git_dir.display()); // FIXME (#9639): This needs to handle non-utf8 paths diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index 15f9760a6379f..b4c2a66c1aafd 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -52,7 +52,7 @@ pub fn pkg_parent_workspaces(cx: &Context, pkgid: &PkgId) -> ~[Path] { } pub fn is_workspace(p: &Path) -> bool { - os::path_is_dir(&p.join_str("src")) + os::path_is_dir(&p.join("src")) } /// Construct a workspace and package-ID name based on the current directory. @@ -60,7 +60,7 @@ pub fn is_workspace(p: &Path) -> bool { pub fn cwd_to_workspace() -> Option<(Path, PkgId)> { let cwd = os::getcwd(); for path in rust_path().move_iter() { - let srcpath = path.join_str("src"); + let srcpath = path.join("src"); if srcpath.is_ancestor_of(&cwd) { let rel = cwd.path_relative_from(&srcpath); let rel_s = rel.and_then_ref(|p|p.as_str()); diff --git a/src/libstd/io.rs b/src/libstd/io.rs index 434d781805bf8..c03fc6d7ffb61 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -1893,7 +1893,7 @@ mod tests { #[test] fn test_simple() { - let tmpfile = &Path::from_str("tmp/lib-io-test-simple.tmp"); + let tmpfile = &Path::new("tmp/lib-io-test-simple.tmp"); debug2!("{}", tmpfile.display()); let frood: ~str = ~"A hoopy frood who really knows where his towel is."; @@ -1911,7 +1911,7 @@ mod tests { #[test] fn test_each_byte_each_char_file() { // Issue #5056 -- shouldn't include trailing EOF. - let path = Path::from_str("tmp/lib-io-test-each-byte-each-char-file.tmp"); + let path = Path::new("tmp/lib-io-test-each-byte-each-char-file.tmp"); { // create empty, enough to reproduce a problem @@ -2011,7 +2011,7 @@ mod tests { #[test] fn file_reader_not_exist() { - match io::file_reader(&Path::from_str("not a file")) { + match io::file_reader(&Path::new("not a file")) { Err(e) => { assert_eq!(e, ~"error opening not a file"); } @@ -2022,7 +2022,7 @@ mod tests { #[test] #[should_fail] fn test_read_buffer_too_small() { - let path = &Path::from_str("tmp/lib-io-test-read-buffer-too-small.tmp"); + let path = &Path::new("tmp/lib-io-test-read-buffer-too-small.tmp"); // ensure the file exists io::file_writer(path, [io::Create]).unwrap(); @@ -2033,7 +2033,7 @@ mod tests { #[test] fn test_read_buffer_big_enough() { - let path = &Path::from_str("tmp/lib-io-test-read-buffer-big-enough.tmp"); + let path = &Path::new("tmp/lib-io-test-read-buffer-big-enough.tmp"); // ensure the file exists io::file_writer(path, [io::Create]).unwrap(); @@ -2044,14 +2044,14 @@ mod tests { #[test] fn test_write_empty() { - let file = io::file_writer(&Path::from_str("tmp/lib-io-test-write-empty.tmp"), + let file = io::file_writer(&Path::new("tmp/lib-io-test-write-empty.tmp"), [io::Create]).unwrap(); file.write([]); } #[test] fn file_writer_bad_name() { - match io::file_writer(&Path("?/?"), []) { + match io::file_writer(&Path::new("?/?"), []) { Err(e) => { assert!(e.starts_with("error opening")); } @@ -2076,7 +2076,7 @@ mod tests { #[test] fn test_read_write_le() { - let path = Path::from_str("tmp/lib-io-test-read-write-le.tmp"); + let path = Path::new("tmp/lib-io-test-read-write-le.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file @@ -2098,7 +2098,7 @@ mod tests { #[test] fn test_read_write_be() { - let path = Path::from_str("tmp/lib-io-test-read-write-be.tmp"); + let path = Path::new("tmp/lib-io-test-read-write-be.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file @@ -2120,7 +2120,7 @@ mod tests { #[test] fn test_read_be_int_n() { - let path = Path::from_str("tmp/lib-io-test-read-be-int-n.tmp"); + let path = Path::new("tmp/lib-io-test-read-be-int-n.tmp"); let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; // write the ints to the file @@ -2144,7 +2144,7 @@ mod tests { #[test] fn test_read_f32() { - let path = Path::from_str("tmp/lib-io-test-read-f32.tmp"); + let path = Path::new("tmp/lib-io-test-read-f32.tmp"); //big-endian floating-point 8.1250 let buf = ~[0x41, 0x02, 0x00, 0x00]; @@ -2162,7 +2162,7 @@ mod tests { #[test] fn test_read_write_f32() { - let path = Path::from_str("tmp/lib-io-test-read-write-f32.tmp"); + let path = Path::new("tmp/lib-io-test-read-write-f32.tmp"); let f:f32 = 8.1250; { diff --git a/src/libstd/os.rs b/src/libstd/os.rs index bfde1a86771ed..449888b4e2a45 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -513,7 +513,7 @@ pub fn self_exe_path() -> Option { } } - load_self().and_then(|path| Path::from_vec_opt(path).map(|p| p.dir_path())) + load_self().and_then(|path| Path::new_opt(path).map(|p| { p.pop(); p })) } @@ -538,7 +538,7 @@ pub fn self_exe_path() -> Option { pub fn homedir() -> Option { // FIXME (#7188): getenv needs a ~[u8] variant return match getenv("HOME") { - Some(ref p) if !p.is_empty() => Path::from_str_opt(*p), + Some(ref p) if !p.is_empty() => Path::new_opt(p.as_slice()), _ => secondary() }; @@ -551,7 +551,7 @@ pub fn homedir() -> Option { fn secondary() -> Option { do getenv("USERPROFILE").and_then |p| { if !p.is_empty() { - Path::from_str_opt(p) + Path::new_opt(p) } else { None } @@ -580,7 +580,7 @@ pub fn tmpdir() -> Path { if x.is_empty() { None } else { - Path::from_str_opt(x) + Path::new_opt(x) }, _ => None } @@ -589,9 +589,9 @@ pub fn tmpdir() -> Path { #[cfg(unix)] fn lookup() -> Path { if cfg!(target_os = "android") { - Path::from_str("/data/tmp") + Path::new("/data/tmp") } else { - getenv_nonempty("TMPDIR").unwrap_or(Path::from_str("/tmp")) + getenv_nonempty("TMPDIR").unwrap_or(Path::new("/tmp")) } } @@ -600,7 +600,7 @@ pub fn tmpdir() -> Path { getenv_nonempty("TMP").or( getenv_nonempty("TEMP").or( getenv_nonempty("USERPROFILE").or( - getenv_nonempty("WINDIR")))).unwrap_or(Path::from_str("C:\\Windows")) + getenv_nonempty("WINDIR")))).unwrap_or(Path::new("C:\\Windows")) } } @@ -762,7 +762,7 @@ pub fn list_dir(p: &Path) -> ~[Path] { fn rust_list_dir_wfd_size() -> libc::size_t; fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; } - let star = p.join_str("*"); + let star = p.join("*"); do as_utf16_p(star.as_str().unwrap()) |path_ptr| { let mut paths = ~[]; let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); @@ -778,7 +778,7 @@ pub fn list_dir(p: &Path) -> ~[Path] { let fp_vec = vec::from_buf( fp_buf, wcslen(fp_buf) as uint); let fp_str = str::from_utf16(fp_vec); - paths.push(Path::from_str(fp_str)); + paths.push(Path::new(fp_str)); } more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); } @@ -1830,13 +1830,13 @@ mod tests { #[test] fn test() { - assert!((!Path::from_str("test-path").is_absolute())); + assert!((!Path::new("test-path").is_absolute())); let cwd = getcwd(); debug2!("Current working directory: {}", cwd.display()); - debug2!("{:?}", make_absolute(&Path::from_str("test-path"))); - debug2!("{:?}", make_absolute(&Path::from_str("/usr/bin"))); + debug2!("{:?}", make_absolute(&Path::new("test-path"))); + debug2!("{:?}", make_absolute(&Path::new("/usr/bin"))); } #[test] @@ -1845,7 +1845,7 @@ mod tests { let oldhome = getenv("HOME"); setenv("HOME", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::new("/home/MountainView"))); setenv("HOME", ""); assert!(os::homedir().is_none()); @@ -1866,16 +1866,16 @@ mod tests { assert!(os::homedir().is_none()); setenv("HOME", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::new("/home/MountainView"))); setenv("HOME", ""); setenv("USERPROFILE", "/home/MountainView"); - assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::new("/home/MountainView"))); setenv("HOME", "/home/MountainView"); setenv("USERPROFILE", "/home/PaloAlto"); - assert_eq!(os::homedir(), Some(Path::from_str("/home/MountainView"))); + assert_eq!(os::homedir(), Some(Path::new("/home/MountainView"))); for s in oldhome.iter() { setenv("HOME", *s) } for s in olduserprofile.iter() { setenv("USERPROFILE", *s) } @@ -1891,12 +1891,12 @@ mod tests { // Issue #712 #[test] fn test_list_dir_no_invalid_memory_access() { - os::list_dir(&Path::from_str(".")); + os::list_dir(&Path::new(".")); } #[test] fn list_dir() { - let dirs = os::list_dir(&Path::from_str(".")); + let dirs = os::list_dir(&Path::new(".")); // Just assuming that we've got some contents in the current directory assert!(dirs.len() > 0u); @@ -1908,35 +1908,35 @@ mod tests { #[test] #[cfg(not(windows))] fn list_dir_root() { - let dirs = os::list_dir(&Path::from_str("/")); + let dirs = os::list_dir(&Path::new("/")); assert!(dirs.len() > 1); } #[test] #[cfg(windows)] fn list_dir_root() { - let dirs = os::list_dir(&Path::from_str("C:\\")); + let dirs = os::list_dir(&Path::new("C:\\")); assert!(dirs.len() > 1); } #[test] fn path_is_dir() { - assert!((os::path_is_dir(&Path::from_str(".")))); - assert!((!os::path_is_dir(&Path::from_str("test/stdtest/fs.rs")))); + assert!((os::path_is_dir(&Path::new(".")))); + assert!((!os::path_is_dir(&Path::new("test/stdtest/fs.rs")))); } #[test] fn path_exists() { - assert!((os::path_exists(&Path::from_str(".")))); - assert!((!os::path_exists(&Path::from_str( + assert!((os::path_exists(&Path::new(".")))); + assert!((!os::path_exists(&Path::new( "test/nonexistent-bogus-path")))); } #[test] fn copy_file_does_not_exist() { - assert!(!os::copy_file(&Path::from_str("test/nonexistent-bogus-path"), - &Path::from_str("test/other-bogus-path"))); - assert!(!os::path_exists(&Path::from_str("test/other-bogus-path"))); + assert!(!os::copy_file(&Path::new("test/nonexistent-bogus-path"), + &Path::new("test/other-bogus-path"))); + assert!(!os::path_exists(&Path::new("test/other-bogus-path"))); } #[test] @@ -1946,8 +1946,8 @@ mod tests { unsafe { let tempdir = getcwd(); // would like to use $TMPDIR, // doesn't seem to work on Linux - let input = tempdir.join_str("in.txt"); - let out = tempdir.join_str("out.txt"); + let input = tempdir.join("in.txt"); + let out = tempdir.join("out.txt"); /* Write the temp input file */ let ostream = do input.with_c_str |fromp| { @@ -1983,7 +1983,7 @@ mod tests { #[test] fn recursive_mkdir_slash() { - let path = Path::from_str("/"); + let path = Path::new("/"); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); } @@ -2032,7 +2032,7 @@ mod tests { } let mut path = tmpdir(); - path.push_str("mmap_file.tmp"); + path.push("mmap_file.tmp"); let size = MemoryMap::granularity() * 2; remove_file(&path); diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 1ecb31a2a8797..561155f22584f 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -20,8 +20,7 @@ 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 `::from_str()` or -`.component_iter()`. +identically for both path flavors, such as `.component_iter()`. 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 @@ -35,8 +34,8 @@ code, `Path` should be used to refer to the platform-native path, and methods used should be restricted to those defined in `GenericPath`, and those methods that are declared identically on both `PosixPath` and `WindowsPath`. -Creation of a path is typically done with either `Path::from_str(some_str)` or -`Path::from_vec(some_vec)`. This path can be modified with `.push()` and +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 @@ -44,10 +43,10 @@ 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::from_str("a/b/../c")` will return the path `a/c`. Similarly any attempt +`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 display, there is a method `.display()` +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 @@ -56,10 +55,10 @@ actually operates on the path; it is only intended for display. ## Example ```rust -let mut path = Path::from_str("/tmp/path"); +let mut path = Path::new("/tmp/path"); debug2!("path: {}", path.display()); -path.set_filename_str("foo"); -path.push_str("bar"); +path.set_filename("foo"); +path.push("bar"); debug2!("new path: {}", path.display()); let b = std::os::path_exists(&path); debug2!("path exists: {}", b); @@ -70,6 +69,7 @@ debug2!("path exists: {}", b); use container::Container; use c_str::CString; use clone::Clone; +use either::{Left, Right}; use fmt; use iter::Iterator; use option::{Option, None, Some}; @@ -131,7 +131,7 @@ condition! { /// A trait that represents the generic operations available on paths pub trait GenericPath: Clone + GenericPathUnsafe { - /// Creates a new Path from a byte vector. + /// Creates a new Path from a byte vector or string. /// The resulting Path will always be normalized. /// /// # Failure @@ -140,52 +140,24 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// See individual Path impls for additional restrictions. #[inline] - fn from_vec(path: &[u8]) -> Self { - if contains_nul(path) { - let path = self::null_byte::cond.raise(path.to_owned()); + fn new(path: T) -> Self { + if contains_nul(path.container_as_bytes()) { + let path = self::null_byte::cond.raise(path.container_into_owned_bytes()); assert!(!contains_nul(path)); - unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + unsafe { GenericPathUnsafe::new_unchecked(path) } } else { - unsafe { GenericPathUnsafe::from_vec_unchecked(path) } + unsafe { GenericPathUnsafe::new_unchecked(path) } } } - /// Creates a new Path from a byte vector, if possible. + /// Creates a new Path from a byte vector or string, if possible. /// The resulting Path will always be normalized. #[inline] - fn from_vec_opt(path: &[u8]) -> Option { - if contains_nul(path) { + fn new_opt(path: T) -> Option { + if contains_nul(path.container_as_bytes()) { None } else { - Some(unsafe { GenericPathUnsafe::from_vec_unchecked(path) }) - } - } - - /// Creates a new Path from a string. - /// The resulting Path will always be normalized. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the path contains a NUL. - #[inline] - fn from_str(path: &str) -> Self { - let v = path.as_bytes(); - if contains_nul(v) { - GenericPath::from_vec(path.as_bytes()) // let from_vec handle the condition - } else { - unsafe { GenericPathUnsafe::from_str_unchecked(path) } - } - } - - /// Creates a new Path from a string, if possible. - /// The resulting Path will always be normalized. - #[inline] - fn from_str_opt(path: &str) -> Option { - let v = path.as_bytes(); - if contains_nul(v) { - None - } else { - Some(unsafe { GenericPathUnsafe::from_str_unchecked(path) }) + Some(unsafe { GenericPathUnsafe::new_unchecked(path) }) } } @@ -199,7 +171,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { let v = path.as_bytes(); // v is NUL-terminated. Strip it off let v = v.slice_to(v.len()-1); - unsafe { GenericPathUnsafe::from_vec_unchecked(v) } + unsafe { GenericPathUnsafe::new_unchecked(v) } } /// Returns the path as a string, if possible. @@ -209,9 +181,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe { str::from_utf8_slice_opt(self.as_vec()) } + /// Converts the Path into an owned string, if possible + fn into_str(self) -> Option<~str>; + /// Returns the path as a byte vector fn as_vec<'a>(&'a self) -> &'a [u8]; + /// Converts the Path into an owned byte vector + fn into_vec(self) -> ~[u8]; + /// Provides the path as a string /// /// If the path is not UTF-8, invalid sequences will be replaced with the unicode @@ -345,115 +323,91 @@ pub trait GenericPath: Clone + GenericPathUnsafe { self.extension().and_then(str::from_utf8_slice_opt) } - /// Replaces the directory portion of the path with the given byte vector. + /// Replaces the directory portion of the path with the given byte vector or string. /// If `self` represents the root of the filesystem hierarchy, the last path component - /// of the given byte vector becomes the filename. + /// of the argument becomes the filename. /// /// # Failure /// /// Raises the `null_byte` condition if the dirname contains a NUL. #[inline] - fn set_dirname(&mut self, dirname: &[u8]) { - if contains_nul(dirname) { - let dirname = self::null_byte::cond.raise(dirname.to_owned()); + fn set_dirname(&mut self, dirname: T) { + if contains_nul(dirname.container_as_bytes()) { + let dirname = self::null_byte::cond.raise(dirname.container_into_owned_bytes()); assert!(!contains_nul(dirname)); unsafe { self.set_dirname_unchecked(dirname) } } else { unsafe { self.set_dirname_unchecked(dirname) } } } - /// Replaces the directory portion of the path with the given string. - /// See `set_dirname` for details. - #[inline] - fn set_dirname_str(&mut self, dirname: &str) { - if contains_nul(dirname.as_bytes()) { - self.set_dirname(dirname.as_bytes()) // triggers null_byte condition - } else { - unsafe { self.set_dirname_str_unchecked(dirname) } - } - } - /// Replaces the filename portion of the path with the given byte vector. + /// 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. /// /// # Failure /// /// Raises the `null_byte` condition if the filename contains a NUL. #[inline] - fn set_filename(&mut self, filename: &[u8]) { - if contains_nul(filename) { - let filename = self::null_byte::cond.raise(filename.to_owned()); + fn set_filename(&mut self, filename: T) { + if contains_nul(filename.container_as_bytes()) { + let filename = self::null_byte::cond.raise(filename.container_into_owned_bytes()); assert!(!contains_nul(filename)); unsafe { self.set_filename_unchecked(filename) } } else { unsafe { self.set_filename_unchecked(filename) } } } - /// Replaces the filename portion of the path with the given string. - /// See `set_filename` for details. - #[inline] - fn set_filename_str(&mut self, filename: &str) { - if contains_nul(filename.as_bytes()) { - self.set_filename(filename.as_bytes()) // triggers null_byte condition - } else { - unsafe { self.set_filename_str_unchecked(filename) } - } - } - /// Replaces the filestem with the given byte vector. + /// Replaces the filestem with the given byte vector or string. /// If there is no extension in `self` (or `self` has no filename), this is equivalent - /// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including + /// to `set_filename`. Otherwise, if the argument is [] or "", the extension (including /// the preceding '.') becomes the new filename. /// /// # Failure /// /// Raises the `null_byte` condition if the filestem contains a NUL. - fn set_filestem(&mut self, filestem: &[u8]) { + fn set_filestem(&mut self, filestem: T) { // borrowck is being a pain here let val = { match self.filename() { - None => None, + None => Left(filestem), Some(name) => { let dot = '.' as u8; match name.rposition_elem(&dot) { - None | Some(0) => None, + None | Some(0) => Left(filestem), Some(idx) => { let mut v; - if contains_nul(filestem) { - let filestem = self::null_byte::cond.raise(filestem.to_owned()); + if contains_nul(filestem.container_as_bytes()) { + let filestem = filestem.container_into_owned_bytes(); + let filestem = self::null_byte::cond.raise(filestem); assert!(!contains_nul(filestem)); v = filestem; let n = v.len(); v.reserve(n + name.len() - idx); } else { + let filestem = filestem.container_as_bytes(); v = vec::with_capacity(filestem.len() + name.len() - idx); v.push_all(filestem); } v.push_all(name.slice_from(idx)); - Some(v) + Right(v) } } } } }; match val { - None => self.set_filename(filestem), - Some(v) => unsafe { self.set_filename_unchecked(v) } + Left(v) => self.set_filename(v), + Right(v) => unsafe { self.set_filename_unchecked(v) } } } - /// Replaces the filestem with the given string. - /// See `set_filestem` for details. - #[inline] - fn set_filestem_str(&mut self, filestem: &str) { - self.set_filestem(filestem.as_bytes()) - } - /// Replaces the extension with the given byte vector. + /// Replaces the extension with the given byte vector or string. /// If there is no extension in `self`, this adds one. - /// If the given byte vector is [], this removes the extension. + /// If the argument is [] or "", this removes the extension. /// If `self` has no filename, this is a no-op. /// /// # Failure /// /// Raises the `null_byte` condition if the extension contains a NUL. - fn set_extension(&mut self, extension: &[u8]) { + fn set_extension(&mut self, extension: T) { // borrowck causes problems here too let val = { match self.filename() { @@ -462,12 +416,12 @@ pub trait GenericPath: Clone + GenericPathUnsafe { let dot = '.' as u8; match name.rposition_elem(&dot) { None | Some(0) => { - if extension.is_empty() { + if extension.container_as_bytes().is_empty() { None } else { let mut v; - if contains_nul(extension) { - let ext = extension.to_owned(); + if contains_nul(extension.container_as_bytes()) { + let ext = extension.container_into_owned_bytes(); let extension = self::null_byte::cond.raise(ext); assert!(!contains_nul(extension)); v = vec::with_capacity(name.len() + extension.len() + 1); @@ -475,6 +429,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { v.push(dot); v.push_all(extension); } else { + let extension = extension.container_as_bytes(); v = vec::with_capacity(name.len() + extension.len() + 1); v.push_all(name); v.push(dot); @@ -484,18 +439,19 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } } Some(idx) => { - if extension.is_empty() { + if extension.container_as_bytes().is_empty() { Some(name.slice_to(idx).to_owned()) } else { let mut v; - if contains_nul(extension) { - let ext = extension.to_owned(); + if contains_nul(extension.container_as_bytes()) { + let ext = extension.container_into_owned_bytes(); let extension = self::null_byte::cond.raise(ext); assert!(!contains_nul(extension)); v = vec::with_capacity(idx + extension.len() + 1); v.push_all(name.slice_to(idx+1)); v.push_all(extension); } else { + let extension = extension.container_as_bytes(); v = vec::with_capacity(idx + extension.len() + 1); v.push_all(name.slice_to(idx+1)); v.push_all(extension); @@ -512,31 +468,25 @@ pub trait GenericPath: Clone + GenericPathUnsafe { Some(v) => unsafe { self.set_filename_unchecked(v) } } } - /// Replaces the extension with the given string. - /// See `set_extension` for details. - #[inline] - fn set_extension_str(&mut self, extension: &str) { - self.set_extension(extension.as_bytes()) - } - /// Adds the given extension (as a byte vector) to the file. + /// Adds the given extension (as a byte vector or string) to the file. /// This does not remove any existing extension. /// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`. /// If `self` has no filename, this is a no-op. - /// If the given byte vector is [], this is a no-op. + /// If the argument is [] or "", this is a no-op. /// /// # Failure /// /// Raises the `null_byte` condition if the extension contains a NUL. - fn add_extension(&mut self, extension: &[u8]) { - if extension.is_empty() { return; } + fn add_extension(&mut self, extension: T) { + if extension.container_as_bytes().is_empty() { return; } // appease borrowck let val = { match self.filename() { None => None, Some(name) => { let mut v; - if contains_nul(extension) { - let ext = extension.to_owned(); + if contains_nul(extension.container_as_bytes()) { + let ext = extension.container_into_owned_bytes(); let extension = self::null_byte::cond.raise(ext); assert!(!contains_nul(extension)); v = vec::with_capacity(name.len() + 1 + extension.len()); @@ -544,6 +494,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { v.push('.' as u8); v.push_all(extension); } else { + let extension = extension.container_as_bytes(); v = vec::with_capacity(name.len() + 1 + extension.len()); v.push_all(name); v.push('.' as u8); @@ -558,105 +509,71 @@ pub trait GenericPath: Clone + GenericPathUnsafe { Some(v) => unsafe { self.set_filename_unchecked(v) } } } - /// Adds the given extension (as a string) to the file. - /// See `add_extension` for details. - #[inline] - fn add_extension_str(&mut self, extension: &str) { - self.add_extension(extension.as_bytes()) - } - /// Returns a new Path constructed by replacing the dirname with the given byte vector. + /// Returns a new Path constructed by replacing the dirname with the given + /// byte vector or string. /// See `set_dirname` for details. /// /// # Failure /// /// Raises the `null_byte` condition if the dirname contains a NUL. #[inline] - fn with_dirname(&self, dirname: &[u8]) -> Self { + fn with_dirname(&self, dirname: T) -> Self { let mut p = self.clone(); p.set_dirname(dirname); p } - /// Returns a new Path constructed by replacing the dirname with the given string. - /// See `set_dirname` for details. - #[inline] - fn with_dirname_str(&self, dirname: &str) -> Self { - let mut p = self.clone(); - p.set_dirname_str(dirname); - p - } - /// Returns a new Path constructed by replacing the filename with the given byte vector. + /// Returns a new Path constructed by replacing the filename with the given + /// byte vector or string. /// See `set_filename` for details. /// /// # Failure /// /// Raises the `null_byte` condition if the filename contains a NUL. #[inline] - fn with_filename(&self, filename: &[u8]) -> Self { + fn with_filename(&self, filename: T) -> Self { let mut p = self.clone(); p.set_filename(filename); p } - /// Returns a new Path constructed by replacing the filename with the given string. - /// See `set_filename` for details. - #[inline] - fn with_filename_str(&self, filename: &str) -> Self { - let mut p = self.clone(); - p.set_filename_str(filename); - p - } - /// Returns a new Path constructed by setting the filestem to the given byte vector. + /// Returns a new Path constructed by setting the filestem to the given + /// byte vector or string. /// See `set_filestem` for details. /// /// # Failure /// /// Raises the `null_byte` condition if the filestem contains a NUL. #[inline] - fn with_filestem(&self, filestem: &[u8]) -> Self { + fn with_filestem(&self, filestem: T) -> Self { let mut p = self.clone(); p.set_filestem(filestem); p } - /// Returns a new Path constructed by setting the filestem to the given string. - /// See `set_filestem` for details. - #[inline] - fn with_filestem_str(&self, filestem: &str) -> Self { - let mut p = self.clone(); - p.set_filestem_str(filestem); - p - } - /// Returns a new Path constructed by setting the extension to the given byte vector. + /// Returns a new Path constructed by setting the extension to the given + /// byte vector or string. /// See `set_extension` for details. /// /// # Failure /// /// Raises the `null_byte` condition if the extension contains a NUL. #[inline] - fn with_extension(&self, extension: &[u8]) -> Self { + fn with_extension(&self, extension: T) -> Self { let mut p = self.clone(); p.set_extension(extension); p } - /// Returns a new Path constructed by setting the extension to the given string. - /// See `set_extension` for details. - #[inline] - fn with_extension_str(&self, extension: &str) -> Self { - let mut p = self.clone(); - p.set_extension_str(extension); - p - } /// Returns the directory component of `self`, as a Path. /// If `self` represents the root of the filesystem hierarchy, returns `self`. fn dir_path(&self) -> Self { // self.dirname() returns a NUL-free vector - unsafe { GenericPathUnsafe::from_vec_unchecked(self.dirname()) } + unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) } } /// Returns the file component of `self`, as a relative Path. /// If `self` represents the root of the filesystem hierarchy, returns None. fn file_path(&self) -> Option { // self.filename() returns a NUL-free vector - self.filename().map_move(|v| unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) + self.filename().map_move(|v| unsafe { GenericPathUnsafe::new_unchecked(v) }) } /// Returns a Path that represents the filesystem root that `self` is rooted in. @@ -664,51 +581,41 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// If `self` is not absolute, or vol-relative in the case of Windows, this returns None. fn root_path(&self) -> Option; - /// Pushes a path (as a byte vector) onto `self`. + /// Pushes a path (as a byte vector or string) onto `self`. /// If the argument represents an absolute path, it replaces `self`. /// /// # Failure /// /// Raises the `null_byte` condition if the path contains a NUL. #[inline] - fn push(&mut self, path: &[u8]) { - if contains_nul(path) { - let path = self::null_byte::cond.raise(path.to_owned()); + fn push(&mut self, path: T) { + if contains_nul(path.container_as_bytes()) { + let path = self::null_byte::cond.raise(path.container_into_owned_bytes()); assert!(!contains_nul(path)); unsafe { self.push_unchecked(path) } } else { unsafe { self.push_unchecked(path) } } } - /// Pushes a path (as a string) onto `self. - /// See `push` for details. - #[inline] - fn push_str(&mut self, path: &str) { - if contains_nul(path.as_bytes()) { - self.push(path.as_bytes()) // triggers null_byte condition - } else { - unsafe { self.push_str_unchecked(path) } - } - } /// Pushes a Path onto `self`. /// If the argument represents an absolute path, it replaces `self`. #[inline] fn push_path(&mut self, path: &Self) { self.push(path.as_vec()) } - /// Pushes multiple paths (as byte vectors) onto `self`. + /// Pushes multiple paths (as byte vectors or strings) onto `self`. /// See `push` for details. #[inline] - fn push_many>(&mut self, paths: &[V]) { - for p in paths.iter() { - self.push(p.as_slice()); - } - } - /// Pushes multiple paths (as strings) onto `self`. - #[inline] - fn push_many_str(&mut self, paths: &[S]) { - for p in paths.iter() { - self.push_str(p.as_slice()); + fn push_many(&mut self, paths: &[T]) { + let t: Option = None; + if BytesContainer::is_str(t) { + for p in paths.iter() { + self.push(p.container_as_str()) + } + } else { + for p in paths.iter() { + self.push(p.container_as_bytes()) + } } } /// Pops the last path component off of `self` and returns it. @@ -722,26 +629,19 @@ pub trait GenericPath: Clone + GenericPathUnsafe { self.pop().and_then(|v| str::from_utf8_owned_opt(v)) } - /// Returns a new Path constructed by joining `self` with the given path (as a byte vector). + /// 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. /// /// # Failure /// /// Raises the `null_byte` condition if the path contains a NUL. #[inline] - fn join(&self, path: &[u8]) -> Self { + fn join(&self, path: T) -> Self { let mut p = self.clone(); p.push(path); p } - /// Returns a new Path constructed by joining `self` with the given path (as a string). - /// See `join` for details. - #[inline] - fn join_str(&self, path: &str) -> Self { - let mut p = self.clone(); - p.push_str(path); - p - } /// Returns a new Path constructed by joining `self` with the given path. /// If the given path is absolute, the new Path will represent just that. #[inline] @@ -750,22 +650,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe { p.push_path(path); p } - /// Returns a new Path constructed by joining `self` with the given paths (as byte vectors). + /// Returns a new Path constructed by joining `self` with the given paths + /// (as byte vectors or strings). /// See `join` for details. #[inline] - fn join_many>(&self, paths: &[V]) -> Self { + fn join_many(&self, paths: &[T]) -> Self { let mut p = self.clone(); p.push_many(paths); p } - /// Returns a new Path constructed by joining `self` with the given paths (as strings). - /// See `join` for details. - #[inline] - fn join_many_str(&self, paths: &[S]) -> Self { - let mut p = self.clone(); - p.push_many_str(paths); - p - } /// Returns whether `self` represents an absolute path. /// An absolute path is defined as one that, when joined to another path, will @@ -805,57 +698,59 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } true } + + /// Returns whether the relative path `child` is a suffix of `self`. + fn ends_with_path(&self, child: &Self) -> bool; +} + +/// A trait that represents something bytes-like (e.g. a &[u8] or a &str) +pub trait BytesContainer { + /// Returns a &[u8] representing the receiver + fn container_as_bytes<'a>(&'a self) -> &'a [u8]; + /// Consumes the receiver and converts it into ~[u8] + #[inline] + fn container_into_owned_bytes(self) -> ~[u8] { + self.container_as_bytes().to_owned() + } + /// Returns the receiver interpreted as a utf-8 string + /// + /// # Failure + /// + /// Raises `str::null_byte` if not utf-8 + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + str::from_utf8_slice(self.container_as_bytes()) + } + /// Returns the receiver interpreted as a utf-8 string, if possible + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + str::from_utf8_slice_opt(self.container_as_bytes()) + } + /// Returns whether the concrete receiver is a string type + // FIXME (#8888): Remove unused arg once :: works + #[inline] + fn is_str(_: Option) -> bool { false } } /// A trait that represents the unsafe operations on GenericPaths pub trait GenericPathUnsafe { - /// Creates a new Path from a byte vector without checking for null bytes. + /// Creates a new Path without checking for null bytes. /// The resulting Path will always be normalized. - unsafe fn from_vec_unchecked(path: &[u8]) -> Self; + unsafe fn new_unchecked(path: T) -> Self; - /// Creates a new Path from a str without checking for null bytes. - /// The resulting Path will always be normalized. - #[inline] - unsafe fn from_str_unchecked(path: &str) -> Self { - GenericPathUnsafe::from_vec_unchecked(path.as_bytes()) - } - - /// Replaces the directory portion of the path with the given byte vector without - /// checking for null bytes. + /// Replaces the directory portion of the path without checking for null + /// bytes. /// See `set_dirname` for details. - unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]); - - /// Replaces the directory portion of the path with the given str without - /// checking for null bytes. - /// See `set_dirname_str` for details. - #[inline] - unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) { - self.set_dirname_unchecked(dirname.as_bytes()) - } + unsafe fn set_dirname_unchecked(&mut self, dirname: T); - /// Replaces the filename portion of the path with the given byte vector without - /// checking for null bytes. + /// Replaces the filename portion of the path without checking for null + /// bytes. /// See `set_filename` for details. - unsafe fn set_filename_unchecked(&mut self, filename: &[u8]); - - /// Replaces the filename portion of the path with the given str without - /// checking for null bytes. - /// See `set_filename_str` for details. - #[inline] - unsafe fn set_filename_str_unchecked(&mut self, filename: &str) { - self.set_filename_unchecked(filename.as_bytes()) - } + unsafe fn set_filename_unchecked(&mut self, filename: T); - /// Pushes a byte vector onto `self` without checking for null bytes. + /// Pushes a path onto `self` without checking for null bytes. /// See `push` for details. - unsafe fn push_unchecked(&mut self, path: &[u8]); - - /// Pushes a str onto `self` without checking for null bytes. - /// See `push_str` for details. - #[inline] - unsafe fn push_str_unchecked(&mut self, path: &str) { - self.push_unchecked(path.as_bytes()) - } + unsafe fn push_unchecked(&mut self, path: T); } /// Helper struct for printing paths with format!() @@ -883,6 +778,86 @@ impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> { } } +impl<'self> BytesContainer for &'self str { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_bytes() + } + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + *self + } + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + Some(*self) + } + #[inline] + fn is_str(_: Option<&'self str>) -> bool { true } +} + +impl BytesContainer for ~str { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_bytes() + } + #[inline] + fn container_into_owned_bytes(self) -> ~[u8] { + self.into_bytes() + } + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + self.as_slice() + } + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + Some(self.as_slice()) + } + #[inline] + fn is_str(_: Option<~str>) -> bool { true } +} + +impl BytesContainer for @str { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_bytes() + } + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + self.as_slice() + } + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + Some(self.as_slice()) + } + #[inline] + fn is_str(_: Option<@str>) -> bool { true } +} + +impl<'self> BytesContainer for &'self [u8] { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + *self + } +} + +impl BytesContainer for ~[u8] { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_slice() + } + #[inline] + fn container_into_owned_bytes(self) -> ~[u8] { + self + } +} + +impl BytesContainer for @[u8] { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_slice() + } +} + #[inline(always)] fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index f39062e66cb33..3400b673c3012 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -23,7 +23,7 @@ use to_bytes::IterBytes; use util; use vec; use vec::{CopyableVector, RSplitIterator, SplitIterator, Vector, VectorVector}; -use super::{GenericPath, GenericPathUnsafe}; +use super::{BytesContainer, GenericPath, GenericPathUnsafe}; #[cfg(not(target_os = "win32"))] use libc; @@ -65,12 +65,7 @@ impl Eq for Path { impl FromStr for Path { fn from_str(s: &str) -> Option { - let v = s.as_bytes(); - if contains_nul(v) { - None - } else { - Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) - } + Path::new_opt(s) } } @@ -95,14 +90,15 @@ impl IterBytes for Path { } impl GenericPathUnsafe for Path { - unsafe fn from_vec_unchecked(path: &[u8]) -> Path { - let path = Path::normalize(path); + unsafe fn new_unchecked(path: T) -> Path { + let path = Path::normalize(path.container_as_bytes()); assert!(!path.is_empty()); let idx = path.rposition_elem(&sep); Path{ repr: path, sepidx: idx } } - unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { + unsafe fn set_dirname_unchecked(&mut self, dirname: T) { + let dirname = dirname.container_as_bytes(); match self.sepidx { None if bytes!(".") == self.repr || bytes!("..") == self.repr => { self.repr = Path::normalize(dirname); @@ -134,7 +130,8 @@ impl GenericPathUnsafe for Path { self.sepidx = self.repr.rposition_elem(&sep); } - unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { + unsafe fn set_filename_unchecked(&mut self, filename: T) { + let filename = filename.container_as_bytes(); match self.sepidx { None if bytes!("..") == self.repr => { let mut v = vec::with_capacity(3 + filename.len()); @@ -163,7 +160,8 @@ impl GenericPathUnsafe for Path { self.sepidx = self.repr.rposition_elem(&sep); } - unsafe fn push_unchecked(&mut self, path: &[u8]) { + unsafe fn push_unchecked(&mut self, path: T) { + let path = path.container_as_bytes(); if !path.is_empty() { if path[0] == sep { self.repr = Path::normalize(path); @@ -185,6 +183,14 @@ impl GenericPath for Path { self.repr.as_slice() } + fn into_vec(self) -> ~[u8] { + self.repr + } + + fn into_str(self) -> Option<~str> { + str::from_utf8_owned_opt(self.repr) + } + fn dirname<'a>(&'a self) -> &'a [u8] { match self.sepidx { None if bytes!("..") == self.repr => self.repr.as_slice(), @@ -230,7 +236,7 @@ impl GenericPath for Path { fn root_path(&self) -> Option { if self.is_absolute() { - Some(Path::from_str("/")) + Some(Path::new("/")) } else { None } @@ -299,52 +305,41 @@ impl GenericPath for Path { } } } - Some(Path::from_vec(comps.connect_vec(&sep))) + Some(Path::new(comps.connect_vec(&sep))) + } + } + + fn ends_with_path(&self, child: &Path) -> bool { + if !child.is_relative() { return false; } + let mut selfit = self.rev_component_iter(); + let mut childit = child.rev_component_iter(); + 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 + /// Returns a new Path from a byte vector or string /// /// # Failure /// /// Raises the `null_byte` condition if the vector contains a NUL. #[inline] - pub fn from_vec(v: &[u8]) -> Path { - GenericPath::from_vec(v) - } - - /// Returns a new Path from a byte vector, if possible - #[inline] - pub fn from_vec_opt(v: &[u8]) -> Option { - GenericPath::from_vec_opt(v) - } - - /// Returns a new Path from a string - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the str contains a NUL. - #[inline] - pub fn from_str(s: &str) -> Path { - GenericPath::from_str(s) + pub fn new(path: T) -> Path { + GenericPath::new(path) } - /// Returns a new Path from a string, if possible + /// Returns a new Path from a byte vector or string, if possible #[inline] - pub fn from_str_opt(s: &str) -> Option { - GenericPath::from_str_opt(s) - } - - /// Converts the Path into an owned byte vector - pub fn into_vec(self) -> ~[u8] { - self.repr - } - - /// Converts the Path into an owned string, if possible - pub fn into_str(self) -> Option<~str> { - str::from_utf8_owned_opt(self.repr) + pub fn new_opt(path: T) -> Option { + GenericPath::new_opt(path) } /// Returns a normalized byte vector representation of a path, by removing all empty @@ -427,22 +422,6 @@ impl Path { pub fn rev_str_component_iter<'a>(&'a self) -> RevStrComponentIter<'a> { self.rev_component_iter().map(str::from_utf8_slice_opt) } - - /// Returns whether the relative path `child` is a suffix of `self`. - pub fn ends_with_path(&self, child: &Path) -> bool { - if !child.is_relative() { return false; } - let mut selfit = self.rev_component_iter(); - let mut childit = child.rev_component_iter(); - 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 - } } // None result means the byte vector didn't need normalizing @@ -611,54 +590,55 @@ mod tests { #[test] fn test_paths() { - t!(v: Path::from_vec([]), b!(".")); - t!(v: Path::from_vec(b!("/")), b!("/")); - t!(v: Path::from_vec(b!("a/b/c")), b!("a/b/c")); - t!(v: Path::from_vec(b!("a/b/c", 0xff)), b!("a/b/c", 0xff)); - t!(v: Path::from_vec(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); - let p = Path::from_vec(b!("a/b/c", 0xff)); + 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", 0xff)), b!("a/b/c", 0xff)); + t!(v: Path::new(b!(0xff, "/../foo", 0x80)), b!("foo", 0x80)); + let p = Path::new(b!("a/b/c", 0xff)); assert_eq!(p.as_str(), None); - t!(s: Path::from_str(""), "."); - t!(s: Path::from_str("/"), "/"); - t!(s: Path::from_str("hi"), "hi"); - t!(s: Path::from_str("hi/"), "hi"); - t!(s: Path::from_str("/lib"), "/lib"); - t!(s: Path::from_str("/lib/"), "/lib"); - t!(s: Path::from_str("hi/there"), "hi/there"); - t!(s: Path::from_str("hi/there.txt"), "hi/there.txt"); - - t!(s: Path::from_str("hi/there/"), "hi/there"); - t!(s: Path::from_str("hi/../there"), "there"); - t!(s: Path::from_str("../hi/there"), "../hi/there"); - t!(s: Path::from_str("/../hi/there"), "/hi/there"); - t!(s: Path::from_str("foo/.."), "."); - t!(s: Path::from_str("/foo/.."), "/"); - t!(s: Path::from_str("/foo/../.."), "/"); - t!(s: Path::from_str("/foo/../../bar"), "/bar"); - t!(s: Path::from_str("/./hi/./there/."), "/hi/there"); - t!(s: Path::from_str("/./hi/./there/./.."), "/hi"); - t!(s: Path::from_str("foo/../.."), ".."); - t!(s: Path::from_str("foo/../../.."), "../.."); - t!(s: Path::from_str("foo/../../bar"), "../bar"); - - assert_eq!(Path::from_vec(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); - assert_eq!(Path::from_vec(b!("/foo/../../bar")).into_vec(), + 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").to_owned()); + assert_eq!(Path::new(b!("/foo/../../bar")).into_vec(), b!("/bar").to_owned()); - assert_eq!(Path::from_str("foo/bar").into_str(), Some(~"foo/bar")); - assert_eq!(Path::from_str("/foo/../../bar").into_str(), Some(~"/bar")); + assert_eq!(Path::new("foo/bar").into_str(), Some(~"foo/bar")); + assert_eq!(Path::new("/foo/../../bar").into_str(), Some(~"/bar")); - let p = Path::from_vec(b!("foo/bar", 0x80)); + let p = Path::new(b!("foo/bar", 0x80)); assert_eq!(p.as_str(), None); - assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).into_str(), None); + assert_eq!(Path::new(b!("foo", 0xff, "/bar")).into_str(), None); } #[test] fn test_opt_paths() { - assert_eq!(Path::from_vec_opt(b!("foo/bar", 0)), None); - t!(v: Path::from_vec_opt(b!("foo/bar")).unwrap(), b!("foo/bar")); - assert_eq!(Path::from_str_opt("foo/bar\0"), None); - t!(s: Path::from_str_opt("foo/bar").unwrap(), "foo/bar"); + assert_eq!(Path::new_opt(b!("foo/bar", 0)), None); + t!(v: Path::new_opt(b!("foo/bar")).unwrap(), b!("foo/bar")); + assert_eq!(Path::new_opt("foo/bar\0"), None); + t!(s: Path::new_opt("foo/bar").unwrap(), "foo/bar"); } #[test] @@ -671,7 +651,7 @@ mod tests { assert_eq!(v.as_slice(), b!("foo/bar", 0)); (b!("/bar").to_owned()) }).inside { - Path::from_vec(b!("foo/bar", 0)) + Path::new(b!("foo/bar", 0)) }; assert!(handled); assert_eq!(p.as_vec(), b!("/bar")); @@ -727,16 +707,16 @@ mod tests { ) ) - t!(~"from_vec() w/nul" => { + t!(~"new() w/nul" => { do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { - Path::from_vec(b!("foo/bar", 0)) + Path::new(b!("foo/bar", 0)) }; }) t!(~"set_filename w/nul" => { - let mut p = Path::from_vec(b!("foo/bar")); + let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -745,7 +725,7 @@ mod tests { }) t!(~"set_dirname w/nul" => { - let mut p = Path::from_vec(b!("foo/bar")); + let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -754,7 +734,7 @@ mod tests { }) t!(~"push w/nul" => { - let mut p = Path::from_vec(b!("foo/bar")); + let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -765,46 +745,46 @@ mod tests { #[test] fn test_display_str() { - assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); - assert_eq!(Path::from_vec(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD"); - assert_eq!(Path::from_vec(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar"); - assert_eq!(Path::from_vec(b!("foo", 0xff, "/bar")).to_filename_display_str(), Some(~"bar")); - assert_eq!(Path::from_vec(b!("foo/", 0xff, "bar")).to_filename_display_str(), + assert_eq!(Path::new("foo").to_display_str(), ~"foo"); + assert_eq!(Path::new(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD"); + assert_eq!(Path::new(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar"); + assert_eq!(Path::new(b!("foo", 0xff, "/bar")).to_filename_display_str(), Some(~"bar")); + assert_eq!(Path::new(b!("foo/", 0xff, "bar")).to_filename_display_str(), Some(~"\uFFFDbar")); - assert_eq!(Path::from_vec(b!("/")).to_filename_display_str(), None); + assert_eq!(Path::new(b!("/")).to_filename_display_str(), None); let mut called = false; - do Path::from_str("foo").with_display_str |s| { + do Path::new("foo").with_display_str |s| { assert_eq!(s, "foo"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("foo", 0x80)).with_display_str |s| { + do Path::new(b!("foo", 0x80)).with_display_str |s| { assert_eq!(s, "foo\uFFFD"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("foo", 0xff, "bar")).with_display_str |s| { + do Path::new(b!("foo", 0xff, "bar")).with_display_str |s| { assert_eq!(s, "foo\uFFFDbar"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("foo", 0xff, "/bar")).with_filename_display_str |s| { + do Path::new(b!("foo", 0xff, "/bar")).with_filename_display_str |s| { assert_eq!(s, Some("bar")); called = true; } assert!(called); called = false; - do Path::from_vec(b!("foo/", 0xff, "bar")).with_filename_display_str |s| { + do Path::new(b!("foo/", 0xff, "bar")).with_filename_display_str |s| { assert_eq!(s, Some("\uFFFDbar")); called = true; } assert!(called); called = false; - do Path::from_vec(b!("/")).with_filename_display_str |s| { + do Path::new(b!("/")).with_filename_display_str |s| { assert!(s.is_none()); called = true; } @@ -816,7 +796,7 @@ mod tests { macro_rules! t( ($path:expr, $exp:expr, $expf:expr) => ( { - let path = Path::from_vec($path); + let path = Path::new($path); let f = format!("{}", path.display()); assert_eq!(f.as_slice(), $exp); let f = format!("{}", path.filename_display()); @@ -839,20 +819,20 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); assert_eq!(path.$op(), ($exp).as_bytes()); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let left = path.$op().map(|&x| str::from_utf8_slice(x)); assert_eq!(left, $exp); } ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::from_vec($path); + let path = Path::new($path); assert_eq!(path.$op(), $exp); } ); @@ -924,10 +904,10 @@ mod tests { { let path = ($path); let join = ($join); - let mut p1 = Path::from_str(path); + let mut p1 = Path::new(path); let p2 = p1.clone(); - p1.push_str(join); - assert_eq!(p1, p2.join_str(join)); + p1.push(join); + assert_eq!(p1, p2.join(join)); } ) ) @@ -943,8 +923,8 @@ mod tests { macro_rules! t( (s: $path:expr, $push:expr, $exp:expr) => ( { - let mut p = Path::from_str($path); - let push = Path::from_str($push); + let mut p = Path::new($path); + let push = Path::new($push); p.push_path(&push); assert_eq!(p.as_str(), Some($exp)); } @@ -966,14 +946,14 @@ mod tests { macro_rules! t( (s: $path:expr, $push:expr, $exp:expr) => ( { - let mut p = Path::from_str($path); - p.push_many_str($push); + 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::from_vec($path); + let mut p = Path::new($path); p.push_many($push); assert_eq!(p.as_vec(), $exp); } @@ -997,7 +977,7 @@ mod tests { macro_rules! t( (s: $path:expr, $left:expr, $right:expr) => ( { - let mut p = Path::from_str($path); + let mut p = Path::new($path); let file = p.pop_str(); assert_eq!(p.as_str(), Some($left)); assert_eq!(file.map(|s| s.as_slice()), $right); @@ -1005,7 +985,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { - let mut p = Path::from_vec(b!($($path),+)); + let mut p = Path::new(b!($($path),+)); let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); @@ -1013,7 +993,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], None) => ( { - let mut p = Path::from_vec(b!($($path),+)); + let mut p = Path::new(b!($($path),+)); let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); @@ -1036,27 +1016,27 @@ mod tests { t!(s: "/a", "/", Some("a")); t!(s: "/", "/", None); - assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_str(), None); - assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar")); + assert_eq!(Path::new(b!("foo/bar", 0x80)).pop_str(), None); + assert_eq!(Path::new(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar")); } #[test] fn test_root_path() { - assert_eq!(Path::from_vec(b!("a/b/c")).root_path(), None); - assert_eq!(Path::from_vec(b!("/a/b/c")).root_path(), Some(Path::from_str("/"))); + 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::from_vec(b!("a/b/c")).join(b!("..")), b!("a/b")); - t!(v: Path::from_vec(b!("/a/b/c")).join(b!("d")), b!("/a/b/c/d")); - t!(v: Path::from_vec(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); - t!(s: Path::from_str("a/b/c").join_str(".."), "a/b"); - t!(s: Path::from_str("/a/b/c").join_str("d"), "/a/b/c/d"); - t!(s: Path::from_str("a/b").join_str("c/d"), "a/b/c/d"); - t!(s: Path::from_str("a/b").join_str("/c/d"), "/c/d"); - t!(s: Path::from_str(".").join_str("a/b"), "a/b"); - t!(s: Path::from_str("/").join_str("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")); + t!(v: Path::new(b!("a/", 0x80, "/c")).join(b!(0xff)), b!("a/", 0x80, "/c/", 0xff)); + 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] @@ -1064,8 +1044,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let join = Path::from_str($join); + let path = Path::new($path); + let join = Path::new($join); let res = path.join_path(&join); assert_eq!(res.as_str(), Some($exp)); } @@ -1087,14 +1067,14 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let res = path.join_many_str($join); + 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::from_vec($path); + let path = Path::new($path); let res = path.join_many($join); assert_eq!(res.as_vec(), $exp); } @@ -1114,100 +1094,102 @@ mod tests { #[test] fn test_with_helpers() { - t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); - t!(v: Path::from_vec(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); - t!(v: Path::from_vec(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); - t!(v: Path::from_vec(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), + let empty: &[u8] = []; + + t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); + t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); + t!(v: Path::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); + t!(v: Path::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), b!("/", 0xcd, "/", 0x80)); - t!(s: Path::from_str("a/b/c").with_dirname_str("d"), "d/c"); - t!(s: Path::from_str("a/b/c").with_dirname_str("d/e"), "d/e/c"); - t!(s: Path::from_str("a/b/c").with_dirname_str(""), "c"); - t!(s: Path::from_str("a/b/c").with_dirname_str("/"), "/c"); - t!(s: Path::from_str("a/b/c").with_dirname_str("."), "c"); - t!(s: Path::from_str("a/b/c").with_dirname_str(".."), "../c"); - t!(s: Path::from_str("/").with_dirname_str("foo"), "foo"); - t!(s: Path::from_str("/").with_dirname_str(""), "."); - t!(s: Path::from_str("/foo").with_dirname_str("bar"), "bar/foo"); - t!(s: Path::from_str("..").with_dirname_str("foo"), "foo"); - t!(s: Path::from_str("../..").with_dirname_str("foo"), "foo"); - t!(s: Path::from_str("..").with_dirname_str(""), "."); - t!(s: Path::from_str("../..").with_dirname_str(""), "."); - t!(s: Path::from_str("foo").with_dirname_str(".."), "../foo"); - t!(s: Path::from_str("foo").with_dirname_str("../.."), "../../foo"); - - t!(v: Path::from_vec(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); - t!(v: Path::from_vec(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); - t!(v: Path::from_vec(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), + t!(s: Path::new("a/b/c").with_dirname("d"), "d/c"); + t!(s: Path::new("a/b/c").with_dirname("d/e"), "d/e/c"); + t!(s: Path::new("a/b/c").with_dirname(""), "c"); + t!(s: Path::new("a/b/c").with_dirname("/"), "/c"); + t!(s: Path::new("a/b/c").with_dirname("."), "c"); + t!(s: Path::new("a/b/c").with_dirname(".."), "../c"); + t!(s: Path::new("/").with_dirname("foo"), "foo"); + t!(s: Path::new("/").with_dirname(""), "."); + t!(s: Path::new("/foo").with_dirname("bar"), "bar/foo"); + t!(s: Path::new("..").with_dirname("foo"), "foo"); + t!(s: Path::new("../..").with_dirname("foo"), "foo"); + t!(s: Path::new("..").with_dirname(""), "."); + t!(s: Path::new("../..").with_dirname(""), "."); + t!(s: Path::new("foo").with_dirname(".."), "../foo"); + t!(s: Path::new("foo").with_dirname("../.."), "../../foo"); + + t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); + t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); + t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), b!("/", 0xff, "/", 0xcd)); - t!(s: Path::from_str("a/b/c").with_filename_str("d"), "a/b/d"); - t!(s: Path::from_str(".").with_filename_str("foo"), "foo"); - t!(s: Path::from_str("/a/b/c").with_filename_str("d"), "/a/b/d"); - t!(s: Path::from_str("/").with_filename_str("foo"), "/foo"); - t!(s: Path::from_str("/a").with_filename_str("foo"), "/foo"); - t!(s: Path::from_str("foo").with_filename_str("bar"), "bar"); - t!(s: Path::from_str("/").with_filename_str("foo/"), "/foo"); - t!(s: Path::from_str("/a").with_filename_str("foo/"), "/foo"); - t!(s: Path::from_str("a/b/c").with_filename_str(""), "a/b"); - t!(s: Path::from_str("a/b/c").with_filename_str("."), "a/b"); - t!(s: Path::from_str("a/b/c").with_filename_str(".."), "a"); - t!(s: Path::from_str("/a").with_filename_str(""), "/"); - t!(s: Path::from_str("foo").with_filename_str(""), "."); - t!(s: Path::from_str("a/b/c").with_filename_str("d/e"), "a/b/d/e"); - t!(s: Path::from_str("a/b/c").with_filename_str("/d"), "a/b/d"); - t!(s: Path::from_str("..").with_filename_str("foo"), "../foo"); - t!(s: Path::from_str("../..").with_filename_str("foo"), "../../foo"); - t!(s: Path::from_str("..").with_filename_str(""), ".."); - t!(s: Path::from_str("../..").with_filename_str(""), "../.."); - - t!(v: Path::from_vec(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), + 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", 0x80, ".txt")).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt")); - t!(v: Path::from_vec(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), + t!(v: Path::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), b!("hi/", 0xff, ".txt", 0x80)); - t!(v: Path::from_vec(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); - t!(v: Path::from_vec(b!("hi", 0x80, "/there")).with_filestem([]), b!("hi", 0x80)); - t!(s: Path::from_str("hi/there.txt").with_filestem_str("here"), "hi/here.txt"); - t!(s: Path::from_str("hi/there.txt").with_filestem_str(""), "hi/.txt"); - t!(s: Path::from_str("hi/there.txt").with_filestem_str("."), "hi/..txt"); - t!(s: Path::from_str("hi/there.txt").with_filestem_str(".."), "hi/...txt"); - t!(s: Path::from_str("hi/there.txt").with_filestem_str("/"), "hi/.txt"); - t!(s: Path::from_str("hi/there.txt").with_filestem_str("foo/bar"), "hi/foo/bar.txt"); - t!(s: Path::from_str("hi/there.foo.txt").with_filestem_str("here"), "hi/here.txt"); - t!(s: Path::from_str("hi/there").with_filestem_str("here"), "hi/here"); - t!(s: Path::from_str("hi/there").with_filestem_str(""), "hi"); - t!(s: Path::from_str("hi").with_filestem_str(""), "."); - t!(s: Path::from_str("/hi").with_filestem_str(""), "/"); - t!(s: Path::from_str("hi/there").with_filestem_str(".."), "."); - t!(s: Path::from_str("hi/there").with_filestem_str("."), "hi"); - t!(s: Path::from_str("hi/there.").with_filestem_str("foo"), "hi/foo."); - t!(s: Path::from_str("hi/there.").with_filestem_str(""), "hi"); - t!(s: Path::from_str("hi/there.").with_filestem_str("."), "."); - t!(s: Path::from_str("hi/there.").with_filestem_str(".."), "hi/..."); - t!(s: Path::from_str("/").with_filestem_str("foo"), "/foo"); - t!(s: Path::from_str(".").with_filestem_str("foo"), "foo"); - t!(s: Path::from_str("hi/there..").with_filestem_str("here"), "hi/here."); - t!(s: Path::from_str("hi/there..").with_filestem_str(""), "hi"); - - t!(v: Path::from_vec(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), + t!(v: Path::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); + t!(v: Path::new(b!("hi", 0x80, "/there")).with_filestem(empty), b!("hi", 0x80)); + t!(s: Path::new("hi/there.txt").with_filestem("here"), "hi/here.txt"); + t!(s: Path::new("hi/there.txt").with_filestem(""), "hi/.txt"); + t!(s: Path::new("hi/there.txt").with_filestem("."), "hi/..txt"); + t!(s: Path::new("hi/there.txt").with_filestem(".."), "hi/...txt"); + t!(s: Path::new("hi/there.txt").with_filestem("/"), "hi/.txt"); + t!(s: Path::new("hi/there.txt").with_filestem("foo/bar"), "hi/foo/bar.txt"); + t!(s: Path::new("hi/there.foo.txt").with_filestem("here"), "hi/here.txt"); + t!(s: Path::new("hi/there").with_filestem("here"), "hi/here"); + t!(s: Path::new("hi/there").with_filestem(""), "hi"); + t!(s: Path::new("hi").with_filestem(""), "."); + t!(s: Path::new("/hi").with_filestem(""), "/"); + t!(s: Path::new("hi/there").with_filestem(".."), "."); + t!(s: Path::new("hi/there").with_filestem("."), "hi"); + t!(s: Path::new("hi/there.").with_filestem("foo"), "hi/foo."); + t!(s: Path::new("hi/there.").with_filestem(""), "hi"); + t!(s: Path::new("hi/there.").with_filestem("."), "."); + t!(s: Path::new("hi/there.").with_filestem(".."), "hi/..."); + t!(s: Path::new("/").with_filestem("foo"), "/foo"); + t!(s: Path::new(".").with_filestem("foo"), "foo"); + t!(s: Path::new("hi/there..").with_filestem("here"), "hi/here."); + t!(s: Path::new("hi/there..").with_filestem(""), "hi"); + + t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), b!("hi/there", 0x80, ".exe")); - t!(v: Path::from_vec(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), + t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), b!("hi/there.", 0xff)); - t!(v: Path::from_vec(b!("hi/there", 0x80)).with_extension(b!(0xff)), + t!(v: Path::new(b!("hi/there", 0x80)).with_extension(b!(0xff)), b!("hi/there", 0x80, ".", 0xff)); - t!(v: Path::from_vec(b!("hi/there.", 0xff)).with_extension([]), b!("hi/there")); - t!(s: Path::from_str("hi/there.txt").with_extension_str("exe"), "hi/there.exe"); - t!(s: Path::from_str("hi/there.txt").with_extension_str(""), "hi/there"); - t!(s: Path::from_str("hi/there.txt").with_extension_str("."), "hi/there.."); - t!(s: Path::from_str("hi/there.txt").with_extension_str(".."), "hi/there..."); - t!(s: Path::from_str("hi/there").with_extension_str("txt"), "hi/there.txt"); - t!(s: Path::from_str("hi/there").with_extension_str("."), "hi/there.."); - t!(s: Path::from_str("hi/there").with_extension_str(".."), "hi/there..."); - t!(s: Path::from_str("hi/there.").with_extension_str("txt"), "hi/there.txt"); - t!(s: Path::from_str("hi/.foo").with_extension_str("txt"), "hi/.foo.txt"); - t!(s: Path::from_str("hi/there.txt").with_extension_str(".foo"), "hi/there..foo"); - t!(s: Path::from_str("/").with_extension_str("txt"), "/"); - t!(s: Path::from_str("/").with_extension_str("."), "/"); - t!(s: Path::from_str("/").with_extension_str(".."), "/"); - t!(s: Path::from_str(".").with_extension_str("txt"), "."); + t!(v: Path::new(b!("hi/there.", 0xff)).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] @@ -1217,9 +1199,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::from_str(path); + let mut p1 = Path::new(path); p1.$set(arg); - let p2 = Path::from_str(path); + let p2 = Path::new(path); assert_eq!(p1, p2.$with(arg)); } ); @@ -1227,9 +1209,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::from_vec(path); + let mut p1 = Path::new(path); p1.$set(arg); - let p2 = Path::from_vec(path); + let p2 = Path::new(path); assert_eq!(p1, p2.$with(arg)); } ) @@ -1238,39 +1220,39 @@ mod tests { t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d")); t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d"); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, "d/e"); - t!(s: "/", set_dirname_str, with_dirname_str, "foo"); - t!(s: "/foo", set_dirname_str, with_dirname_str, "bar"); - t!(s: "a/b/c", set_dirname_str, with_dirname_str, ""); - t!(s: "../..", set_dirname_str, with_dirname_str, "x"); - t!(s: "foo", set_dirname_str, with_dirname_str, "../.."); + t!(s: "a/b/c", set_dirname, with_dirname, "d"); + t!(s: "a/b/c", set_dirname, with_dirname, "d/e"); + t!(s: "/", set_dirname, with_dirname, "foo"); + t!(s: "/foo", set_dirname, with_dirname, "bar"); + t!(s: "a/b/c", set_dirname, with_dirname, ""); + t!(s: "../..", set_dirname, with_dirname, "x"); + t!(s: "foo", set_dirname, with_dirname, "../.."); t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); t!(v: b!("/"), set_filename, with_filename, b!("foo")); t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); - t!(s: "a/b/c", set_filename_str, with_filename_str, "d"); - t!(s: "/", set_filename_str, with_filename_str, "foo"); - t!(s: ".", set_filename_str, with_filename_str, "foo"); - t!(s: "a/b", set_filename_str, with_filename_str, ""); - t!(s: "a", set_filename_str, with_filename_str, ""); + 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_filestem, with_filestem, b!("here")); t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); - t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there.", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi/there.txt", set_filestem_str, with_filestem_str, ""); - t!(s: "hi/there", set_filestem_str, with_filestem_str, ""); + t!(s: "hi/there.txt", set_filestem, with_filestem, "here"); + t!(s: "hi/there.", set_filestem, with_filestem, "here"); + t!(s: "hi/there", set_filestem, with_filestem, "here"); + t!(s: "hi/there.txt", set_filestem, with_filestem, ""); + t!(s: "hi/there", set_filestem, with_filestem, ""); t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); - t!(s: "hi/there.txt", set_extension_str, with_extension_str, "exe"); - t!(s: "hi/there.", set_extension_str, with_extension_str, "txt"); - t!(s: "hi/there", set_extension_str, with_extension_str, "txt"); - t!(s: "hi/there.txt", set_extension_str, with_extension_str, ""); - t!(s: "hi/there", set_extension_str, with_extension_str, ""); - t!(s: ".", set_extension_str, with_extension_str, "txt"); + 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] @@ -1278,14 +1260,14 @@ mod tests { macro_rules! t( (s: $path:expr, $ext:expr, $exp:expr) => ( { - let mut path = Path::from_str($path); - path.add_extension_str($ext); + let mut path = Path::new($path); + path.add_extension($ext); assert_eq!(path.as_str(), Some($exp)); } ); (v: $path:expr, $ext:expr, $exp:expr) => ( { - let mut path = Path::from_vec($path); + let mut path = Path::new($path); path.add_extension($ext); assert_eq!(path.as_vec(), $exp); } @@ -1338,39 +1320,39 @@ mod tests { ) ) - t!(v: Path::from_vec(b!("a/b/c")), Some(b!("c")), b!("a/b"), Some(b!("c")), None); - t!(v: Path::from_vec(b!("a/b/", 0xff)), Some(b!(0xff)), b!("a/b"), Some(b!(0xff)), None); - t!(v: Path::from_vec(b!("hi/there.", 0xff)), Some(b!("there.", 0xff)), b!("hi"), + 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/", 0xff)), Some(b!(0xff)), b!("a/b"), Some(b!(0xff)), None); + t!(v: Path::new(b!("hi/there.", 0xff)), Some(b!("there.", 0xff)), b!("hi"), Some(b!("there")), Some(b!(0xff))); - t!(s: Path::from_str("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); - t!(s: Path::from_str("."), None, Some("."), None, None); - t!(s: Path::from_str("/"), None, Some("/"), None, None); - t!(s: Path::from_str(".."), None, Some(".."), None, None); - t!(s: Path::from_str("../.."), None, Some("../.."), None, None); - t!(s: Path::from_str("hi/there.txt"), Some("there.txt"), Some("hi"), + 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::from_str("hi/there"), Some("there"), Some("hi"), Some("there"), None); - t!(s: Path::from_str("hi/there."), Some("there."), Some("hi"), + 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::from_str("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); - t!(s: Path::from_str("hi/..there"), Some("..there"), Some("hi"), + 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::from_vec(b!("a/b/", 0xff)), None, Some("a/b"), None, None); - t!(s: Path::from_vec(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); - t!(s: Path::from_vec(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); - t!(s: Path::from_vec(b!(0xff, "/b")), Some("b"), None, Some("b"), None); + t!(s: Path::new(b!("a/b/", 0xff)), None, Some("a/b"), None, None); + t!(s: Path::new(b!("a/b/", 0xff, ".txt")), None, Some("a/b"), None, Some("txt")); + t!(s: Path::new(b!("a/b/c.", 0x80)), None, Some("a/b"), Some("c"), None); + t!(s: Path::new(b!(0xff, "/b")), Some("b"), None, Some("b"), None); } #[test] fn test_dir_file_path() { - t!(v: Path::from_vec(b!("hi/there", 0x80)).dir_path(), b!("hi")); - t!(v: Path::from_vec(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); - t!(s: Path::from_str("hi/there").dir_path(), "hi"); - t!(s: Path::from_str("hi").dir_path(), "."); - t!(s: Path::from_str("/hi").dir_path(), "/"); - t!(s: Path::from_str("/").dir_path(), "/"); - t!(s: Path::from_str("..").dir_path(), ".."); - t!(s: Path::from_str("../..").dir_path(), "../.."); + t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); + t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); + 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(), "../.."); macro_rules! t( (s: $path:expr, $exp:expr) => ( @@ -1389,14 +1371,14 @@ mod tests { ) ) - t!(v: Path::from_vec(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); - t!(v: Path::from_vec(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); - t!(s: Path::from_str("hi/there").file_path(), Some("there")); - t!(s: Path::from_str("hi").file_path(), Some("hi")); - t!(s: Path::from_str(".").file_path(), None); - t!(s: Path::from_str("/").file_path(), None); - t!(s: Path::from_str("..").file_path(), None); - t!(s: Path::from_str("../..").file_path(), None); + t!(v: Path::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); + t!(v: Path::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); + t!(s: Path::new("hi/there").file_path(), Some("there")); + t!(s: Path::new("hi").file_path(), Some("hi")); + t!(s: Path::new(".").file_path(), None); + t!(s: Path::new("/").file_path(), None); + t!(s: Path::new("..").file_path(), None); + t!(s: Path::new("../..").file_path(), None); } #[test] @@ -1404,7 +1386,7 @@ mod tests { macro_rules! t( (s: $path:expr, $abs:expr, $rel:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); assert_eq!(path.is_absolute(), $abs); assert_eq!(path.is_relative(), $rel); } @@ -1425,8 +1407,8 @@ mod tests { macro_rules! t( (s: $path:expr, $dest:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let dest = Path::from_str($dest); + let path = Path::new($path); + let dest = Path::new($dest); assert_eq!(path.is_ancestor_of(&dest), $exp); } ) @@ -1459,15 +1441,15 @@ mod tests { macro_rules! t( (s: $path:expr, $child:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let child = Path::from_str($child); + 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::from_vec($path); - let child = Path::from_vec($child); + let path = Path::new($path); + let child = Path::new($child); assert_eq!(path.ends_with_path(&child), $exp); } ) @@ -1498,8 +1480,8 @@ mod tests { macro_rules! t( (s: $path:expr, $other:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let other = Path::from_str($other); + let path = Path::new($path); + let other = Path::new($other); let res = path.path_relative_from(&other); assert_eq!(res.and_then_ref(|x| x.as_str()), $exp); } @@ -1543,7 +1525,7 @@ mod tests { macro_rules! t( (s: $path:expr, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let comps = path.component_iter().to_owned_vec(); let exp: &[&str] = $exp; let exps = exp.iter().map(|x| x.as_bytes()).to_owned_vec(); @@ -1557,7 +1539,7 @@ mod tests { ); (v: [$($arg:expr),+], [$([$($exp:expr),*]),*]) => ( { - let path = Path::from_vec(b!($($arg),+)); + let path = Path::new(b!($($arg),+)); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = [$(b!($($exp),*)),*]; assert!(comps.as_slice() == exp, "component_iter: Expected {:?}, found {:?}", @@ -1592,7 +1574,7 @@ mod tests { macro_rules! t( (v: [$($arg:expr),+], $exp:expr) => ( { - let path = Path::from_vec(b!($($arg),+)); + let path = Path::new(b!($($arg),+)); let comps = path.str_component_iter().to_owned_vec(); let exp: &[Option<&str>] = $exp; assert!(comps.as_slice() == exp, @@ -1616,13 +1598,13 @@ mod tests { #[test] fn test_each_parent() { - assert!(Path::from_str("/foo/bar").each_parent(|_| true)); - assert!(!Path::from_str("/foo/bar").each_parent(|_| false)); + assert!(Path::new("/foo/bar").each_parent(|_| true)); + assert!(!Path::new("/foo/bar").each_parent(|_| false)); macro_rules! t( (s: $path:expr, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let exp: &[&str] = $exp; let mut comps = exp.iter().map(|&x|x); do path.each_parent |p| { @@ -1638,7 +1620,7 @@ mod tests { ); (v: $path:expr, $exp:expr) => ( { - let path = Path::from_vec($path); + let path = Path::new($path); let exp: &[&[u8]] = $exp; let mut comps = exp.iter().map(|&x|x); do path.each_parent |p| { diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 5dfe5b4f35a87..cc04261ec6695 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -22,7 +22,7 @@ use str::{CharSplitIterator, OwnedStr, Str, StrVector}; use to_bytes::IterBytes; use util; use vec::Vector; -use super::{GenericPath, GenericPathUnsafe}; +use super::{BytesContainer, GenericPath, GenericPathUnsafe}; #[cfg(target_os = "win32")] use libc; @@ -97,11 +97,7 @@ impl Eq for Path { impl FromStr for Path { fn from_str(s: &str) -> Option { - if contains_nul(s.as_bytes()) { - None - } else { - Some(unsafe { GenericPathUnsafe::from_str_unchecked(s) }) - } + Path::new_opt(s) } } @@ -132,18 +128,8 @@ impl GenericPathUnsafe for Path { /// /// Raises the `str::not_utf8` condition if not valid UTF-8. #[inline] - unsafe fn from_vec_unchecked(path: &[u8]) -> Path { - if !str::is_utf8(path) { - let path = str::from_utf8(path); // triggers not_utf8 condition - GenericPathUnsafe::from_str_unchecked(path) - } else { - GenericPathUnsafe::from_str_unchecked(cast::transmute(path)) - } - } - - #[inline] - unsafe fn from_str_unchecked(path: &str) -> Path { - let (prefix, path) = Path::normalize_(path); + unsafe fn new_unchecked(path: T) -> Path { + let (prefix, path) = Path::normalize_(path.container_as_str()); assert!(!path.is_empty()); let mut ret = Path{ repr: path, prefix: prefix, sepidx: None }; ret.update_sepidx(); @@ -155,17 +141,8 @@ impl GenericPathUnsafe for Path { /// # Failure /// /// Raises the `str::not_utf8` condition if not valid UTF-8. - #[inline] - unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]) { - if !str::is_utf8(dirname) { - let dirname = str::from_utf8(dirname); // triggers not_utf8 condition - self.set_dirname_str_unchecked(dirname); - } else { - self.set_dirname_str_unchecked(cast::transmute(dirname)) - } - } - - unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) { + unsafe fn set_dirname_unchecked(&mut self, dirname: T) { + let dirname = dirname.container_as_str(); match self.sepidx_or_prefix_len() { None if "." == self.repr || ".." == self.repr => { self.update_normalized(dirname); @@ -207,17 +184,8 @@ impl GenericPathUnsafe for Path { /// # Failure /// /// Raises the `str::not_utf8` condition if not valid UTF-8. - #[inline] - unsafe fn set_filename_unchecked(&mut self, filename: &[u8]) { - if !str::is_utf8(filename) { - let filename = str::from_utf8(filename); // triggers not_utf8 condition - self.set_filename_str_unchecked(filename) - } else { - self.set_filename_str_unchecked(cast::transmute(filename)) - } - } - - unsafe fn set_filename_str_unchecked(&mut self, filename: &str) { + unsafe fn set_filename_unchecked(&mut self, filename: T) { + let filename = filename.container_as_str(); match self.sepidx_or_prefix_len() { None if ".." == self.repr => { let mut s = str::with_capacity(3 + filename.len()); @@ -254,20 +222,6 @@ impl GenericPathUnsafe for Path { /// See `GenericPathUnsafe::push_unchecked`. /// - /// # Failure - /// - /// Raises the `str::not_utf8` condition if not valid UTF-8. - unsafe fn push_unchecked(&mut self, path: &[u8]) { - if !str::is_utf8(path) { - let path = str::from_utf8(path); // triggers not_utf8 condition - self.push_str_unchecked(path); - } else { - self.push_str_unchecked(cast::transmute(path)); - } - } - - /// See `GenericPathUnsafe::push_str_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 @@ -276,7 +230,8 @@ impl GenericPathUnsafe for Path { /// 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_str_unchecked(&mut self, path: &str) { + unsafe fn push_unchecked(&mut self, path: T) { + let path = path.container_as_str(); fn is_vol_abs(path: &str, prefix: Option) -> bool { // assume prefix is Some(DiskPrefix) let rest = path.slice_from(prefix_len(prefix)); @@ -357,11 +312,17 @@ impl GenericPathUnsafe for Path { impl GenericPath for Path { #[inline] - fn from_vec_opt(v: &[u8]) -> Option { - if contains_nul(v) || !str::is_utf8(v) { - None - } else { - Some(unsafe { GenericPathUnsafe::from_vec_unchecked(v) }) + fn new_opt(path: T) -> Option { + let s = path.container_as_str_opt(); + match s { + None => None, + Some(s) => { + if contains_nul(s.as_bytes()) { + None + } else { + Some(unsafe { GenericPathUnsafe::new_unchecked(s) }) + } + } } } @@ -372,11 +333,23 @@ impl GenericPath for Path { Some(self.repr.as_slice()) } + /// See `GenericPath::into_str` for info. + /// Always returns a `Some` value. + #[inline] + fn into_str(self) -> Option<~str> { + Some(self.repr) + } + #[inline] fn as_vec<'a>(&'a self) -> &'a [u8] { self.repr.as_bytes() } + #[inline] + fn into_vec(self) -> ~[u8] { + self.repr.into_bytes() + } + #[inline] fn with_display_str(&self, f: &fn(&str) -> T) -> T { f(self.repr.as_slice()) @@ -448,16 +421,16 @@ impl GenericPath for Path { } fn dir_path(&self) -> Path { - unsafe { GenericPathUnsafe::from_str_unchecked(self.dirname_str().unwrap()) } + unsafe { GenericPathUnsafe::new_unchecked(self.dirname_str().unwrap()) } } fn file_path(&self) -> Option { - self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::from_str_unchecked(s) }) + self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::new_unchecked(s) }) } #[inline] fn push_path(&mut self, path: &Path) { - self.push_str(path.as_str().unwrap()) + self.push(path.as_str().unwrap()) } #[inline] @@ -494,14 +467,14 @@ impl GenericPath for Path { fn root_path(&self) -> Option { if self.is_absolute() { - Some(Path::from_str(match self.prefix { + Some(Path::new(match self.prefix { Some(VerbatimDiskPrefix)|Some(DiskPrefix) => { self.repr.slice_to(self.prefix_len()+1) } _ => self.repr.slice_to(self.prefix_len()) })) } else if self.is_vol_relative() { - Some(Path::from_str(self.repr.slice_to(1))) + Some(Path::new(self.repr.slice_to(1))) } else { None } @@ -631,11 +604,10 @@ impl GenericPath for Path { } } } - Some(Path::from_str(comps.connect("\\"))) + Some(Path::new(comps.connect("\\"))) } } - /// Executes a callback with the receiver and every parent fn each_parent(&self, f: &fn(&Path) -> bool) -> bool { let mut p = self.clone(); loop { @@ -649,52 +621,39 @@ impl GenericPath for Path { } true } + + fn ends_with_path(&self, child: &Path) -> bool { + if !child.is_relative() { return false; } + let mut selfit = self.str_component_iter().invert(); + let mut childit = child.str_component_iter().invert(); + 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 + /// Returns a new Path from a byte vector or string /// /// # Failure /// /// Raises the `null_byte` condition if the vector contains a NUL. /// Raises the `str::not_utf8` condition if invalid UTF-8. #[inline] - pub fn from_vec(v: &[u8]) -> Path { - GenericPath::from_vec(v) - } - - /// Returns a new Path from a byte vector, if possible - #[inline] - pub fn from_vec_opt(v: &[u8]) -> Option { - GenericPath::from_vec_opt(v) + pub fn new(path: T) -> Path { + GenericPath::new(path) } - /// Returns a new Path from a string - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the vector contains a NUL. + /// Returns a new Path from a byte vector or string, if possible #[inline] - pub fn from_str(s: &str) -> Path { - GenericPath::from_str(s) - } - - /// Returns a new Path from a string, if possible - #[inline] - pub fn from_str_opt(s: &str) -> Option { - GenericPath::from_str_opt(s) - } - - /// Converts the Path into an owned byte vector - pub fn into_vec(self) -> ~[u8] { - self.repr.into_bytes() - } - - /// Converts the Path into an owned string - /// Returns an Option for compatibility with posix::Path, but the - /// return value will always be Some. - pub fn into_str(self) -> Option<~str> { - Some(self.repr) + pub fn new_opt(path: T) -> Option { + GenericPath::new_opt(path) } /// Returns an iterator that yields each component of the path in turn as a Option<&str>. @@ -745,22 +704,6 @@ impl Path { self.rev_str_component_iter().map(convert) } - /// Returns whether the relative path `child` is a suffix of `self`. - pub fn ends_with_path(&self, child: &Path) -> bool { - if !child.is_relative() { return false; } - let mut selfit = self.str_component_iter().invert(); - let mut childit = child.str_component_iter().invert(); - 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 - } - /// 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. @@ -1310,102 +1253,103 @@ mod tests { #[test] fn test_paths() { - t!(v: Path::from_vec([]), b!(".")); - t!(v: Path::from_vec(b!("\\")), b!("\\")); - t!(v: Path::from_vec(b!("a\\b\\c")), b!("a\\b\\c")); - - t!(s: Path::from_str(""), "."); - t!(s: Path::from_str("\\"), "\\"); - t!(s: Path::from_str("hi"), "hi"); - t!(s: Path::from_str("hi\\"), "hi"); - t!(s: Path::from_str("\\lib"), "\\lib"); - t!(s: Path::from_str("\\lib\\"), "\\lib"); - t!(s: Path::from_str("hi\\there"), "hi\\there"); - t!(s: Path::from_str("hi\\there.txt"), "hi\\there.txt"); - t!(s: Path::from_str("/"), "\\"); - t!(s: Path::from_str("hi/"), "hi"); - t!(s: Path::from_str("/lib"), "\\lib"); - t!(s: Path::from_str("/lib/"), "\\lib"); - t!(s: Path::from_str("hi/there"), "hi\\there"); - - t!(s: Path::from_str("hi\\there\\"), "hi\\there"); - t!(s: Path::from_str("hi\\..\\there"), "there"); - t!(s: Path::from_str("hi/../there"), "there"); - t!(s: Path::from_str("..\\hi\\there"), "..\\hi\\there"); - t!(s: Path::from_str("\\..\\hi\\there"), "\\hi\\there"); - t!(s: Path::from_str("/../hi/there"), "\\hi\\there"); - t!(s: Path::from_str("foo\\.."), "."); - t!(s: Path::from_str("\\foo\\.."), "\\"); - t!(s: Path::from_str("\\foo\\..\\.."), "\\"); - t!(s: Path::from_str("\\foo\\..\\..\\bar"), "\\bar"); - t!(s: Path::from_str("\\.\\hi\\.\\there\\."), "\\hi\\there"); - t!(s: Path::from_str("\\.\\hi\\.\\there\\.\\.."), "\\hi"); - t!(s: Path::from_str("foo\\..\\.."), ".."); - t!(s: Path::from_str("foo\\..\\..\\.."), "..\\.."); - t!(s: Path::from_str("foo\\..\\..\\bar"), "..\\bar"); - - assert_eq!(Path::from_vec(b!("foo\\bar")).into_vec(), b!("foo\\bar").to_owned()); - assert_eq!(Path::from_vec(b!("\\foo\\..\\..\\bar")).into_vec(), + 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").to_owned()); + assert_eq!(Path::new(b!("\\foo\\..\\..\\bar")).into_vec(), b!("\\bar").to_owned()); - assert_eq!(Path::from_str("foo\\bar").into_str(), Some(~"foo\\bar")); - assert_eq!(Path::from_str("\\foo\\..\\..\\bar").into_str(), Some(~"\\bar")); - - t!(s: Path::from_str("\\\\a"), "\\a"); - t!(s: Path::from_str("\\\\a\\"), "\\a"); - t!(s: Path::from_str("\\\\a\\b"), "\\\\a\\b"); - t!(s: Path::from_str("\\\\a\\b\\"), "\\\\a\\b"); - t!(s: Path::from_str("\\\\a\\b/"), "\\\\a\\b"); - t!(s: Path::from_str("\\\\\\b"), "\\b"); - t!(s: Path::from_str("\\\\a\\\\b"), "\\a\\b"); - t!(s: Path::from_str("\\\\a\\b\\c"), "\\\\a\\b\\c"); - t!(s: Path::from_str("\\\\server\\share/path"), "\\\\server\\share\\path"); - t!(s: Path::from_str("\\\\server/share/path"), "\\\\server\\share\\path"); - t!(s: Path::from_str("C:a\\b.txt"), "C:a\\b.txt"); - t!(s: Path::from_str("C:a/b.txt"), "C:a\\b.txt"); - t!(s: Path::from_str("z:\\a\\b.txt"), "Z:\\a\\b.txt"); - t!(s: Path::from_str("z:/a/b.txt"), "Z:\\a\\b.txt"); - t!(s: Path::from_str("ab:/a/b.txt"), "ab:\\a\\b.txt"); - t!(s: Path::from_str("C:\\"), "C:\\"); - t!(s: Path::from_str("C:"), "C:"); - t!(s: Path::from_str("q:"), "Q:"); - t!(s: Path::from_str("C:/"), "C:\\"); - t!(s: Path::from_str("C:\\foo\\.."), "C:\\"); - t!(s: Path::from_str("C:foo\\.."), "C:"); - t!(s: Path::from_str("C:\\a\\"), "C:\\a"); - t!(s: Path::from_str("C:\\a/"), "C:\\a"); - t!(s: Path::from_str("C:\\a\\b\\"), "C:\\a\\b"); - t!(s: Path::from_str("C:\\a\\b/"), "C:\\a\\b"); - t!(s: Path::from_str("C:a\\"), "C:a"); - t!(s: Path::from_str("C:a/"), "C:a"); - t!(s: Path::from_str("C:a\\b\\"), "C:a\\b"); - t!(s: Path::from_str("C:a\\b/"), "C:a\\b"); - t!(s: Path::from_str("\\\\?\\z:\\a\\b.txt"), "\\\\?\\z:\\a\\b.txt"); - t!(s: Path::from_str("\\\\?\\C:/a/b.txt"), "\\\\?\\C:/a/b.txt"); - t!(s: Path::from_str("\\\\?\\C:\\a/b.txt"), "\\\\?\\C:\\a/b.txt"); - t!(s: Path::from_str("\\\\?\\test\\a\\b.txt"), "\\\\?\\test\\a\\b.txt"); - t!(s: Path::from_str("\\\\?\\foo\\bar\\"), "\\\\?\\foo\\bar\\"); - t!(s: Path::from_str("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); - t!(s: Path::from_str("\\\\.\\"), "\\\\.\\"); - t!(s: Path::from_str("\\\\?\\UNC\\server\\share\\foo"), "\\\\?\\UNC\\server\\share\\foo"); - t!(s: Path::from_str("\\\\?\\UNC\\server/share"), "\\\\?\\UNC\\server/share\\"); - t!(s: Path::from_str("\\\\?\\UNC\\server"), "\\\\?\\UNC\\server\\"); - t!(s: Path::from_str("\\\\?\\UNC\\"), "\\\\?\\UNC\\\\"); - t!(s: Path::from_str("\\\\?\\UNC"), "\\\\?\\UNC"); + assert_eq!(Path::new("foo\\bar").into_str(), Some(~"foo\\bar")); + assert_eq!(Path::new("\\foo\\..\\..\\bar").into_str(), Some(~"\\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::from_str("\\\\.\\foo/bar"), "\\\\.\\foo/bar"); - t!(s: Path::from_str("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); + t!(s: Path::new("\\\\.\\foo/bar"), "\\\\.\\foo/bar"); + t!(s: Path::new("\\\\.\\foo\\bar"), "\\\\.\\foo\\bar"); } #[test] fn test_opt_paths() { - assert_eq!(Path::from_vec_opt(b!("foo\\bar", 0)), None); - assert_eq!(Path::from_vec_opt(b!("foo\\bar", 0x80)), None); - t!(v: Path::from_vec_opt(b!("foo\\bar")).unwrap(), b!("foo\\bar")); - assert_eq!(Path::from_str_opt("foo\\bar\0"), None); - t!(s: Path::from_str_opt("foo\\bar").unwrap(), "foo\\bar"); + assert_eq!(Path::new_opt(b!("foo\\bar", 0)), None); + assert_eq!(Path::new_opt(b!("foo\\bar", 0x80)), None); + t!(v: Path::new_opt(b!("foo\\bar")).unwrap(), b!("foo\\bar")); + assert_eq!(Path::new_opt("foo\\bar\0"), None); + t!(s: Path::new_opt("foo\\bar").unwrap(), "foo\\bar"); } #[test] @@ -1418,7 +1362,7 @@ mod tests { assert_eq!(v.as_slice(), b!("foo\\bar", 0)); (b!("\\bar").to_owned()) }).inside { - Path::from_vec(b!("foo\\bar", 0)) + Path::new(b!("foo\\bar", 0)) }; assert!(handled); assert_eq!(p.as_vec(), b!("\\bar")); @@ -1478,12 +1422,12 @@ mod tests { do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { - Path::from_vec(b!("foo\\bar", 0)) + Path::new(b!("foo\\bar", 0)) }; }) t!(~"set_filename w\\nul" => { - let mut p = Path::from_vec(b!("foo\\bar")); + let mut p = Path::new(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1492,7 +1436,7 @@ mod tests { }) t!(~"set_dirname w\\nul" => { - let mut p = Path::from_vec(b!("foo\\bar")); + let mut p = Path::new(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1501,7 +1445,7 @@ mod tests { }) t!(~"push w\\nul" => { - let mut p = Path::from_vec(b!("foo\\bar")); + let mut p = Path::new(b!("foo\\bar")); do cond.trap(|_| { (b!("null", 0).to_owned()) }).inside { @@ -1513,22 +1457,22 @@ mod tests { #[test] #[should_fail] fn test_not_utf8_fail() { - Path::from_vec(b!("hello", 0x80, ".txt")); + Path::new(b!("hello", 0x80, ".txt")); } #[test] fn test_display_str() { - assert_eq!(Path::from_str("foo").to_display_str(), ~"foo"); - assert_eq!(Path::from_vec(b!("\\")).to_filename_display_str(), None); + assert_eq!(Path::new("foo").to_display_str(), ~"foo"); + assert_eq!(Path::new(b!("\\")).to_filename_display_str(), None); let mut called = false; - do Path::from_str("foo").with_display_str |s| { + do Path::new("foo").with_display_str |s| { assert_eq!(s, "foo"); called = true; }; assert!(called); called = false; - do Path::from_vec(b!("\\")).with_filename_display_str |s| { + do Path::new(b!("\\")).with_filename_display_str |s| { assert!(s.is_none()); called = true; } @@ -1540,7 +1484,7 @@ mod tests { macro_rules! t( ($path:expr, $exp:expr, $expf:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let f = format!("{}", path.display()); assert_eq!(f.as_slice(), $exp); let f = format!("{}", path.filename_display()); @@ -1559,20 +1503,20 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); assert_eq!(path.$op(), Some($exp)); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let left = path.$op(); assert_eq!(left, $exp); } ); (v: $path:expr, $op:ident, $exp:expr) => ( { - let path = Path::from_vec($path); + let path = Path::new($path); assert_eq!(path.$op(), $exp); } ) @@ -1681,10 +1625,10 @@ mod tests { { let path = ($path); let join = ($join); - let mut p1 = Path::from_str(path); + let mut p1 = Path::new(path); let p2 = p1.clone(); - p1.push_str(join); - assert_eq!(p1, p2.join_str(join)); + p1.push(join); + assert_eq!(p1, p2.join(join)); } ) ) @@ -1693,19 +1637,19 @@ mod tests { 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_str and join_str share an implementation, + // 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::from_str("\\\\?\\C:"); + let mut p = Path::new("\\\\?\\C:"); assert_eq!(p.prefix(), Some(VerbatimPrefix(2))); - p.push_str("foo"); + p.push("foo"); assert_eq!(p.prefix(), Some(VerbatimDiskPrefix)); assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo")); // and another with verbatim non-normalized paths - let mut p = Path::from_str("\\\\?\\C:\\a\\"); - p.push_str("foo"); + let mut p = Path::new("\\\\?\\C:\\a\\"); + p.push("foo"); assert_eq!(p.as_str(), Some("\\\\?\\C:\\a\\foo")); } @@ -1714,8 +1658,8 @@ mod tests { macro_rules! t( (s: $path:expr, $push:expr, $exp:expr) => ( { - let mut p = Path::from_str($path); - let push = Path::from_str($push); + let mut p = Path::new($path); + let push = Path::new($push); p.push_path(&push); assert_eq!(p.as_str(), Some($exp)); } @@ -1766,14 +1710,14 @@ mod tests { macro_rules! t( (s: $path:expr, $push:expr, $exp:expr) => ( { - let mut p = Path::from_str($path); - p.push_many_str($push); + 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::from_vec($path); + let mut p = Path::new($path); p.push_many($push); assert_eq!(p.as_vec(), $exp); } @@ -1798,7 +1742,7 @@ mod tests { (s: $path:expr, $left:expr, $right:expr) => ( { let pstr = $path; - let mut p = Path::from_str(pstr); + let mut p = Path::new(pstr); let file = p.pop_str(); let left = $left; assert!(p.as_str() == Some(left), @@ -1812,7 +1756,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( { - let mut p = Path::from_vec(b!($($path),+)); + let mut p = Path::new(b!($($path),+)); let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); @@ -1820,7 +1764,7 @@ mod tests { ); (v: [$($path:expr),+], [$($left:expr),+], None) => ( { - let mut p = Path::from_vec(b!($($path),+)); + let mut p = Path::new(b!($($path),+)); let file = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); assert_eq!(file, None); @@ -1866,28 +1810,28 @@ mod tests { #[test] fn test_root_path() { - assert_eq!(Path::from_str("a\\b\\c").root_path(), None); - assert_eq!(Path::from_str("\\a\\b\\c").root_path(), Some(Path::from_str("\\"))); - assert_eq!(Path::from_str("C:a").root_path(), None); - assert_eq!(Path::from_str("C:\\a").root_path(), Some(Path::from_str("C:\\"))); - assert_eq!(Path::from_str("\\\\a\\b\\c").root_path(), Some(Path::from_str("\\\\a\\b"))); - assert_eq!(Path::from_str("\\\\?\\a\\b").root_path(), Some(Path::from_str("\\\\?\\a"))); - assert_eq!(Path::from_str("\\\\?\\C:\\a").root_path(), Some(Path::from_str("\\\\?\\C:\\"))); - assert_eq!(Path::from_str("\\\\?\\UNC\\a\\b\\c").root_path(), - Some(Path::from_str("\\\\?\\UNC\\a\\b"))); - assert_eq!(Path::from_str("\\\\.\\a\\b").root_path(), Some(Path::from_str("\\\\.\\a"))); + 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(), None); + 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::from_str("a\\b\\c").join_str(".."), "a\\b"); - t!(s: Path::from_str("\\a\\b\\c").join_str("d"), "\\a\\b\\c\\d"); - t!(s: Path::from_str("a\\b").join_str("c\\d"), "a\\b\\c\\d"); - t!(s: Path::from_str("a\\b").join_str("\\c\\d"), "\\c\\d"); - t!(s: Path::from_str(".").join_str("a\\b"), "a\\b"); - t!(s: Path::from_str("\\").join_str("a\\b"), "\\a\\b"); - t!(v: Path::from_vec(b!("a\\b\\c")).join(b!("..")), b!("a\\b")); - t!(v: Path::from_vec(b!("\\a\\b\\c")).join(b!("d")), b!("\\a\\b\\c\\d")); + 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 } @@ -1897,8 +1841,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let join = Path::from_str($join); + let path = Path::new($path); + let join = Path::new($join); let res = path.join_path(&join); assert_eq!(res.as_str(), Some($exp)); } @@ -1922,14 +1866,14 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let res = path.join_many_str($join); + 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::from_vec($path); + let path = Path::new($path); let res = path.join_many($join); assert_eq!(res.as_vec(), $exp); } @@ -1953,7 +1897,7 @@ mod tests { (s: $path:expr, $op:ident, $arg:expr, $res:expr) => ( { let pstr = $path; - let path = Path::from_str(pstr); + let path = Path::new(pstr); let arg = $arg; let res = path.$op(arg); let exp = $res; @@ -1963,137 +1907,137 @@ mod tests { } ) ) - t!(s: "a\\b\\c", with_dirname_str, "d", "d\\c"); - t!(s: "a\\b\\c", with_dirname_str, "d\\e", "d\\e\\c"); - t!(s: "a\\b\\c", with_dirname_str, "", "c"); - t!(s: "a\\b\\c", with_dirname_str, "\\", "\\c"); - t!(s: "a\\b\\c", with_dirname_str, "/", "\\c"); - t!(s: "a\\b\\c", with_dirname_str, ".", "c"); - t!(s: "a\\b\\c", with_dirname_str, "..", "..\\c"); - t!(s: "\\", with_dirname_str, "foo", "foo"); - t!(s: "\\", with_dirname_str, "", "."); - t!(s: "\\foo", with_dirname_str, "bar", "bar\\foo"); - t!(s: "..", with_dirname_str, "foo", "foo"); - t!(s: "..\\..", with_dirname_str, "foo", "foo"); - t!(s: "..", with_dirname_str, "", "."); - t!(s: "..\\..", with_dirname_str, "", "."); - t!(s: ".", with_dirname_str, "foo", "foo"); - t!(s: "foo", with_dirname_str, "..", "..\\foo"); - t!(s: "foo", with_dirname_str, "..\\..", "..\\..\\foo"); - t!(s: "C:\\a\\b", with_dirname_str, "foo", "foo\\b"); - t!(s: "foo", with_dirname_str, "C:\\a\\b", "C:\\a\\b\\foo"); - t!(s: "C:a\\b", with_dirname_str, "\\\\server\\share", "\\\\server\\share\\b"); - t!(s: "a", with_dirname_str, "\\\\server\\share", "\\\\server\\share\\a"); - t!(s: "a\\b", with_dirname_str, "\\\\?\\", "\\\\?\\b"); - t!(s: "a\\b", with_dirname_str, "C:", "C:b"); - t!(s: "a\\b", with_dirname_str, "C:\\", "C:\\b"); - t!(s: "a\\b", with_dirname_str, "C:/", "C:\\b"); - t!(s: "C:\\", with_dirname_str, "foo", "foo"); - t!(s: "C:", with_dirname_str, "foo", "foo"); - t!(s: ".", with_dirname_str, "C:\\", "C:\\"); - t!(s: ".", with_dirname_str, "C:/", "C:\\"); - t!(s: "\\\\?\\C:\\foo", with_dirname_str, "C:\\", "C:\\foo"); - t!(s: "\\\\?\\C:\\", with_dirname_str, "bar", "bar"); - t!(s: "foo\\bar", with_dirname_str, "\\\\?\\C:\\baz", "\\\\?\\C:\\baz\\bar"); - t!(s: "\\\\?\\foo", with_dirname_str, "C:\\bar", "C:\\bar"); - t!(s: "\\\\?\\a\\foo", with_dirname_str, "C:\\bar", "C:\\bar\\foo"); - t!(s: "\\\\?\\a\\foo/bar", with_dirname_str, "C:\\baz", "C:\\baz\\foo\\bar"); - t!(s: "\\\\?\\UNC\\server\\share\\baz", with_dirname_str, "a", "a\\baz"); - t!(s: "foo\\bar", with_dirname_str, "\\\\?\\UNC\\server\\share\\baz", + t!(s: "a\\b\\c", with_dirname, "d", "d\\c"); + t!(s: "a\\b\\c", with_dirname, "d\\e", "d\\e\\c"); + t!(s: "a\\b\\c", with_dirname, "", "c"); + t!(s: "a\\b\\c", with_dirname, "\\", "\\c"); + t!(s: "a\\b\\c", with_dirname, "/", "\\c"); + t!(s: "a\\b\\c", with_dirname, ".", "c"); + t!(s: "a\\b\\c", with_dirname, "..", "..\\c"); + t!(s: "\\", with_dirname, "foo", "foo"); + t!(s: "\\", with_dirname, "", "."); + t!(s: "\\foo", with_dirname, "bar", "bar\\foo"); + t!(s: "..", with_dirname, "foo", "foo"); + t!(s: "..\\..", with_dirname, "foo", "foo"); + t!(s: "..", with_dirname, "", "."); + t!(s: "..\\..", with_dirname, "", "."); + t!(s: ".", with_dirname, "foo", "foo"); + t!(s: "foo", with_dirname, "..", "..\\foo"); + t!(s: "foo", with_dirname, "..\\..", "..\\..\\foo"); + t!(s: "C:\\a\\b", with_dirname, "foo", "foo\\b"); + t!(s: "foo", with_dirname, "C:\\a\\b", "C:\\a\\b\\foo"); + t!(s: "C:a\\b", with_dirname, "\\\\server\\share", "\\\\server\\share\\b"); + t!(s: "a", with_dirname, "\\\\server\\share", "\\\\server\\share\\a"); + t!(s: "a\\b", with_dirname, "\\\\?\\", "\\\\?\\b"); + t!(s: "a\\b", with_dirname, "C:", "C:b"); + t!(s: "a\\b", with_dirname, "C:\\", "C:\\b"); + t!(s: "a\\b", with_dirname, "C:/", "C:\\b"); + t!(s: "C:\\", with_dirname, "foo", "foo"); + t!(s: "C:", with_dirname, "foo", "foo"); + t!(s: ".", with_dirname, "C:\\", "C:\\"); + t!(s: ".", with_dirname, "C:/", "C:\\"); + t!(s: "\\\\?\\C:\\foo", with_dirname, "C:\\", "C:\\foo"); + t!(s: "\\\\?\\C:\\", with_dirname, "bar", "bar"); + t!(s: "foo\\bar", with_dirname, "\\\\?\\C:\\baz", "\\\\?\\C:\\baz\\bar"); + t!(s: "\\\\?\\foo", with_dirname, "C:\\bar", "C:\\bar"); + t!(s: "\\\\?\\a\\foo", with_dirname, "C:\\bar", "C:\\bar\\foo"); + t!(s: "\\\\?\\a\\foo/bar", with_dirname, "C:\\baz", "C:\\baz\\foo\\bar"); + t!(s: "\\\\?\\UNC\\server\\share\\baz", with_dirname, "a", "a\\baz"); + t!(s: "foo\\bar", with_dirname, "\\\\?\\UNC\\server\\share\\baz", "\\\\?\\UNC\\server\\share\\baz\\bar"); - t!(s: "\\\\.\\foo", with_dirname_str, "bar", "bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz", "baz\\bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz\\", "baz\\bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname_str, "baz/", "baz\\bar"); - - t!(s: "a\\b\\c", with_filename_str, "d", "a\\b\\d"); - t!(s: ".", with_filename_str, "foo", "foo"); - t!(s: "\\a\\b\\c", with_filename_str, "d", "\\a\\b\\d"); - t!(s: "\\", with_filename_str, "foo", "\\foo"); - t!(s: "\\a", with_filename_str, "foo", "\\foo"); - t!(s: "foo", with_filename_str, "bar", "bar"); - t!(s: "\\", with_filename_str, "foo\\", "\\foo"); - t!(s: "\\a", with_filename_str, "foo\\", "\\foo"); - t!(s: "a\\b\\c", with_filename_str, "", "a\\b"); - t!(s: "a\\b\\c", with_filename_str, ".", "a\\b"); - t!(s: "a\\b\\c", with_filename_str, "..", "a"); - t!(s: "\\a", with_filename_str, "", "\\"); - t!(s: "foo", with_filename_str, "", "."); - t!(s: "a\\b\\c", with_filename_str, "d\\e", "a\\b\\d\\e"); - t!(s: "a\\b\\c", with_filename_str, "\\d", "a\\b\\d"); - t!(s: "..", with_filename_str, "foo", "..\\foo"); - t!(s: "..\\..", with_filename_str, "foo", "..\\..\\foo"); - t!(s: "..", with_filename_str, "", ".."); - t!(s: "..\\..", with_filename_str, "", "..\\.."); - t!(s: "C:\\foo\\bar", with_filename_str, "baz", "C:\\foo\\baz"); - t!(s: "C:\\foo", with_filename_str, "bar", "C:\\bar"); - t!(s: "C:\\", with_filename_str, "foo", "C:\\foo"); - t!(s: "C:foo\\bar", with_filename_str, "baz", "C:foo\\baz"); - t!(s: "C:foo", with_filename_str, "bar", "C:bar"); - t!(s: "C:", with_filename_str, "foo", "C:foo"); - t!(s: "C:\\foo", with_filename_str, "", "C:\\"); - t!(s: "C:foo", with_filename_str, "", "C:"); - t!(s: "C:\\foo\\bar", with_filename_str, "..", "C:\\"); - t!(s: "C:\\foo", with_filename_str, "..", "C:\\"); - t!(s: "C:\\", with_filename_str, "..", "C:\\"); - t!(s: "C:foo\\bar", with_filename_str, "..", "C:"); - t!(s: "C:foo", with_filename_str, "..", "C:.."); - t!(s: "C:", with_filename_str, "..", "C:.."); - t!(s: "\\\\server\\share\\foo", with_filename_str, "bar", "\\\\server\\share\\bar"); - t!(s: "\\\\server\\share", with_filename_str, "foo", "\\\\server\\share\\foo"); - t!(s: "\\\\server\\share\\foo", with_filename_str, "", "\\\\server\\share"); - t!(s: "\\\\server\\share", with_filename_str, "", "\\\\server\\share"); - t!(s: "\\\\server\\share\\foo", with_filename_str, "..", "\\\\server\\share"); - t!(s: "\\\\server\\share", with_filename_str, "..", "\\\\server\\share"); - t!(s: "\\\\?\\C:\\foo\\bar", with_filename_str, "baz", "\\\\?\\C:\\foo\\baz"); - t!(s: "\\\\?\\C:\\foo", with_filename_str, "bar", "\\\\?\\C:\\bar"); - t!(s: "\\\\?\\C:\\", with_filename_str, "foo", "\\\\?\\C:\\foo"); - t!(s: "\\\\?\\C:\\foo", with_filename_str, "..", "\\\\?\\C:\\.."); - t!(s: "\\\\?\\foo\\bar", with_filename_str, "baz", "\\\\?\\foo\\baz"); - t!(s: "\\\\?\\foo", with_filename_str, "bar", "\\\\?\\foo\\bar"); - t!(s: "\\\\?\\", with_filename_str, "foo", "\\\\?\\\\foo"); - t!(s: "\\\\?\\foo\\bar", with_filename_str, "..", "\\\\?\\foo\\.."); - t!(s: "\\\\.\\foo\\bar", with_filename_str, "baz", "\\\\.\\foo\\baz"); - t!(s: "\\\\.\\foo", with_filename_str, "bar", "\\\\.\\foo\\bar"); - t!(s: "\\\\.\\foo\\bar", with_filename_str, "..", "\\\\.\\foo\\.."); - - t!(s: "hi\\there.txt", with_filestem_str, "here", "hi\\here.txt"); - t!(s: "hi\\there.txt", with_filestem_str, "", "hi\\.txt"); - t!(s: "hi\\there.txt", with_filestem_str, ".", "hi\\..txt"); - t!(s: "hi\\there.txt", with_filestem_str, "..", "hi\\...txt"); - t!(s: "hi\\there.txt", with_filestem_str, "\\", "hi\\.txt"); - t!(s: "hi\\there.txt", with_filestem_str, "foo\\bar", "hi\\foo\\bar.txt"); - t!(s: "hi\\there.foo.txt", with_filestem_str, "here", "hi\\here.txt"); - t!(s: "hi\\there", with_filestem_str, "here", "hi\\here"); - t!(s: "hi\\there", with_filestem_str, "", "hi"); - t!(s: "hi", with_filestem_str, "", "."); - t!(s: "\\hi", with_filestem_str, "", "\\"); - t!(s: "hi\\there", with_filestem_str, "..", "."); - t!(s: "hi\\there", with_filestem_str, ".", "hi"); - t!(s: "hi\\there.", with_filestem_str, "foo", "hi\\foo."); - t!(s: "hi\\there.", with_filestem_str, "", "hi"); - t!(s: "hi\\there.", with_filestem_str, ".", "."); - t!(s: "hi\\there.", with_filestem_str, "..", "hi\\..."); - t!(s: "\\", with_filestem_str, "foo", "\\foo"); - t!(s: ".", with_filestem_str, "foo", "foo"); - t!(s: "hi\\there..", with_filestem_str, "here", "hi\\here."); - t!(s: "hi\\there..", with_filestem_str, "", "hi"); + t!(s: "\\\\.\\foo", with_dirname, "bar", "bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname, "baz", "baz\\bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname, "baz\\", "baz\\bar"); + t!(s: "\\\\.\\foo\\bar", with_dirname, "baz/", "baz\\bar"); + + 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_filestem, "here", "hi\\here.txt"); + t!(s: "hi\\there.txt", with_filestem, "", "hi\\.txt"); + t!(s: "hi\\there.txt", with_filestem, ".", "hi\\..txt"); + t!(s: "hi\\there.txt", with_filestem, "..", "hi\\...txt"); + t!(s: "hi\\there.txt", with_filestem, "\\", "hi\\.txt"); + t!(s: "hi\\there.txt", with_filestem, "foo\\bar", "hi\\foo\\bar.txt"); + t!(s: "hi\\there.foo.txt", with_filestem, "here", "hi\\here.txt"); + t!(s: "hi\\there", with_filestem, "here", "hi\\here"); + t!(s: "hi\\there", with_filestem, "", "hi"); + t!(s: "hi", with_filestem, "", "."); + t!(s: "\\hi", with_filestem, "", "\\"); + t!(s: "hi\\there", with_filestem, "..", "."); + t!(s: "hi\\there", with_filestem, ".", "hi"); + t!(s: "hi\\there.", with_filestem, "foo", "hi\\foo."); + t!(s: "hi\\there.", with_filestem, "", "hi"); + t!(s: "hi\\there.", with_filestem, ".", "."); + t!(s: "hi\\there.", with_filestem, "..", "hi\\..."); + t!(s: "\\", with_filestem, "foo", "\\foo"); + t!(s: ".", with_filestem, "foo", "foo"); + t!(s: "hi\\there..", with_filestem, "here", "hi\\here."); + t!(s: "hi\\there..", with_filestem, "", "hi"); // filestem setter calls filename setter internally, no need for extended tests - t!(s: "hi\\there.txt", with_extension_str, "exe", "hi\\there.exe"); - t!(s: "hi\\there.txt", with_extension_str, "", "hi\\there"); - t!(s: "hi\\there.txt", with_extension_str, ".", "hi\\there.."); - t!(s: "hi\\there.txt", with_extension_str, "..", "hi\\there..."); - t!(s: "hi\\there", with_extension_str, "txt", "hi\\there.txt"); - t!(s: "hi\\there", with_extension_str, ".", "hi\\there.."); - t!(s: "hi\\there", with_extension_str, "..", "hi\\there..."); - t!(s: "hi\\there.", with_extension_str, "txt", "hi\\there.txt"); - t!(s: "hi\\.foo", with_extension_str, "txt", "hi\\.foo.txt"); - t!(s: "hi\\there.txt", with_extension_str, ".foo", "hi\\there..foo"); - t!(s: "\\", with_extension_str, "txt", "\\"); - t!(s: "\\", with_extension_str, ".", "\\"); - t!(s: "\\", with_extension_str, "..", "\\"); - t!(s: ".", with_extension_str, "txt", "."); + 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 } @@ -2104,9 +2048,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::from_str(path); + let mut p1 = Path::new(path); p1.$set(arg); - let p2 = Path::from_str(path); + let p2 = Path::new(path); assert_eq!(p1, p2.$with(arg)); } ); @@ -2114,9 +2058,9 @@ mod tests { { let path = $path; let arg = $arg; - let mut p1 = Path::from_vec(path); + let mut p1 = Path::new(path); p1.$set(arg); - let p2 = Path::from_vec(path); + let p2 = Path::new(path); assert_eq!(p1, p2.$with(arg)); } ) @@ -2124,36 +2068,36 @@ mod tests { t!(v: b!("a\\b\\c"), set_dirname, with_dirname, b!("d")); t!(v: b!("a\\b\\c"), set_dirname, with_dirname, b!("d\\e")); - t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, "d"); - t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, "d\\e"); - t!(s: "\\", set_dirname_str, with_dirname_str, "foo"); - t!(s: "\\foo", set_dirname_str, with_dirname_str, "bar"); - t!(s: "a\\b\\c", set_dirname_str, with_dirname_str, ""); - t!(s: "..\\..", set_dirname_str, with_dirname_str, "x"); - t!(s: "foo", set_dirname_str, with_dirname_str, "..\\.."); + t!(s: "a\\b\\c", set_dirname, with_dirname, "d"); + t!(s: "a\\b\\c", set_dirname, with_dirname, "d\\e"); + t!(s: "\\", set_dirname, with_dirname, "foo"); + t!(s: "\\foo", set_dirname, with_dirname, "bar"); + t!(s: "a\\b\\c", set_dirname, with_dirname, ""); + t!(s: "..\\..", set_dirname, with_dirname, "x"); + t!(s: "foo", set_dirname, with_dirname, "..\\.."); 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_str, with_filename_str, "d"); - t!(s: "\\", set_filename_str, with_filename_str, "foo"); - t!(s: ".", set_filename_str, with_filename_str, "foo"); - t!(s: "a\\b", set_filename_str, with_filename_str, ""); - t!(s: "a", set_filename_str, with_filename_str, ""); + 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_filestem, with_filestem, b!("here")); - t!(s: "hi\\there.txt", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi\\there.", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi\\there", set_filestem_str, with_filestem_str, "here"); - t!(s: "hi\\there.txt", set_filestem_str, with_filestem_str, ""); - t!(s: "hi\\there", set_filestem_str, with_filestem_str, ""); + t!(s: "hi\\there.txt", set_filestem, with_filestem, "here"); + t!(s: "hi\\there.", set_filestem, with_filestem, "here"); + t!(s: "hi\\there", set_filestem, with_filestem, "here"); + t!(s: "hi\\there.txt", set_filestem, with_filestem, ""); + t!(s: "hi\\there", set_filestem, with_filestem, ""); t!(v: b!("hi\\there.txt"), set_extension, with_extension, b!("exe")); - t!(s: "hi\\there.txt", set_extension_str, with_extension_str, "exe"); - t!(s: "hi\\there.", set_extension_str, with_extension_str, "txt"); - t!(s: "hi\\there", set_extension_str, with_extension_str, "txt"); - t!(s: "hi\\there.txt", set_extension_str, with_extension_str, ""); - t!(s: "hi\\there", set_extension_str, with_extension_str, ""); - t!(s: ".", set_extension_str, with_extension_str, "txt"); + 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. @@ -2164,14 +2108,14 @@ mod tests { macro_rules! t( (s: $path:expr, $ext:expr, $exp:expr) => ( { - let mut path = Path::from_str($path); - path.add_extension_str($ext); + let mut path = Path::new($path); + path.add_extension($ext); assert_eq!(path.as_str(), Some($exp)); } ); (v: $path:expr, $ext:expr, $exp:expr) => ( { - let mut path = Path::from_vec($path); + let mut path = Path::new($path); path.add_extension($ext); assert_eq!(path.as_vec(), $exp); } @@ -2221,19 +2165,19 @@ mod tests { ) ) - t!(v: Path::from_vec(b!("a\\b\\c")), Some(b!("c")), b!("a\\b"), Some(b!("c")), None); - t!(s: Path::from_str("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); - t!(s: Path::from_str("."), None, Some("."), None, None); - t!(s: Path::from_str("\\"), None, Some("\\"), None, None); - t!(s: Path::from_str(".."), None, Some(".."), None, None); - t!(s: Path::from_str("..\\.."), None, Some("..\\.."), None, None); - t!(s: Path::from_str("hi\\there.txt"), Some("there.txt"), Some("hi"), + 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::from_str("hi\\there"), Some("there"), Some("hi"), Some("there"), None); - t!(s: Path::from_str("hi\\there."), Some("there."), Some("hi"), + 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::from_str("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None); - t!(s: Path::from_str("hi\\..there"), Some("..there"), Some("hi"), + 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 @@ -2241,12 +2185,12 @@ mod tests { #[test] fn test_dir_file_path() { - t!(s: Path::from_str("hi\\there").dir_path(), "hi"); - t!(s: Path::from_str("hi").dir_path(), "."); - t!(s: Path::from_str("\\hi").dir_path(), "\\"); - t!(s: Path::from_str("\\").dir_path(), "\\"); - t!(s: Path::from_str("..").dir_path(), ".."); - t!(s: Path::from_str("..\\..").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(), "..\\.."); macro_rules! t( ($path:expr, $exp:expr) => ( @@ -2258,12 +2202,12 @@ mod tests { ); ) - t!(Path::from_str("hi\\there").file_path(), Some("there")); - t!(Path::from_str("hi").file_path(), Some("hi")); - t!(Path::from_str(".").file_path(), None); - t!(Path::from_str("\\").file_path(), None); - t!(Path::from_str("..").file_path(), None); - t!(Path::from_str("..\\..").file_path(), None); + t!(Path::new("hi\\there").file_path(), Some("there")); + t!(Path::new("hi").file_path(), Some("hi")); + t!(Path::new(".").file_path(), None); + t!(Path::new("\\").file_path(), None); + t!(Path::new("..").file_path(), None); + t!(Path::new("..\\..").file_path(), None); // dir_path and file_path are just dirname and filename interpreted as paths. // No need for extended tests @@ -2274,7 +2218,7 @@ mod tests { macro_rules! t( ($path:expr, $abs:expr, $vol:expr, $cwd:expr, $rel:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let (abs, vol, cwd, rel) = ($abs, $vol, $cwd, $rel); let b = path.is_absolute(); assert!(b == abs, "Path '{}'.is_absolute(): expected {:?}, found {:?}", @@ -2314,8 +2258,8 @@ mod tests { macro_rules! t( (s: $path:expr, $dest:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let dest = Path::from_str($dest); + let path = Path::new($path); + let dest = Path::new($dest); let exp = $exp; let res = path.is_ancestor_of(&dest); assert!(res == exp, @@ -2417,8 +2361,8 @@ mod tests { macro_rules! t( (s: $path:expr, $child:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let child = Path::from_str($child); + let path = Path::new($path); + let child = Path::new($child); assert_eq!(path.ends_with_path(&child), $exp); } ); @@ -2449,8 +2393,8 @@ mod tests { macro_rules! t( (s: $path:expr, $other:expr, $exp:expr) => ( { - let path = Path::from_str($path); - let other = Path::from_str($other); + let path = Path::new($path); + let other = Path::new($other); let res = path.path_relative_from(&other); let exp = $exp; assert!(res.and_then_ref(|x| x.as_str()) == exp, @@ -2583,7 +2527,7 @@ mod tests { macro_rules! t( (s: $path:expr, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; assert!(comps.as_slice() == exp, @@ -2598,7 +2542,7 @@ mod tests { ); (v: [$($arg:expr),+], $exp:expr) => ( { - let path = Path::from_vec(b!($($arg),+)); + let path = Path::new(b!($($arg),+)); let comps = path.str_component_iter().map(|x|x.unwrap()).to_owned_vec(); let exp: &[&str] = $exp; assert!(comps.as_slice() == exp, @@ -2658,7 +2602,7 @@ mod tests { macro_rules! t( (s: $path:expr, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let comps = path.component_iter().to_owned_vec(); let exp: &[&[u8]] = $exp; assert!(comps.as_slice() == exp, "component_iter: Expected {:?}, found {:?}", @@ -2679,13 +2623,13 @@ mod tests { #[test] fn test_each_parent() { - assert!(Path::from_str("/foo/bar").each_parent(|_| true)); - assert!(!Path::from_str("/foo/bar").each_parent(|_| false)); + assert!(Path::new("/foo/bar").each_parent(|_| true)); + assert!(!Path::new("/foo/bar").each_parent(|_| false)); macro_rules! t( (s: $path:expr, $exp:expr) => ( { - let path = Path::from_str($path); + let path = Path::new($path); let exp: &[&str] = $exp; let mut comps = exp.iter().map(|&x|x); do path.each_parent |p| { diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 9fbb897c2a7da..39c3c5692f8a2 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -703,7 +703,7 @@ mod test { fn file_test_io_smoke_test() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; - let filename = &Path::from_str("./tmp/file_rt_io_file_test.txt"); + let filename = &Path::new("./tmp/file_rt_io_file_test.txt"); { let mut write_stream = open(filename, Create, ReadWrite).unwrap(); write_stream.write(message.as_bytes()); @@ -725,7 +725,7 @@ mod test { #[test] fn file_test_io_invalid_path_opened_without_create_should_raise_condition() { do run_in_mt_newsched_task { - let filename = &Path::from_str("./tmp/file_that_does_not_exist.txt"); + let filename = &Path::new("./tmp/file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -740,7 +740,7 @@ mod test { #[test] fn file_test_iounlinking_invalid_path_should_raise_condition() { do run_in_mt_newsched_task { - let filename = &Path::from_str("./tmp/file_another_file_that_does_not_exist.txt"); + let filename = &Path::new("./tmp/file_another_file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -757,7 +757,7 @@ mod test { use str; let message = "ten-four"; let mut read_mem = [0, .. 8]; - let filename = &Path::from_str("./tmp/file_rt_io_file_test_positional.txt"); + let filename = &Path::new("./tmp/file_rt_io_file_test_positional.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(message.as_bytes()); @@ -788,7 +788,7 @@ mod test { let set_cursor = 4 as u64; let mut tell_pos_pre_read; let mut tell_pos_post_read; - let filename = &Path::from_str("./tmp/file_rt_io_file_test_seeking.txt"); + let filename = &Path::new("./tmp/file_rt_io_file_test_seeking.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(message.as_bytes()); @@ -817,7 +817,7 @@ mod test { let final_msg = "foo-the-bar!!"; let seek_idx = 3; let mut read_mem = [0, .. 13]; - let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_and_write.txt"); + let filename = &Path::new("./tmp/file_rt_io_file_test_seek_and_write.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(initial_msg.as_bytes()); @@ -843,7 +843,7 @@ mod test { let chunk_two = "asdf"; let chunk_three = "zxcv"; let mut read_mem = [0, .. 4]; - let filename = &Path::from_str("./tmp/file_rt_io_file_test_seek_shakedown.txt"); + let filename = &Path::new("./tmp/file_rt_io_file_test_seek_shakedown.txt"); { let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); rw_stream.write(initial_msg.as_bytes()); @@ -873,7 +873,7 @@ mod test { #[test] fn file_test_stat_is_correct_on_is_file() { do run_in_mt_newsched_task { - let filename = &Path::from_str("./tmp/file_stat_correct_on_is_file.txt"); + let filename = &Path::new("./tmp/file_stat_correct_on_is_file.txt"); { let mut fs = open(filename, Create, ReadWrite).unwrap(); let msg = "hw"; @@ -891,7 +891,7 @@ mod test { #[test] fn file_test_stat_is_correct_on_is_dir() { do run_in_mt_newsched_task { - let filename = &Path::from_str("./tmp/file_stat_correct_on_is_dir"); + let filename = &Path::new("./tmp/file_stat_correct_on_is_dir"); mkdir(filename); let stat_res = match stat(filename) { Some(s) => s, @@ -905,7 +905,7 @@ mod test { #[test] fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { do run_in_mt_newsched_task { - let dir = &Path::from_str("./tmp/fileinfo_false_on_dir"); + let dir = &Path::new("./tmp/fileinfo_false_on_dir"); mkdir(dir); assert!(dir.is_file() == false); rmdir(dir); @@ -915,7 +915,7 @@ mod test { #[test] fn file_test_fileinfo_check_exists_before_and_after_file_creation() { do run_in_mt_newsched_task { - let file = &Path::from_str("./tmp/fileinfo_check_exists_b_and_a.txt"); + let file = &Path::new("./tmp/fileinfo_check_exists_b_and_a.txt"); { let msg = "foo".as_bytes(); let mut w = file.open_writer(Create); @@ -930,7 +930,7 @@ mod test { #[test] fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { do run_in_mt_newsched_task { - let dir = &Path::from_str("./tmp/before_and_after_dir"); + let dir = &Path::new("./tmp/before_and_after_dir"); assert!(!dir.exists()); dir.mkdir(); assert!(dir.exists()); @@ -944,11 +944,11 @@ mod test { fn file_test_directoryinfo_readdir() { use str; do run_in_mt_newsched_task { - let dir = &Path::from_str("./tmp/di_readdir"); + let dir = &Path::new("./tmp/di_readdir"); dir.mkdir(); let prefix = "foo"; for n in range(0,3) { - let f = dir.join_str(format!("{}.txt", n)); + let f = dir.join(format!("{}.txt", n)); let mut w = f.open_writer(Create); let msg_str = (prefix + n.to_str().to_owned()).to_owned(); let msg = msg_str.as_bytes(); diff --git a/src/libstd/rt/io/support.rs b/src/libstd/rt/io/support.rs index a872423c255ae..31040bc51a135 100644 --- a/src/libstd/rt/io/support.rs +++ b/src/libstd/rt/io/support.rs @@ -35,7 +35,7 @@ mod test { #[test] fn path_like_smoke_test() { let expected = if cfg!(unix) { "/home" } else { "C:\\" }; - let path = Path::from_str(expected); + let path = Path::new(expected); path.path_as_str(|p| assert!(p == expected)); path.path_as_str(|p| assert!(p == expected)); } diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 7756448adf829..cb5054626d4c4 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -391,7 +391,7 @@ mod test { let read_mem = vec::from_elem(read_buf_len, 0u8); let read_buf = slice_to_uv_buf(read_mem); let read_buf_ptr: *Buf = &read_buf; - let p = Path::from_str(path_str); + let p = Path::new(path_str); let open_req = FsRequest::new(); do open_req.open(&loop_, &p, create_flags as int, mode as int) |req, uverr| { @@ -405,7 +405,7 @@ mod test { assert!(uverr.is_none()); let loop_ = req.get_loop(); let open_req = FsRequest::new(); - do open_req.open(&loop_, &Path::from_str(path_str), read_flags as int,0) + do open_req.open(&loop_, &Path::new(path_str), read_flags as int,0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); @@ -431,7 +431,7 @@ mod test { assert!(uverr.is_none()); let loop_ = &req.get_loop(); let unlink_req = FsRequest::new(); - do unlink_req.unlink(loop_, &Path::from_str(path_str)) + do unlink_req.unlink(loop_, &Path::new(path_str)) |_,uverr| { assert!(uverr.is_none()); }; @@ -465,7 +465,7 @@ mod test { let write_buf = slice_to_uv_buf(write_val); // open/create let open_req = FsRequest::new(); - let result = open_req.open_sync(&loop_, &Path::from_str(path_str), + let result = open_req.open_sync(&loop_, &Path::new(path_str), create_flags as int, mode as int); assert!(result.is_ok()); let fd = result.unwrap(); @@ -479,7 +479,7 @@ mod test { assert!(result.is_ok()); // re-open let open_req = FsRequest::new(); - let result = open_req.open_sync(&loop_, &Path::from_str(path_str), + let result = open_req.open_sync(&loop_, &Path::new(path_str), read_flags as int,0); assert!(result.is_ok()); let len = 1028; @@ -503,7 +503,7 @@ mod test { assert!(result.is_ok()); // unlink let unlink_req = FsRequest::new(); - let result = unlink_req.unlink_sync(&loop_, &Path::from_str(path_str)); + let result = unlink_req.unlink_sync(&loop_, &Path::new(path_str)); assert!(result.is_ok()); } else { fail2!("nread was 0.. wudn't expectin' that."); } loop_.close(); diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index a139c4e95eff4..d5893d6d01414 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -18,6 +18,7 @@ use ops::Drop; use option::*; use ptr; use str; +use str::Str; use result::*; use rt::io::IoError; use rt::io::net::ip::{SocketAddr, IpAddr}; @@ -631,7 +632,7 @@ impl IoFactory for UvIoFactory { None => { let stat = req.get_stat(); Ok(FileStat { - path: Path::from_str(path_str), + path: Path::new(path_str.as_slice()), is_file: stat.is_file(), is_dir: stat.is_dir(), size: stat.st_size, @@ -720,8 +721,8 @@ impl IoFactory for UvIoFactory { let rel_paths = req.get_paths(); let mut paths = ~[]; for r in rel_paths.iter() { - let mut p = Path::from_str(path_str); - p.push_str(*r); + let mut p = Path::new(path_str.as_slice()); + p.push(r.as_slice()); paths.push(p); } Ok(paths) @@ -2179,20 +2180,20 @@ fn file_test_uvio_full_simple_impl() { { let create_fm = Create; let create_fa = ReadWrite; - let mut fd = (*io).fs_open(&Path::from_str(path), create_fm, create_fa).unwrap(); + let mut fd = (*io).fs_open(&Path::new(path), create_fm, create_fa).unwrap(); let write_buf = write_val.as_bytes(); fd.write(write_buf); } { let ro_fm = Open; let ro_fa = Read; - let mut fd = (*io).fs_open(&Path::from_str(path), ro_fm, ro_fa).unwrap(); + let mut fd = (*io).fs_open(&Path::new(path), ro_fm, ro_fa).unwrap(); let mut read_vec = [0, .. 1028]; let nread = fd.read(read_vec).unwrap(); let read_val = str::from_utf8(read_vec.slice(0, nread as uint)); assert!(read_val == write_val.to_owned()); } - (*io).fs_unlink(&Path::from_str(path)); + (*io).fs_unlink(&Path::new(path)); } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 3f7ce3eae5864..0d32efbba883d 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -579,7 +579,7 @@ mod tests { let output = str::from_utf8(prog.finish_with_output().output); let parent_dir = os::getcwd(); - let child_dir = Path::from_str(output.trim()); + let child_dir = Path::new(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); @@ -596,7 +596,7 @@ mod tests { let mut prog = run_pwd(Some(&parent_dir)); let output = str::from_utf8(prog.finish_with_output().output); - let child_dir = Path::from_str(output.trim()); + let child_dir = Path::new(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index a91d68366f33a..58ff51fe102bd 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -121,7 +121,7 @@ mod test { fn test_errors_do_not_crash() { // Open /dev/null as a library to get an error, and make sure // that only causes an error, and not a crash. - let path = GenericPath::from_str("/dev/null"); + let path = GenericPath::new("/dev/null"); match DynamicLibrary::open(Some(&path)) { Err(_) => {} Ok(_) => fail2!("Successfully opened the empty library.") diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index ef09315a88777..c7ac3e1da9efe 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -81,7 +81,7 @@ pub fn expand_include(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) let file = get_single_str_from_tts(cx, sp, tts, "include!"); let p = parse::new_sub_parser_from_file( cx.parse_sess(), cx.cfg(), - &res_rel_file(cx, sp, &Path::from_str(file)), sp); + &res_rel_file(cx, sp, &Path::new(file)), sp); base::MRExpr(p.parse_expr()) } @@ -89,7 +89,7 @@ pub fn expand_include(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) pub fn expand_include_str(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult { let file = get_single_str_from_tts(cx, sp, tts, "include_str!"); - let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path::from_str(file))); + let res = io::read_whole_file_str(&res_rel_file(cx, sp, &Path::new(file))); match res { result::Ok(res) => { base::MRExpr(cx.expr_str(sp, res.to_managed())) @@ -103,7 +103,7 @@ pub fn expand_include_str(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) pub fn expand_include_bin(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult { let file = get_single_str_from_tts(cx, sp, tts, "include_bin!"); - match io::read_whole_file(&res_rel_file(cx, sp, &Path::from_str(file))) { + match io::read_whole_file(&res_rel_file(cx, sp, &Path::new(file))) { result::Ok(src) => { let u8_exprs: ~[@ast::Expr] = src.iter().map(|char| cx.expr_u8(sp, *char)).collect(); base::MRExpr(cx.expr_vec(sp, u8_exprs)) @@ -145,7 +145,7 @@ fn topmost_expn_info(expn_info: @codemap::ExpnInfo) -> @codemap::ExpnInfo { fn res_rel_file(cx: @ExtCtxt, sp: codemap::Span, arg: &Path) -> Path { // NB: relative paths are resolved relative to the compilation unit if !arg.is_absolute() { - let mut cu = Path::from_str(cx.codemap().span_to_filename(sp)); + let mut cu = Path::new(cx.codemap().span_to_filename(sp)); cu.pop(); cu.push_path(arg); cu diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ad565fd2ec4ac..32cd45c86cb2a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3992,20 +3992,20 @@ impl Parser { outer_attrs: &[ast::Attribute], id_sp: Span) -> (ast::item_, ~[ast::Attribute]) { - let mut prefix = Path::from_str(self.sess.cm.span_to_filename(*self.span)); + let mut prefix = Path::new(self.sess.cm.span_to_filename(*self.span)); prefix.pop(); let mod_path_stack = &*self.mod_path_stack; - let mod_path = Path::from_str(".").join_many_str(*mod_path_stack); + let mod_path = Path::new(".").join_many(*mod_path_stack); let dir_path = prefix.join_path(&mod_path); let file_path = match ::attr::first_attr_value_str_by_name( outer_attrs, "path") { - Some(d) => dir_path.join_str(d), + Some(d) => dir_path.join(d), None => { let mod_name = token::interner_get(id.name).to_owned(); let default_path_str = mod_name + ".rs"; let secondary_path_str = mod_name + "/mod.rs"; - let default_path = dir_path.join_str(default_path_str); - let secondary_path = dir_path.join_str(secondary_path_str); + let default_path = dir_path.join(default_path_str.as_slice()); + let secondary_path = dir_path.join(secondary_path_str.as_slice()); let default_exists = default_path.exists(); let secondary_exists = secondary_path.exists(); match (default_exists, secondary_exists) { diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs index 8a5cb016a738c..6ce289620fb79 100644 --- a/src/test/bench/core-std.rs +++ b/src/test/bench/core-std.rs @@ -73,8 +73,8 @@ fn read_line() { use std::rt::io::file::FileInfo; use std::rt::io::buffered::BufferedReader; - let path = Path::from_str(env!("CFG_SRC_DIR")) - .join_str("src/test/bench/shootout-k-nucleotide.data"); + let mut path = Path::new(env!("CFG_SRC_DIR")); + path.push("src/test/bench/shootout-k-nucleotide.data"); for _ in range(0, 3) { let mut reader = BufferedReader::new(path.open_reader(Open).unwrap()); diff --git a/src/test/bench/shootout-fasta.rs b/src/test/bench/shootout-fasta.rs index addb43c49952c..77c3a0e39835f 100644 --- a/src/test/bench/shootout-fasta.rs +++ b/src/test/bench/shootout-fasta.rs @@ -122,7 +122,7 @@ fn main() { }; let writer = if os::getenv("RUST_BENCH").is_some() { - io::file_writer(&Path::from_str("./shootout-fasta.data"), + io::file_writer(&Path::new("./shootout-fasta.data"), [io::Truncate, io::Create]).unwrap() } else { io::stdout() diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index 58ec5d440718c..c0464dcc676c8 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -164,8 +164,8 @@ fn main() { let rdr = if os::getenv("RUST_BENCH").is_some() { // FIXME: Using this compile-time env variable is a crummy way to // get to this massive data set, but include_bin! chokes on it (#2598) - let path = Path::from_str(env!("CFG_SRC_DIR")) - .join_str("src/test/bench/shootout-k-nucleotide.data"); + let mut path = Path::new(env!("CFG_SRC_DIR")); + path.push("src/test/bench/shootout-k-nucleotide.data"); ~path.open_reader(Open).unwrap() as ~Reader } else { ~stdio::stdin() as ~Reader diff --git a/src/test/run-pass/glob-std.rs b/src/test/run-pass/glob-std.rs index 170645f4404b5..c86a438344adc 100644 --- a/src/test/run-pass/glob-std.rs +++ b/src/test/run-pass/glob-std.rs @@ -20,14 +20,14 @@ use std::{io, os, unstable}; pub fn main() { fn mk_file(path: &str, directory: bool) { if directory { - os::make_dir(&Path::from_str(path), 0xFFFF); + os::make_dir(&Path::new(path), 0xFFFF); } else { - io::mk_file_writer(&Path::from_str(path), [io::Create]); + io::mk_file_writer(&Path::new(path), [io::Create]); } } fn abs_path(path: &str) -> Path { - os::getcwd().join_path(&Path::from_str(path)) + os::getcwd().join_path(&Path::new(path)) } fn glob_vec(pattern: &str) -> ~[Path] { diff --git a/src/test/run-pass/issue-3424.rs b/src/test/run-pass/issue-3424.rs index 01d895d1bcf05..f860426ffd270 100644 --- a/src/test/run-pass/issue-3424.rs +++ b/src/test/run-pass/issue-3424.rs @@ -23,7 +23,7 @@ fn tester() { let loader: rsrc_loader = |_path| {result::Ok(~"more blah")}; - let path = path::Path::from_str("blah"); + let path = path::Path::new("blah"); assert!(loader(&path).is_ok()); } diff --git a/src/test/run-pass/rename-directory.rs b/src/test/run-pass/rename-directory.rs index 608624670fc37..76a1d32705b2d 100644 --- a/src/test/run-pass/rename-directory.rs +++ b/src/test/run-pass/rename-directory.rs @@ -25,9 +25,9 @@ fn rename_directory() { let tmpdir = TempDir::new("rename_directory").expect("rename_directory failed"); let tmpdir = tmpdir.path(); - let old_path = tmpdir.join_many_str(["foo", "bar", "baz"]); + let old_path = tmpdir.join_many(["foo", "bar", "baz"]); assert!(os::mkdir_recursive(&old_path, U_RWX)); - let test_file = &old_path.join_str("temp.txt"); + let test_file = &old_path.join("temp.txt"); /* Write the temp input file */ let ostream = do test_file.with_c_str |fromp| { @@ -46,11 +46,11 @@ fn rename_directory() { } assert_eq!(libc::fclose(ostream), (0u as libc::c_int)); - let new_path = tmpdir.join_many_str(["quux", "blat"]); + let new_path = tmpdir.join_many(["quux", "blat"]); assert!(os::mkdir_recursive(&new_path, U_RWX)); - assert!(os::rename_file(&old_path, &new_path.join_str("newdir"))); - assert!(os::path_is_dir(&new_path.join_str("newdir"))); - assert!(os::path_exists(&new_path.join_many_str(["newdir", "temp.txt"]))); + assert!(os::rename_file(&old_path, &new_path.join("newdir"))); + assert!(os::path_is_dir(&new_path.join("newdir"))); + assert!(os::path_exists(&new_path.join_many(["newdir", "temp.txt"]))); } } diff --git a/src/test/run-pass/stat.rs b/src/test/run-pass/stat.rs index 5cd62368aa222..aa0661d49a219 100644 --- a/src/test/run-pass/stat.rs +++ b/src/test/run-pass/stat.rs @@ -18,8 +18,8 @@ use std::io; use std::os; pub fn main() { - let dir = tempfile::TempDir::new_in(&Path::from_str("."), "").unwrap(); - let path = dir.path().join_str("file"); + let dir = tempfile::TempDir::new_in(&Path::new("."), "").unwrap(); + let path = dir.path().join("file"); { match io::file_writer(&path, [io::Create, io::Truncate]) { diff --git a/src/test/run-pass/tempfile.rs b/src/test/run-pass/tempfile.rs index 80e63396350b2..837194fcf9f07 100644 --- a/src/test/run-pass/tempfile.rs +++ b/src/test/run-pass/tempfile.rs @@ -28,7 +28,7 @@ use std::cell::Cell; fn test_tempdir() { let path = { - let p = TempDir::new_in(&Path::from_str("."), "foobar").unwrap(); + let p = TempDir::new_in(&Path::new("."), "foobar").unwrap(); let p = p.path(); assert!(ends_with(p.as_vec(), bytes!("foobar"))); p.clone() @@ -84,7 +84,7 @@ fn test_rm_tempdir() { // Ideally these would be in std::os but then core would need // to depend on std fn recursive_mkdir_rel() { - let path = Path::from_str("frob"); + let path = Path::new("frob"); let cwd = os::getcwd(); debug2!("recursive_mkdir_rel: Making: {} in cwd {} [{:?}]", path.display(), cwd.display(), os::path_exists(&path)); @@ -95,21 +95,21 @@ fn recursive_mkdir_rel() { } fn recursive_mkdir_dot() { - let dot = Path::from_str("."); + let dot = Path::new("."); assert!(os::mkdir_recursive(&dot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); - let dotdot = Path::from_str(".."); + let dotdot = Path::new(".."); assert!(os::mkdir_recursive(&dotdot, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); } fn recursive_mkdir_rel_2() { - let path = Path::from_str("./frob/baz"); + let path = Path::new("./frob/baz"); let cwd = os::getcwd(); debug2!("recursive_mkdir_rel_2: Making: {} in cwd {} [{:?}]", path.display(), cwd.display(), os::path_exists(&path)); assert!(os::mkdir_recursive(&path, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); assert!(os::path_is_dir(&path)); assert!(os::path_is_dir(&path.dir_path())); - let path2 = Path::from_str("quux/blat"); + let path2 = Path::new("quux/blat"); debug2!("recursive_mkdir_rel_2: Making: {} in cwd {}", path2.display(), cwd.display()); assert!(os::mkdir_recursive(&path2, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)); @@ -124,17 +124,17 @@ pub fn test_rmdir_recursive_ok() { let tmpdir = TempDir::new("test").expect("test_rmdir_recursive_ok: \ couldn't create temp dir"); let tmpdir = tmpdir.path(); - let root = tmpdir.join_str("foo"); + let root = tmpdir.join("foo"); debug2!("making {}", root.display()); assert!(os::make_dir(&root, rwx)); - assert!(os::make_dir(&root.join_str("foo"), rwx)); - assert!(os::make_dir(&root.join_str("foo").join_str("bar"), rwx)); - assert!(os::make_dir(&root.join_str("foo").join_str("bar").join_str("blat"), rwx)); + assert!(os::make_dir(&root.join("foo"), rwx)); + assert!(os::make_dir(&root.join("foo").join("bar"), rwx)); + assert!(os::make_dir(&root.join("foo").join("bar").join("blat"), rwx)); assert!(os::remove_dir_recursive(&root)); assert!(!os::path_exists(&root)); - assert!(!os::path_exists(&root.join_str("bar"))); - assert!(!os::path_exists(&root.join_str("bar").join_str("blat"))); + assert!(!os::path_exists(&root.join("bar"))); + assert!(!os::path_exists(&root.join("bar").join("blat"))); } fn in_tmpdir(f: &fn()) { From c01a97b7a981fb5ae008be7e06df4bf6a85eba4f Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 6 Oct 2013 18:51:49 -0700 Subject: [PATCH 25/30] path2: Remove .with_display_str and friends Rewrite these methods as methods on Display and FilenameDisplay. This turns do path.with_display_str |s| { ... } into do path.display().with_str |s| { ... } --- src/compiletest/runtest.rs | 2 +- src/librustdoc/html/render.rs | 2 +- src/librustpkg/installed_packages.rs | 2 +- src/librustpkg/package_id.rs | 2 +- src/librustpkg/package_source.rs | 2 +- src/librustpkg/rustpkg.rs | 4 +- src/librustpkg/workspace.rs | 2 +- src/libstd/io.rs | 5 +- src/libstd/path/mod.rs | 119 ++++++++++++++------------- src/libstd/path/posix.rs | 91 ++++++++++---------- src/libstd/path/windows.rs | 24 ++---- src/libsyntax/parse/parser.rs | 4 +- 12 files changed, 132 insertions(+), 127 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index b5b81f7b8102c..4956be3c95917 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -334,7 +334,7 @@ fn check_error_patterns(props: &TestProps, testfile: &Path, ProcRes: &ProcRes) { if props.error_patterns.is_empty() { - do testfile.with_display_str |s| { + do testfile.display().with_str |s| { fatal(~"no error pattern specified in " + s); } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 7ed424136be22..260f106906be1 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -454,7 +454,7 @@ impl<'self> SourceCollector<'self> { let w = cur.open_writer(io::CreateOrTruncate); let mut w = BufferedWriter::new(w); - let title = cur.with_filename_display_str(|s| format!("{} -- source", s.unwrap())); + let title = cur.filename_display().with_str(|s| format!("{} -- source", s)); let page = layout::Page { title: title, ty: "source", diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs index 485cbfef2f602..ecc0b7f07e240 100644 --- a/src/librustpkg/installed_packages.rs +++ b/src/librustpkg/installed_packages.rs @@ -40,7 +40,7 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { let rel_p = lib.path_relative_from(&parent).unwrap(); debug2!("Rel: {}", rel_p.display()); let rel_path = rel_p.join(basename); - do rel_path.with_display_str |s| { + do rel_path.display().with_str |s| { debug2!("Rel name: {}", s); f(&PkgId::new(s)); } diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index 38b922ec05d64..0fc614d7f3ce8 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -88,7 +88,7 @@ impl PkgId { pub fn hash(&self) -> ~str { // FIXME (#9639): hash should take a &[u8] so we can hash the real path - do self.path.with_display_str |s| { + do self.path.display().with_str |s| { let vers = self.version.to_str(); format!("{}-{}-{}", s, hash(s + vers), vers) } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 4e0db8cac4f69..5e242c5376a02 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -114,7 +114,7 @@ impl PkgSrc { } - debug2!("Checking dirs: {:?}", to_try.map(|p| p.to_display_str()).connect(":")); + debug2!("Checking dirs: {:?}", to_try.map(|p| p.display().to_str()).connect(":")); let path = to_try.iter().find(|&d| os::path_exists(d)); diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 5ef8948c37772..e6b93e0607330 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -337,7 +337,7 @@ impl CtxMethods for BuildContext { "list" => { io::println("Installed packages:"); do installed_packages::list_installed_packages |pkg_id| { - do pkg_id.path.with_display_str |s| { + do pkg_id.path.display().with_str |s| { println(s); } true @@ -564,7 +564,7 @@ impl CtxMethods for BuildContext { &pkg_src.destination_workspace, &id).map(|s| Path::new(*s)); debug2!("install: id = {}, about to call discover_outputs, {:?}", - id.to_str(), result.map(|p| p.to_display_str())); + id.to_str(), result.map(|p| p.display().to_str())); installed_files = installed_files + result; note(format!("Installed package {} to {}", id.to_str(), diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index b4c2a66c1aafd..a24767f1e6857 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -27,7 +27,7 @@ pub fn each_pkg_parent_workspace(cx: &Context, pkgid: &PkgId, action: &fn(&Path) fail2!("Package {} not found in any of \ the following workspaces: {}", pkgid.path.display(), - rust_path().map(|p| p.to_display_str()).to_str()); + rust_path().map(|p| p.display().to_str()).to_str()); } for ws in workspaces.iter() { if action(ws) { diff --git a/src/libstd/io.rs b/src/libstd/io.rs index c03fc6d7ffb61..b92806d715f00 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -65,6 +65,7 @@ use ptr; use result::{Result, Ok, Err}; use str::{StrSlice, OwnedStr}; use str; +use to_str::ToStr; use uint; use vec::{MutableVector, ImmutableVector, OwnedVector, OwnedCopyableVector, CopyableVector}; use vec; @@ -1068,7 +1069,7 @@ pub fn file_reader(path: &Path) -> Result<@Reader, ~str> { }; if f as uint == 0u { - do path.with_display_str |p| { + do path.display().with_str |p| { Err(~"error opening " + p) } } else { @@ -1753,7 +1754,7 @@ pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> { if str::is_utf8(bytes) { Ok(str::from_utf8(bytes)) } else { - Err(file.to_display_str() + " is not UTF-8") + Err(file.display().to_str() + " is not UTF-8") } } } diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 561155f22584f..ff370e9d7c1dc 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -75,6 +75,7 @@ use iter::Iterator; use option::{Option, None, Some}; use str; use str::{OwnedStr, Str, StrSlice}; +use to_str::ToStr; use vec; use vec::{CopyableVector, OwnedCopyableVector, OwnedVector, Vector}; use vec::{ImmutableEqVector, ImmutableVector}; @@ -190,59 +191,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// Converts the Path into an owned byte vector fn into_vec(self) -> ~[u8]; - /// Provides the path as a string - /// - /// If the path is not UTF-8, invalid sequences will be replaced with the unicode - /// replacement char. This involves allocation. - #[inline] - fn with_display_str(&self, f: &fn(&str) -> T) -> T { - match self.as_str() { - Some(s) => f(s), - None => { - let s = self.to_display_str(); - f(s.as_slice()) - } - } - } - - /// Returns the path as a string - /// - /// If the path is not UTF-8, invalid sequences will be replaced with the unicode - /// replacement char. This involves allocation. - /// - /// This is similar to `with_display_str()` except it will always allocate a new ~str. - fn to_display_str(&self) -> ~str { - from_utf8_with_replacement(self.as_vec()) - } - - /// Provides the filename as a string - /// - /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode - /// replacement char. This involves allocation. - #[inline] - fn with_filename_display_str(&self, f: &fn(Option<&str>) -> T) -> T { - match self.filename_str() { - s@Some(_) => f(s), - None => { - let o = self.to_filename_display_str(); - f(o.map(|s|s.as_slice())) - } - } - } - - /// Returns the filename as a string - /// - /// If the filename is not UTF-8, invalid sequences will be replaced with the unicode - /// replacement char. This involves allocation. - /// - /// This is similar to `to_filename_display_str` except it will always allocate a new ~str. - fn to_filename_display_str(&self) -> Option<~str> { - match self.filename() { - None => None, - Some(v) => Some(from_utf8_with_replacement(v)) - } - } - /// Returns an object that implements `fmt::Default` for printing paths /// /// This will print the equivalent of `to_display_str()` when used with a {} format parameter. @@ -764,16 +712,75 @@ pub struct FilenameDisplay<'self, P> { impl<'self, P: GenericPath> fmt::Default for Display<'self, P> { fn fmt(d: &Display

, f: &mut fmt::Formatter) { - do d.path.with_display_str |s| { + do d.with_str |s| { f.pad(s) } } } +impl<'self, P: GenericPath> ToStr for Display<'self, P> { + /// Returns the path as a string + /// + /// If the path is not UTF-8, invalid sequences with be replaced with the + /// unicode replacement char. This involves allocation. + fn to_str(&self) -> ~str { + from_utf8_with_replacement(self.path.as_vec()) + } +} + +impl<'self, P: GenericPath> Display<'self, P> { + /// Provides the path as a string to a closure + /// + /// If the path is not UTF-8, invalid sequences will be replaced with the + /// unicode replacement char. This involves allocation. + #[inline] + pub fn with_str(&self, f: &fn(&str) -> T) -> T { + match self.path.as_str() { + Some(s) => f(s), + None => { + let s = self.to_str(); + f(s.as_slice()) + } + } + } +} + impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> { fn fmt(d: &FilenameDisplay

, f: &mut fmt::Formatter) { - do d.path.with_filename_display_str |s| { - f.pad(s.unwrap_or("")) + do d.with_str |s| { + f.pad(s) + } + } +} + +impl<'self, P: GenericPath> ToStr for FilenameDisplay<'self, P> { + /// Returns the filename as a string. If there is no filename, ~"" will be + /// returned. + /// + /// If the filename is not UTF-8, invalid sequences will be replaced with + /// the unicode replacement char. This involves allocation. + fn to_str(&self) -> ~str { + match self.path.filename() { + None => ~"", + Some(v) => from_utf8_with_replacement(v) + } + } +} + +impl<'self, P: GenericPath> FilenameDisplay<'self, P> { + /// Provides the filename as a string to a closure. If there is no + /// filename, "" will be provided. + /// + /// If the filename is not UTF-8, invalid sequences will be replaced with + /// the unicode replacement char. This involves allocation. + #[inline] + pub fn with_str(&self, f: &fn(&str) -> T) -> T { + match self.path.filename_str() { + Some(s) => f(s), + None => { + let s = self.to_str(); + f(s.as_slice()) + } } } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 3400b673c3012..8acc1346bcc2f 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -745,50 +745,53 @@ mod tests { #[test] fn test_display_str() { - assert_eq!(Path::new("foo").to_display_str(), ~"foo"); - assert_eq!(Path::new(b!("foo", 0x80)).to_display_str(), ~"foo\uFFFD"); - assert_eq!(Path::new(b!("foo", 0xff, "bar")).to_display_str(), ~"foo\uFFFDbar"); - assert_eq!(Path::new(b!("foo", 0xff, "/bar")).to_filename_display_str(), Some(~"bar")); - assert_eq!(Path::new(b!("foo/", 0xff, "bar")).to_filename_display_str(), - Some(~"\uFFFDbar")); - assert_eq!(Path::new(b!("/")).to_filename_display_str(), None); - - let mut called = false; - do Path::new("foo").with_display_str |s| { - assert_eq!(s, "foo"); - called = true; - }; - assert!(called); - called = false; - do Path::new(b!("foo", 0x80)).with_display_str |s| { - assert_eq!(s, "foo\uFFFD"); - called = true; - }; - assert!(called); - called = false; - do Path::new(b!("foo", 0xff, "bar")).with_display_str |s| { - assert_eq!(s, "foo\uFFFDbar"); - called = true; - }; - assert!(called); - called = false; - do Path::new(b!("foo", 0xff, "/bar")).with_filename_display_str |s| { - assert_eq!(s, Some("bar")); - called = true; - } - assert!(called); - called = false; - do Path::new(b!("foo/", 0xff, "bar")).with_filename_display_str |s| { - assert_eq!(s, Some("\uFFFDbar")); - called = true; - } - assert!(called); - called = false; - do Path::new(b!("/")).with_filename_display_str |s| { - assert!(s.is_none()); - called = true; - } - assert!(called); + macro_rules! t( + ($path:expr, $disp:ident, $exp:expr) => ( + { + let path = Path::new($path); + assert_eq!(path.$disp().to_str(), ~$exp); + } + ) + ) + t!("foo", display, "foo"); + t!(b!("foo", 0x80), display, "foo\uFFFD"); + t!(b!("foo", 0xff, "bar"), display, "foo\uFFFDbar"); + t!(b!("foo", 0xff, "/bar"), filename_display, "bar"); + t!(b!("foo/", 0xff, "bar"), filename_display, "\uFFFDbar"); + t!(b!("/"), filename_display, ""); + + macro_rules! t( + ($path:expr, $exp:expr) => ( + { + let mut called = false; + let path = Path::new($path); + do path.display().with_str |s| { + assert_eq!(s, $exp); + called = true; + }; + assert!(called); + } + ); + ($path:expr, $exp:expr, filename) => ( + { + let mut called = false; + let path = Path::new($path); + do path.filename_display().with_str |s| { + assert_eq!(s, $exp); + called = true; + + }; + assert!(called); + } + ) + ) + + t!("foo", "foo"); + t!(b!("foo", 0x80), "foo\uFFFD"); + t!(b!("foo", 0xff, "bar"), "foo\uFFFDbar"); + t!(b!("foo", 0xff, "/bar"), "bar", filename); + t!(b!("foo/", 0xff, "bar"), "\uFFFDbar", filename); + t!(b!("/"), "", filename); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index cc04261ec6695..86c4bb6f8a370 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -350,16 +350,6 @@ impl GenericPath for Path { self.repr.into_bytes() } - #[inline] - fn with_display_str(&self, f: &fn(&str) -> T) -> T { - f(self.repr.as_slice()) - } - - #[inline] - fn to_display_str(&self) -> ~str { - self.repr.clone() - } - #[inline] fn dirname<'a>(&'a self) -> &'a [u8] { self.dirname_str().unwrap().as_bytes() @@ -1462,18 +1452,22 @@ mod tests { #[test] fn test_display_str() { - assert_eq!(Path::new("foo").to_display_str(), ~"foo"); - assert_eq!(Path::new(b!("\\")).to_filename_display_str(), None); + let path = Path::new("foo"); + assert_eq!(path.display().to_str(), ~"foo"); + let path = Path::new(b!("\\")); + assert_eq!(path.filename_display().to_str(), ~""); let mut called = false; - do Path::new("foo").with_display_str |s| { + let path = Path::new("foo"); + do path.display().with_str |s| { assert_eq!(s, "foo"); called = true; }; assert!(called); called = false; - do Path::new(b!("\\")).with_filename_display_str |s| { - assert!(s.is_none()); + let path = Path::new(b!("\\")); + do path.filename_display().with_str |s| { + assert_eq!(s, ""); called = true; } assert!(called); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 32cd45c86cb2a..3e1a008f6ca64 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4038,12 +4038,12 @@ impl Parser { let stack = &self.sess.included_mod_stack; let mut err = ~"circular modules: "; for p in stack.slice(i, stack.len()).iter() { - do p.with_display_str |s| { + do p.display().with_str |s| { err.push_str(s); } err.push_str(" -> "); } - do path.with_display_str |s| { + do path.display().with_str |s| { err.push_str(s); } self.span_fatal(id_sp, err); From bab7eb20dff32294c65fa28cece552481c40cf0b Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Mon, 7 Oct 2013 19:16:58 -0700 Subject: [PATCH 26/30] path2: Update based on more review feedback Standardize the is_sep() functions to be the same in both posix and windows, and re-export from path. Update extra::glob to use this. Remove the usage of either, as it's going away. Move the WindowsPath-specific methods out of WindowsPath and make them top-level functions of path::windows instead. This way you cannot accidentally write code that will fail to compile on non-windows architectures without typing ::windows anywhere. Remove GenericPath::from_c_str() and just impl BytesContainer for CString instead. Remove .join_path() and .push_path() and just implement BytesContainer for Path instead. Remove FilenameDisplay and add a boolean flag to Display instead. Remove .each_parent(). It only had one caller, so just inline its definition there. --- src/compiletest/runtest.rs | 6 +- src/libextra/glob.rs | 21 +-- src/librustc/back/rpath.rs | 4 +- src/librustc/metadata/filesearch.rs | 35 ++-- src/librustpkg/package_source.rs | 14 +- src/librustpkg/path_util.rs | 14 +- src/librustpkg/rustpkg.rs | 12 +- src/librustpkg/tests.rs | 4 +- src/librustpkg/util.rs | 2 +- src/librustpkg/version.rs | 2 +- src/libstd/os.rs | 10 +- src/libstd/path/mod.rs | 156 ++++++------------ src/libstd/path/posix.rs | 131 ++++++--------- src/libstd/path/windows.rs | 247 +++++++++++++--------------- src/libsyntax/ext/source_util.rs | 2 +- src/libsyntax/parse/parser.rs | 2 +- src/test/run-pass/glob-std.rs | 2 +- 17 files changed, 274 insertions(+), 390 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 4956be3c95917..b1d8d83f9d3e5 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -189,7 +189,7 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) { let mut expected = match props.pp_exact { Some(ref file) => { - let filepath = testfile.dir_path().join_path(file); + let filepath = testfile.dir_path().join(file); io::read_whole_file_str(&filepath).unwrap() } None => { srcs[srcs.len() - 2u].clone() } @@ -657,7 +657,7 @@ fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { // what we return here is not particularly important, as it // happens; rustc ignores everything except for the directory. let auxname = output_testname(auxfile); - aux_output_dir_name(config, testfile).join_path(&auxname) + aux_output_dir_name(config, testfile).join(&auxname) } fn make_exe_name(config: &config, testfile: &Path) -> Path { @@ -757,7 +757,7 @@ fn output_testname(testfile: &Path) -> Path { fn output_base_name(config: &config, testfile: &Path) -> Path { config.build_base - .join_path(&output_testname(testfile)) + .join(&output_testname(testfile)) .with_extension(config.stage_id.as_slice()) } diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index 1a6c8e08e3b84..83456777c40c1 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -24,6 +24,7 @@ */ use std::{os, path}; +use std::path::is_sep; use sort; @@ -81,11 +82,7 @@ pub fn glob(pattern: &str) -> GlobIterator { */ pub fn glob_with(pattern: &str, options: MatchOptions) -> GlobIterator { #[cfg(windows)] - use is_sep = std::path::windows::is_sep2; - #[cfg(not(windows))] - fn is_sep(c: char) -> bool { c <= '\x7F' && ::std::path::posix::is_sep(&(c as u8)) } - #[cfg(windows)] - fn check_windows_verbatim(p: &Path) -> bool { p.is_verbatim() } + fn check_windows_verbatim(p: &Path) -> bool { path::windows::is_verbatim(p) } #[cfg(not(windows))] fn check_windows_verbatim(_: &Path) -> bool { false } @@ -98,7 +95,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> GlobIterator { // since we can't very well find all UNC shares with a 1-letter server name. return GlobIterator { root: root, dir_patterns: ~[], options: options, todo: ~[] }; } - root.push_path(pat_root.get_ref()); + root.push(pat_root.get_ref()); } let root_len = pat_root.map_move_default(0u, |p| p.as_vec().len()); @@ -462,7 +459,7 @@ fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptio /// A helper function to determine if two chars are (possibly case-insensitively) equal. fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { - if cfg!(windows) && path::windows::is_sep2(a) && path::windows::is_sep2(b) { + if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) { true } else if !case_sensitive && a.is_ascii() && b.is_ascii() { // FIXME: work with non-ascii chars properly (issue #1347) @@ -472,16 +469,6 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { } } -/// A helper function to determine if a char is a path separator on the current platform. -fn is_sep(c: char) -> bool { - if cfg!(windows) { - path::windows::is_sep2(c) - } else { - c <= '\x7F' && path::posix::is_sep(&(c as u8)) - } -} - - /** * Configuration options to modify the behaviour of `Pattern::matches_with(..)` */ diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index b7ae9c1ecb58e..0626ec0ece0a0 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -45,7 +45,7 @@ pub fn get_rpath_flags(sess: session::Session, out_filename: &Path) fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path { let r = filesearch::relative_target_lib_path(sess.opts.target_triple); - let mut p = sess.filesearch.sysroot().join_path(&r); + let mut p = sess.filesearch.sysroot().join(&r); p.push(os::dll_filename("rustrt")); p } @@ -148,7 +148,7 @@ pub fn get_install_prefix_rpath(target_triple: &str) -> ~str { let tlib = filesearch::relative_target_lib_path(target_triple); let mut path = Path::new(install_prefix); - path.push_path(&tlib); + path.push(&tlib); let path = os::make_absolute(&path); // FIXME (#9639): This needs to handle non-utf8 paths path.as_str().expect("non-utf8 component in rpath").to_owned() diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 6335df47d7388..2274f7d3c694f 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -100,7 +100,7 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, } fn get_target_lib_file_path(&self, file: &Path) -> Path { let mut p = self.get_target_lib_path(); - p.push_path(file); + p.push(file); p } } @@ -148,7 +148,7 @@ pub fn relative_target_lib_path(target_triple: &str) -> Path { fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> Path { - sysroot.join_path(&relative_target_lib_path(target_triple)) + sysroot.join(&relative_target_lib_path(target_triple)) } fn make_rustpkg_target_lib_path(dir: &Path, @@ -196,7 +196,7 @@ pub fn rust_path() -> ~[Path] { } None => ~[] }; - let cwd = os::getcwd(); + let mut cwd = os::getcwd(); // now add in default entries let cwd_dot_rust = cwd.join(".rust"); if !env_rust_path.contains(&cwd_dot_rust) { @@ -205,30 +205,27 @@ pub fn rust_path() -> ~[Path] { if !env_rust_path.contains(&cwd) { env_rust_path.push(cwd.clone()); } - do cwd.each_parent() |p| { - if !env_rust_path.contains(&p.join(".rust")) { - push_if_exists(&mut env_rust_path, p); + loop { + let f = cwd.pop(); + if f.is_none() || bytes!("..") == f.unwrap() { + break; } - true - }; + cwd.push(".rust"); + if !env_rust_path.contains(&cwd) && os::path_exists(&cwd) { + env_rust_path.push(cwd.clone()); + } + cwd.pop(); + } let h = os::homedir(); for h in h.iter() { - if !env_rust_path.contains(&h.join(".rust")) { - push_if_exists(&mut env_rust_path, h); + let p = h.join(".rust"); + if !env_rust_path.contains(&p) && os::path_exists(&p) { + env_rust_path.push(p); } } env_rust_path } - -/// Adds p/.rust into vec, only if it exists -fn push_if_exists(vec: &mut ~[Path], p: &Path) { - let maybe_dir = p.join(".rust"); - if os::path_exists(&maybe_dir) { - vec.push(maybe_dir); - } -} - // The name of the directory rustc expects libraries to be located. // On Unix should be "lib", on windows "bin" pub fn libdir() -> ~str { diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 5e242c5376a02..874a30394d1ba 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -95,20 +95,20 @@ impl PkgSrc { // We search for sources under both src/ and build/ , because build/ is where // automatically-checked-out sources go. let mut result = source_workspace.join("src"); - result.push_path(&id.path.dir_path()); + result.push(&id.path.dir_path()); result.push(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result); let mut result = source_workspace.join("src"); - result.push_path(&id.path); + result.push(&id.path); to_try.push(result); let mut result = build_dir.join("src"); - result.push_path(&id.path.dir_path()); + result.push(&id.path.dir_path()); result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result.clone()); output_names.push(result); let mut other_result = build_dir.join("src"); - other_result.push_path(&id.path); + other_result.push(&id.path); to_try.push(other_result.clone()); output_names.push(other_result); @@ -146,7 +146,7 @@ impl PkgSrc { source_workspace: source.clone(), build_in_destination: build_in_destination, destination_workspace: destination, - start_dir: start.join_path(&suffix), + start_dir: start.join(&suffix), id: id, libs: ~[], mains: ~[], @@ -371,7 +371,7 @@ impl PkgSrc { cfgs: &[~str], what: OutputType) { for crate in crates.iter() { - let path = self.start_dir.join_path(&crate.file); + let path = self.start_dir.join(&crate.file); debug2!("build_crates: compiling {}", path.display()); let cfgs = crate.cfgs + cfgs; @@ -416,7 +416,7 @@ impl PkgSrc { debug2!("In declare inputs, self = {}", self.to_str()); for cs in to_do.iter() { for c in cs.iter() { - let path = self.start_dir.join_path(&c.file); + let path = self.start_dir.join(&c.file); debug2!("Declaring input: {}", path.display()); // FIXME (#9639): This needs to handle non-utf8 paths prep.declare_input("file", path.as_str().unwrap(), diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 0f5f1470b6fe6..0fe3abdefa3ad 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -68,7 +68,7 @@ pub fn workspace_contains_package_id_(pkgid: &PkgId, workspace: &Path, let mut found = None; do os::walk_dir(&src_dir) |p| { if os::path_is_dir(p) { - if *p == src_dir.join_path(&pkgid.path) || { + if *p == src_dir.join(&pkgid.path) || { let pf = p.filename_str(); do pf.iter().any |&g| { match split_version_general(g, '-') { @@ -196,7 +196,7 @@ pub fn library_in_workspace(path: &Path, short_name: &str, where: Target, prefix = {}", short_name, where, workspace.display(), prefix); let dir_to_search = match where { - Build => target_build_dir(workspace).join_path(path), + Build => target_build_dir(workspace).join(path), Install => target_lib_dir(workspace) }; @@ -273,7 +273,7 @@ fn library_in(short_name: &str, version: &Version, dir_to_search: &Path) -> Opti // Return the filename that matches, which we now know exists // (if result_filename != None) let abs_path = do result_filename.map |result_filename| { - let absolute_path = dir_to_search.join_path(&result_filename); + let absolute_path = dir_to_search.join(&result_filename); debug2!("result_filename = {}", absolute_path.display()); absolute_path }; @@ -329,7 +329,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, // Artifacts in the build directory live in a package-ID-specific subdirectory, // but installed ones don't. let result = match (where, what) { - (Build, _) => target_build_dir(workspace).join_path(&pkgid.path), + (Build, _) => target_build_dir(workspace).join(&pkgid.path), (Install, Lib) => target_lib_dir(workspace), (Install, _) => target_bin_dir(workspace) }; @@ -347,7 +347,7 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { use conditions::bad_path::cond; let mut result = target_build_dir(workspace); - result.push_path(&pkgid.path); + result.push(&pkgid.path); debug2!("Creating build dir {} for package id {}", result.display(), pkgid.to_str()); if os::path_exists(&result) || os::mkdir_recursive(&result, U_RWX) { @@ -370,7 +370,7 @@ pub fn mk_output_path(what: OutputType, where: Target, // If we're installing, it just goes under ... Install => workspace, // and if we're just building, it goes in a package-specific subdir - Build => workspace.join_path(&pkg_id.path) + Build => workspace.join(&pkg_id.path) }; debug2!("[{:?}:{:?}] mk_output_path: short_name = {}, path = {}", what, where, if what == Lib { short_name_with_version.clone() } else { pkg_id.short_name.clone() }, @@ -388,7 +388,7 @@ pub fn mk_output_path(what: OutputType, where: Target, os::EXE_SUFFIX)) }; if !output_path.is_absolute() { - output_path = os::getcwd().join_path(&output_path); + output_path = os::getcwd().join(&output_path); } debug2!("mk_output_path: returning {}", output_path.display()); output_path diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index e6b93e0607330..7b10bf2a551b9 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -416,15 +416,15 @@ impl CtxMethods for BuildContext { debug2!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \ pkgid = {} pkgsrc start_dir = {}", workspace.display(), - in_rust_path(&workspace), is_git_dir(&workspace.join_path(&pkgid.path)), + in_rust_path(&workspace), is_git_dir(&workspace.join(&pkgid.path)), pkgid.to_str(), pkg_src.start_dir.display()); // If workspace isn't in the RUST_PATH, and it's a git repo, // then clone it into the first entry in RUST_PATH, and repeat - if !in_rust_path(&workspace) && is_git_dir(&workspace.join_path(&pkgid.path)) { + if !in_rust_path(&workspace) && is_git_dir(&workspace.join(&pkgid.path)) { let mut out_dir = default_workspace().join("src"); - out_dir.push_path(&pkgid.path); - let git_result = source_control::safe_git_clone(&workspace.join_path(&pkgid.path), + out_dir.push(&pkgid.path); + let git_result = source_control::safe_git_clone(&workspace.join(&pkgid.path), &pkgid.version, &out_dir); match git_result { @@ -494,7 +494,7 @@ impl CtxMethods for BuildContext { // We expect that p is relative to the package source's start directory, // so check that assumption debug2!("JustOne: p = {}", p.display()); - assert!(os::path_exists(&pkg_src.start_dir.join_path(p))); + assert!(os::path_exists(&pkg_src.start_dir.join(p))); if is_lib(p) { PkgSrc::push_crate(&mut pkg_src.libs, 0, p); } else if is_main(p) { @@ -553,7 +553,7 @@ impl CtxMethods for BuildContext { debug2!("In declare inputs for {}", id.to_str()); for cs in to_do.iter() { for c in cs.iter() { - let path = pkg_src.start_dir.join_path(&c.file); + let path = pkg_src.start_dir.join(&c.file); debug2!("Recording input: {}", path.display()); // FIXME (#9639): This needs to handle non-utf8 paths inputs.push((~"file", path.as_str().unwrap().to_owned())); diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index cb40c3c00906a..4e8bcbb1cec8a 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -156,7 +156,7 @@ fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &st fn init_git_repo(p: &Path) -> TempDir { assert!(p.is_relative()); let tmp = TempDir::new("git_local").expect("couldn't create temp dir"); - let work_dir = tmp.path().join_path(p); + let work_dir = tmp.path().join(p); let work_dir_for_opts = work_dir.clone(); assert!(os::mkdir_recursive(&work_dir, U_RWX)); debug2!("Running: git init in {}", work_dir.display()); @@ -793,7 +793,7 @@ fn test_package_request_version() { == repo.join_many([".rust", "bin", "test_pkg_version"])); let mut dir = target_build_dir(&repo.join(".rust")); - dir.push_path(&Path::new("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); + dir.push(&Path::new("src/mockgithub.com/catamorphism/test_pkg_version-0.3")); debug2!("dir = {}", dir.display()); assert!(os::path_is_dir(&dir)); assert!(os::path_exists(&dir.join("version-0.3-file.txt"))); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index c5e81e6dc4a2c..23d9af77c0157 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -177,7 +177,7 @@ pub fn compile_input(context: &BuildContext, // not sure if we should support anything else let mut out_dir = target_build_dir(workspace); - out_dir.push_path(&pkg_id.path); + out_dir.push(&pkg_id.path); // Make the output directory if it doesn't exist already assert!(os::mkdir_recursive(&out_dir, U_RWX)); diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs index 21abeeb03e3a2..218f410e4dcfc 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -98,7 +98,7 @@ pub fn parse_vers(vers: ~str) -> result::Result { pub fn try_getting_local_version(local_path: &Path) -> Option { let rustpath = rust_path(); for rp in rustpath.iter() { - let local_path = rp.join_path(local_path); + let local_path = rp.join(local_path); let git_dir = local_path.join(".git"); if !os::path_is_dir(&git_dir) { continue; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 449888b4e2a45..be50f6d1a9fbd 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -78,7 +78,7 @@ pub fn getcwd() -> Path { fail2!() } - GenericPath::from_c_str(CString::new(buf as *c_char, false)) + Path::new(CString::new(buf as *c_char, false)) } } } @@ -608,7 +608,7 @@ pub fn tmpdir() -> Path { pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { let r = list_dir(p); r.iter().advance(|q| { - let path = &p.join_path(q); + let path = &p.join(q); f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p))) }) } @@ -648,7 +648,7 @@ pub fn make_absolute(p: &Path) -> Path { p.clone() } else { let mut ret = getcwd(); - ret.push_path(p); + ret.push(p); ret } } @@ -730,7 +730,7 @@ pub fn list_dir(p: &Path) -> ~[Path] { let mut entry_ptr = readdir(dir_ptr); while (entry_ptr as uint != 0) { let cstr = CString::new(rust_list_dir_val(entry_ptr), false); - paths.push(GenericPath::from_c_str(cstr)); + paths.push(Path::new(cstr)); entry_ptr = readdir(dir_ptr); } closedir(dir_ptr); @@ -800,7 +800,7 @@ pub fn list_dir(p: &Path) -> ~[Path] { * This version prepends each entry with the directory. */ pub fn list_dir_path(p: &Path) -> ~[Path] { - list_dir(p).map(|f| p.join_path(f)) + list_dir(p).map(|f| p.join(f)) } /// Removes a directory at the specified path, after removing diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index ff370e9d7c1dc..278a4ab36ff43 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -30,9 +30,7 @@ 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, and methods -used should be restricted to those defined in `GenericPath`, and those methods -that are declared identically on both `PosixPath` and `WindowsPath`. +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 @@ -69,7 +67,6 @@ debug2!("path exists: {}", b); use container::Container; use c_str::CString; use clone::Clone; -use either::{Left, Right}; use fmt; use iter::Iterator; use option::{Option, None, Some}; @@ -121,6 +118,19 @@ pub use StrComponentIter = self::windows::StrComponentIter; #[cfg(windows)] pub use RevStrComponentIter = self::windows::RevStrComponentIter; +/// Typedef for the platform-native separator char func +#[cfg(unix)] +pub use is_sep = self::posix::is_sep; +/// Typedef for the platform-native separator char func +#[cfg(windows)] +pub use is_sep = self::windows::is_sep; +/// Typedef for the platform-native separator byte func +#[cfg(unix)] +pub use is_sep_byte = self::posix::is_sep_byte; +/// Typedef for the platform-native separator byte func +#[cfg(windows)] +pub use is_sep_byte = self::windows::is_sep_byte; + pub mod posix; pub mod windows; @@ -162,19 +172,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } } - /// Creates a new Path from a CString. - /// The resulting Path will always be normalized. - /// - /// See individual Path impls for potential restrictions. - #[inline] - fn from_c_str(path: CString) -> Self { - // CStrings can't contain NULs - let v = path.as_bytes(); - // v is NUL-terminated. Strip it off - let v = v.slice_to(v.len()-1); - unsafe { GenericPathUnsafe::new_unchecked(v) } - } - /// Returns the path as a string, if possible. /// If the path is not representable in utf-8, this returns None. #[inline] @@ -195,15 +192,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// /// This will print the equivalent of `to_display_str()` when used with a {} format parameter. fn display<'a>(&'a self) -> Display<'a, Self> { - Display{ path: self } + Display{ path: self, filename: false } } /// Returns an object that implements `fmt::Default` for printing filenames /// /// This will print the equivalent of `to_filename_display_str()` when used with a {} /// format parameter. If there is no filename, nothing will be printed. - fn filename_display<'a>(&'a self) -> FilenameDisplay<'a, Self> { - FilenameDisplay{ path: self } + 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). @@ -314,13 +311,17 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// Raises the `null_byte` condition if the filestem contains a NUL. fn set_filestem(&mut self, filestem: T) { // borrowck is being a pain here + enum Value { + Checked(T), + Unchecked(~[u8]) + } let val = { match self.filename() { - None => Left(filestem), + None => Checked(filestem), Some(name) => { let dot = '.' as u8; match name.rposition_elem(&dot) { - None | Some(0) => Left(filestem), + None | Some(0) => Checked(filestem), Some(idx) => { let mut v; if contains_nul(filestem.container_as_bytes()) { @@ -336,15 +337,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe { v.push_all(filestem); } v.push_all(name.slice_from(idx)); - Right(v) + Unchecked(v) } } } } }; match val { - Left(v) => self.set_filename(v), - Right(v) => unsafe { self.set_filename_unchecked(v) } + Checked(v) => self.set_filename(v), + Unchecked(v) => unsafe { self.set_filename_unchecked(v) } } } /// Replaces the extension with the given byte vector or string. @@ -545,12 +546,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { unsafe { self.push_unchecked(path) } } } - /// Pushes a Path onto `self`. - /// If the argument represents an absolute path, it replaces `self`. - #[inline] - fn push_path(&mut self, path: &Self) { - self.push(path.as_vec()) - } /// Pushes multiple paths (as byte vectors or strings) onto `self`. /// See `push` for details. #[inline] @@ -590,14 +585,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { p.push(path); p } - /// Returns a new Path constructed by joining `self` with the given path. - /// If the given path is absolute, the new Path will represent just that. - #[inline] - fn join_path(&self, path: &Self) -> Self { - let mut p = self.clone(); - p.push_path(path); - p - } /// Returns a new Path constructed by joining `self` with the given paths /// (as byte vectors or strings). /// See `join` for details. @@ -632,21 +619,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// paths refer to separate drives, an absolute path is returned. fn path_relative_from(&self, base: &Self) -> Option; - /// Executes a callback with the receiver and every parent - fn each_parent(&self, f: &fn(&Self) -> bool) -> bool { - let mut p = self.clone(); - loop { - if !f(&p) { - return false; - } - let f = p.pop(); - if f.is_none() || bytes!("..") == f.unwrap() { - break; - } - } - true - } - /// Returns whether the relative path `child` is a suffix of `self`. fn ends_with_path(&self, child: &Self) -> bool; } @@ -674,7 +646,7 @@ pub trait BytesContainer { fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { str::from_utf8_slice_opt(self.container_as_bytes()) } - /// Returns whether the concrete receiver is a string type + /// Returns whether .container_as_str() is guaranteed to not fail // FIXME (#8888): Remove unused arg once :: works #[inline] fn is_str(_: Option) -> bool { false } @@ -703,11 +675,8 @@ pub trait GenericPathUnsafe { /// Helper struct for printing paths with format!() pub struct Display<'self, P> { - priv path: &'self P -} -/// Helper struct for printing filenames with format!() -pub struct FilenameDisplay<'self, P> { - priv path: &'self P + priv path: &'self P, + priv filename: bool } impl<'self, P: GenericPath> fmt::Default for Display<'self, P> { @@ -724,7 +693,14 @@ impl<'self, P: GenericPath> ToStr for Display<'self, P> { /// If the path is not UTF-8, invalid sequences with be replaced with the /// unicode replacement char. This involves allocation. fn to_str(&self) -> ~str { - from_utf8_with_replacement(self.path.as_vec()) + if self.filename { + match self.path.filename() { + None => ~"", + Some(v) => from_utf8_with_replacement(v) + } + } else { + from_utf8_with_replacement(self.path.as_vec()) + } } } @@ -735,47 +711,9 @@ impl<'self, P: GenericPath> Display<'self, P> { /// unicode replacement char. This involves allocation. #[inline] pub fn with_str(&self, f: &fn(&str) -> T) -> T { - match self.path.as_str() { - Some(s) => f(s), - None => { - let s = self.to_str(); - f(s.as_slice()) - } - } - } -} - -impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> { - fn fmt(d: &FilenameDisplay

, f: &mut fmt::Formatter) { - do d.with_str |s| { - f.pad(s) - } - } -} - -impl<'self, P: GenericPath> ToStr for FilenameDisplay<'self, P> { - /// Returns the filename as a string. If there is no filename, ~"" will be - /// returned. - /// - /// If the filename is not UTF-8, invalid sequences will be replaced with - /// the unicode replacement char. This involves allocation. - fn to_str(&self) -> ~str { - match self.path.filename() { - None => ~"", - Some(v) => from_utf8_with_replacement(v) - } - } -} - -impl<'self, P: GenericPath> FilenameDisplay<'self, P> { - /// Provides the filename as a string to a closure. If there is no - /// filename, "" will be provided. - /// - /// If the filename is not UTF-8, invalid sequences will be replaced with - /// the unicode replacement char. This involves allocation. - #[inline] - pub fn with_str(&self, f: &fn(&str) -> T) -> T { - match self.path.filename_str() { + let opt = if self.filename { self.path.filename_str() } + else { self.path.as_str() }; + match opt { Some(s) => f(s), None => { let s = self.to_str(); @@ -865,6 +803,14 @@ impl BytesContainer for @[u8] { } } +impl BytesContainer for CString { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + let s = self.as_bytes(); + s.slice_to(s.len()-1) + } +} + #[inline(always)] fn contains_nul(v: &[u8]) -> bool { v.iter().any(|&x| x == 0) @@ -1121,13 +1067,13 @@ mod tests { use c_str::ToCStr; #[test] - fn test_from_c_str() { + fn test_cstring() { let input = "/foo/bar/baz"; - let path: PosixPath = GenericPath::from_c_str(input.to_c_str()); + let path: PosixPath = PosixPath::new(input.to_c_str()); assert_eq!(path.as_vec(), input.as_bytes()); let input = "\\foo\\bar\\baz"; - let path: WindowsPath = GenericPath::from_c_str(input.to_c_str()); + let path: WindowsPath = WindowsPath::new(input.to_c_str()); assert_eq!(path.as_str().unwrap(), input.as_slice()); } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 8acc1346bcc2f..fdc943fb882f9 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -48,12 +48,19 @@ pub struct Path { } /// The standard path separator character -pub static sep: u8 = '/' as u8; +pub static sep: char = '/'; +static sep_byte: u8 = sep as u8; /// Returns whether the given byte is a path separator #[inline] -pub fn is_sep(u: &u8) -> bool { - *u == sep +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 } impl Eq for Path { @@ -89,11 +96,29 @@ impl IterBytes for Path { } } +impl BytesContainer for Path { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_vec() + } + #[inline] + fn container_into_owned_bytes(self) -> ~[u8] { + self.into_vec() + } +} + +impl<'self> BytesContainer for &'self Path { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_vec() + } +} + impl GenericPathUnsafe for Path { unsafe fn new_unchecked(path: T) -> Path { let path = Path::normalize(path.container_as_bytes()); assert!(!path.is_empty()); - let idx = path.rposition_elem(&sep); + let idx = path.rposition_elem(&sep_byte); Path{ repr: path, sepidx: idx } } @@ -106,11 +131,11 @@ impl GenericPathUnsafe for Path { None => { let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); v.push_all(dirname); - v.push(sep); + v.push(sep_byte); v.push_all(self.repr); self.repr = Path::normalize(v); } - Some(0) if self.repr.len() == 1 && self.repr[0] == sep => { + Some(0) if self.repr.len() == 1 && self.repr[0] == sep_byte => { self.repr = Path::normalize(dirname); } Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { @@ -127,7 +152,7 @@ impl GenericPathUnsafe for Path { self.repr = Path::normalize(v); } } - self.sepidx = self.repr.rposition_elem(&sep); + self.sepidx = self.repr.rposition_elem(&sep_byte); } unsafe fn set_filename_unchecked(&mut self, filename: T) { @@ -136,7 +161,7 @@ impl GenericPathUnsafe for Path { None if bytes!("..") == self.repr => { let mut v = vec::with_capacity(3 + filename.len()); v.push_all(dot_dot_static); - v.push(sep); + v.push(sep_byte); v.push_all(filename); self.repr = Path::normalize(v); } @@ -146,7 +171,7 @@ impl GenericPathUnsafe for Path { Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); v.push_all(self.repr); - v.push(sep); + v.push(sep_byte); v.push_all(filename); self.repr = Path::normalize(v); } @@ -157,22 +182,22 @@ impl GenericPathUnsafe for Path { self.repr = Path::normalize(v); } } - self.sepidx = self.repr.rposition_elem(&sep); + self.sepidx = self.repr.rposition_elem(&sep_byte); } unsafe fn push_unchecked(&mut self, path: T) { let path = path.container_as_bytes(); if !path.is_empty() { - if path[0] == sep { + 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); - v.push(sep); + v.push(sep_byte); v.push_all(path); self.repr = Path::normalize(v); } - self.sepidx = self.repr.rposition_elem(&sep); + self.sepidx = self.repr.rposition_elem(&sep_byte); } } } @@ -228,7 +253,7 @@ impl GenericPath for Path { } else { self.repr.truncate(idx); } - self.sepidx = self.repr.rposition_elem(&sep); + self.sepidx = self.repr.rposition_elem(&sep_byte); Some(v) } } @@ -244,7 +269,7 @@ impl GenericPath for Path { #[inline] fn is_absolute(&self) -> bool { - self.repr[0] == sep + self.repr[0] == sep_byte } fn is_ancestor_of(&self, other: &Path) -> bool { @@ -305,7 +330,7 @@ impl GenericPath for Path { } } } - Some(Path::new(comps.connect_vec(&sep))) + Some(Path::new(comps.connect_vec(&sep_byte))) } } @@ -347,14 +372,14 @@ impl Path { fn normalize+CopyableVector>(v: V) -> ~[u8] { // borrowck is being very picky let val = { - let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep; + let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep_byte; let v_ = if is_abs { v.as_slice().slice_from(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(~[sep]) + Some(~[sep_byte]) } else { let n = if is_abs { comps.len() } else { comps.len() - 1} + comps.iter().map(|v| v.len()).sum(); @@ -367,7 +392,7 @@ impl Path { } } for comp in it { - v.push(sep); + v.push(sep_byte); v.push_all(comp); } Some(v) @@ -386,10 +411,10 @@ impl Path { /// /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 component_iter<'a>(&'a self) -> ComponentIter<'a> { - let v = if self.repr[0] == sep { + let v = if self.repr[0] == sep_byte { self.repr.slice_from(1) } else { self.repr.as_slice() }; - let mut ret = v.split_iter(is_sep); + let mut ret = v.split_iter(is_sep_byte); if v.is_empty() { // consume the empty "" component ret.next(); @@ -400,10 +425,10 @@ impl Path { /// Returns an iterator that yields each component of the path in reverse. /// See component_iter() for details. pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> { - let v = if self.repr[0] == sep { + let v = if self.repr[0] == sep_byte { self.repr.slice_from(1) } else { self.repr.as_slice() }; - let mut ret = v.rsplit_iter(is_sep); + let mut ret = v.rsplit_iter(is_sep_byte); if v.is_empty() { // consume the empty "" component ret.next(); @@ -432,7 +457,7 @@ fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> { let mut comps: ~[&'a [u8]] = ~[]; let mut n_up = 0u; let mut changed = false; - for comp in v.split_iter(is_sep) { + for comp in v.split_iter(is_sep_byte) { if comp.is_empty() { changed = true } else if comp == bytes!(".") { changed = true } else if comp == bytes!("..") { @@ -928,7 +953,7 @@ mod tests { { let mut p = Path::new($path); let push = Path::new($push); - p.push_path(&push); + p.push(&push); assert_eq!(p.as_str(), Some($exp)); } ) @@ -1049,7 +1074,7 @@ mod tests { { let path = Path::new($path); let join = Path::new($join); - let res = path.join_path(&join); + let res = path.join(&join); assert_eq!(res.as_str(), Some($exp)); } ) @@ -1598,58 +1623,4 @@ mod tests { // str_component_iter is a wrapper around component_iter, so no need to do // the full set of tests } - - #[test] - fn test_each_parent() { - assert!(Path::new("/foo/bar").each_parent(|_| true)); - assert!(!Path::new("/foo/bar").each_parent(|_| false)); - - macro_rules! t( - (s: $path:expr, $exp:expr) => ( - { - let path = Path::new($path); - let exp: &[&str] = $exp; - let mut comps = exp.iter().map(|&x|x); - do path.each_parent |p| { - let p = p.as_str(); - assert!(p.is_some()); - let e = comps.next(); - assert!(e.is_some()); - assert_eq!(p.unwrap(), e.unwrap()); - true - }; - assert!(comps.next().is_none()); - } - ); - (v: $path:expr, $exp:expr) => ( - { - let path = Path::new($path); - let exp: &[&[u8]] = $exp; - let mut comps = exp.iter().map(|&x|x); - do path.each_parent |p| { - let p = p.as_vec(); - let e = comps.next(); - assert!(e.is_some()); - assert_eq!(p, e.unwrap()); - true - }; - assert!(comps.next().is_none()); - } - ) - ) - - t!(s: "/foo/bar", ["/foo/bar", "/foo", "/"]); - t!(s: "/foo/bar/baz", ["/foo/bar/baz", "/foo/bar", "/foo", "/"]); - t!(s: "/foo", ["/foo", "/"]); - t!(s: "/", ["/"]); - t!(s: "foo/bar/baz", ["foo/bar/baz", "foo/bar", "foo", "."]); - t!(s: "foo/bar", ["foo/bar", "foo", "."]); - t!(s: "foo", ["foo", "."]); - t!(s: ".", ["."]); - t!(s: "..", [".."]); - t!(s: "../../foo", ["../../foo", "../.."]); - - t!(v: b!("foo/bar", 0x80), [b!("foo/bar", 0x80), b!("foo"), b!(".")]); - t!(v: b!(0xff, "/bar"), [b!(0xff, "/bar"), b!(0xff), b!(".")]); - } } diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 86c4bb6f8a370..5f8e1dc58fa29 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -121,6 +121,44 @@ impl IterBytes for Path { } } +impl BytesContainer for Path { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_vec() + } + #[inline] + fn container_into_owned_bytes(self) -> ~[u8] { + self.into_vec() + } + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + self.as_str().unwrap() + } + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + self.as_str() + } + #[inline] + fn is_str(_: Option) -> bool { true } +} + +impl<'self> BytesContainer for &'self Path { + #[inline] + fn container_as_bytes<'a>(&'a self) -> &'a [u8] { + self.as_vec() + } + #[inline] + fn container_as_str<'a>(&'a self) -> &'a str { + self.as_str().unwrap() + } + #[inline] + fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> { + self.as_str() + } + #[inline] + fn is_str(_: Option<&'self Path>) -> bool { true } +} + impl GenericPathUnsafe for Path { /// See `GenericPathUnsafe::from_vec_unchecked`. /// @@ -235,7 +273,7 @@ impl GenericPathUnsafe for Path { fn is_vol_abs(path: &str, prefix: Option) -> bool { // assume prefix is Some(DiskPrefix) let rest = path.slice_from(prefix_len(prefix)); - !rest.is_empty() && rest[0].is_ascii() && is_sep2(rest[0] as char) + !rest.is_empty() && rest[0].is_ascii() && is_sep(rest[0] as char) } fn shares_volume(me: &Path, path: &str) -> bool { // path is assumed to have a prefix of Some(DiskPrefix) @@ -246,8 +284,8 @@ impl GenericPathUnsafe for Path { } } fn is_sep_(prefix: Option, u: u8) -> bool { - u.is_ascii() && if prefix_is_verbatim(prefix) { is_sep(u as char) } - else { is_sep2(u as char) } + 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) { @@ -262,7 +300,7 @@ impl GenericPathUnsafe for Path { 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 me.is_verbatim() { Path::normalize__(path, None) } + let path_ = if is_verbatim(me) { Path::normalize__(path, None) } else { None }; let pathlen = path_.map_default(path.len(), |p| p.len()); let mut s = str::with_capacity(me.repr.len() + 1 + pathlen); @@ -291,7 +329,7 @@ impl GenericPathUnsafe for Path { } None if !path.is_empty() && is_sep_(self.prefix, path[0]) => { // volume-relative path - if self.prefix().is_some() { + if self.prefix.is_some() { // truncate self down to the prefix, then append let n = self.prefix_len(); self.repr.truncate(n); @@ -418,11 +456,6 @@ impl GenericPath for Path { self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::new_unchecked(s) }) } - #[inline] - fn push_path(&mut self, path: &Path) { - self.push(path.as_str().unwrap()) - } - #[inline] fn pop(&mut self) -> Option<~[u8]> { self.pop_str().map_move(|s| s.into_bytes()) @@ -463,7 +496,7 @@ impl GenericPath for Path { } _ => self.repr.slice_to(self.prefix_len()) })) - } else if self.is_vol_relative() { + } else if is_vol_relative(self) { Some(Path::new(self.repr.slice_to(1))) } else { None @@ -493,14 +526,14 @@ impl GenericPath for Path { #[inline] fn is_relative(&self) -> bool { - self.prefix.is_none() && !self.is_vol_relative() + 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() || - self.is_vol_relative() != other.is_vol_relative() { + is_vol_relative(self) != is_vol_relative(other) { false } else { let mut ita = self.str_component_iter().map(|x|x.unwrap()); @@ -544,8 +577,8 @@ impl GenericPath for Path { } else { None } - } else if self.is_vol_relative() != base.is_vol_relative() { - if self.is_vol_relative() { + } else if is_vol_relative(self) != is_vol_relative(base) { + if is_vol_relative(self) { Some(self.clone()) } else { None @@ -555,8 +588,8 @@ impl GenericPath for Path { let mut itb = base.str_component_iter().map(|x|x.unwrap()); let mut comps = ~[]; - let a_verb = self.is_verbatim(); - let b_verb = base.is_verbatim(); + let a_verb = is_verbatim(self); + let b_verb = is_verbatim(base); loop { match (ita.next(), itb.next()) { (None, None) => break, @@ -598,20 +631,6 @@ impl GenericPath for Path { } } - fn each_parent(&self, f: &fn(&Path) -> bool) -> bool { - let mut p = self.clone(); - loop { - if !f(&p) { - return false; - } - let f = p.pop(); - if f.is_none() || (!p.is_verbatim() && bytes!("..") == f.unwrap()) { - break; - } - } - true - } - fn ends_with_path(&self, child: &Path) -> bool { if !child.is_relative() { return false; } let mut selfit = self.str_component_iter().invert(); @@ -694,34 +713,6 @@ impl Path { self.rev_str_component_iter().map(convert) } - /// 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(&self) -> bool { - self.prefix.is_none() && self.repr[0] == sep as u8 - } - - /// 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(&self) -> bool { - self.prefix == Some(DiskPrefix) && !self.is_absolute() - } - - /// Returns the PathPrefix for this Path - #[inline] - pub fn prefix(&self) -> Option { - self.prefix - } - - /// Returns whether the prefix is a verbatim prefix, i.e. \\?\ - #[inline] - pub fn is_verbatim(&self) -> bool { - prefix_is_verbatim(self.prefix) - } - fn equiv_prefix(&self, other: &Path) -> bool { match (self.prefix, other.prefix) { (Some(DiskPrefix), Some(VerbatimDiskPrefix)) => { @@ -866,8 +857,8 @@ impl Path { let s = if self.has_nonsemantic_trailing_slash() { self.repr.slice_to(self.repr.len()-1) } else { self.repr.as_slice() }; - let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep2 } - else { is_sep }); + let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep } + else { is_sep_verbatim }); let prefixlen = self.prefix_len(); self.sepidx = idx.and_then(|x| if x < prefixlen { None } else { Some(x) }); } @@ -893,7 +884,7 @@ impl Path { } fn has_nonsemantic_trailing_slash(&self) -> bool { - self.is_verbatim() && self.repr.len() > self.prefix_len()+1 && + is_verbatim(self) && self.repr.len() > self.prefix_len()+1 && self.repr[self.repr.len()-1] == sep as u8 } @@ -905,23 +896,65 @@ impl Path { } } +/// 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[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 { + 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) +} + /// The standard path separator character pub static sep: char = '\\'; /// The alternative path separator character pub static sep2: char = '/'; -/// Returns whether the given byte is a path separator. -/// Only allows the primary separator '\'; use is_sep2 to allow '/'. +/// 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_sep2(c: char) -> bool { - c == sep || c == sep2 +pub fn is_sep_byte(u: &u8) -> bool { + *u as char == sep || *u as char == sep2 +} + +/// 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 as char == sep } /// Prefix types for Path @@ -953,7 +986,7 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option { if path.starts_with("UNC\\") { // \\?\UNC\server\share path = path.slice_from(4); - let (idx_a, idx_b) = match parse_two_comps(path, is_sep) { + let (idx_a, idx_b) = match parse_two_comps(path, is_sep_verbatim) { Some(x) => x, None => (path.len(), 0) }; @@ -977,7 +1010,7 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option { let idx = path.find('\\').unwrap_or(path.len()); return Some(DeviceNSPrefix(idx)); } - match parse_two_comps(path, is_sep2) { + 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)); @@ -1006,14 +1039,14 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option { // None result means the string didn't need normalizing fn normalize_helper<'a>(s: &'a str, prefix: Option) -> (bool,Option<~[&'a str]>) { - let f = if !prefix_is_verbatim(prefix) { is_sep2 } else { is_sep }; + let f = 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.slice_from(prefix_len(prefix)); let s_ = if is_abs { s_.slice_from(1) } else { s_ }; if is_abs && s_.is_empty() { return (is_abs, match prefix { - Some(DiskPrefix) | None => (if is_sep(s.char_at(prefix_len(prefix))) { None } + Some(DiskPrefix) | None => (if is_sep_verbatim(s.char_at(prefix_len(prefix))) { None } else { Some(~[]) }), Some(_) => Some(~[]), // need to trim the trailing separator }); @@ -1036,7 +1069,7 @@ fn normalize_helper<'a>(s: &'a str, prefix: Option) -> (bool,Option< } else { comps.push(comp) } } if !changed && !prefix_is_verbatim(prefix) { - changed = s.find(is_sep2).is_some(); + changed = s.find(is_sep).is_some(); } if changed { if comps.is_empty() && !is_abs && prefix.is_none() { @@ -1078,8 +1111,8 @@ fn prefix_len(p: Option) -> uint { } fn prefix_is_sep(p: Option, c: u8) -> bool { - c.is_ascii() && if !prefix_is_verbatim(p) { is_sep2(c as char) } - else { is_sep(c as char) } + c.is_ascii() && if !prefix_is_verbatim(p) { is_sep(c as char) } + else { is_sep_verbatim(c as char) } } // Stat support @@ -1636,9 +1669,9 @@ mod 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!(p.prefix(), Some(VerbatimPrefix(2))); + assert_eq!(prefix(&p), Some(VerbatimPrefix(2))); p.push("foo"); - assert_eq!(p.prefix(), Some(VerbatimDiskPrefix)); + assert_eq!(prefix(&p), Some(VerbatimDiskPrefix)); assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo")); // and another with verbatim non-normalized paths @@ -1654,7 +1687,7 @@ mod tests { { let mut p = Path::new($path); let push = Path::new($push); - p.push_path(&push); + p.push(&push); assert_eq!(p.as_str(), Some($exp)); } ) @@ -1837,7 +1870,7 @@ mod tests { { let path = Path::new($path); let join = Path::new($join); - let res = path.join_path(&join); + let res = path.join(&join); assert_eq!(res.as_str(), Some($exp)); } ) @@ -1849,7 +1882,7 @@ mod tests { t!(s: "a\\b", "\\c\\d", "\\c\\d"); t!(s: ".", "a\\b", "a\\b"); t!(s: "\\", "a\\b", "\\a\\b"); - // join_path is implemented using push_path, so there's no need for + // join is implemented using push, so there's no need for // the full set of prefix tests } @@ -2217,11 +2250,11 @@ mod tests { let b = path.is_absolute(); assert!(b == abs, "Path '{}'.is_absolute(): expected {:?}, found {:?}", path.as_str().unwrap(), abs, b); - let b = path.is_vol_relative(); - assert!(b == vol, "Path '{}'.is_vol_relative(): expected {:?}, found {:?}", + let b = is_vol_relative(&path); + assert!(b == vol, "is_vol_relative('{}'): expected {:?}, found {:?}", path.as_str().unwrap(), vol, b); - let b = path.is_cwd_relative(); - assert!(b == cwd, "Path '{}'.is_cwd_relative(): expected {:?}, found {:?}", + let b = is_cwd_relative(&path); + assert!(b == cwd, "is_cwd_relative('{}'): expected {:?}, found {:?}", path.as_str().unwrap(), cwd, b); let b = path.is_relative(); assert!(b == rel, "Path '{}'.is_relativf(): expected {:?}, found {:?}", @@ -2614,54 +2647,4 @@ mod tests { t!(s: ".", [b!(".")]); // since this is really a wrapper around str_component_iter, those tests suffice } - - #[test] - fn test_each_parent() { - assert!(Path::new("/foo/bar").each_parent(|_| true)); - assert!(!Path::new("/foo/bar").each_parent(|_| false)); - - macro_rules! t( - (s: $path:expr, $exp:expr) => ( - { - let path = Path::new($path); - let exp: &[&str] = $exp; - let mut comps = exp.iter().map(|&x|x); - do path.each_parent |p| { - let p = p.as_str(); - assert!(p.is_some()); - let e = comps.next(); - assert!(e.is_some()); - assert_eq!(p.unwrap(), e.unwrap()); - true - }; - assert!(comps.next().is_none()); - } - ) - ) - - t!(s: "\\foo\\bar", ["\\foo\\bar", "\\foo", "\\"]); - t!(s: "\\foo\\bar\\baz", ["\\foo\\bar\\baz", "\\foo\\bar", "\\foo", "\\"]); - t!(s: "\\foo", ["\\foo", "\\"]); - t!(s: "\\", ["\\"]); - t!(s: "foo\\bar\\baz", ["foo\\bar\\baz", "foo\\bar", "foo", "."]); - t!(s: "foo\\bar", ["foo\\bar", "foo", "."]); - t!(s: "foo", ["foo", "."]); - t!(s: ".", ["."]); - t!(s: "..", [".."]); - t!(s: "..\\..\\foo", ["..\\..\\foo", "..\\.."]); - t!(s: "C:\\a\\b", ["C:\\a\\b", "C:\\a", "C:\\"]); - t!(s: "C:\\", ["C:\\"]); - t!(s: "C:a\\b", ["C:a\\b", "C:a", "C:"]); - t!(s: "C:", ["C:"]); - t!(s: "C:..\\..\\a", ["C:..\\..\\a", "C:..\\.."]); - t!(s: "C:..", ["C:.."]); - t!(s: "\\\\a\\b\\c", ["\\\\a\\b\\c", "\\\\a\\b"]); - t!(s: "\\\\a\\b", ["\\\\a\\b"]); - t!(s: "\\\\?\\a\\b\\c", ["\\\\?\\a\\b\\c", "\\\\?\\a\\b", "\\\\?\\a"]); - t!(s: "\\\\?\\C:\\a\\b", ["\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", "\\\\?\\C:\\"]); - t!(s: "\\\\?\\UNC\\a\\b\\c", ["\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b"]); - t!(s: "\\\\.\\a\\b\\c", ["\\\\.\\a\\b\\c", "\\\\.\\a\\b", "\\\\.\\a"]); - t!(s: "\\\\?\\a\\..\\b\\.\\c/d", ["\\\\?\\a\\..\\b\\.\\c/d", "\\\\?\\a\\..\\b\\.", - "\\\\?\\a\\..\\b", "\\\\?\\a\\..", "\\\\?\\a"]); - } } diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index c7ac3e1da9efe..1c13beb790dd5 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -147,7 +147,7 @@ fn res_rel_file(cx: @ExtCtxt, sp: codemap::Span, arg: &Path) -> Path { if !arg.is_absolute() { let mut cu = Path::new(cx.codemap().span_to_filename(sp)); cu.pop(); - cu.push_path(arg); + cu.push(arg); cu } else { arg.clone() diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3e1a008f6ca64..c776e5bfd38fb 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3996,7 +3996,7 @@ impl Parser { prefix.pop(); let mod_path_stack = &*self.mod_path_stack; let mod_path = Path::new(".").join_many(*mod_path_stack); - let dir_path = prefix.join_path(&mod_path); + let dir_path = prefix.join(&mod_path); let file_path = match ::attr::first_attr_value_str_by_name( outer_attrs, "path") { Some(d) => dir_path.join(d), diff --git a/src/test/run-pass/glob-std.rs b/src/test/run-pass/glob-std.rs index c86a438344adc..acb2dde99adf6 100644 --- a/src/test/run-pass/glob-std.rs +++ b/src/test/run-pass/glob-std.rs @@ -27,7 +27,7 @@ pub fn main() { } fn abs_path(path: &str) -> Path { - os::getcwd().join_path(&Path::new(path)) + os::getcwd().join(&Path::new(path)) } fn glob_vec(pattern: &str) -> ~[Path] { From 0c7e8f7a92318d19fd9de96ba70fa36fc41de1e2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 9 Oct 2013 22:05:14 -0700 Subject: [PATCH 27/30] path2: Remove some API functions Delete the following API functions: - set_dirname() - with_dirname() - set_filestem() - with_filestem() - add_extension() - file_path() Also change pop() to return a boolean instead of an owned copy of the old filename. --- src/compiletest/header.rs | 2 +- src/compiletest/runtest.rs | 12 +- src/librustc/metadata/filesearch.rs | 9 +- src/librustpkg/path_util.rs | 4 +- src/libstd/os.rs | 5 +- src/libstd/path/mod.rs | 156 +------------- src/libstd/path/posix.rs | 245 +++------------------- src/libstd/path/windows.rs | 311 +++++----------------------- 8 files changed, 101 insertions(+), 643 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 9cd3d3683cded..efe681ce830a0 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -164,7 +164,7 @@ fn parse_pp_exact(line: &str, testfile: &Path) -> Option { Some(s) => Some(Path::new(s)), None => { if parse_name_directive(line, "pp-exact") { - testfile.file_path() + testfile.filename().map_move(|s| Path::new(s)) } else { None } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index b1d8d83f9d3e5..9c6edb8d566bf 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -662,7 +662,12 @@ fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { fn make_exe_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - f.add_extension(os::EXE_EXTENSION); + if !os::EXE_SUFFIX.is_empty() { + match f.filename().map_move(|s| s + os::EXE_SUFFIX.as_bytes()) { + Some(v) => f.set_filename(v), + None => () + } + } f } @@ -747,7 +752,10 @@ fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path { fn aux_output_dir_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - f.add_extension("libaux"); + match f.filename().map_move(|s| s + bytes!(".libaux")) { + Some(v) => f.set_filename(v), + None => () + } f } diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 2274f7d3c694f..8f42614707886 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -24,7 +24,7 @@ pub enum FileMatch { FileMatches, FileDoesntMatch } pub type pick<'self> = &'self fn(path: &Path) -> FileMatch; pub fn pick_file(file: Path, path: &Path) -> Option { - if path.file_path() == Some(file) { + if path.filename() == Some(file.as_vec()) { Some(path.clone()) } else { None @@ -206,11 +206,10 @@ pub fn rust_path() -> ~[Path] { env_rust_path.push(cwd.clone()); } loop { - let f = cwd.pop(); - if f.is_none() || bytes!("..") == f.unwrap() { - break; + if { let f = cwd.filename(); f.is_none() || f.unwrap() == bytes!("..") } { + break } - cwd.push(".rust"); + cwd.set_filename(".rust"); if !env_rust_path.contains(&cwd) && os::path_exists(&cwd) { env_rust_path.push(cwd.clone()); } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 0fe3abdefa3ad..69c3f6e0f1c83 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -449,8 +449,8 @@ pub fn user_set_rust_path() -> bool { /// Append the version string onto the end of the path's filename pub fn versionize(p: &Path, v: &Version) -> Path { - let q = p.file_path().expect("path is a directory"); - let mut q = q.as_vec().to_owned(); + let q = p.filename().expect("path is a directory"); + let mut q = q.to_owned(); q.push('-' as u8); let vs = v.to_str(); q.push_all(vs.as_bytes()); diff --git a/src/libstd/os.rs b/src/libstd/os.rs index be50f6d1a9fbd..8701365d321e0 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -693,8 +693,9 @@ pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { if path_is_dir(p) { return true; } - let mut p_ = p.clone(); - if p_.pop().is_some() { + if p.filename().is_some() { + let mut p_ = p.clone(); + p_.pop(); if !mkdir_recursive(&p_, mode) { return false; } diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 278a4ab36ff43..08a29f6552a2d 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -268,23 +268,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { self.extension().and_then(str::from_utf8_slice_opt) } - /// Replaces the directory portion of the path with the given byte vector or string. - /// If `self` represents the root of the filesystem hierarchy, the last path component - /// of the argument becomes the filename. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the dirname contains a NUL. - #[inline] - fn set_dirname(&mut self, dirname: T) { - if contains_nul(dirname.container_as_bytes()) { - let dirname = self::null_byte::cond.raise(dirname.container_into_owned_bytes()); - assert!(!contains_nul(dirname)); - unsafe { self.set_dirname_unchecked(dirname) } - } else { - unsafe { self.set_dirname_unchecked(dirname) } - } - } /// 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. /// @@ -301,53 +284,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { unsafe { self.set_filename_unchecked(filename) } } } - /// Replaces the filestem with the given byte vector or string. - /// If there is no extension in `self` (or `self` has no filename), this is equivalent - /// to `set_filename`. Otherwise, if the argument is [] or "", the extension (including - /// the preceding '.') becomes the new filename. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filestem contains a NUL. - fn set_filestem(&mut self, filestem: T) { - // borrowck is being a pain here - enum Value { - Checked(T), - Unchecked(~[u8]) - } - let val = { - match self.filename() { - None => Checked(filestem), - Some(name) => { - let dot = '.' as u8; - match name.rposition_elem(&dot) { - None | Some(0) => Checked(filestem), - Some(idx) => { - let mut v; - if contains_nul(filestem.container_as_bytes()) { - let filestem = filestem.container_into_owned_bytes(); - let filestem = self::null_byte::cond.raise(filestem); - assert!(!contains_nul(filestem)); - v = filestem; - let n = v.len(); - v.reserve(n + name.len() - idx); - } else { - let filestem = filestem.container_as_bytes(); - v = vec::with_capacity(filestem.len() + name.len() - idx); - v.push_all(filestem); - } - v.push_all(name.slice_from(idx)); - Unchecked(v) - } - } - } - } - }; - match val { - Checked(v) => self.set_filename(v), - Unchecked(v) => unsafe { self.set_filename_unchecked(v) } - } - } /// 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. @@ -417,61 +353,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { Some(v) => unsafe { self.set_filename_unchecked(v) } } } - /// Adds the given extension (as a byte vector or string) to the file. - /// This does not remove any existing extension. - /// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`. - /// If `self` has no filename, this is a no-op. - /// If the argument is [] or "", this is a no-op. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the extension contains a NUL. - fn add_extension(&mut self, extension: T) { - if extension.container_as_bytes().is_empty() { return; } - // appease borrowck - let val = { - match self.filename() { - None => None, - Some(name) => { - let mut v; - if contains_nul(extension.container_as_bytes()) { - let ext = extension.container_into_owned_bytes(); - let extension = self::null_byte::cond.raise(ext); - assert!(!contains_nul(extension)); - v = vec::with_capacity(name.len() + 1 + extension.len()); - v.push_all(name); - v.push('.' as u8); - v.push_all(extension); - } else { - let extension = extension.container_as_bytes(); - v = vec::with_capacity(name.len() + 1 + extension.len()); - v.push_all(name); - v.push('.' as u8); - v.push_all(extension); - } - Some(v) - } - } - }; - match val { - None => (), - Some(v) => unsafe { self.set_filename_unchecked(v) } - } - } - /// Returns a new Path constructed by replacing the dirname with the given - /// byte vector or string. - /// See `set_dirname` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the dirname contains a NUL. - #[inline] - fn with_dirname(&self, dirname: T) -> Self { - let mut p = self.clone(); - p.set_dirname(dirname); - p - } /// Returns a new Path constructed by replacing the filename with the given /// byte vector or string. /// See `set_filename` for details. @@ -485,19 +367,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { p.set_filename(filename); p } - /// Returns a new Path constructed by setting the filestem to the given - /// byte vector or string. - /// See `set_filestem` for details. - /// - /// # Failure - /// - /// Raises the `null_byte` condition if the filestem contains a NUL. - #[inline] - fn with_filestem(&self, filestem: T) -> Self { - let mut p = self.clone(); - p.set_filestem(filestem); - p - } /// Returns a new Path constructed by setting the extension to the given /// byte vector or string. /// See `set_extension` for details. @@ -518,12 +387,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { // self.dirname() returns a NUL-free vector unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) } } - /// Returns the file component of `self`, as a relative Path. - /// If `self` represents the root of the filesystem hierarchy, returns None. - fn file_path(&self) -> Option { - // self.filename() returns a NUL-free vector - self.filename().map_move(|v| unsafe { GenericPathUnsafe::new_unchecked(v) }) - } /// Returns a Path that represents the filesystem root that `self` is rooted in. /// @@ -561,16 +424,10 @@ pub trait GenericPath: Clone + GenericPathUnsafe { } } } - /// Pops the last path component off of `self` and returns it. - /// If `self` represents the root of the file hierarchy, None is returned. - fn pop(&mut self) -> Option<~[u8]>; - /// Pops the last path component off of `self` and returns it as a string, if possible. - /// `self` will still be modified even if None is returned. - /// See `pop` for details. - #[inline] - fn pop_str(&mut self) -> Option<~str> { - self.pop().and_then(|v| str::from_utf8_owned_opt(v)) - } + /// Removes the last path component from the receiver. + /// Returns `true` if the receiver was modified, or `false` if it already + /// represented the root of the file hierarchy. + fn pop(&mut self) -> bool; /// Returns a new Path constructed by joining `self` with the given path /// (as a byte vector or string). @@ -658,11 +515,6 @@ pub trait GenericPathUnsafe { /// The resulting Path will always be normalized. unsafe fn new_unchecked(path: T) -> Self; - /// Replaces the directory portion of the path without checking for null - /// bytes. - /// See `set_dirname` for details. - unsafe fn set_dirname_unchecked(&mut self, dirname: T); - /// Replaces the filename portion of the path without checking for null /// bytes. /// See `set_filename` for details. diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index fdc943fb882f9..a7c89cb6029df 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -20,7 +20,6 @@ use option::{Option, None, Some}; use str; use str::Str; use to_bytes::IterBytes; -use util; use vec; use vec::{CopyableVector, RSplitIterator, SplitIterator, Vector, VectorVector}; use super::{BytesContainer, GenericPath, GenericPathUnsafe}; @@ -122,39 +121,6 @@ impl GenericPathUnsafe for Path { Path{ repr: path, sepidx: idx } } - unsafe fn set_dirname_unchecked(&mut self, dirname: T) { - let dirname = dirname.container_as_bytes(); - match self.sepidx { - None if bytes!(".") == self.repr || bytes!("..") == self.repr => { - self.repr = Path::normalize(dirname); - } - None => { - let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1); - v.push_all(dirname); - v.push(sep_byte); - v.push_all(self.repr); - self.repr = Path::normalize(v); - } - Some(0) if self.repr.len() == 1 && self.repr[0] == sep_byte => { - self.repr = Path::normalize(dirname); - } - Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { - self.repr = Path::normalize(dirname); - } - Some(idx) if dirname.is_empty() => { - let v = Path::normalize(self.repr.slice_from(idx+1)); - self.repr = v; - } - Some(idx) => { - let mut v = vec::with_capacity(dirname.len() + self.repr.len() - idx); - v.push_all(dirname); - v.push_all(self.repr.slice_from(idx)); - self.repr = Path::normalize(v); - } - } - self.sepidx = self.repr.rposition_elem(&sep_byte); - } - unsafe fn set_filename_unchecked(&mut self, filename: T) { let filename = filename.container_as_bytes(); match self.sepidx { @@ -236,25 +202,23 @@ impl GenericPath for Path { } } - fn pop(&mut self) -> Option<~[u8]> { + fn pop(&mut self) -> bool { match self.sepidx { - None if bytes!(".") == self.repr => None, + None if bytes!(".") == self.repr => false, None => { - let mut v = ~['.' as u8]; - util::swap(&mut v, &mut self.repr); + self.repr = ~['.' as u8]; self.sepidx = None; - Some(v) + true } - Some(0) if bytes!("/") == self.repr => None, + Some(0) if bytes!("/") == self.repr => false, Some(idx) => { - let v = self.repr.slice_from(idx+1).to_owned(); if idx == 0 { self.repr.truncate(idx+1); } else { self.repr.truncate(idx); } self.sepidx = self.repr.rposition_elem(&sep_byte); - Some(v) + true } } } @@ -692,17 +656,6 @@ mod tests { assert!(handled); assert_eq!(p.as_vec(), b!("/foo")); - handled = false; - do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("null/", 0, "/byte")); - (b!("null/byte").to_owned()) - }).inside { - p.set_dirname(b!("null/", 0, "/byte")); - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("null/byte/foo")); - handled = false; do cond.trap(|v| { handled = true; @@ -712,7 +665,7 @@ mod tests { p.push(b!("f", 0, "o")); }; assert!(handled); - assert_eq!(p.as_vec(), b!("null/byte/foo/foo")); + assert_eq!(p.as_vec(), b!("/foo/foo")); } #[test] @@ -749,15 +702,6 @@ mod tests { }; }) - t!(~"set_dirname w/nul" => { - let mut p = Path::new(b!("foo/bar")); - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - p.set_dirname(b!("foo", 0)) - }; - }) - t!(~"push w/nul" => { let mut p = Path::new(b!("foo/bar")); do cond.trap(|_| { @@ -1006,46 +950,35 @@ mod tests { (s: $path:expr, $left:expr, $right:expr) => ( { let mut p = Path::new($path); - let file = p.pop_str(); + let result = p.pop(); assert_eq!(p.as_str(), Some($left)); - assert_eq!(file.map(|s| s.as_slice()), $right); + assert_eq!(result, $right); } ); - (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( + (v: [$($path:expr),+], [$($left:expr),+], $right:expr) => ( { let mut p = Path::new(b!($($path),+)); - let file = p.pop(); + let result = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); - } - ); - (v: [$($path:expr),+], [$($left:expr),+], None) => ( - { - let mut p = Path::new(b!($($path),+)); - let file = p.pop(); - assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file, None); + assert_eq!(result, $right); } ) ) - t!(v: ["a/b/c"], ["a/b"], Some("c")); - t!(v: ["a"], ["."], Some("a")); - t!(v: ["."], ["."], None); - t!(v: ["/a"], ["/"], Some("a")); - t!(v: ["/"], ["/"], None); - t!(v: ["a/b/c", 0x80], ["a/b"], Some("c", 0x80)); - t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], Some("c")); - t!(v: [0xff], ["."], Some(0xff)); - t!(v: ["/", 0xff], ["/"], Some(0xff)); - t!(s: "a/b/c", "a/b", Some("c")); - t!(s: "a", ".", Some("a")); - t!(s: ".", ".", None); - t!(s: "/a", "/", Some("a")); - t!(s: "/", "/", None); - - assert_eq!(Path::new(b!("foo/bar", 0x80)).pop_str(), None); - assert_eq!(Path::new(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar")); + t!(v: ["a/b/c"], ["a/b"], true); + t!(v: ["a"], ["."], true); + t!(v: ["."], ["."], false); + t!(v: ["/a"], ["/"], true); + t!(v: ["/"], ["/"], false); + t!(v: ["a/b/c", 0x80], ["a/b"], true); + t!(v: ["a/b", 0x80, "/c"], ["a/b", 0x80], true); + t!(v: [0xff], ["."], true); + t!(v: ["/", 0xff], ["/"], true); + t!(s: "a/b/c", "a/b", true); + t!(s: "a", ".", true); + t!(s: ".", ".", false); + t!(s: "/a", "/", true); + t!(s: "/", "/", false); } #[test] @@ -1124,27 +1057,6 @@ mod tests { fn test_with_helpers() { let empty: &[u8] = []; - t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d")), b!("d/c")); - t!(v: Path::new(b!("a/b/c")).with_dirname(b!("d/e")), b!("d/e/c")); - t!(v: Path::new(b!("a/", 0x80, "b/c")).with_dirname(b!(0xff)), b!(0xff, "/c")); - t!(v: Path::new(b!("a/b/", 0x80)).with_dirname(b!("/", 0xcd)), - b!("/", 0xcd, "/", 0x80)); - t!(s: Path::new("a/b/c").with_dirname("d"), "d/c"); - t!(s: Path::new("a/b/c").with_dirname("d/e"), "d/e/c"); - t!(s: Path::new("a/b/c").with_dirname(""), "c"); - t!(s: Path::new("a/b/c").with_dirname("/"), "/c"); - t!(s: Path::new("a/b/c").with_dirname("."), "c"); - t!(s: Path::new("a/b/c").with_dirname(".."), "../c"); - t!(s: Path::new("/").with_dirname("foo"), "foo"); - t!(s: Path::new("/").with_dirname(""), "."); - t!(s: Path::new("/foo").with_dirname("bar"), "bar/foo"); - t!(s: Path::new("..").with_dirname("foo"), "foo"); - t!(s: Path::new("../..").with_dirname("foo"), "foo"); - t!(s: Path::new("..").with_dirname(""), "."); - t!(s: Path::new("../..").with_dirname(""), "."); - t!(s: Path::new("foo").with_dirname(".."), "../foo"); - t!(s: Path::new("foo").with_dirname("../.."), "../../foo"); - t!(v: Path::new(b!("a/b/c")).with_filename(b!("d")), b!("a/b/d")); t!(v: Path::new(b!("a/b/c", 0xff)).with_filename(b!(0x80)), b!("a/b/", 0x80)); t!(v: Path::new(b!("/", 0xff, "/foo")).with_filename(b!(0xcd)), @@ -1169,34 +1081,6 @@ mod tests { t!(s: Path::new("..").with_filename(""), ".."); t!(s: Path::new("../..").with_filename(""), "../.."); - t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_filestem(b!(0xff)), - b!("hi/", 0xff, ".txt")); - t!(v: Path::new(b!("hi/there.txt", 0x80)).with_filestem(b!(0xff)), - b!("hi/", 0xff, ".txt", 0x80)); - t!(v: Path::new(b!("hi/there", 0xff)).with_filestem(b!(0x80)), b!("hi/", 0x80)); - t!(v: Path::new(b!("hi", 0x80, "/there")).with_filestem(empty), b!("hi", 0x80)); - t!(s: Path::new("hi/there.txt").with_filestem("here"), "hi/here.txt"); - t!(s: Path::new("hi/there.txt").with_filestem(""), "hi/.txt"); - t!(s: Path::new("hi/there.txt").with_filestem("."), "hi/..txt"); - t!(s: Path::new("hi/there.txt").with_filestem(".."), "hi/...txt"); - t!(s: Path::new("hi/there.txt").with_filestem("/"), "hi/.txt"); - t!(s: Path::new("hi/there.txt").with_filestem("foo/bar"), "hi/foo/bar.txt"); - t!(s: Path::new("hi/there.foo.txt").with_filestem("here"), "hi/here.txt"); - t!(s: Path::new("hi/there").with_filestem("here"), "hi/here"); - t!(s: Path::new("hi/there").with_filestem(""), "hi"); - t!(s: Path::new("hi").with_filestem(""), "."); - t!(s: Path::new("/hi").with_filestem(""), "/"); - t!(s: Path::new("hi/there").with_filestem(".."), "."); - t!(s: Path::new("hi/there").with_filestem("."), "hi"); - t!(s: Path::new("hi/there.").with_filestem("foo"), "hi/foo."); - t!(s: Path::new("hi/there.").with_filestem(""), "hi"); - t!(s: Path::new("hi/there.").with_filestem("."), "."); - t!(s: Path::new("hi/there.").with_filestem(".."), "hi/..."); - t!(s: Path::new("/").with_filestem("foo"), "/foo"); - t!(s: Path::new(".").with_filestem("foo"), "foo"); - t!(s: Path::new("hi/there..").with_filestem("here"), "hi/here."); - t!(s: Path::new("hi/there..").with_filestem(""), "hi"); - t!(v: Path::new(b!("hi/there", 0x80, ".txt")).with_extension(b!("exe")), b!("hi/there", 0x80, ".exe")); t!(v: Path::new(b!("hi/there.txt", 0x80)).with_extension(b!(0xff)), @@ -1245,17 +1129,6 @@ mod tests { ) ) - t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d")); - t!(v: b!("a/b/c"), set_dirname, with_dirname, b!("d/e")); - t!(v: b!("a/", 0x80, "/c"), set_dirname, with_dirname, b!(0xff)); - t!(s: "a/b/c", set_dirname, with_dirname, "d"); - t!(s: "a/b/c", set_dirname, with_dirname, "d/e"); - t!(s: "/", set_dirname, with_dirname, "foo"); - t!(s: "/foo", set_dirname, with_dirname, "bar"); - t!(s: "a/b/c", set_dirname, with_dirname, ""); - t!(s: "../..", set_dirname, with_dirname, "x"); - t!(s: "foo", set_dirname, with_dirname, "../.."); - t!(v: b!("a/b/c"), set_filename, with_filename, b!("d")); t!(v: b!("/"), set_filename, with_filename, b!("foo")); t!(v: b!(0x80), set_filename, with_filename, b!(0xff)); @@ -1265,14 +1138,6 @@ mod tests { t!(s: "a/b", set_filename, with_filename, ""); t!(s: "a", set_filename, with_filename, ""); - t!(v: b!("hi/there.txt"), set_filestem, with_filestem, b!("here")); - t!(v: b!("hi/there", 0x80, ".txt"), set_filestem, with_filestem, b!("here", 0xff)); - t!(s: "hi/there.txt", set_filestem, with_filestem, "here"); - t!(s: "hi/there.", set_filestem, with_filestem, "here"); - t!(s: "hi/there", set_filestem, with_filestem, "here"); - t!(s: "hi/there.txt", set_filestem, with_filestem, ""); - t!(s: "hi/there", set_filestem, with_filestem, ""); - t!(v: b!("hi/there.txt"), set_extension, with_extension, b!("exe")); t!(v: b!("hi/there.t", 0x80, "xt"), set_extension, with_extension, b!("exe", 0xff)); t!(s: "hi/there.txt", set_extension, with_extension, "exe"); @@ -1283,36 +1148,6 @@ mod tests { t!(s: ".", set_extension, with_extension, "txt"); } - #[test] - fn test_add_extension() { - macro_rules! t( - (s: $path:expr, $ext:expr, $exp:expr) => ( - { - let mut path = Path::new($path); - path.add_extension($ext); - assert_eq!(path.as_str(), Some($exp)); - } - ); - (v: $path:expr, $ext:expr, $exp:expr) => ( - { - let mut path = Path::new($path); - path.add_extension($ext); - assert_eq!(path.as_vec(), $exp); - } - ) - ) - - t!(s: "hi/there.txt", "foo", "hi/there.txt.foo"); - t!(s: "hi/there.", "foo", "hi/there..foo"); - t!(s: "hi/there", ".foo", "hi/there..foo"); - t!(s: "hi/there.txt", "", "hi/there.txt"); - t!(v: b!("hi/there.txt"), b!("foo"), b!("hi/there.txt.foo")); - t!(v: b!("hi/there"), b!("bar"), b!("hi/there.bar")); - t!(v: b!("/"), b!("foo"), b!("/")); - t!(v: b!("."), b!("foo"), b!(".")); - t!(v: b!("hi/there.", 0x80, "foo"), b!(0xff), b!("hi/there.", 0x80, "foo.", 0xff)); - } - #[test] fn test_getters() { macro_rules! t( @@ -1372,7 +1207,7 @@ mod tests { } #[test] - fn test_dir_file_path() { + fn test_dir_path() { t!(v: Path::new(b!("hi/there", 0x80)).dir_path(), b!("hi")); t!(v: Path::new(b!("hi", 0xff, "/there")).dir_path(), b!("hi", 0xff)); t!(s: Path::new("hi/there").dir_path(), "hi"); @@ -1381,32 +1216,6 @@ mod tests { t!(s: Path::new("/").dir_path(), "/"); t!(s: Path::new("..").dir_path(), ".."); t!(s: Path::new("../..").dir_path(), "../.."); - - macro_rules! t( - (s: $path:expr, $exp:expr) => ( - { - let path = $path; - let left = path.and_then_ref(|p| p.as_str()); - assert_eq!(left, $exp); - } - ); - (v: $path:expr, $exp:expr) => ( - { - let path = $path; - let left = path.map(|p| p.as_vec()); - assert_eq!(left, $exp); - } - ) - ) - - t!(v: Path::new(b!("hi/there", 0x80)).file_path(), Some(b!("there", 0x80))); - t!(v: Path::new(b!("hi", 0xff, "/there")).file_path(), Some(b!("there"))); - t!(s: Path::new("hi/there").file_path(), Some("there")); - t!(s: Path::new("hi").file_path(), Some("hi")); - t!(s: Path::new(".").file_path(), None); - t!(s: Path::new("/").file_path(), None); - t!(s: Path::new("..").file_path(), None); - t!(s: Path::new("../..").file_path(), None); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 5f8e1dc58fa29..77232196f7856 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -20,7 +20,6 @@ use option::{Option, Some, None}; use str; use str::{CharSplitIterator, OwnedStr, Str, StrVector}; use to_bytes::IterBytes; -use util; use vec::Vector; use super::{BytesContainer, GenericPath, GenericPathUnsafe}; @@ -174,49 +173,6 @@ impl GenericPathUnsafe for Path { ret } - /// See `GenericPathUnsafe::set_dirname_unchecked`. - /// - /// # Failure - /// - /// Raises the `str::not_utf8` condition if not valid UTF-8. - unsafe fn set_dirname_unchecked(&mut self, dirname: T) { - let dirname = dirname.container_as_str(); - match self.sepidx_or_prefix_len() { - None if "." == self.repr || ".." == self.repr => { - self.update_normalized(dirname); - } - None => { - let mut s = str::with_capacity(dirname.len() + self.repr.len() + 1); - s.push_str(dirname); - s.push_char(sep); - s.push_str(self.repr); - self.update_normalized(s); - } - Some((_,idxa,end)) if self.repr.slice(idxa,end) == ".." => { - self.update_normalized(dirname); - } - Some((_,idxa,end)) if dirname.is_empty() => { - let (prefix, path) = Path::normalize_(self.repr.slice(idxa,end)); - self.repr = path; - self.prefix = prefix; - self.update_sepidx(); - } - Some((idxb,idxa,end)) => { - let idx = if dirname.ends_with("\\") { idxa } - else { - let prefix = parse_prefix(dirname); - if prefix == Some(DiskPrefix) && prefix_len(prefix) == dirname.len() { - idxa - } else { idxb } - }; - let mut s = str::with_capacity(dirname.len() + end - idx); - s.push_str(dirname); - s.push_str(self.repr.slice(idx,end)); - self.update_normalized(s); - } - } - } - /// See `GenericPathUnsafe::set_filename_unchecekd`. /// /// # Failure @@ -452,28 +408,18 @@ impl GenericPath for Path { unsafe { GenericPathUnsafe::new_unchecked(self.dirname_str().unwrap()) } } - fn file_path(&self) -> Option { - self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::new_unchecked(s) }) - } - #[inline] - fn pop(&mut self) -> Option<~[u8]> { - self.pop_str().map_move(|s| s.into_bytes()) - } - - fn pop_str(&mut self) -> Option<~str> { + fn pop(&mut self) -> bool { match self.sepidx_or_prefix_len() { - None if "." == self.repr => None, + None if "." == self.repr => false, None => { - let mut s = ~"."; - util::swap(&mut s, &mut self.repr); + self.repr = ~"."; self.sepidx = None; - Some(s) + true } - Some((idxb,idxa,end)) if idxb == idxa && idxb == end => None, - Some((idxb,_,end)) if self.repr.slice(idxb, end) == "\\" => None, - Some((idxb,idxa,end)) => { - let s = self.repr.slice(idxa, end).to_owned(); + Some((idxb,idxa,end)) if idxb == idxa && idxb == end => false, + Some((idxb,_,end)) if self.repr.slice(idxb, end) == "\\" => false, + Some((idxb,idxa,_)) => { let trunc = match self.prefix { Some(DiskPrefix) | Some(VerbatimDiskPrefix) | None => { let plen = self.prefix_len(); @@ -483,7 +429,7 @@ impl GenericPath for Path { }; self.repr.truncate(trunc); self.update_sepidx(); - Some(s) + true } } } @@ -1401,17 +1347,6 @@ mod tests { assert!(handled); assert_eq!(p.as_vec(), b!("\\foo")); - handled = false; - do cond.trap(|v| { - handled = true; - assert_eq!(v.as_slice(), b!("null\\", 0, "\\byte")); - (b!("null\\byte").to_owned()) - }).inside { - p.set_dirname(b!("null\\", 0, "\\byte")); - }; - assert!(handled); - assert_eq!(p.as_vec(), b!("null\\byte\\foo")); - handled = false; do cond.trap(|v| { handled = true; @@ -1421,7 +1356,7 @@ mod tests { p.push(b!("f", 0, "o")); }; assert!(handled); - assert_eq!(p.as_vec(), b!("null\\byte\\foo\\foo")); + assert_eq!(p.as_vec(), b!("\\foo\\foo")); } #[test] @@ -1458,15 +1393,6 @@ mod tests { }; }) - t!(~"set_dirname w\\nul" => { - let mut p = Path::new(b!("foo\\bar")); - do cond.trap(|_| { - (b!("null", 0).to_owned()) - }).inside { - p.set_dirname(b!("foo", 0)) - }; - }) - t!(~"push w\\nul" => { let mut p = Path::new(b!("foo\\bar")); do cond.trap(|_| { @@ -1770,69 +1696,58 @@ mod tests { { let pstr = $path; let mut p = Path::new(pstr); - let file = p.pop_str(); + let result = p.pop(); let left = $left; assert!(p.as_str() == Some(left), "`{}`.pop() failed; expected remainder `{}`, found `{}`", pstr, left, p.as_str().unwrap()); - let right = $right; - let res = file.map(|s| s.as_slice()); - assert!(res == right, "`{}`.pop() failed; expected `{:?}`, found `{:?}`", - pstr, right, res); - } - ); - (v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => ( - { - let mut p = Path::new(b!($($path),+)); - let file = p.pop(); - assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+))); + assert_eq!(result, $right); } ); - (v: [$($path:expr),+], [$($left:expr),+], None) => ( + (v: [$($path:expr),+], [$($left:expr),+], $right:expr) => ( { let mut p = Path::new(b!($($path),+)); - let file = p.pop(); + let result = p.pop(); assert_eq!(p.as_vec(), b!($($left),+)); - assert_eq!(file, None); + assert_eq!(result, $right); } ) ) - t!(s: "a\\b\\c", "a\\b", Some("c")); - t!(s: "a", ".", Some("a")); - t!(s: ".", ".", None); - t!(s: "\\a", "\\", Some("a")); - t!(s: "\\", "\\", None); - t!(v: ["a\\b\\c"], ["a\\b"], Some("c")); - t!(v: ["a"], ["."], Some("a")); - t!(v: ["."], ["."], None); - t!(v: ["\\a"], ["\\"], Some("a")); - t!(v: ["\\"], ["\\"], None); - - t!(s: "C:\\a\\b", "C:\\a", Some("b")); - t!(s: "C:\\a", "C:\\", Some("a")); - t!(s: "C:\\", "C:\\", None); - t!(s: "C:a\\b", "C:a", Some("b")); - t!(s: "C:a", "C:", Some("a")); - t!(s: "C:", "C:", None); - t!(s: "\\\\server\\share\\a\\b", "\\\\server\\share\\a", Some("b")); - t!(s: "\\\\server\\share\\a", "\\\\server\\share", Some("a")); - t!(s: "\\\\server\\share", "\\\\server\\share", None); - t!(s: "\\\\?\\a\\b\\c", "\\\\?\\a\\b", Some("c")); - t!(s: "\\\\?\\a\\b", "\\\\?\\a", Some("b")); - t!(s: "\\\\?\\a", "\\\\?\\a", None); - t!(s: "\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", Some("b")); - t!(s: "\\\\?\\C:\\a", "\\\\?\\C:\\", Some("a")); - t!(s: "\\\\?\\C:\\", "\\\\?\\C:\\", None); - t!(s: "\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", Some("b")); - t!(s: "\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share", Some("a")); - t!(s: "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", None); - t!(s: "\\\\.\\a\\b\\c", "\\\\.\\a\\b", Some("c")); - t!(s: "\\\\.\\a\\b", "\\\\.\\a", Some("b")); - t!(s: "\\\\.\\a", "\\\\.\\a", None); - - t!(s: "\\\\?\\a\\b\\", "\\\\?\\a", Some("b")); + t!(s: "a\\b\\c", "a\\b", true); + t!(s: "a", ".", true); + t!(s: ".", ".", false); + t!(s: "\\a", "\\", true); + t!(s: "\\", "\\", false); + t!(v: ["a\\b\\c"], ["a\\b"], true); + t!(v: ["a"], ["."], true); + t!(v: ["."], ["."], false); + t!(v: ["\\a"], ["\\"], true); + t!(v: ["\\"], ["\\"], 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] @@ -1934,48 +1849,6 @@ mod tests { } ) ) - t!(s: "a\\b\\c", with_dirname, "d", "d\\c"); - t!(s: "a\\b\\c", with_dirname, "d\\e", "d\\e\\c"); - t!(s: "a\\b\\c", with_dirname, "", "c"); - t!(s: "a\\b\\c", with_dirname, "\\", "\\c"); - t!(s: "a\\b\\c", with_dirname, "/", "\\c"); - t!(s: "a\\b\\c", with_dirname, ".", "c"); - t!(s: "a\\b\\c", with_dirname, "..", "..\\c"); - t!(s: "\\", with_dirname, "foo", "foo"); - t!(s: "\\", with_dirname, "", "."); - t!(s: "\\foo", with_dirname, "bar", "bar\\foo"); - t!(s: "..", with_dirname, "foo", "foo"); - t!(s: "..\\..", with_dirname, "foo", "foo"); - t!(s: "..", with_dirname, "", "."); - t!(s: "..\\..", with_dirname, "", "."); - t!(s: ".", with_dirname, "foo", "foo"); - t!(s: "foo", with_dirname, "..", "..\\foo"); - t!(s: "foo", with_dirname, "..\\..", "..\\..\\foo"); - t!(s: "C:\\a\\b", with_dirname, "foo", "foo\\b"); - t!(s: "foo", with_dirname, "C:\\a\\b", "C:\\a\\b\\foo"); - t!(s: "C:a\\b", with_dirname, "\\\\server\\share", "\\\\server\\share\\b"); - t!(s: "a", with_dirname, "\\\\server\\share", "\\\\server\\share\\a"); - t!(s: "a\\b", with_dirname, "\\\\?\\", "\\\\?\\b"); - t!(s: "a\\b", with_dirname, "C:", "C:b"); - t!(s: "a\\b", with_dirname, "C:\\", "C:\\b"); - t!(s: "a\\b", with_dirname, "C:/", "C:\\b"); - t!(s: "C:\\", with_dirname, "foo", "foo"); - t!(s: "C:", with_dirname, "foo", "foo"); - t!(s: ".", with_dirname, "C:\\", "C:\\"); - t!(s: ".", with_dirname, "C:/", "C:\\"); - t!(s: "\\\\?\\C:\\foo", with_dirname, "C:\\", "C:\\foo"); - t!(s: "\\\\?\\C:\\", with_dirname, "bar", "bar"); - t!(s: "foo\\bar", with_dirname, "\\\\?\\C:\\baz", "\\\\?\\C:\\baz\\bar"); - t!(s: "\\\\?\\foo", with_dirname, "C:\\bar", "C:\\bar"); - t!(s: "\\\\?\\a\\foo", with_dirname, "C:\\bar", "C:\\bar\\foo"); - t!(s: "\\\\?\\a\\foo/bar", with_dirname, "C:\\baz", "C:\\baz\\foo\\bar"); - t!(s: "\\\\?\\UNC\\server\\share\\baz", with_dirname, "a", "a\\baz"); - t!(s: "foo\\bar", with_dirname, "\\\\?\\UNC\\server\\share\\baz", - "\\\\?\\UNC\\server\\share\\baz\\bar"); - t!(s: "\\\\.\\foo", with_dirname, "bar", "bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname, "baz", "baz\\bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname, "baz\\", "baz\\bar"); - t!(s: "\\\\.\\foo\\bar", with_dirname, "baz/", "baz\\bar"); t!(s: "a\\b\\c", with_filename, "d", "a\\b\\d"); t!(s: ".", with_filename, "foo", "foo"); @@ -2028,29 +1901,6 @@ mod tests { t!(s: "\\\\.\\foo", with_filename, "bar", "\\\\.\\foo\\bar"); t!(s: "\\\\.\\foo\\bar", with_filename, "..", "\\\\.\\foo\\.."); - t!(s: "hi\\there.txt", with_filestem, "here", "hi\\here.txt"); - t!(s: "hi\\there.txt", with_filestem, "", "hi\\.txt"); - t!(s: "hi\\there.txt", with_filestem, ".", "hi\\..txt"); - t!(s: "hi\\there.txt", with_filestem, "..", "hi\\...txt"); - t!(s: "hi\\there.txt", with_filestem, "\\", "hi\\.txt"); - t!(s: "hi\\there.txt", with_filestem, "foo\\bar", "hi\\foo\\bar.txt"); - t!(s: "hi\\there.foo.txt", with_filestem, "here", "hi\\here.txt"); - t!(s: "hi\\there", with_filestem, "here", "hi\\here"); - t!(s: "hi\\there", with_filestem, "", "hi"); - t!(s: "hi", with_filestem, "", "."); - t!(s: "\\hi", with_filestem, "", "\\"); - t!(s: "hi\\there", with_filestem, "..", "."); - t!(s: "hi\\there", with_filestem, ".", "hi"); - t!(s: "hi\\there.", with_filestem, "foo", "hi\\foo."); - t!(s: "hi\\there.", with_filestem, "", "hi"); - t!(s: "hi\\there.", with_filestem, ".", "."); - t!(s: "hi\\there.", with_filestem, "..", "hi\\..."); - t!(s: "\\", with_filestem, "foo", "\\foo"); - t!(s: ".", with_filestem, "foo", "foo"); - t!(s: "hi\\there..", with_filestem, "here", "hi\\here."); - t!(s: "hi\\there..", with_filestem, "", "hi"); - // filestem setter calls filename setter internally, no need for extended tests - 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.."); @@ -2093,16 +1943,6 @@ mod tests { ) ) - t!(v: b!("a\\b\\c"), set_dirname, with_dirname, b!("d")); - t!(v: b!("a\\b\\c"), set_dirname, with_dirname, b!("d\\e")); - t!(s: "a\\b\\c", set_dirname, with_dirname, "d"); - t!(s: "a\\b\\c", set_dirname, with_dirname, "d\\e"); - t!(s: "\\", set_dirname, with_dirname, "foo"); - t!(s: "\\foo", set_dirname, with_dirname, "bar"); - t!(s: "a\\b\\c", set_dirname, with_dirname, ""); - t!(s: "..\\..", set_dirname, with_dirname, "x"); - t!(s: "foo", set_dirname, with_dirname, "..\\.."); - 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"); @@ -2111,13 +1951,6 @@ mod tests { t!(s: "a\\b", set_filename, with_filename, ""); t!(s: "a", set_filename, with_filename, ""); - t!(v: b!("hi\\there.txt"), set_filestem, with_filestem, b!("here")); - t!(s: "hi\\there.txt", set_filestem, with_filestem, "here"); - t!(s: "hi\\there.", set_filestem, with_filestem, "here"); - t!(s: "hi\\there", set_filestem, with_filestem, "here"); - t!(s: "hi\\there.txt", set_filestem, with_filestem, ""); - t!(s: "hi\\there", set_filestem, with_filestem, ""); - 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"); @@ -2130,33 +1963,6 @@ mod tests { // will suffice. No need for the full set of prefix tests. } - #[test] - fn test_add_extension() { - macro_rules! t( - (s: $path:expr, $ext:expr, $exp:expr) => ( - { - let mut path = Path::new($path); - path.add_extension($ext); - assert_eq!(path.as_str(), Some($exp)); - } - ); - (v: $path:expr, $ext:expr, $exp:expr) => ( - { - let mut path = Path::new($path); - path.add_extension($ext); - assert_eq!(path.as_vec(), $exp); - } - ) - ) - - t!(v: b!("hi\\there.txt"), b!("foo"), b!("hi\\there.txt.foo")); - t!(v: b!("hi\\there"), b!("bar"), b!("hi\\there.bar")); - t!(v: b!("\\"), b!("foo"), b!("\\")); - t!(v: b!("."), b!("foo"), b!(".")); - t!(s: "hi\\there.", "foo", "hi\\there..foo"); - t!(s: "hi\\there.txt", "", "hi\\there.txt"); - } - #[test] fn test_getters() { macro_rules! t( @@ -2211,7 +2017,7 @@ mod tests { } #[test] - fn test_dir_file_path() { + 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(), "\\"); @@ -2219,24 +2025,7 @@ mod tests { t!(s: Path::new("..").dir_path(), ".."); t!(s: Path::new("..\\..").dir_path(), "..\\.."); - macro_rules! t( - ($path:expr, $exp:expr) => ( - { - let path = $path; - let left = path.and_then_ref(|p| p.as_str()); - assert_eq!(left, $exp); - } - ); - ) - - t!(Path::new("hi\\there").file_path(), Some("there")); - t!(Path::new("hi").file_path(), Some("hi")); - t!(Path::new(".").file_path(), None); - t!(Path::new("\\").file_path(), None); - t!(Path::new("..").file_path(), None); - t!(Path::new("..\\..").file_path(), None); - - // dir_path and file_path are just dirname and filename interpreted as paths. + // dir_path is just dirname interpreted as a path. // No need for extended tests } From 40b324f0dec364a9fa71f9f8b47b8ab156f6d61e Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 15 Oct 2013 20:01:33 -0700 Subject: [PATCH 28/30] path2: Remove Path.into_str() --- src/libstd/path/mod.rs | 3 --- src/libstd/path/posix.rs | 7 ------- src/libstd/path/windows.rs | 9 --------- 3 files changed, 19 deletions(-) diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index 08a29f6552a2d..4962b63c8cff6 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -179,9 +179,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe { str::from_utf8_slice_opt(self.as_vec()) } - /// Converts the Path into an owned string, if possible - fn into_str(self) -> Option<~str>; - /// Returns the path as a byte vector fn as_vec<'a>(&'a self) -> &'a [u8]; diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index a7c89cb6029df..5eea65a5b919c 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -178,10 +178,6 @@ impl GenericPath for Path { self.repr } - fn into_str(self) -> Option<~str> { - str::from_utf8_owned_opt(self.repr) - } - fn dirname<'a>(&'a self) -> &'a [u8] { match self.sepidx { None if bytes!("..") == self.repr => self.repr.as_slice(), @@ -614,12 +610,9 @@ mod tests { assert_eq!(Path::new(b!("foo/bar")).into_vec(), b!("foo/bar").to_owned()); assert_eq!(Path::new(b!("/foo/../../bar")).into_vec(), b!("/bar").to_owned()); - assert_eq!(Path::new("foo/bar").into_str(), Some(~"foo/bar")); - assert_eq!(Path::new("/foo/../../bar").into_str(), Some(~"/bar")); let p = Path::new(b!("foo/bar", 0x80)); assert_eq!(p.as_str(), None); - assert_eq!(Path::new(b!("foo", 0xff, "/bar")).into_str(), None); } #[test] diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 77232196f7856..9576e62f43926 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -327,13 +327,6 @@ impl GenericPath for Path { Some(self.repr.as_slice()) } - /// See `GenericPath::into_str` for info. - /// Always returns a `Some` value. - #[inline] - fn into_str(self) -> Option<~str> { - Some(self.repr) - } - #[inline] fn as_vec<'a>(&'a self) -> &'a [u8] { self.repr.as_bytes() @@ -1260,8 +1253,6 @@ mod tests { assert_eq!(Path::new(b!("foo\\bar")).into_vec(), b!("foo\\bar").to_owned()); assert_eq!(Path::new(b!("\\foo\\..\\..\\bar")).into_vec(), b!("\\bar").to_owned()); - assert_eq!(Path::new("foo\\bar").into_str(), Some(~"foo\\bar")); - assert_eq!(Path::new("\\foo\\..\\..\\bar").into_str(), Some(~"\\bar")); t!(s: Path::new("\\\\a"), "\\a"); t!(s: Path::new("\\\\a\\"), "\\a"); From 6eade9e9143e496167d66298e8bbac5d9dfa3e19 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Tue, 15 Oct 2013 23:32:14 -0700 Subject: [PATCH 29/30] path2: Update for latest master Also fix some issues that crept into earlier commits during the conflict resoution for the rebase. --- src/compiletest/header.rs | 2 +- src/compiletest/runtest.rs | 4 ++-- src/libextra/glob.rs | 6 +++--- src/librustpkg/package_source.rs | 21 ++++++++++-------- src/librustpkg/path_util.rs | 4 ++-- src/librustpkg/rustpkg.rs | 14 ++++++------ src/librustpkg/source_control.rs | 4 +++- src/librustpkg/tests.rs | 37 +++++++++++++++++--------------- src/librustpkg/util.rs | 6 +++--- src/librustpkg/workspace.rs | 2 +- src/libstd/os.rs | 4 ++-- src/libstd/path/posix.rs | 4 ++-- src/libstd/path/windows.rs | 27 +++++++++++++++-------- 13 files changed, 76 insertions(+), 59 deletions(-) diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index efe681ce830a0..541aa082f5166 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -164,7 +164,7 @@ fn parse_pp_exact(line: &str, testfile: &Path) -> Option { Some(s) => Some(Path::new(s)), None => { if parse_name_directive(line, "pp-exact") { - testfile.filename().map_move(|s| Path::new(s)) + testfile.filename().map(|s| Path::new(s)) } else { None } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 9c6edb8d566bf..627a80ace6916 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -663,7 +663,7 @@ fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path { fn make_exe_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); if !os::EXE_SUFFIX.is_empty() { - match f.filename().map_move(|s| s + os::EXE_SUFFIX.as_bytes()) { + match f.filename().map(|s| s + os::EXE_SUFFIX.as_bytes()) { Some(v) => f.set_filename(v), None => () } @@ -752,7 +752,7 @@ fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path { fn aux_output_dir_name(config: &config, testfile: &Path) -> Path { let mut f = output_base_name(config, testfile); - match f.filename().map_move(|s| s + bytes!(".libaux")) { + match f.filename().map(|s| s + bytes!(".libaux")) { Some(v) => f.set_filename(v), None => () } diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index 83456777c40c1..031545c1cd203 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -98,7 +98,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> GlobIterator { root.push(pat_root.get_ref()); } - let root_len = pat_root.map_move_default(0u, |p| p.as_vec().len()); + let root_len = pat_root.map_default(0u, |p| p.as_vec().len()); let dir_patterns = pattern.slice_from(root_len.min(&pattern.len())) .split_terminator_iter(is_sep).map(|s| Pattern::new(s)).to_owned_vec(); @@ -303,7 +303,7 @@ impl Pattern { */ pub fn matches_path(&self, path: &Path) -> bool { // FIXME (#9639): This needs to handle non-utf8 paths - do path.as_str().map_move_default(false) |s| { + do path.as_str().map_default(false) |s| { self.matches(s) } } @@ -321,7 +321,7 @@ impl Pattern { */ pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool { // FIXME (#9639): This needs to handle non-utf8 paths - do path.as_str().map_move_default(false) |s| { + do path.as_str().map_default(false) |s| { self.matches_with(s, options) } } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index 874a30394d1ba..fba6cba911660 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -104,7 +104,7 @@ impl PkgSrc { let mut result = build_dir.join("src"); result.push(&id.path.dir_path()); - result.push_str(format!("{}-{}", id.short_name, id.version.to_str())); + result.push(format!("{}-{}", id.short_name, id.version.to_str())); to_try.push(result.clone()); output_names.push(result); let mut other_result = build_dir.join("src"); @@ -174,17 +174,19 @@ impl PkgSrc { } match ok_d { Some(ref d) => { - if d.is_parent_of(&id.path) - || d.is_parent_of(&versionize(&id.path, &id.version)) { + if d.is_ancestor_of(&id.path) + || d.is_ancestor_of(&versionize(&id.path, &id.version)) { // Strip off the package ID source_workspace = d.clone(); - for _ in id.path.components().iter() { - source_workspace = source_workspace.pop(); + for _ in id.path.component_iter() { + source_workspace.pop(); } // Strip off the src/ part - source_workspace = source_workspace.pop(); + source_workspace.pop(); // Strip off the build/ part to get the workspace - destination_workspace = source_workspace.pop().pop(); + destination_workspace = source_workspace.clone(); + destination_workspace.pop(); + destination_workspace.pop(); } break; } @@ -244,9 +246,10 @@ impl PkgSrc { pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option { use conditions::git_checkout_failed::cond; + let cwd = os::getcwd(); debug2!("Checking whether {} (path = {}) exists locally. Cwd = {}, does it? {:?}", pkgid.to_str(), pkgid.path.display(), - os::getcwd().display(), + cwd.display(), os::path_exists(&pkgid.path)); match safe_git_clone(&pkgid.path, &pkgid.version, local) { @@ -400,7 +403,7 @@ impl PkgSrc { // into account. I'm not sure if the workcache really likes seeing the // output as "Some(\"path\")". But I don't know what to do about it. // FIXME (#9639): This needs to handle non-utf8 paths - let result = result.map(|p|p.as_str().unwrap()); + let result = result.as_ref().map(|p|p.as_str().unwrap()); debug2!("Result of compiling {} was {}", subpath.display(), result.to_str()); result.to_str() } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 69c3f6e0f1c83..f5b2251a2a6ec 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -462,7 +462,7 @@ pub fn versionize(p: &Path, v: &Version) -> Path { pub fn chmod_read_only(p: &Path) -> bool { #[fixed_stack_segment]; unsafe { - do p.to_str().with_c_str |src_buf| { + do p.with_c_str |src_buf| { libc::chmod(src_buf, S_IRUSR as libc::c_int) == 0 as libc::c_int } } @@ -472,7 +472,7 @@ pub fn chmod_read_only(p: &Path) -> bool { pub fn chmod_read_only(p: &Path) -> bool { #[fixed_stack_segment]; unsafe { - do p.to_str().with_c_str |src_buf| { + do p.with_c_str |src_buf| { libc::chmod(src_buf, S_IRUSR as libc::mode_t) == 0 as libc::c_int } diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 7b10bf2a551b9..985dcd805ce28 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -247,8 +247,9 @@ impl CtxMethods for BuildContext { dest_ws = determine_destination(os::getcwd(), self.context.use_rust_path_hack, workspace); - let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); - dest_ws = Some(self.build(&mut pkg_src, what)); + let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(), + false, pkgid.clone()); + self.build(&mut pkg_src, what); true }; // n.b. If this builds multiple packages, it only returns the workspace for @@ -430,7 +431,7 @@ impl CtxMethods for BuildContext { match git_result { CheckedOutSources => make_read_only(&out_dir), // FIXME (#9639): This needs to handle non-utf8 paths - _ => cond.raise((pkgid.path.as_str().unwrap(), out_dir.clone())) + _ => cond.raise((pkgid.path.as_str().unwrap().to_owned(), out_dir.clone())) }; let default_ws = default_workspace(); debug2!("Calling build recursively with {:?} and {:?}", default_ws.display(), @@ -562,7 +563,7 @@ impl CtxMethods for BuildContext { let result = self.install_no_build(pkg_src.build_workspace(), &pkg_src.destination_workspace, - &id).map(|s| Path::new(*s)); + &id).map(|s| Path::new(s.as_slice())); debug2!("install: id = {}, about to call discover_outputs, {:?}", id.to_str(), result.map(|p| p.display().to_str())); installed_files = installed_files + result; @@ -580,7 +581,7 @@ impl CtxMethods for BuildContext { use conditions::copy_failed::cond; debug2!("install_no_build: assuming {} comes from {} with target {}", - id.to_str(), build_workspace.to_str(), target_workspace.to_str()); + id.to_str(), build_workspace.display(), target_workspace.display()); // Now copy stuff into the install dirs let maybe_executable = built_executable_in_workspace(id, build_workspace); @@ -631,8 +632,7 @@ impl CtxMethods for BuildContext { let mut target_lib = sub_target_lib .clone().expect(format!("I built {} but apparently \ didn't install it!", lib.display())); - let target_lib = target_lib - .set_filename(lib.filename().expect("weird target lib")); + target_lib.set_filename(lib.filename().expect("weird target lib")); if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && os::copy_file(lib, &target_lib)) { cond.raise(((*lib).clone(), target_lib.clone())); diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs index 2fc925a8a6a93..3c879af34cf1e 100644 --- a/src/librustpkg/source_control.rs +++ b/src/librustpkg/source_control.rs @@ -107,7 +107,9 @@ pub fn make_read_only(target: &Path) { pub fn git_clone_url(source: &str, target: &Path, v: &Version) { use conditions::git_checkout_failed::cond; - let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]); + // FIXME (#9639): This needs to handle non-utf8 paths + let outp = run::process_output("git", [~"clone", source.to_owned(), + target.as_str().unwrap().to_owned()]); if outp.status != 0 { debug2!("{}", str::from_utf8_owned(outp.output.clone())); debug2!("{}", str::from_utf8_owned(outp.error)); diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 4e8bcbb1cec8a..d367399c0bf28 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -88,7 +88,7 @@ fn writeFile(file_path: &Path, contents: &str) { fn mk_emptier_workspace(tag: &str) -> TempDir { let workspace = TempDir::new(tag).expect("couldn't create temp dir"); - let package_dir = workspace.path().push("src"); + let package_dir = workspace.path().join("src"); assert!(os::mkdir_recursive(&package_dir, U_RWX)); workspace } @@ -110,9 +110,10 @@ fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path fn mk_temp_workspace(short_name: &Path, version: &Version) -> (TempDir, Path) { let workspace_dir = mk_empty_workspace(short_name, version, "temp_workspace"); + // FIXME (#9639): This needs to handle non-utf8 paths let package_dir = workspace_dir.path().join_many([~"src", format!("{}-{}", - short_name.to_str(), + short_name.as_str().unwrap(), version.to_str())]); debug2!("Created {} and does it exist? {:?}", package_dir.display(), @@ -912,7 +913,8 @@ fn rust_path_test() { // FIXME (#9639): This needs to handle non-utf8 paths command_line_test_with_env([~"install", ~"foo"], &cwd, - Some(~[(~"RUST_PATH", dir_for_path.path().as_str().unwrap())])); + Some(~[(~"RUST_PATH", + dir_for_path.path().as_str().unwrap().to_owned())])); assert_executable_exists(dir_for_path.path(), "foo"); } @@ -1078,7 +1080,7 @@ fn do_rebuild_dep_dates_change() { command_line_test([~"build", ~"foo"], workspace); let bar_lib_name = lib_output_file_name(workspace, "bar"); let bar_date = datestamp(&bar_lib_name); - debug2!("Datestamp on {} is {:?}", bar_lib_name.to_str(), bar_date); + debug2!("Datestamp on {} is {:?}", bar_lib_name.display(), bar_date); touch_source_file(workspace, &dep_id); command_line_test([~"build", ~"foo"], workspace); let new_bar_date = datestamp(&bar_lib_name); @@ -1326,6 +1328,7 @@ fn rust_path_hack_test(hack_flag: bool) { let workspace = workspace.path(); let dest_workspace = mk_empty_workspace(&Path::new("bar"), &NoVersion, "dest_workspace"); let dest_workspace = dest_workspace.path(); + let foo_path = workspace.join_many(["src", "foo-0.1"]); let rust_path = Some(~[(~"RUST_PATH", format!("{}:{}", dest_workspace.as_str().unwrap(), @@ -1568,7 +1571,7 @@ fn dash_S() { let workspace = workspace.path(); let test_sys = test_sysroot(); // FIXME (#9639): This needs to handle non-utf8 paths - command_line_test([test_sysroot().as_str().unwrap().to_owned(), + command_line_test([test_sys.as_str().unwrap().to_owned(), ~"build", ~"-S", ~"foo"], @@ -1809,7 +1812,7 @@ fn test_recursive_deps() { let c_id = PkgId::new("c"); let b_workspace = create_local_package_with_dep(&b_id, &c_id); let b_workspace = b_workspace.path(); - writeFile(&b_workspace.join_many(["src", "c-0.1", "lib.rs"])), + writeFile(&b_workspace.join_many(["src", "c-0.1", "lib.rs"]), "pub fn g() {}"); let a_workspace = create_local_package(&a_id); let a_workspace = a_workspace.path(); @@ -1879,7 +1882,7 @@ fn test_target_specific_install_dir() { ~"install", ~"foo"], workspace); - assert!(os::path_is_dir(&workspace.join_many(["lib", host_triple()]))); + assert!(os::path_is_dir(&workspace.join_many([~"lib", host_triple()]))); assert_lib_exists(workspace, &Path::new("foo"), NoVersion); assert!(os::list_dir(&workspace.join("lib")).len() == 1); assert!(os::path_is_dir(&workspace.join("bin"))); @@ -2051,7 +2054,7 @@ fn test_installed_read_only() { let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); debug2!("repo = {}", repo.display()); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg"]); debug2!("repo_subdir = {}", repo_subdir.display()); writeFile(&repo_subdir.join("main.rs"), @@ -2077,8 +2080,8 @@ fn test_installed_read_only() { assert!(is_rwx(&built_lib)); // Make sure sources are (a) under "build" and (b) read-only - let src1 = target_build_dir(&ws).join_many_str(["src", temp_pkg_id.to_str(), "main.rs"]); - let src2 = target_build_dir(&ws).join_many_str(["src", temp_pkg_id.to_str(), "lib.rs"]); + let src1 = target_build_dir(&ws).join_many([~"src", temp_pkg_id.to_str(), ~"main.rs"]); + let src2 = target_build_dir(&ws).join_many([~"src", temp_pkg_id.to_str(), ~"lib.rs"]); assert!(os::path_exists(&src1)); assert!(os::path_exists(&src2)); assert!(is_read_only(&src1)); @@ -2091,9 +2094,9 @@ fn test_installed_local_changes() { let repo = init_git_repo(&temp_pkg_id.path); let repo = repo.path(); debug2!("repo = {}", repo.display()); - let repo_subdir = repo.join_many_str(["mockgithub.com", "catamorphism", "test-pkg"]); + let repo_subdir = repo.join_many(["mockgithub.com", "catamorphism", "test-pkg"]); debug2!("repo_subdir = {}", repo_subdir.display()); - assert!(os::mkdir_recursive(&repo.join_many_str([".rust", "src"]), U_RWX)); + assert!(os::mkdir_recursive(&repo.join_many([".rust", "src"]), U_RWX)); writeFile(&repo_subdir.join("main.rs"), "fn main() { let _x = (); }"); @@ -2109,10 +2112,10 @@ fn test_installed_local_changes() { // Now start a new workspace and clone it into it let hacking_workspace = mk_emptier_workspace("hacking_workspace"); let hacking_workspace = hacking_workspace.path(); - let target_dir = hacking_workspace.join_many_str(["src", - "mockgithub.com", - "catamorphism", - "test-pkg-0.1"]); + let target_dir = hacking_workspace.join_many(["src", + "mockgithub.com", + "catamorphism", + "test-pkg-0.1"]); debug2!("---- git clone {} {}", repo_subdir.display(), target_dir.display()); let c_res = safe_git_clone(&repo_subdir, &NoVersion, &target_dir); @@ -2159,7 +2162,7 @@ fn test_compile_error() { let foo_id = PkgId::new("foo"); let foo_workspace = create_local_package(&foo_id); let foo_workspace = foo_workspace.path(); - let main_crate = foo_workspace.join_many_str(["src", "foo-0.1", "main.rs"]); + let main_crate = foo_workspace.join_many(["src", "foo-0.1", "main.rs"]); // Write something bogus writeFile(&main_crate, "pub fn main() { if 42 != ~\"the answer\" { fail!(); } }"); let result = command_line_test_partial([~"build", ~"foo"], foo_workspace); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 23d9af77c0157..66c1aaea1ed64 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -482,9 +482,9 @@ impl<'self> Visitor<()> for ViewItemVisitor<'self> { dep.as_str().unwrap(), digest_only_date(dep)); // Also, add an additional search path - debug2!("Installed {} into {}", dep.display(), - dep.dir_path().display()); - (self.save)(dep.dir_path()); + let dep_dir = dep.dir_path(); + debug2!("Installed {} into {}", dep.display(), dep_dir.display()); + (self.save)(dep_dir); } for &(ref what, ref dep) in inputs_disc.iter() { if *what == ~"file" { diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index a24767f1e6857..13d70f153329b 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -63,7 +63,7 @@ pub fn cwd_to_workspace() -> Option<(Path, PkgId)> { let srcpath = path.join("src"); if srcpath.is_ancestor_of(&cwd) { let rel = cwd.path_relative_from(&srcpath); - let rel_s = rel.and_then_ref(|p|p.as_str()); + let rel_s = rel.as_ref().and_then(|p|p.as_str()); if rel_s.is_some() { return Some((path, PkgId::new(rel_s.unwrap()))); } diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 8701365d321e0..348bfd5c61a43 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -509,11 +509,11 @@ pub fn self_exe_path() -> Option { use os::win32::fill_utf16_buf_and_decode; do fill_utf16_buf_and_decode() |buf, sz| { libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) - }.map_move(|s| s.into_bytes()) + }.map(|s| s.into_bytes()) } } - load_self().and_then(|path| Path::new_opt(path).map(|p| { p.pop(); p })) + load_self().and_then(|path| Path::new_opt(path).map(|mut p| { p.pop(); p })) } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 5eea65a5b919c..271937dfcf2df 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -791,7 +791,7 @@ mod tests { (s: $path:expr, $op:ident, $exp:expr, opt) => ( { let path = Path::new($path); - let left = path.$op().map(|&x| str::from_utf8_slice(x)); + let left = path.$op().map(|x| str::from_utf8_slice(x)); assert_eq!(left, $exp); } ); @@ -1313,7 +1313,7 @@ mod tests { let path = Path::new($path); let other = Path::new($other); let res = path.path_relative_from(&other); - assert_eq!(res.and_then_ref(|x| x.as_str()), $exp); + assert_eq!(res.as_ref().and_then(|x| x.as_str()), $exp); } ) ) diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 9576e62f43926..0ee0d9c79d1bf 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -258,7 +258,7 @@ impl GenericPathUnsafe for Path { // 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_.map_default(path.len(), |p| p.len()); + let pathlen = path_.as_ref().map_default(path.len(), |p| p.len()); let mut s = str::with_capacity(me.repr.len() + 1 + pathlen); s.push_str(me.repr); let plen = me.prefix_len(); @@ -368,7 +368,7 @@ impl GenericPath for Path { #[inline] fn filename<'a>(&'a self) -> Option<&'a [u8]> { - self.filename_str().map_move(|x| x.as_bytes()) + self.filename_str().map(|x| x.as_bytes()) } /// See `GenericPath::filename_str` for info. @@ -388,13 +388,13 @@ impl GenericPath for Path { #[inline] fn filestem_str<'a>(&'a self) -> Option<&'a str> { // filestem() returns a byte vector that's guaranteed valid UTF-8 - self.filestem().map_move(cast::transmute) + self.filestem().map(cast::transmute) } #[inline] fn extension_str<'a>(&'a self) -> Option<&'a str> { // extension() returns a byte vector that's guaranteed valid UTF-8 - self.extension().map_move(cast::transmute) + self.extension().map(cast::transmute) } fn dir_path(&self) -> Path { @@ -728,16 +728,25 @@ impl Path { DiskPrefix => { let len = prefix_len(prefix) + is_abs as uint; let mut s = s.slice_to(len).to_owned(); - s[0] = s[0].to_ascii().to_upper().to_byte(); + unsafe { + str::raw::as_owned_vec(&mut s)[0] = + s[0].to_ascii().to_upper().to_byte(); + } if is_abs { - s[2] = sep as u8; // normalize C:/ to C:\ + // normalize C:/ to C:\ + unsafe { + str::raw::as_owned_vec(&mut s)[2] = sep as u8; + } } Some(s) } VerbatimDiskPrefix => { let len = prefix_len(prefix) + is_abs as uint; let mut s = s.slice_to(len).to_owned(); - s[4] = s[4].to_ascii().to_upper().to_byte(); + unsafe { + str::raw::as_owned_vec(&mut s)[4] = + s[4].to_ascii().to_upper().to_byte(); + } Some(s) } _ => { @@ -2204,10 +2213,10 @@ mod tests { let other = Path::new($other); let res = path.path_relative_from(&other); let exp = $exp; - assert!(res.and_then_ref(|x| x.as_str()) == exp, + assert!(res.as_ref().and_then(|x| x.as_str()) == exp, "`{}`.path_relative_from(`{}`): Expected {:?}, got {:?}", path.as_str().unwrap(), other.as_str().unwrap(), exp, - res.and_then_ref(|x| x.as_str())); + res.as_ref().and_then(|x| x.as_str())); } ) ) From d108a22fd1b38c108e2b9b6cd7276d953524ffa2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Wed, 16 Oct 2013 09:55:55 -0700 Subject: [PATCH 30/30] path2: Update for privacy changes Remove redundant `contains_nul` definition. Make `parse_prefix` private. --- src/libstd/path/posix.rs | 6 ------ src/libstd/path/windows.rs | 12 +++--------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 271937dfcf2df..87821105d37e1 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -439,12 +439,6 @@ fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> { } } -// FIXME (#8169): Pull this into parent module once visibility works -#[inline(always)] -fn contains_nul(v: &[u8]) -> bool { - v.iter().any(|&x| x == 0) -} - static dot_static: &'static [u8] = bytes!("."); static dot_dot_static: &'static [u8] = bytes!(".."); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 0ee0d9c79d1bf..0de2bd4c742b9 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -21,7 +21,7 @@ use str; use str::{CharSplitIterator, OwnedStr, Str, StrVector}; use to_bytes::IterBytes; use vec::Vector; -use super::{BytesContainer, GenericPath, GenericPathUnsafe}; +use super::{contains_nul, BytesContainer, GenericPath, GenericPathUnsafe}; #[cfg(target_os = "win32")] use libc; @@ -922,9 +922,8 @@ pub enum PathPrefix { DiskPrefix } -/// Internal function; only public for tests. Don't use. // FIXME (#8169): Make private once visibility is fixed -pub fn parse_prefix<'a>(mut path: &'a str) -> Option { +fn parse_prefix<'a>(mut path: &'a str) -> Option { if path.starts_with("\\\\") { // \\ path = path.slice_from(2); @@ -1032,12 +1031,6 @@ fn normalize_helper<'a>(s: &'a str, prefix: Option) -> (bool,Option< } } -// FIXME (#8169): Pull this into parent module once visibility works -#[inline(always)] -fn contains_nul(v: &[u8]) -> bool { - v.iter().any(|&x| x == 0) -} - fn prefix_is_verbatim(p: Option) -> bool { match p { Some(VerbatimPrefix(_)) | Some(VerbatimUNCPrefix(_,_)) | Some(VerbatimDiskPrefix) => true, @@ -1136,6 +1129,7 @@ impl Path { #[cfg(test)] mod tests { use super::*; + use super::parse_prefix; use option::{Some,None}; use iter::Iterator; use vec::Vector;