fix ls and ls tests (#931)

* fix `ls` and ls tests

* tweak to ls so it doesn't scream on empty dirs

* clippy

* reworked `ls` to put in what was left out
This commit is contained in:
Darren Schroeder 2022-02-04 14:32:13 -06:00 committed by GitHub
parent c2f6dfa75c
commit 8204cc4f28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 24 deletions

View File

@ -6,8 +6,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, DataSource, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata, Category, DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Value, PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
}; };
use pathdiff::diff_paths; use pathdiff::diff_paths;
#[cfg(unix)] #[cfg(unix)]
@ -67,27 +67,70 @@ impl Command for Ls {
let full_paths = call.has_flag("full-paths"); let full_paths = call.has_flag("full-paths");
let du = call.has_flag("du"); let du = call.has_flag("du");
let ctrl_c = engine_state.ctrlc.clone(); let ctrl_c = engine_state.ctrlc.clone();
let call_span = call.head; let call_span = call.head;
let cwd = current_dir(engine_state, stack)?; let cwd = current_dir(engine_state, stack)?;
let pattern_arg = call.opt::<Spanned<String>>(engine_state, stack, 0)?; let pattern_arg = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
let pattern = if let Some(pattern) = pattern_arg { let (path, p_tag) = match pattern_arg {
pattern Some(p) => {
let p_tag = p.span;
let mut p = PathBuf::from(p.item);
if p.is_dir() {
if permission_denied(&p) {
#[cfg(unix)]
let error_msg = format!(
"The permissions of {:o} do not allow access for this user",
p.metadata()
.expect(
"this shouldn't be called since we already know there is a dir"
)
.permissions()
.mode()
& 0o0777
);
#[cfg(not(unix))]
let error_msg = String::from("Permission denied");
return Err(ShellError::SpannedLabeledError(
"Permission denied".to_string(),
error_msg,
p_tag,
));
}
if is_empty_dir(&p) {
return Ok(Value::nothing(call_span).into_pipeline_data());
}
p.push("*");
}
(p, p_tag)
}
None => {
if is_empty_dir(current_dir(engine_state, stack)?) {
return Ok(Value::nothing(call_span).into_pipeline_data());
} else { } else {
Spanned { (PathBuf::from("./*"), call_span)
item: cwd.join("*").to_string_lossy().to_string(), }
span: call_span,
} }
}; };
let (prefix, glob) = nu_engine::glob_from(&pattern, &cwd, call_span)?; let hidden_dir_specified = is_hidden_dir(&path);
let glob_path = Spanned {
item: path.display().to_string(),
span: p_tag,
};
let (prefix, paths) = nu_engine::glob_from(&glob_path, &cwd, call_span)?;
let mut paths_peek = paths.peekable();
if paths_peek.peek().is_none() {
return Err(ShellError::LabeledError(
format!("No matches found for {}", &path.display().to_string()),
"no matches found".to_string(),
));
}
let hidden_dir_specified = is_hidden_dir(&pattern.item);
let mut hidden_dirs = vec![]; let mut hidden_dirs = vec![];
Ok(glob Ok(paths_peek
.into_iter() .into_iter()
.filter_map(move |x| match x { .filter_map(move |x| match x {
Ok(path) => { Ok(path) => {
@ -163,6 +206,20 @@ impl Command for Ls {
} }
} }
fn permission_denied(dir: impl AsRef<Path>) -> bool {
match dir.as_ref().read_dir() {
Err(e) => matches!(e.kind(), std::io::ErrorKind::PermissionDenied),
Ok(_) => false,
}
}
fn is_empty_dir(dir: impl AsRef<Path>) -> bool {
match dir.as_ref().read_dir() {
Err(_) => true,
Ok(mut s) => s.next().is_none(),
}
}
fn is_hidden_dir(dir: impl AsRef<Path>) -> bool { fn is_hidden_dir(dir: impl AsRef<Path>) -> bool {
#[cfg(windows)] #[cfg(windows)]
{ {

View File

@ -67,8 +67,6 @@ fn lists_regular_files_using_question_mark_wildcard() {
}) })
} }
// FIXME: jt: needs more work
#[ignore]
#[test] #[test]
fn lists_all_files_in_directories_from_stream() { fn lists_all_files_in_directories_from_stream() {
Playground::setup("ls_test_4", |dirs, sandbox| { Playground::setup("ls_test_4", |dirs, sandbox| {
@ -90,7 +88,7 @@ fn lists_all_files_in_directories_from_stream() {
r#" r#"
echo dir_a dir_b echo dir_a dir_b
| each { ls $it } | each { ls $it }
| length | flatten | length
"# "#
)); ));
@ -115,8 +113,6 @@ fn does_not_fail_if_glob_matches_empty_directory() {
}) })
} }
// FIXME: jt: needs more work
#[ignore]
#[test] #[test]
fn fails_when_glob_doesnt_match() { fn fails_when_glob_doesnt_match() {
Playground::setup("ls_test_5", |dirs, sandbox| { Playground::setup("ls_test_5", |dirs, sandbox| {
@ -292,8 +288,6 @@ fn lists_files_including_starting_with_dot() {
}) })
} }
// FIXME: jt: needs more work
#[ignore]
#[test] #[test]
fn list_all_columns() { fn list_all_columns() {
Playground::setup("ls_test_all_columns", |dirs, sandbox| { Playground::setup("ls_test_all_columns", |dirs, sandbox| {
@ -306,14 +300,14 @@ fn list_all_columns() {
// Normal Operation // Normal Operation
let actual = nu!( let actual = nu!(
cwd: dirs.test(), cwd: dirs.test(),
"ls | get | to md" "ls | columns | to md"
); );
let expected = ["name", "type", "size", "modified"].join(""); let expected = ["name", "type", "size", "modified"].join("");
assert_eq!(actual.out, expected, "column names are incorrect for ls"); assert_eq!(actual.out, expected, "column names are incorrect for ls");
// Long // Long
let actual = nu!( let actual = nu!(
cwd: dirs.test(), cwd: dirs.test(),
"ls -l | get | to md" "ls -l | columns | to md"
); );
let expected = { let expected = {
#[cfg(unix)] #[cfg(unix)]
@ -322,10 +316,10 @@ fn list_all_columns() {
"name", "name",
"type", "type",
"target", "target",
"num_links",
"inode",
"readonly", "readonly",
"mode", "mode",
"num_links",
"inode",
"uid", "uid",
"group", "group",
"size", "size",