diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 48e93de0f9..a172e2d33f 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -455,11 +455,13 @@ pub fn create_default_context( #[cfg(feature = "uuid_crate")] whole_stream_command(RandomUUID), // Path - whole_stream_command(PathCommand), - whole_stream_command(PathExtension), whole_stream_command(PathBasename), - whole_stream_command(PathExpand), + whole_stream_command(PathCommand), + whole_stream_command(PathDirname), whole_stream_command(PathExists), + whole_stream_command(PathExpand), + whole_stream_command(PathExtension), + whole_stream_command(PathFilestem), whole_stream_command(PathType), // Url whole_stream_command(UrlCommand), diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 52e651ce94..50f8c5ca85 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -207,7 +207,10 @@ pub(crate) use next::Next; pub(crate) use nth::Nth; pub(crate) use open::Open; pub(crate) use parse::Parse; -pub(crate) use path::{PathBasename, PathCommand, PathExists, PathExpand, PathExtension, PathType}; +pub(crate) use path::{ + PathBasename, PathCommand, PathDirname, PathExists, PathExpand, PathExtension, PathFilestem, + PathType, +}; pub(crate) use pivot::Pivot; pub(crate) use prepend::Prepend; pub(crate) use prev::Previous; diff --git a/crates/nu-cli/src/commands/path/dirname.rs b/crates/nu-cli/src/commands/path/dirname.rs new file mode 100644 index 0000000000..84e0328f35 --- /dev/null +++ b/crates/nu-cli/src/commands/path/dirname.rs @@ -0,0 +1,60 @@ +use super::{operate, DefaultArguments}; +use crate::commands::WholeStreamCommand; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; +use std::path::Path; + +pub struct PathDirname; + +#[async_trait] +impl WholeStreamCommand for PathDirname { + fn name(&self) -> &str { + "path dirname" + } + + fn signature(&self) -> Signature { + Signature::build("path dirname").rest(SyntaxShape::ColumnPath, "optionally operate by path") + } + + fn usage(&self) -> &str { + "gets the dirname of a path" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + let tag = args.call_info.name_tag.clone(); + let (DefaultArguments { rest }, input) = args.process(®istry).await?; + operate(input, rest, &action, tag.span).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Get dirname of a path", + example: "echo '/home/joe/test.txt' | path dirname", + result: Some(vec![Value::from("/home/joe")]), + }] + } +} + +fn action(path: &Path) -> UntaggedValue { + UntaggedValue::string(match path.parent() { + Some(dirname) => dirname.to_string_lossy().to_string(), + _ => "".to_string(), + }) +} + +#[cfg(test)] +mod tests { + use super::PathDirname; + + #[test] + fn examples_work_as_expected() { + use crate::examples::test as test_examples; + + test_examples(PathDirname {}) + } +} diff --git a/crates/nu-cli/src/commands/path/filestem.rs b/crates/nu-cli/src/commands/path/filestem.rs new file mode 100644 index 0000000000..6facde0628 --- /dev/null +++ b/crates/nu-cli/src/commands/path/filestem.rs @@ -0,0 +1,61 @@ +use super::{operate, DefaultArguments}; +use crate::commands::WholeStreamCommand; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; +use std::path::Path; + +pub struct PathFilestem; + +#[async_trait] +impl WholeStreamCommand for PathFilestem { + fn name(&self) -> &str { + "path filestem" + } + + fn signature(&self) -> Signature { + Signature::build("path filestem") + .rest(SyntaxShape::ColumnPath, "optionally operate by path") + } + + fn usage(&self) -> &str { + "gets the filestem of a path" + } + + async fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + let tag = args.call_info.name_tag.clone(); + let (DefaultArguments { rest }, input) = args.process(®istry).await?; + operate(input, rest, &action, tag.span).await + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Get filestem of a path", + example: "echo '/home/joe/test.txt' | path filestem", + result: Some(vec![Value::from("test")]), + }] + } +} + +fn action(path: &Path) -> UntaggedValue { + UntaggedValue::string(match path.file_stem() { + Some(stem) => stem.to_string_lossy().to_string(), + _ => "".to_string(), + }) +} + +#[cfg(test)] +mod tests { + use super::PathFilestem; + + #[test] + fn examples_work_as_expected() { + use crate::examples::test as test_examples; + + test_examples(PathFilestem {}) + } +} diff --git a/crates/nu-cli/src/commands/path/mod.rs b/crates/nu-cli/src/commands/path/mod.rs index 55550e0f7d..44edc07531 100644 --- a/crates/nu-cli/src/commands/path/mod.rs +++ b/crates/nu-cli/src/commands/path/mod.rs @@ -1,8 +1,10 @@ mod basename; mod command; +mod dirname; mod exists; mod expand; mod extension; +mod filestem; mod r#type; use crate::prelude::*; @@ -13,9 +15,11 @@ use std::path::Path; pub use basename::PathBasename; pub use command::Path as PathCommand; +pub use dirname::PathDirname; pub use exists::PathExists; pub use expand::PathExpand; pub use extension::PathExtension; +pub use filestem::PathFilestem; pub use r#type::PathType; #[derive(Deserialize)] diff --git a/crates/nu-cli/src/completion/command.rs b/crates/nu-cli/src/completion/command.rs index 6a68d6231b..a4e9d17305 100644 --- a/crates/nu-cli/src/completion/command.rs +++ b/crates/nu-cli/src/completion/command.rs @@ -34,7 +34,7 @@ impl Completer { // These is_executable/pathext implementations are copied from ichwh and modified // to not be async -#[cfg(windows)] +#[cfg(all(windows, feature = "ichwh"))] fn pathext() -> IchwhResult> { Ok(std::env::var_os("PATHEXT") .ok_or(IchwhError::PathextNotDefined)? @@ -45,6 +45,11 @@ fn pathext() -> IchwhResult> { .collect::>()) } +#[cfg(all(windows, not(feature = "ichwh")))] +fn pathext() -> Result, ()> { + Err(()) +} + #[cfg(windows)] fn is_executable(file: &DirEntry) -> bool { if let Ok(metadata) = file.metadata() {