mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Wildcard support adventure starting with rm command.
This commit is contained in:
parent
1cf8413c49
commit
375f311331
5 changed files with 232 additions and 85 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1821,6 +1821,7 @@ dependencies = [
|
|||
"futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"git2 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heim 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"image 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -56,6 +56,7 @@ adhoc_derive = "0.1.2"
|
|||
lazy_static = "1.3.0"
|
||||
git2 = "0.9.2"
|
||||
dirs = "2.0.2"
|
||||
glob = "0.3.0"
|
||||
ctrlc = "3.1.3"
|
||||
ptree = "0.2"
|
||||
clipboard = "0.5"
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::errors::ShellError;
|
|||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::registry::{CommandConfig, NamedType, PositionalType};
|
||||
use crate::prelude::*;
|
||||
|
||||
use glob::glob;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub struct Remove;
|
||||
|
@ -43,7 +45,10 @@ pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
file => full_path.push(file),
|
||||
}
|
||||
|
||||
if full_path.is_dir() {
|
||||
for entry in glob(&full_path.to_string_lossy()).expect("Failed to read glob pattern") {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
if path.is_dir() {
|
||||
if !args.has("recursive") {
|
||||
return Err(ShellError::labeled_error(
|
||||
"is a directory",
|
||||
|
@ -51,9 +56,13 @@ pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
|||
args.call_info.name_span.unwrap(),
|
||||
));
|
||||
}
|
||||
std::fs::remove_dir_all(&full_path).expect("can not remove directory");
|
||||
} else if full_path.is_file() {
|
||||
std::fs::remove_file(&full_path).expect("can not remove file");
|
||||
std::fs::remove_dir_all(&path).expect("can not remove directory");
|
||||
} else if path.is_file() {
|
||||
std::fs::remove_file(&path).expect("can not remove file");
|
||||
}
|
||||
}
|
||||
Err(e) => return Err(ShellError::string(&format!("{:?}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::empty())
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
mod helpers;
|
||||
|
||||
use h::in_directory as cwd;
|
||||
use h::{in_directory as cwd, Playground, Stub::*};
|
||||
use helpers as h;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn lines() {
|
||||
|
@ -83,17 +84,28 @@ fn open_error_if_file_not_found() {
|
|||
|
||||
#[test]
|
||||
fn save_figures_out_intelligently_where_to_write_out_with_metadata() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("save_smart_test");
|
||||
let sandbox = Playground::setup_for("save_smart_test")
|
||||
.with_files(vec![FileWithContent(
|
||||
"cargo_sample.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "nu"
|
||||
version = "0.1.1"
|
||||
authors = ["Yehuda Katz <wycats@gmail.com>"]
|
||||
description = "A shell for the GitHub era"
|
||||
license = "ISC"
|
||||
edition = "2018"
|
||||
"#,
|
||||
)])
|
||||
.test_dir_name();
|
||||
|
||||
let full_path = format!("{}/{}", playground_path, tests_dir);
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
let subject_file = format!("{}/{}", full_path, "cargo_sample.toml");
|
||||
|
||||
h::copy_file_to("tests/fixtures/formats/cargo_sample.toml", &subject_file);
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd("tests/fixtures"),
|
||||
"open nuplayground/save_smart_test/cargo_sample.toml | inc package.version --minor | save"
|
||||
cwd(&Playground::root()),
|
||||
"open save_smart_test/cargo_sample.toml | inc package.version --minor | save"
|
||||
);
|
||||
|
||||
let actual = h::file_contents(&subject_file);
|
||||
|
@ -102,14 +114,14 @@ fn save_figures_out_intelligently_where_to_write_out_with_metadata() {
|
|||
|
||||
#[test]
|
||||
fn save_can_write_out_csv() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("save_test");
|
||||
let sandbox = Playground::setup_for("save_test").test_dir_name();
|
||||
|
||||
let full_path = format!("{}/{}", playground_path, tests_dir);
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
let expected_file = format!("{}/{}", full_path, "cargo_sample.csv");
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd(&playground_path),
|
||||
cwd(&Playground::root()),
|
||||
"open ../formats/cargo_sample.toml | inc package.version --minor | get package | save save_test/cargo_sample.csv"
|
||||
);
|
||||
|
||||
|
@ -119,14 +131,14 @@ fn save_can_write_out_csv() {
|
|||
|
||||
#[test]
|
||||
fn cp_can_copy_a_file() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("cp_test");
|
||||
let sandbox = Playground::setup_for("cp_test").test_dir_name();
|
||||
|
||||
let full_path = format!("{}/{}", playground_path, tests_dir);
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
let expected_file = format!("{}/{}", full_path, "sample.ini");
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd(&playground_path),
|
||||
cwd(&Playground::root()),
|
||||
"cp ../formats/sample.ini cp_test/sample.ini"
|
||||
);
|
||||
|
||||
|
@ -135,14 +147,14 @@ fn cp_can_copy_a_file() {
|
|||
|
||||
#[test]
|
||||
fn cp_copies_the_file_inside_directory_if_path_to_copy_is_directory() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("cp_test_2");
|
||||
let sandbox = Playground::setup_for("cp_test_2").test_dir_name();
|
||||
|
||||
let full_path = format!("{}/{}", playground_path, tests_dir);
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
let expected_file = format!("{}/{}", full_path, "sample.ini");
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd(&playground_path),
|
||||
cwd(&Playground::root()),
|
||||
"cp ../formats/sample.ini cp_test_2"
|
||||
);
|
||||
|
||||
|
@ -151,86 +163,149 @@ fn cp_copies_the_file_inside_directory_if_path_to_copy_is_directory() {
|
|||
|
||||
#[test]
|
||||
fn cp_error_if_attempting_to_copy_a_directory_to_another_directory() {
|
||||
let (playground_path, _) = h::setup_playground_for("cp_test_3");
|
||||
Playground::setup_for("cp_test_3");
|
||||
|
||||
nu_error!(
|
||||
output,
|
||||
cwd(&playground_path),
|
||||
"cp ../formats cp_test_3"
|
||||
);
|
||||
nu_error!(output, cwd(&Playground::root()), "cp ../formats cp_test_3");
|
||||
|
||||
assert!(output.contains("../formats"));
|
||||
assert!(output.contains("is a directory (not copied)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_can_remove_a_file() {
|
||||
let directory = "tests/fixtures/nuplayground";
|
||||
let file = format!("{}/rm_test.txt", directory);
|
||||
|
||||
h::create_file_at(&file);
|
||||
fn rm_removes_a_file() {
|
||||
let sandbox = Playground::setup_for("rm_test")
|
||||
.with_files(vec![EmptyFile("i_will_be_deleted.txt")])
|
||||
.test_dir_name();
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd(directory),
|
||||
"rm rm_test.txt"
|
||||
cwd(&Playground::root()),
|
||||
"rm rm_test/i_will_be_deleted.txt"
|
||||
);
|
||||
|
||||
assert!(!h::file_exists_at(&file));
|
||||
assert!(!h::file_exists_at(&format!(
|
||||
"{}/{}/{}",
|
||||
Playground::root(),
|
||||
sandbox,
|
||||
"i_will_be_deleted.txt"
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_can_remove_directory_contents_with_recursive_flag() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("rm_test");
|
||||
fn rm_removes_files_with_wildcard() {
|
||||
r#"
|
||||
Given these files and directories
|
||||
src
|
||||
src/cli.rs
|
||||
src/lib.rs
|
||||
src/prelude.rs
|
||||
src/parser
|
||||
src/parser/parse.rs
|
||||
src/parser/parser.rs
|
||||
src/parser/parse
|
||||
src/parser/hir
|
||||
src/parser/parse/token_tree.rs
|
||||
src/parser/hir/baseline_parse.rs
|
||||
src/parser/hir/baseline_parse_tokens.rs
|
||||
"#;
|
||||
|
||||
for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() {
|
||||
h::create_file_at(&format!("{}/{}/{}", playground_path, tests_dir, f));
|
||||
let sandbox = Playground::setup_for("rm_test_wildcard")
|
||||
.within("src")
|
||||
.with_files(vec![
|
||||
EmptyFile("cli.rs"),
|
||||
EmptyFile("lib.rs"),
|
||||
EmptyFile("prelude.rs"),
|
||||
])
|
||||
.within("src/parser")
|
||||
.with_files(vec![EmptyFile("parse.rs"), EmptyFile("parser.rs")])
|
||||
.within("src/parser/parse")
|
||||
.with_files(vec![EmptyFile("token_tree.rs")])
|
||||
.within("src/parser/hir")
|
||||
.with_files(vec![
|
||||
EmptyFile("baseline_parse.rs"),
|
||||
EmptyFile("baseline_parse_tokens.rs"),
|
||||
])
|
||||
.test_dir_name();
|
||||
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
|
||||
r#" The pattern
|
||||
src/*/*/*.rs
|
||||
matches
|
||||
src/parser/parse/token_tree.rs
|
||||
src/parser/hir/baseline_parse.rs
|
||||
src/parser/hir/baseline_parse_tokens.rs
|
||||
"#;
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd("tests/fixtures/nuplayground/rm_test_wildcard"),
|
||||
"rm \"src/*/*/*.rs\""
|
||||
);
|
||||
|
||||
assert!(!h::file_exists_at(&format!(
|
||||
"{}/src/parser/parse/token_tree.rs",
|
||||
full_path
|
||||
)));
|
||||
assert!(!h::file_exists_at(&format!(
|
||||
"{}/src/parser/hir/baseline_parse.rs",
|
||||
full_path
|
||||
)));
|
||||
assert!(!h::file_exists_at(&format!(
|
||||
"{}/src/parser/hir/baseline_parse_tokens.rs",
|
||||
full_path
|
||||
)));
|
||||
|
||||
assert_eq!(
|
||||
Playground::glob_vec(&format!("{}/src/*/*/*.rs", full_path)),
|
||||
Vec::<PathBuf>::new()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_removes_directory_contents_with_recursive_flag() {
|
||||
let sandbox = Playground::setup_for("rm_test_recursive")
|
||||
.with_files(vec![
|
||||
EmptyFile("yehuda.txt"),
|
||||
EmptyFile("jonathan.txt"),
|
||||
EmptyFile("andres.txt"),
|
||||
])
|
||||
.test_dir_name();
|
||||
|
||||
nu!(
|
||||
_output,
|
||||
cwd("tests/fixtures/nuplayground"),
|
||||
"rm rm_test --recursive"
|
||||
"rm rm_test_recursive --recursive"
|
||||
);
|
||||
|
||||
assert!(!h::file_exists_at(&format!("{}/{}", playground_path, tests_dir)));
|
||||
assert!(!h::file_exists_at(&format!(
|
||||
"{}/{}",
|
||||
Playground::root(),
|
||||
sandbox
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() {
|
||||
let (playground_path, tests_dir) = h::setup_playground_for("rm_test_2");
|
||||
let full_path = format!("{}/{}", playground_path, tests_dir);
|
||||
fn rm_errors_if_attempting_to_delete_a_directory_without_recursive_flag() {
|
||||
let sandbox = Playground::setup_for("rm_test_2").test_dir_name();
|
||||
let full_path = format!("{}/{}", Playground::root(), sandbox);
|
||||
|
||||
nu_error!(
|
||||
output,
|
||||
cwd("tests/fixtures/nuplayground"),
|
||||
"rm rm_test_2"
|
||||
);
|
||||
nu_error!(output, cwd(&Playground::root()), "rm rm_test_2");
|
||||
|
||||
assert!(h::file_exists_at(&full_path));
|
||||
assert!(output.contains("is a directory"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_error_if_attempting_to_delete_single_dot_as_argument() {
|
||||
|
||||
nu_error!(
|
||||
output,
|
||||
cwd("tests/fixtures/nuplayground"),
|
||||
"rm ."
|
||||
);
|
||||
fn rm_errors_if_attempting_to_delete_single_dot_as_argument() {
|
||||
nu_error!(output, cwd(&Playground::root()), "rm .");
|
||||
|
||||
assert!(output.contains("may not be removed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rm_error_if_attempting_to_delete_two_dot_as_argument() {
|
||||
|
||||
nu_error!(
|
||||
output,
|
||||
cwd("tests/fixtures/nuplayground"),
|
||||
"rm .."
|
||||
);
|
||||
fn rm_errors_if_attempting_to_delete_two_dot_as_argument() {
|
||||
nu_error!(output, cwd(&Playground::root()), "rm ..");
|
||||
|
||||
assert!(output.contains("may not be removed"));
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use glob::glob;
|
||||
pub use std::path::Path;
|
||||
pub use std::path::PathBuf;
|
||||
|
||||
use std::io::Read;
|
||||
|
@ -79,17 +81,80 @@ macro_rules! nu_error {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn setup_playground_for(topic: &str) -> (String, String) {
|
||||
let home = "tests/fixtures/nuplayground";
|
||||
let full_path = format!("{}/{}", home, topic);
|
||||
|
||||
if file_exists_at(&full_path) {
|
||||
delete_directory_at(&full_path);
|
||||
pub enum Stub<'a> {
|
||||
FileWithContent(&'a str, &'a str),
|
||||
EmptyFile(&'a str),
|
||||
}
|
||||
|
||||
create_directory_at(&full_path);
|
||||
pub struct Playground {
|
||||
tests: String,
|
||||
cwd: PathBuf,
|
||||
}
|
||||
|
||||
(home.to_string(), topic.to_string())
|
||||
impl Playground {
|
||||
pub fn root() -> String {
|
||||
String::from("tests/fixtures/nuplayground")
|
||||
}
|
||||
|
||||
pub fn test_dir_name(&self) -> String {
|
||||
self.tests.clone()
|
||||
}
|
||||
|
||||
pub fn back_to_playground(&mut self) -> &mut Self {
|
||||
self.cwd = PathBuf::from([Playground::root(), self.tests.clone()].join("/"));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn setup_for(topic: &str) -> Playground {
|
||||
let nuplay_dir = format!("{}/{}", Playground::root(), topic);
|
||||
|
||||
if PathBuf::from(&nuplay_dir).exists() {
|
||||
std::fs::remove_dir_all(PathBuf::from(&nuplay_dir)).expect("can not remove directory");
|
||||
}
|
||||
|
||||
std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory");
|
||||
|
||||
Playground {
|
||||
tests: topic.to_string(),
|
||||
cwd: PathBuf::from([Playground::root(), topic.to_string()].join("/")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cd(&mut self, path: &str) -> &mut Self {
|
||||
self.cwd.push(path);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_files(&mut self, files: Vec<Stub>) -> &mut Self {
|
||||
files
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let mut path = PathBuf::from(&self.cwd);
|
||||
|
||||
let (file_name, contents) = match *f {
|
||||
Stub::EmptyFile(name) => (name, "fake data"),
|
||||
Stub::FileWithContent(name, content) => (name, content),
|
||||
};
|
||||
|
||||
path.push(file_name);
|
||||
|
||||
std::fs::write(PathBuf::from(path), contents.as_bytes())
|
||||
.expect("can not create file");
|
||||
})
|
||||
.for_each(drop);
|
||||
self.back_to_playground();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn within(&mut self, directory: &str) -> &mut Self {
|
||||
self.cwd.push(directory);
|
||||
std::fs::create_dir(&self.cwd).expect("can not create directory");
|
||||
self
|
||||
}
|
||||
|
||||
pub fn glob_vec(pattern: &str) -> Vec<PathBuf> {
|
||||
glob(pattern).unwrap().map(|r| r.unwrap()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_contents(full_path: &str) -> String {
|
||||
|
@ -116,10 +181,6 @@ pub fn delete_directory_at(full_path: &str) {
|
|||
std::fs::remove_dir_all(PathBuf::from(full_path)).expect("can not remove directory");
|
||||
}
|
||||
|
||||
pub fn create_directory_at(full_path: &str) {
|
||||
std::fs::create_dir(PathBuf::from(full_path)).expect("can not create directory");
|
||||
}
|
||||
|
||||
pub fn executable_path() -> PathBuf {
|
||||
let mut buf = PathBuf::new();
|
||||
buf.push("target");
|
||||
|
|
Loading…
Reference in a new issue