diff --git a/Cargo.lock b/Cargo.lock index 5c6d42851..12e3b31b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "arrow-format" version = "0.4.0" @@ -325,6 +331,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "brownstone" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030ea61398f34f1395ccbeb046fb68c87b631d1f34567fed0f0f11fa35d18d8d" +dependencies = [ + "arrayvec 0.7.2", +] + [[package]] name = "bstr" version = "0.2.17" @@ -1584,6 +1599,12 @@ dependencies = [ "version_check 0.9.4", ] +[[package]] +name = "indent_write" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" + [[package]] name = "indexmap" version = "1.8.0" @@ -1687,6 +1708,12 @@ dependencies = [ "libc", ] +[[package]] +name = "joinery" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" + [[package]] name = "js-sys" version = "0.3.56" @@ -1979,6 +2006,17 @@ dependencies = [ "nom 1.2.4", ] +[[package]] +name = "miette" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd2adcfcced5d625bf90a958a82ae5b93231f57f3df1383fee28c9b5096d35ed" +dependencies = [ + "miette-derive 3.3.0", + "once_cell", + "thiserror", +] + [[package]] name = "miette" version = "4.2.1" @@ -1987,7 +2025,7 @@ checksum = "e7ea7314b2a8dd373c2f2d2322e866ddea5d62ffd3d6cd7f2bb8c1467e56529f" dependencies = [ "atty", "backtrace", - "miette-derive", + "miette-derive 4.2.1", "once_cell", "owo-colors", "supports-color", @@ -1999,6 +2037,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "miette-derive" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c01a8b61312d367ce87956bb686731f87e4c6dd5dbc550e8f06e3c24fb1f67f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "miette-derive" version = "4.2.1" @@ -2016,6 +2065,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -2141,6 +2196,29 @@ dependencies = [ "version_check 0.1.5", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-supreme" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aadc66631948f6b65da03be4c4cd8bd104d481697ecbb9bbd65719b1ec60bc9f" +dependencies = [ + "brownstone", + "indent_write", + "joinery", + "memchr", + "nom 7.1.1", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -2164,7 +2242,7 @@ dependencies = [ "is_executable", "itertools", "log", - "miette", + "miette 4.2.1", "nu-ansi-term", "nu-cli", "nu-color-config", @@ -2208,7 +2286,7 @@ dependencies = [ "crossterm_winapi", "is_executable", "log", - "miette", + "miette 4.2.1", "nu-ansi-term", "nu-color-config", "nu-engine", @@ -2308,6 +2386,7 @@ dependencies = [ "url", "users", "uuid", + "wax", "which", "zip", ] @@ -2350,7 +2429,7 @@ version = "0.60.1" dependencies = [ "chrono", "log", - "miette", + "miette 4.2.1", "nu-path", "nu-plugin", "nu-protocol", @@ -2395,7 +2474,7 @@ dependencies = [ "chrono-humanize", "im", "indexmap", - "miette", + "miette 4.2.1", "nu-json", "num-format", "serde", @@ -3092,6 +3171,15 @@ dependencies = [ "rayon", ] +[[package]] +name = "pori" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a63d338dec139f56dacc692ca63ad35a6be6a797442479b55acd611d79e906" +dependencies = [ + "nom 7.1.1", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -4570,6 +4658,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc1631c774f0f9570797191e01247cbefde789eebfbf128074cb934115a6133" + [[package]] name = "version_check" version = "0.1.5" @@ -4743,6 +4837,26 @@ version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +[[package]] +name = "wax" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a4ecdf7da7e42385f844503bac3e9a2a066838e3cb66c5f28ce03bafb2f90d" +dependencies = [ + "bstr", + "const_format", + "itertools", + "miette 3.3.0", + "nom 7.1.1", + "nom-supreme", + "pori", + "regex", + "smallvec", + "thiserror", + "vec1", + "walkdir", +] + [[package]] name = "web-sys" version = "0.3.56" diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 72b498d73..2e88be05a 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -79,6 +79,7 @@ uuid = { version = "0.8.2", features = ["v4"] } which = { version = "4.2.2", optional = true } #reedline = {version = "0.3.0", features = ["bashisms"]} reedline = { git = "https://github.com/nushell/reedline", branch = "main", features = ["bashisms"]} +wax = { version = "0.4.0", features = ["diagnostics"] } zip = { version="0.5.9", optional = true } [target.'cfg(unix)'.dependencies] diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 294cac4ee..6ca5ec621 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -193,6 +193,7 @@ pub fn create_default_context(cwd: impl AsRef) -> EngineState { Rm, Save, Touch, + Glob, }; // Platform diff --git a/crates/nu-command/src/filesystem/glob.rs b/crates/nu-command/src/filesystem/glob.rs new file mode 100644 index 000000000..da0ea79d2 --- /dev/null +++ b/crates/nu-command/src/filesystem/glob.rs @@ -0,0 +1,127 @@ +use nu_engine::env::current_dir; +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Spanned, + SyntaxShape, Value, +}; +use wax::Glob as WaxGlob; + +#[derive(Clone)] +pub struct Glob; + +impl Command for Glob { + fn name(&self) -> &str { + "glob" + } + + fn signature(&self) -> Signature { + Signature::build("glob") + .required("glob", SyntaxShape::String, "the glob expression") + .named( + "depth", + SyntaxShape::Int, + "directory depth to search", + Some('d'), + ) + .category(Category::FileSystem) + } + + fn usage(&self) -> &str { + "Creates a list of files and/or folders based on the glob pattern provided." + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Search for *.rs files", + example: "glob *.rs", + result: None, + }, + Example { + description: "Search for *.rs and *.toml files recursively up to 2 folders deep", + example: "glob **/*.{rs,toml} --depth 2", + result: None, + }, + Example { + description: + "Search for files and folders that begin with uppercase C and lowercase c", + example: r#"glob "[Cc]*""#, + result: None, + }, + Example { + description: + "Search for files and folders like abc or xyz substituting a character for ?", + example: r#"glob "{a?c,x?z}""#, + result: None, + }, + Example { + description: "A case-insensitive search for files and folders that begin with c", + example: r#"glob "(?i)c*""#, + result: None, + }, + Example { + description: "Search for files for folders that do not begin with c, C, b, M, or s", + example: r#"glob "[!cCbMs]*""#, + result: None, + }, + Example { + description: "Search for files or folders with 3 a's in a row in the name", + example: "glob ", + result: None, + }, + Example { + description: "Search for files or folders with only a, b, c, or d in the file name between 1 and 10 times", + example: "glob <[a-d]:1,10>", + result: None, + }, + ] + } + + fn extra_usage(&self) -> &str { + r#"For more glob pattern help please refer to https://github.com/olson-sean-k/wax"# + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + let path = current_dir(engine_state, stack)?; + let glob_pattern: Spanned = call.req(engine_state, stack, 0)?; + let depth = call.get_flag(engine_state, stack, "depth")?; + + let folder_depth = if let Some(depth) = depth { + depth + } else { + usize::MAX + }; + + let glob = match WaxGlob::new(&glob_pattern.item) { + Ok(p) => p, + Err(e) => { + return Err(ShellError::LabeledError( + "error with glob pattern".to_string(), + format!("{}", e), + )) + } + }; + + let glob_results: Vec = glob + .walk(path, folder_depth) + .flatten() + .map(|entry| Value::String { + val: entry.into_path().to_string_lossy().to_string(), + span, + }) + .collect(); + + Ok(glob_results + .into_iter() + .into_pipeline_data(engine_state.ctrlc.clone())) + } +} diff --git a/crates/nu-command/src/filesystem/mod.rs b/crates/nu-command/src/filesystem/mod.rs index dd09ca7ba..ca4e84441 100644 --- a/crates/nu-command/src/filesystem/mod.rs +++ b/crates/nu-command/src/filesystem/mod.rs @@ -1,5 +1,6 @@ mod cd; mod cp; +mod glob; mod ls; mod mkdir; mod mv; @@ -11,6 +12,7 @@ mod util; pub use cd::Cd; pub use cp::Cp; +pub use glob::Glob; pub use ls::Ls; pub use mkdir::Mkdir; pub use mv::Mv;