From adabc839bffa6c0d6de83d8f98b429c394299f1e Mon Sep 17 00:00:00 2001 From: xiuxiu62 Date: Thu, 7 Oct 2021 14:18:03 -0700 Subject: [PATCH 1/4] add touch command --- crates/nu-command/src/default_context.rs | 1 + crates/nu-command/src/filesystem/mod.rs | 2 + crates/nu-command/src/filesystem/touch.rs | 56 +++++++++++++++++++++++ crates/nu-protocol/src/shell_error.rs | 4 ++ 4 files changed, 63 insertions(+) create mode 100644 crates/nu-command/src/filesystem/touch.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index fd2ea37555..06abdd8bbd 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -41,6 +41,7 @@ pub fn create_default_context() -> Rc> { working_set.add_decl(Box::new(Select)); working_set.add_decl(Box::new(Sys)); working_set.add_decl(Box::new(Table)); + working_set.add_decl(Box::new(Touch)); working_set.add_decl(Box::new(Use)); working_set.add_decl(Box::new(Where)); working_set.add_decl(Box::new(Wrap)); diff --git a/crates/nu-command/src/filesystem/mod.rs b/crates/nu-command/src/filesystem/mod.rs index 2d2212766a..926ffe2e54 100644 --- a/crates/nu-command/src/filesystem/mod.rs +++ b/crates/nu-command/src/filesystem/mod.rs @@ -2,9 +2,11 @@ mod cd; mod cp; mod ls; mod mv; +mod touch; mod util; pub use cd::Cd; pub use cp::Cp; pub use ls::Ls; pub use mv::Mv; +pub use touch::Touch; diff --git a/crates/nu-command/src/filesystem/touch.rs b/crates/nu-command/src/filesystem/touch.rs new file mode 100644 index 0000000000..c895538858 --- /dev/null +++ b/crates/nu-command/src/filesystem/touch.rs @@ -0,0 +1,56 @@ +use std::fs::OpenOptions; + +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EvaluationContext}; +use nu_protocol::{ShellError, Signature, SyntaxShape, Value}; + +pub struct Touch; + +impl Command for Touch { + fn name(&self) -> &str { + "touch" + } + + fn signature(&self) -> Signature { + Signature::build("touch") + .required( + "filename", + SyntaxShape::Filepath, + "the path of the file you want to create", + ) + .rest("rest", SyntaxShape::Filepath, "additional files to create") + } + + fn usage(&self) -> &str { + "Creates one or more files." + } + + fn run( + &self, + context: &EvaluationContext, + call: &Call, + _input: Value, + ) -> Result { + touch(context, call) + } +} + +fn touch(context: &EvaluationContext, call: &Call) -> Result { + let target: String = call.req(context, 0)?; + let rest: Vec = call.rest(context, 1)?; + + for (index, item) in vec![target].into_iter().chain(rest).enumerate() { + match OpenOptions::new().write(true).create(true).open(&item) { + Ok(_) => continue, + Err(err) => { + return Err(ShellError::CreateNotPossible( + format!("Failed to create file: {}", err), + call.positional[index].span, + )); + } + } + } + + Ok(Value::Nothing { span: call.head }) +} diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 6a7464508e..ab7da16eae 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -109,6 +109,10 @@ pub enum ShellError { #[error("Move not possible")] #[diagnostic(code(nu::shell::move_not_possible_single), url(docsrs))] MoveNotPossibleSingle(String, #[label("{0}")] Span), + + #[error("Create not possible")] + #[diagnostic(code(nu::shell::move_not_possible_single), url(docsrs))] + CreateNotPossible(String, #[label("{0}")] Span), } impl From for ShellError { From e8e1ead99d1892cd4f5c200189b6dd57649df2a8 Mon Sep 17 00:00:00 2001 From: xiuxiu62 Date: Thu, 7 Oct 2021 14:20:03 -0700 Subject: [PATCH 2/4] change diagnostic code on CreateNotPossible --- crates/nu-protocol/src/shell_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index ab7da16eae..1b1c172bc6 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -111,7 +111,7 @@ pub enum ShellError { MoveNotPossibleSingle(String, #[label("{0}")] Span), #[error("Create not possible")] - #[diagnostic(code(nu::shell::move_not_possible_single), url(docsrs))] + #[diagnostic(code(nu::shell::create_not_possible), url(docsrs))] CreateNotPossible(String, #[label("{0}")] Span), } From 8550f50522c738d6d0477d7ed7830fc82a807835 Mon Sep 17 00:00:00 2001 From: xiuxiu62 Date: Thu, 7 Oct 2021 14:36:47 -0700 Subject: [PATCH 3/4] substitute idiomatic call flag check --- crates/nu-command/src/filesystem/cp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nu-command/src/filesystem/cp.rs b/crates/nu-command/src/filesystem/cp.rs index 79751f2c93..b042d50f11 100644 --- a/crates/nu-command/src/filesystem/cp.rs +++ b/crates/nu-command/src/filesystem/cp.rs @@ -60,7 +60,7 @@ impl Command for Cp { } let any_source_is_dir = sources.iter().any(|f| matches!(f, Ok(f) if f.is_dir())); - let recursive = call.named.iter().any(|p| &p.0 == "recursive"); + let recursive: bool = call.has_flag("recursive"); if any_source_is_dir && !recursive { return Err(ShellError::MoveNotPossibleSingle( "Directories must be copied using \"--recursive\"".to_string(), From bdfe8c08882b752b23cf506378e96577181213a3 Mon Sep 17 00:00:00 2001 From: xiuxiu62 Date: Thu, 7 Oct 2021 15:20:23 -0700 Subject: [PATCH 4/4] add mkdir command --- crates/nu-command/src/default_context.rs | 1 + crates/nu-command/src/filesystem/mkdir.rs | 74 +++++++++++++++++++++++ crates/nu-command/src/filesystem/mod.rs | 2 + crates/nu-command/src/filesystem/touch.rs | 30 ++++----- crates/nu-protocol/src/shell_error.rs | 4 ++ 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 crates/nu-command/src/filesystem/mkdir.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 06abdd8bbd..b7e5157dcc 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -35,6 +35,7 @@ pub fn create_default_context() -> Rc> { working_set.add_decl(Box::new(LetEnv)); working_set.add_decl(Box::new(Lines)); working_set.add_decl(Box::new(Ls)); + working_set.add_decl(Box::new(Mkdir)); working_set.add_decl(Box::new(Module)); working_set.add_decl(Box::new(Mv)); working_set.add_decl(Box::new(Ps)); diff --git a/crates/nu-command/src/filesystem/mkdir.rs b/crates/nu-command/src/filesystem/mkdir.rs new file mode 100644 index 0000000000..5635c85538 --- /dev/null +++ b/crates/nu-command/src/filesystem/mkdir.rs @@ -0,0 +1,74 @@ +use std::collections::VecDeque; +use std::env::current_dir; + +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EvaluationContext}; +use nu_protocol::{ShellError, Signature, SyntaxShape, Value, ValueStream}; + +pub struct Mkdir; + +impl Command for Mkdir { + fn name(&self) -> &str { + "mkdir" + } + + fn signature(&self) -> Signature { + Signature::build("mkdir") + .rest( + "rest", + SyntaxShape::Filepath, + "the name(s) of the path(s) to create", + ) + .switch("show-created-paths", "show the path(s) created.", Some('s')) + } + + fn usage(&self) -> &str { + "Make directories, creates intermediary directories as required." + } + + fn run( + &self, + context: &EvaluationContext, + call: &Call, + _input: Value, + ) -> Result { + let path = current_dir()?; + let mut directories = call + .rest::(context, 0)? + .into_iter() + .map(|dir| path.join(dir)) + .peekable(); + + let show_created_paths = call.has_flag("show-created-paths"); + let mut stream: VecDeque = VecDeque::new(); + + if directories.peek().is_none() { + return Err(ShellError::MissingParameter( + "requires directory paths".to_string(), + call.head, + )); + } + + for (i, dir) in directories.enumerate() { + let span = call.positional[i].span; + let dir_res = std::fs::create_dir_all(&dir); + + if let Err(reason) = dir_res { + return Err(ShellError::CreateNotPossible( + format!("failed to create directory: {}", reason), + call.positional[i].span, + )); + } + + if show_created_paths { + let val = format!("{:}", dir.to_string_lossy()); + stream.push_back(Value::String { val, span }); + } + } + + let stream = ValueStream::from_stream(stream.into_iter()); + let span = call.head; + Ok(Value::Stream { stream, span }) + } +} diff --git a/crates/nu-command/src/filesystem/mod.rs b/crates/nu-command/src/filesystem/mod.rs index 926ffe2e54..afbbac5250 100644 --- a/crates/nu-command/src/filesystem/mod.rs +++ b/crates/nu-command/src/filesystem/mod.rs @@ -1,6 +1,7 @@ mod cd; mod cp; mod ls; +mod mkdir; mod mv; mod touch; mod util; @@ -8,5 +9,6 @@ mod util; pub use cd::Cd; pub use cp::Cp; pub use ls::Ls; +pub use mkdir::Mkdir; pub use mv::Mv; pub use touch::Touch; diff --git a/crates/nu-command/src/filesystem/touch.rs b/crates/nu-command/src/filesystem/touch.rs index c895538858..bb23d47189 100644 --- a/crates/nu-command/src/filesystem/touch.rs +++ b/crates/nu-command/src/filesystem/touch.rs @@ -32,25 +32,21 @@ impl Command for Touch { call: &Call, _input: Value, ) -> Result { - touch(context, call) - } -} + let target: String = call.req(context, 0)?; + let rest: Vec = call.rest(context, 1)?; -fn touch(context: &EvaluationContext, call: &Call) -> Result { - let target: String = call.req(context, 0)?; - let rest: Vec = call.rest(context, 1)?; - - for (index, item) in vec![target].into_iter().chain(rest).enumerate() { - match OpenOptions::new().write(true).create(true).open(&item) { - Ok(_) => continue, - Err(err) => { - return Err(ShellError::CreateNotPossible( - format!("Failed to create file: {}", err), - call.positional[index].span, - )); + for (index, item) in vec![target].into_iter().chain(rest).enumerate() { + match OpenOptions::new().write(true).create(true).open(&item) { + Ok(_) => continue, + Err(err) => { + return Err(ShellError::CreateNotPossible( + format!("Failed to create file: {}", err), + call.positional[index].span, + )); + } } } - } - Ok(Value::Nothing { span: call.head }) + Ok(Value::Nothing { span: call.head }) + } } diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 1b1c172bc6..4be91303d0 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -27,6 +27,10 @@ pub enum ShellError { #[diagnostic(code(nu::shell::unknown_operator), url(docsrs))] UnknownOperator(String, #[label = "unsupported operator"] Span), + #[error("Missing parameter: {0}.")] + #[diagnostic(code(nu::shell::missing_parameter), url(docsrs))] + MissingParameter(String, #[label = "missing parameter: {0}"] Span), + #[error("External commands not yet supported")] #[diagnostic(code(nu::shell::external_commands), url(docsrs))] ExternalNotSupported(#[label = "external not supported"] Span),