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.
This commit is contained in:
Kevin Ballard 2013-09-26 17:21:59 -07:00
parent 6741241f40
commit 73d3d00ec4
63 changed files with 2501 additions and 2989 deletions

View file

@ -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;
{

View file

@ -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<Path> {
#[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];
let mut path: ~[u8] = vec::with_capacity(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
};
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<Path> {
* Otherwise, homedir returns option::none.
*/
pub fn homedir() -> Option<Path> {
// 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<Path> {
fn secondary() -> Option<Path> {
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);

File diff suppressed because it is too large Load diff

View file

@ -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<T>(&self, f: &fn(&str) -> T) -> T {
fn with_display_str<T>(&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';
from_utf8_with_replacement(self.as_vec())
}
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);
/// 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<T>(&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> {
// 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<Self>;
/// 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<V: Vector<u8>>(&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<S: 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<V: Vector<u8>>(&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<S: 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);
}
}

View file

@ -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<Path> {
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));

View file

@ -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<Map<'self, &'self str, Option<&'self str>,
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<T>(&self, f: &fn(&str) -> T) -> T {
fn with_display_str<T>(&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<Path> {
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<Path> {
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));

View file

@ -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};

View file

@ -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);
}

View file

@ -22,7 +22,7 @@ impl<'self> PathLike for &'self str {
impl PathLike for Path {
fn path_as_str<T>(&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));
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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));
}
}

View file

@ -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();

View file

@ -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;

View file

@ -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)
}
}