From e39ea2a5c880b252abbf37dade7a9b7ce7b2cb40 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 26 Jun 2013 17:42:24 -0700 Subject: [PATCH 1/3] rustpkg: Make rustpkg commands work without a package ID `rustpkg build`, if executed in a package source directory inside a workspace, will now build that package. By "inside a workspace" I mean that the parent directory has to be called `src`, and rustpkg will create a `build` directory in .. if there isn't already one. Same goes for `rustpkg install` and `rustpkg clean`. For the time being, `rustpkg build` (etc.) will still error out if you run it inside a directory whose parent isn't called `src`. I'm not sure whether or not it's desirable to have it do something in a non-workspace directory. --- src/librustpkg/installed_packages.rs | 11 +++- src/librustpkg/path_util.rs | 10 ++- src/librustpkg/rustpkg.rs | 90 +++++++++++++++----------- src/librustpkg/tests.rs | 96 +++++++++++++++++++++------- src/librustpkg/usage.rs | 7 +- src/librustpkg/workspace.rs | 24 ++++++- src/libstd/path.rs | 42 +++++++++++- 7 files changed, 210 insertions(+), 70 deletions(-) diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs index 6b28b7ed6a112..5f1e0594c47cd 100644 --- a/src/librustpkg/installed_packages.rs +++ b/src/librustpkg/installed_packages.rs @@ -18,11 +18,18 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { for workspaces.iter().advance |p| { let binfiles = os::list_dir(&p.push("bin")); for binfiles.iter().advance() |exec| { - f(&PkgId::new(*exec, p)); + let exec_path = Path(*exec).filestem(); + do exec_path.iter().advance |s| { + f(&PkgId::new(*s, p)) + }; } let libfiles = os::list_dir(&p.push("lib")); for libfiles.iter().advance() |lib| { - f(&PkgId::new(*lib, p)); + debug!("Full name: %s", *lib); + let lib_path = Path(*lib).filestem(); + do lib_path.iter().advance |s| { + f(&PkgId::new(*s, p)) + }; } } true diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index a2cae3ab20eaf..ed5b1118a9c7c 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -54,11 +54,17 @@ pub fn rust_path() -> ~[Path] { }; let cwd = os::getcwd(); // now add in default entries - env_rust_path.push(cwd.push(".rust")); env_rust_path.push(cwd.clone()); do cwd.each_parent() |p| { push_if_exists(&mut env_rust_path, p) }; let h = os::homedir(); - for h.iter().advance |h| { push_if_exists(&mut env_rust_path, h); } + // Avoid adding duplicates + // could still add dups if someone puts one of these in the RUST_PATH + // manually, though... + for h.iter().advance |hdir| { + if !(cwd.is_ancestor_of(hdir) || hdir.is_ancestor_of(&cwd)) { + push_if_exists(&mut env_rust_path, hdir); + } + } env_rust_path } diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index f2845c00ea6f6..f2e39a8e5ed70 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -42,7 +42,7 @@ use path_util::{U_RWX, rust_path, in_rust_path}; use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; use source_control::is_git_dir; -use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces}; +use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, in_workspace, cwd_to_workspace}; use context::Ctx; use package_id::PkgId; use package_source::PkgSrc; @@ -199,26 +199,41 @@ impl CtxMethods for Ctx { match cmd { "build" => { if args.len() < 1 { - return usage::build(); + if !in_workspace(|| { usage::build() } ) { + return; + } + let (workspace, pkgid) = cwd_to_workspace(); + self.build(&workspace, &pkgid); } - // The package id is presumed to be the first command-line - // argument - let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); - for each_pkg_parent_workspace(&pkgid) |workspace| { - debug!("found pkg %s in workspace %s, trying to build", - pkgid.to_str(), workspace.to_str()); - self.build(workspace, &pkgid); + else { + // The package id is presumed to be the first command-line + // argument + let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); + for each_pkg_parent_workspace(&pkgid) |workspace| { + debug!("found pkg %s in workspace %s, trying to build", + pkgid.to_str(), workspace.to_str()); + self.build(workspace, &pkgid); + } } } "clean" => { if args.len() < 1 { - return usage::build(); + if !in_workspace(|| { usage::clean() } ) { + return; + } + // tjc: Maybe clean should clean all the packages in the + // current workspace, though? + let (workspace, pkgid) = cwd_to_workspace(); + self.clean(&workspace, &pkgid); + + } + else { + // The package id is presumed to be the first command-line + // argument + let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); + let cwd = os::getcwd(); + self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd } - // The package id is presumed to be the first command-line - // argument - let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); - let cwd = os::getcwd(); - self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd } "do" => { if args.len() < 2 { @@ -232,37 +247,36 @@ impl CtxMethods for Ctx { } "install" => { if args.len() < 1 { - return usage::install(); - } - - // The package id is presumed to be the first command-line - // argument - let pkgid = PkgId::new(args[0], &os::getcwd()); - let workspaces = pkg_parent_workspaces(&pkgid); - if workspaces.is_empty() { - debug!("install! workspaces was empty"); - let rp = rust_path(); - assert!(!rp.is_empty()); - let src = PkgSrc::new(&rp[0], &build_pkg_id_in_workspace(&pkgid, &rp[0]), - &pkgid); - src.fetch_git(); - self.install(&rp[0], &pkgid); + if !in_workspace(|| { usage::install() }) { + return; + } + let (workspace, pkgid) = cwd_to_workspace(); + self.install(&workspace, &pkgid); } else { - for each_pkg_parent_workspace(&pkgid) |workspace| { - debug!("install: found pkg %s in workspace %s, trying to build", - pkgid.to_str(), workspace.to_str()); - - self.install(workspace, &pkgid); + // The package id is presumed to be the first command-line + // argument + let pkgid = PkgId::new(args[0], &os::getcwd()); + let workspaces = pkg_parent_workspaces(&pkgid); + if workspaces.is_empty() { + let rp = rust_path(); + assert!(!rp.is_empty()); + let src = PkgSrc::new(&rp[0], &build_pkg_id_in_workspace(&pkgid, &rp[0]), + &pkgid); + src.fetch_git(); + self.install(&rp[0], &pkgid); + } + else { + for each_pkg_parent_workspace(&pkgid) |workspace| { + self.install(workspace, &pkgid); + } } } } "list" => { io::println("Installed packages:"); for installed_packages::list_installed_packages |pkg_id| { - io::println(fmt!("%s-%s", - pkg_id.local_path.to_str(), - pkg_id.version.to_str())); + io::println(pkg_id.local_path.to_str()); } } "prefer" => { diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index f6044cbf01fc3..7953d4545a845 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -218,7 +218,6 @@ fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput { fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>) -> ProcessOutput { let cmd = test_sysroot().push("bin").push("rustpkg").to_str(); - let cwd = normalize(RemotePath((*cwd).clone())); debug!("About to run command: %? %? in %s", cmd, args, cwd.to_str()); assert!(os::path_is_dir(&*cwd)); let cwd = (*cwd).clone(); @@ -322,6 +321,14 @@ fn assert_executable_exists(repo: &Path, short_name: &str) { assert!(is_rwx(&exec)); } +fn assert_built_executable_exists(repo: &Path, short_name: &str) { + debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name); + let exec = built_executable_in_workspace(&PkgId::new(short_name, repo), + repo).expect("assert_built_executable_exists failed"); + assert!(os::path_exists(&exec)); + assert!(is_rwx(&exec)); +} + fn command_line_test_output(args: &[~str]) -> ~[~str] { let mut result = ~[]; let p_output = command_line_test(args, &os::getcwd()); @@ -490,30 +497,29 @@ fn test_install_git() { // should have test, bench, lib, and main command_line_test([~"install", temp_pkg_id.local_path.to_str()], &repo); // Check that all files exist - let ws = repo.push(".rust"); - debug!("Checking for files in %s", ws.to_str()); - let exec = target_executable_in_workspace(&temp_pkg_id, &ws); + debug!("Checking for files in %s", repo.to_str()); + let exec = target_executable_in_workspace(&temp_pkg_id, &repo); debug!("exec = %s", exec.to_str()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); let _built_lib = built_library_in_workspace(&temp_pkg_id, - &ws).expect("test_install_git: built lib should exist"); - let lib = target_library_in_workspace(&temp_pkg_id, &ws); + &repo).expect("test_install_git: built lib should exist"); + let lib = target_library_in_workspace(&temp_pkg_id, &repo); debug!("lib = %s", lib.to_str()); assert!(os::path_exists(&lib)); assert!(is_rwx(&lib)); let built_test = built_test_in_workspace(&temp_pkg_id, - &ws).expect("test_install_git: built test should exist"); + &repo).expect("test_install_git: built test should exist"); assert!(os::path_exists(&built_test)); let built_bench = built_bench_in_workspace(&temp_pkg_id, - &ws).expect("test_install_git: built bench should exist"); + &repo).expect("test_install_git: built bench should exist"); assert!(os::path_exists(&built_bench)); // And that the test and bench executables aren't installed - let test = target_test_in_workspace(&temp_pkg_id, &ws); + let test = target_test_in_workspace(&temp_pkg_id, &repo); assert!(!os::path_exists(&test)); debug!("test = %s", test.to_str()); - let bench = target_bench_in_workspace(&temp_pkg_id, &ws); + let bench = target_bench_in_workspace(&temp_pkg_id, &repo); debug!("bench = %s", bench.to_str()); assert!(!os::path_exists(&bench)); } @@ -586,12 +592,12 @@ fn test_package_version() { command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg_version"], &repo); assert!(match built_library_in_workspace(&temp_pkg_id, - &repo.push(".rust")) { + &repo) { Some(p) => p.to_str().ends_with(fmt!("0.4%s", os::consts::DLL_SUFFIX)), None => false }); - assert!(built_executable_in_workspace(&temp_pkg_id, &repo.push(".rust")) - == Some(repo.push(".rust").push("build"). + assert!(built_executable_in_workspace(&temp_pkg_id, &repo) + == Some(repo.push("build"). push("mockgithub.com"). push("catamorphism"). push("test_pkg_version"). @@ -689,7 +695,7 @@ fn rustpkg_library_target() { add_git_tag(&package_dir, ~"1.0"); command_line_test([~"install", ~"foo"], &foo_repo); - assert_lib_exists(&foo_repo.push(".rust"), "foo", ExactRevision(~"1.0")); + assert_lib_exists(&foo_repo, "foo", ExactRevision(~"1.0")); } #[test] @@ -716,14 +722,55 @@ fn package_script_with_default_build() { assert!(os::path_exists(&dir.push("build").push("fancy_lib").push("generated.rs"))); } +#[test] +fn rustpkg_build_no_arg() { + let tmp = mkdtemp(&os::tmpdir(), "rustpkg_build_no_arg").expect("rustpkg_build_no_arg failed"); + let package_dir = tmp.push("src").push("foo"); + assert!(os::mkdir_recursive(&package_dir, U_RWX)); + + writeFile(&package_dir.push("main.rs"), + "fn main() { let _x = (); }"); + debug!("build_no_arg: dir = %s", package_dir.to_str()); + command_line_test([~"build"], &package_dir); + assert_built_executable_exists(&tmp, "foo"); +} + +#[test] +fn rustpkg_install_no_arg() { + let tmp = mkdtemp(&os::tmpdir(), + "rustpkg_install_no_arg").expect("rustpkg_build_no_arg failed"); + let package_dir = tmp.push("src").push("foo"); + assert!(os::mkdir_recursive(&package_dir, U_RWX)); + writeFile(&package_dir.push("lib.rs"), + "fn main() { let _x = (); }"); + debug!("install_no_arg: dir = %s", package_dir.to_str()); + command_line_test([~"install"], &package_dir); + assert_lib_exists(&tmp, "foo", NoVersion); +} + +#[test] +fn rustpkg_clean_no_arg() { + let tmp = mkdtemp(&os::tmpdir(), "rustpkg_clean_no_arg").expect("rustpkg_clean_no_arg failed"); + let package_dir = tmp.push("src").push("foo"); + assert!(os::mkdir_recursive(&package_dir, U_RWX)); + + writeFile(&package_dir.push("main.rs"), + "fn main() { let _x = (); }"); + debug!("clean_no_arg: dir = %s", package_dir.to_str()); + command_line_test([~"build"], &package_dir); + assert_built_executable_exists(&tmp, "foo"); + command_line_test([~"clean"], &package_dir); + assert!(!built_executable_in_workspace(&PkgId::new("foo", &package_dir), + &tmp).map_default(false, |m| { os::path_exists(m) })); +} + #[test] #[ignore (reason = "Un-ignore when #7071 is fixed")] fn rust_path_test() { let dir_for_path = mkdtemp(&os::tmpdir(), "more_rust").expect("rust_path_test failed"); let dir = mk_workspace(&dir_for_path, &normalize(RemotePath(Path("foo"))), &NoVersion); debug!("dir = %s", dir.to_str()); - writeFile(&Path("/Users/tjc/more_rust/src/foo-0.1/main.rs"), - "fn main() { let _x = (); }"); + writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }"); let cwd = os::getcwd(); debug!("cwd = %s", cwd.to_str()); @@ -781,21 +828,24 @@ fn test_list() { let quux = PkgId::new("quux", &dir); create_local_package_in(&quux, &dir); +// NOTE Not really great output, though... +// NOTE do any tests need to be unignored? command_line_test([~"install", ~"foo"], &dir); let env_arg = ~[(~"RUST_PATH", dir.to_str())]; + debug!("RUST_PATH = %s", dir.to_str()); let list_output = command_line_test_output_with_env([~"list"], env_arg.clone()); - assert!(list_output.iter().any(|x| x.starts_with("foo-"))); + assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); command_line_test([~"install", ~"bar"], &dir); let list_output = command_line_test_output_with_env([~"list"], env_arg.clone()); - assert!(list_output.iter().any(|x| x.starts_with("foo-"))); - assert!(list_output.iter().any(|x| x.starts_with("bar-"))); + assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); + assert!(list_output.iter().any(|x| x.starts_with("libbar_"))); command_line_test([~"install", ~"quux"], &dir); let list_output = command_line_test_output_with_env([~"list"], env_arg); - assert!(list_output.iter().any(|x| x.starts_with("foo-"))); - assert!(list_output.iter().any(|x| x.starts_with("bar-"))); - assert!(list_output.iter().any(|x| x.starts_with("quux-"))); + assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); + assert!(list_output.iter().any(|x| x.starts_with("libbar_"))); + assert!(list_output.iter().any(|x| x.starts_with("libquux_"))); } #[test] @@ -836,7 +886,7 @@ fn install_check_duplicates() { let mut contents = ~[]; let check_dups = |p: &PkgId| { if contents.contains(p) { - fail!("package database contains duplicate ID"); + fail!("package %s appears in `list` output more than once", p.local_path.to_str()); } else { contents.push((*p).clone()); diff --git a/src/librustpkg/usage.rs b/src/librustpkg/usage.rs index 59e9e57d643f2..87ab3882df196 100644 --- a/src/librustpkg/usage.rs +++ b/src/librustpkg/usage.rs @@ -23,10 +23,11 @@ Options: } pub fn build() { - io::println("rustpkg [options..] build + io::println("rustpkg [options..] build [package-ID] -Build all targets described in the package script in the current -directory. +Build the given package ID if specified. With no package ID argument, +build the package in the current directory. In that case, the current +directory must be a direct child of an `src` directory in a workspace. Options: -c, --cfg Pass a cfg flag to the package script"); diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index 5876dbdc9dea1..952352a02a5fc 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -10,9 +10,10 @@ // rustpkg utilities having to do with workspaces +use std::os; +use std::path::Path; use path_util::{rust_path, workspace_contains_package_id}; use package_id::PkgId; -use std::path::Path; pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool { // Using the RUST_PATH, find workspaces that contain @@ -38,3 +39,24 @@ pub fn pkg_parent_workspaces(pkgid: &PkgId) -> ~[Path] { .filter(|ws| workspace_contains_package_id(pkgid, ws)) .collect() } + +pub fn in_workspace(complain: &fn()) -> bool { + let dir_part = os::getcwd().pop().components.clone(); + if *(dir_part.last()) != ~"src" { + complain(); + false + } + else { + true + } +} + +/// Construct a workspace and package-ID name based on the current directory. +/// This gets used when rustpkg gets invoked without a package-ID argument. +pub fn cwd_to_workspace() -> (Path, PkgId) { + let cwd = os::getcwd(); + let ws = cwd.pop().pop(); + let cwd_ = cwd.clone(); + let pkgid = cwd_.components.last().to_str(); + (ws, PkgId::new(pkgid, &cwd)) +} diff --git a/src/libstd/path.rs b/src/libstd/path.rs index fe298931d4236..3ec3407b1cc55 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -122,6 +122,9 @@ pub trait GenericPath { /// Returns `true` if `self` is an absolute path. fn is_absolute(&self) -> bool; + + /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples + fn is_ancestor_of(&self, (&Self)) -> bool; } #[cfg(target_os = "linux")] @@ -698,6 +701,15 @@ impl GenericPath for PosixPath { fn is_absolute(&self) -> bool { self.is_absolute } + + fn is_ancestor_of(&self, other: &PosixPath) -> bool { + debug!("%s / %s %? %?", self.to_str(), other.to_str(), self.is_absolute, + self.components.len()); + self == other || + (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) && + self.is_ancestor_of(&other.pop())) + } + } @@ -974,8 +986,13 @@ impl GenericPath for WindowsPath { fn is_absolute(&self) -> bool { self.is_absolute } -} + fn is_ancestor_of(&self, other: &WindowsPath) -> bool { + self == other || + (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) && + self.is_ancestor_of(&other.pop())) + } +} pub fn normalize(components: &[~str]) -> ~[~str] { let mut cs = ~[]; @@ -1290,4 +1307,27 @@ mod tests { assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true); assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true); } + + #[test] + fn test_is_ancestor_of() { + assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); + assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b"))); + assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d"))); + assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); + assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c"))); + assert!(!&PosixPath("/").is_ancestor_of(&PosixPath(""))); + assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath(""))); + assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c"))); + + assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); + assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b"))); + assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d"))); + assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); + assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); + assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath(""))); + assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath(""))); + assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); + + } + } From 49b72bdd77916e27aaf95909516702c1450f11ac Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 17 Jul 2013 10:51:54 -0700 Subject: [PATCH 2/3] std::rt: Use a constant 4 threads for multithreaded sched tests. #7772 Too much overcommit here exhausts the low fd limit on OS X. --- src/libstd/rt/test.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs index f4b9269e8cd48..70620cf59da24 100644 --- a/src/libstd/rt/test.rs +++ b/src/libstd/rt/test.rs @@ -64,7 +64,6 @@ pub fn run_in_mt_newsched_task(f: ~fn()) { use os; use from_str::FromStr; use rt::sched::Shutdown; - use rt::util; let f_cell = Cell::new(f); @@ -72,10 +71,10 @@ pub fn run_in_mt_newsched_task(f: ~fn()) { let nthreads = match os::getenv("RUST_TEST_THREADS") { Some(nstr) => FromStr::from_str(nstr).get(), None => { - // Using more threads than cores in test code - // to force the OS to preempt them frequently. - // Assuming that this help stress test concurrent types. - util::num_cpus() * 2 + // A reasonable number of threads for testing + // multithreading. NB: It's easy to exhaust OS X's + // low maximum fd limit by setting this too high (#7772) + 4 } }; From 3509f9d5ae927781f190dda5e623d30ce34e87e0 Mon Sep 17 00:00:00 2001 From: blake2-ppc Date: Sat, 20 Jul 2013 19:28:38 +0200 Subject: [PATCH 3/3] str: Implement Container for ~str, @str and Mutable for ~str ~str and @str need separate implementations for use in generic functions, where it will not automatically use the impl on &str. --- src/libstd/str.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 0811dab407ef5..597bc72c960ac 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -22,7 +22,7 @@ use cast; use char; use char::Char; use clone::Clone; -use container::Container; +use container::{Container, Mutable}; use iter::Times; use iterator::{Iterator, IteratorUtil, FilterIterator, AdditiveIterator, MapIterator}; use libc; @@ -1211,6 +1211,31 @@ impl<'self> Container for &'self str { } } +impl Container for ~str { + #[inline] + fn len(&self) -> uint { self.as_slice().len() } + #[inline] + fn is_empty(&self) -> bool { self.len() == 0 } +} + +impl Container for @str { + #[inline] + fn len(&self) -> uint { self.as_slice().len() } + #[inline] + fn is_empty(&self) -> bool { self.len() == 0 } +} + +impl Mutable for ~str { + /// Remove all content, make the string empty + #[inline] + fn clear(&mut self) { + unsafe { + raw::set_len(self, 0) + } + } +} + + #[allow(missing_doc)] pub trait StrSlice<'self> { fn contains<'a>(&self, needle: &'a str) -> bool; @@ -2495,6 +2520,18 @@ mod tests { assert_eq!(~"华ประเทศไทย中", data); } + #[test] + fn test_clear() { + let mut empty = ~""; + empty.clear(); + assert_eq!("", empty.as_slice()); + let mut data = ~"ประเทศไทย中"; + data.clear(); + assert_eq!("", data.as_slice()); + data.push_char('华'); + assert_eq!("华", data.as_slice()); + } + #[test] fn test_split_within() { fn t(s: &str, i: uint, u: &[~str]) { @@ -3487,4 +3524,17 @@ mod tests { t::<@str>(); t::<~str>(); } + + #[test] + fn test_str_container() { + fn sum_len(v: &[S]) -> uint { + v.iter().transform(|x| x.len()).sum() + } + + let s = ~"01234"; + assert_eq!(5, sum_len(["012", "", "34"])); + assert_eq!(5, sum_len([@"01", @"2", @"34", @""])); + assert_eq!(5, sum_len([~"01", ~"2", ~"34", ~""])); + assert_eq!(5, sum_len([s.as_slice()])); + } }