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