Merge pull request #237 from androbtech/wildcard_support

Wildcard support adventure starting with rm command.
This commit is contained in:
Jonathan Turner 2019-08-02 10:58:33 +12:00 committed by GitHub
commit 3da5fb93e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 240 additions and 85 deletions

1
Cargo.lock generated
View File

@ -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)",

View File

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

View File

@ -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,18 @@ pub fn rm(args: CommandArgs) -> Result<OutputStream, ShellError> {
file => full_path.push(file),
}
if full_path.is_dir() {
let entries = glob(&full_path.to_string_lossy());
if entries.is_err() {
return Err(ShellError::string("Invalid pattern."));
}
let entries = entries.unwrap();
for entry in entries {
match entry {
Ok(path) => {
if path.is_dir() {
if !args.has("recursive") {
return Err(ShellError::labeled_error(
"is a directory",
@ -51,9 +64,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())

View File

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

View File

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