From 5d7677dd071adba17e097258b7367158bad45000 Mon Sep 17 00:00:00 2001 From: Peter Cunderlik Date: Fri, 23 Jul 2021 08:14:02 +0100 Subject: [PATCH] Implement into path conversion (#3811) This allows converting strings to filepaths without having to use `path expand` roundtrip. Filepaths are taken as-is without any validation/conversion. --- .../src/commands/conversions/into/filepath.rs | 126 ++++++++++++++++++ .../src/commands/conversions/into/mod.rs | 2 + crates/nu-command/src/default_context.rs | 1 + 3 files changed, 129 insertions(+) create mode 100644 crates/nu-command/src/commands/conversions/into/filepath.rs diff --git a/crates/nu-command/src/commands/conversions/into/filepath.rs b/crates/nu-command/src/commands/conversions/into/filepath.rs new file mode 100644 index 000000000..5d93503a7 --- /dev/null +++ b/crates/nu-command/src/commands/conversions/into/filepath.rs @@ -0,0 +1,126 @@ +use std::path::PathBuf; + +use crate::prelude::*; +use nu_engine::WholeStreamCommand; +use nu_errors::ShellError; +use nu_protocol::{ColumnPath, Primitive, Signature, SyntaxShape, UntaggedValue, Value}; + +pub struct SubCommand; + +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "into path" + } + + fn signature(&self) -> Signature { + Signature::build("into path").rest( + SyntaxShape::ColumnPath, + "column paths to convert to filepath (for table input)", + ) + } + + fn usage(&self) -> &str { + "Convert value to filepath" + } + + fn run(&self, args: CommandArgs) -> Result { + into_filepath(args) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Convert string to filepath in table", + example: "echo [[name]; ['/dev/null'] ['C:\\Program Files'] ['../../Cargo.toml']] | into path name", + result: Some(vec![ + UntaggedValue::row(indexmap! { + "name".to_string() => UntaggedValue::filepath("/dev/null").into(), + }) + .into(), + UntaggedValue::row(indexmap! { + "name".to_string() => UntaggedValue::filepath("C:\\Program Files").into(), + }) + .into(), + UntaggedValue::row(indexmap! { + "name".to_string() => UntaggedValue::filepath("../../Cargo.toml").into(), + }) + .into(), + ]), + }, + Example { + description: "Convert string to filepath", + example: "echo 'Cargo.toml' | into path", + result: Some(vec![UntaggedValue::filepath("Cargo.toml").into()]), + }, + ] + } +} + +fn into_filepath(args: CommandArgs) -> Result { + let column_paths: Vec = args.rest(0)?; + + Ok(args + .input + .map(move |v| { + if column_paths.is_empty() { + action(&v, v.tag()) + } else { + let mut ret = v; + for path in &column_paths { + ret = ret.swap_data_by_column_path( + path, + Box::new(move |old| action(old, old.tag())), + )?; + } + + Ok(ret) + } + }) + .into_input_stream()) +} + +pub fn action(input: &Value, tag: impl Into) -> Result { + let tag = tag.into(); + match &input.value { + UntaggedValue::Primitive(prim) => Ok(UntaggedValue::filepath(match prim { + Primitive::String(a_string) => match filepath_from_string(a_string, &tag) { + Ok(n) => n, + Err(e) => { + return Err(e); + } + }, + Primitive::FilePath(a_filepath) => a_filepath.clone(), + _ => { + return Err(ShellError::unimplemented( + "'into path' for non-string primitives", + )) + } + }) + .into_value(&tag)), + UntaggedValue::Row(_) => Err(ShellError::labeled_error( + "specify column name to use, with 'into path COLUMN'", + "found table", + tag, + )), + _ => Err(ShellError::unimplemented( + "'into path' for unsupported type", + )), + } +} + +fn filepath_from_string(a_string: &str, _tag: &Tag) -> Result { + Ok(PathBuf::from(a_string)) +} + +#[cfg(test)] +mod tests { + use super::ShellError; + use super::SubCommand; + + #[test] + fn examples_work_as_expected() -> Result<(), ShellError> { + use crate::examples::test as test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/commands/conversions/into/mod.rs b/crates/nu-command/src/commands/conversions/into/mod.rs index 9163cc78f..efd01476c 100644 --- a/crates/nu-command/src/commands/conversions/into/mod.rs +++ b/crates/nu-command/src/commands/conversions/into/mod.rs @@ -1,9 +1,11 @@ mod binary; mod command; +mod filepath; mod int; pub mod string; pub use binary::SubCommand as IntoBinary; pub use command::Command as Into; +pub use filepath::SubCommand as IntoFilepath; pub use int::SubCommand as IntoInt; pub use string::SubCommand as IntoString; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 7458faefd..00d91c0da 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -131,6 +131,7 @@ pub fn create_default_context(interactive: bool) -> Result