Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add umkdir command #10785

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/nu-command/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ ureq = { version = "2.8", default-features = false, features = ["charset", "gzip
url = "2.2"
uu_cp = "0.0.22"
uu_whoami = "0.0.22"
uu_mkdir = "0.0.22"
uuid = { version = "1.5", features = ["v4"] }
wax = { version = "0.6" }
which = { version = "5.0", optional = true }
Expand Down
1 change: 1 addition & 0 deletions crates/nu-command/src/default_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Cd,
Ls,
Mkdir,
UMkdir,
Mv,
Cp,
UCp,
Expand Down
2 changes: 2 additions & 0 deletions crates/nu-command/src/filesystem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod save;
mod start;
mod touch;
mod ucp;
mod umkdir;
mod util;
mod watch;

Expand All @@ -25,4 +26,5 @@ pub use save::Save;
pub use start::Start;
pub use touch::Touch;
pub use ucp::UCp;
pub use umkdir::UMkdir;
pub use watch::Watch;
98 changes: 98 additions & 0 deletions crates/nu-command/src/filesystem/umkdir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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, PipelineData, ShellError, Signature, SyntaxShape, Type};

use uu_mkdir::mkdir;

#[derive(Clone)]
pub struct UMkdir;

const IS_RECURSIVE: bool = true;
// This is the same default as Rust's std uses:
// https://doc.rust-lang.org/nightly/std/os/unix/fs/trait.DirBuilderExt.html#tymethod.mode
const DEFAULT_MODE: u32 = 0o777;

impl Command for UMkdir {
fn name(&self) -> &str {
"umkdir"
}

fn usage(&self) -> &str {
"Create directories, with intermediary directories if required using uutils/coreutils mkdir."
}

fn search_terms(&self) -> Vec<&str> {
vec!["directory", "folder", "create", "make_dirs", "coreutils"]
}

fn signature(&self) -> Signature {
Signature::build("umkdir")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest(
"rest",
SyntaxShape::Directory,
"the name(s) of the path(s) to create",
)
.switch(
"verbose",
"print a message for each created directory.",
Some('v'),
)
.category(Category::FileSystem)
}

fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let path = current_dir(engine_state, stack)?;
let mut directories = call
.rest::<String>(engine_state, stack, 0)?
.into_iter()
.map(|dir| path.join(dir))
.peekable();

let is_verbose = call.has_flag("verbose");

if directories.peek().is_none() {
return Err(ShellError::MissingParameter {
param_name: "requires directory paths".to_string(),
span: call.head,
});
}

for dir in directories {
if let Err(error) = mkdir(&dir, IS_RECURSIVE, DEFAULT_MODE, is_verbose) {
return Err(ShellError::GenericError(
format!("{}", error),
format!("{}", error),
None,
None,
Vec::new(),
));
}
}

Ok(PipelineData::empty())
}

fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Make a directory named foo",
example: "umkdir foo",
result: None,
},
Example {
description: "Make multiple directories and show the paths created",
example: "umkdir -v foo/bar foo2",
result: None,
},
]
}
}
1 change: 1 addition & 0 deletions crates/nu-command/tests/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ mod touch;
mod transpose;
mod try_;
mod ucp;
mod umkdir;
mod uniq;
mod uniq_by;
mod update;
Expand Down
124 changes: 124 additions & 0 deletions crates/nu-command/tests/commands/umkdir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use nu_test_support::fs::files_exist_at;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
use std::path::Path;

#[test]
fn creates_directory() {
Playground::setup("umkdir_test_1", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir my_new_directory"
);

let expected = dirs.test().join("my_new_directory");

assert!(expected.exists());
})
}

#[test]
fn accepts_and_creates_directories() {
Playground::setup("umkdir_test_2", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir dir_1 dir_2 dir_3"
);

assert!(files_exist_at(
vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")],
dirs.test()
));
})
}

#[test]
fn creates_intermediary_directories() {
Playground::setup("umkdir_test_3", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir some_folder/another/deeper_one"
);

let expected = dirs.test().join("some_folder/another/deeper_one");

assert!(expected.exists());
})
}

#[test]
fn create_directory_two_parents_up_using_multiple_dots() {
Playground::setup("umkdir_test_4", |dirs, sandbox| {
sandbox.within("foo").mkdir("bar");

nu!(
cwd: dirs.test().join("foo/bar"),
"umkdir .../boo"
);

let expected = dirs.test().join("boo");

assert!(expected.exists());
})
}

#[test]
fn print_created_paths() {
Playground::setup("umkdir_test_2", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
pipeline("umkdir -v dir_1 dir_2 dir_3")
);

assert!(files_exist_at(
vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")],
dirs.test()
));

assert!(actual.out.contains("dir_1"));
assert!(actual.out.contains("dir_2"));
assert!(actual.out.contains("dir_3"));
})
}

#[test]
fn creates_directory_three_dots() {
Playground::setup("umkdir_test_1", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir test..."
);

let expected = dirs.test().join("test...");

assert!(expected.exists());
})
}

#[test]
fn creates_directory_four_dots() {
Playground::setup("umkdir_test_1", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir test...."
);

let expected = dirs.test().join("test....");

assert!(expected.exists());
})
}

#[test]
fn creates_directory_three_dots_quotation_marks() {
Playground::setup("umkdir_test_1", |dirs, _| {
nu!(
cwd: dirs.test(),
"umkdir 'test...'"
);

let expected = dirs.test().join("test...");

assert!(expected.exists());
})
}