From db06edc5d34125b3efc6647d473e7a779bafb8d8 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 27 Dec 2022 12:46:23 -0600 Subject: [PATCH] add `--mime-type(-m)` to `ls` in the `type` column (#7616) # Description This PR adds the `mime-type` to the `type` column if you add the `--mime-type(-m)` flag to `ls`. Screenshot 2022-12-27 at 11 43 20 AM Screenshot 2022-12-27 at 11 45 53 AM # User-Facing Changes If you specify the `-m` flag, you get the "guessed at" mime type. The guess is based on the file name and uses this crate https://docs.rs/mime_guess/latest/mime_guess/ for the guessing. Part of issue #7612 and and #7524 There's some debate on if the `mime-type` should be added to the `type` column or if there should be a separate `mime` column. I tend to lean on the side of `type` since it's technically a type and it's only in that column if you ask it to be there. Also, I'd prefer to reuse a column rather than having a list of sprawling columns. Also, as @KodiCraft suggested, there is precedence as with `ls -d` where the summed size is in the size column. I could go either way and if someone wants to create a `mime` column, we'd probably accept it. # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --- Cargo.lock | 20 ++++++++++++++++++ crates/nu-cli/tests/completions.rs | 4 +++- crates/nu-command/Cargo.toml | 1 + crates/nu-command/src/filesystem/ls.rs | 29 ++++++++++++++++++++------ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13a0131e5f..e5c6360d48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2271,6 +2271,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2620,6 +2630,7 @@ dependencies = [ "md-5", "meval", "mime", + "mime_guess", "notify", "nu-ansi-term", "nu-color-config", @@ -5241,6 +5252,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/crates/nu-cli/tests/completions.rs b/crates/nu-cli/tests/completions.rs index dec2afb2b4..fb293b69b3 100644 --- a/crates/nu-cli/tests/completions.rs +++ b/crates/nu-cli/tests/completions.rs @@ -459,7 +459,7 @@ fn flag_completions() { // Test completions for the 'ls' flags let suggestions = completer.complete("ls -", 4); - assert_eq!(14, suggestions.len()); + assert_eq!(16, suggestions.len()); let expected: Vec = vec![ "--all".into(), @@ -468,6 +468,7 @@ fn flag_completions() { "--full-paths".into(), "--help".into(), "--long".into(), + "--mime-type".into(), "--short-names".into(), "-D".into(), "-a".into(), @@ -475,6 +476,7 @@ fn flag_completions() { "-f".into(), "-h".into(), "-l".into(), + "-m".into(), "-s".into(), ]; diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 5649fce3da..01439241cd 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -59,6 +59,7 @@ lscolors = { version = "0.12.0", features = ["crossterm"], default-features = fa md5 = { package = "md-5", version = "0.10.0" } meval = "0.2.0" mime = "0.3.16" +mime_guess = "2.0.4" notify = "4.0.17" num = { version = "0.4.0", optional = true } num-traits = "0.2.14" diff --git a/crates/nu-command/src/filesystem/ls.rs b/crates/nu-command/src/filesystem/ls.rs index 948a944a21..f90f1ab036 100644 --- a/crates/nu-command/src/filesystem/ls.rs +++ b/crates/nu-command/src/filesystem/ls.rs @@ -62,6 +62,7 @@ impl Command for Ls { "List the specified directory itself instead of its contents", Some('D'), ) + .switch("mime-type", "Show mime-type in type column", Some('m')) .category(Category::FileSystem) } @@ -78,6 +79,7 @@ impl Command for Ls { let full_paths = call.has_flag("full-paths"); let du = call.has_flag("du"); let directory = call.has_flag("directory"); + let use_mime_type = call.has_flag("mime-type"); let ctrl_c = engine_state.ctrlc.clone(); let call_span = call.head; let cwd = current_dir(engine_state, stack)?; @@ -250,6 +252,7 @@ impl Command for Ls { long, du, ctrl_c.clone(), + use_mime_type, ); match entry { Ok(value) => Some(value), @@ -365,7 +368,7 @@ use std::os::unix::fs::FileTypeExt; use std::path::Path; use std::sync::atomic::AtomicBool; -pub fn get_file_type(md: &std::fs::Metadata) -> &str { +pub fn get_file_type(md: &std::fs::Metadata, display_name: &str, use_mime_type: bool) -> String { let ft = md.file_type(); let mut file_type = "unknown"; if ft.is_dir() { @@ -388,18 +391,32 @@ pub fn get_file_type(md: &std::fs::Metadata) -> &str { } } } - file_type + if use_mime_type { + let guess = mime_guess::from_path(display_name); + let mime_guess = match guess.first() { + Some(mime_type) => mime_type.essence_str().to_string(), + None => "unknown".to_string(), + }; + if file_type == "file" { + mime_guess + } else { + file_type.to_string() + } + } else { + file_type.to_string() + } } #[allow(clippy::too_many_arguments)] pub(crate) fn dir_entry_dict( filename: &std::path::Path, // absolute path - display_name: &str, // gile name to be displayed + display_name: &str, // file name to be displayed metadata: Option<&std::fs::Metadata>, span: Span, long: bool, du: bool, ctrl_c: Option>, + use_mime_type: bool, ) -> Result { #[cfg(windows)] if metadata.is_none() { @@ -408,7 +425,7 @@ pub(crate) fn dir_entry_dict( let mut cols = vec![]; let mut vals = vec![]; - let mut file_type = "unknown"; + let mut file_type = "unknown".to_string(); cols.push("name".into()); vals.push(Value::String { @@ -417,10 +434,10 @@ pub(crate) fn dir_entry_dict( }); if let Some(md) = metadata { - file_type = get_file_type(md); + file_type = get_file_type(md, display_name, use_mime_type); cols.push("type".into()); vals.push(Value::String { - val: file_type.to_string(), + val: file_type.clone(), span, }); } else {