Path commands: Put column path args behid flag; Allow path join appending without flag (#4008)

* Change path join signature

* Appending now works without flag
* Column path operation is behind a -c flag

* Move column path arg retrieval to a function

Also improves errors

* Fix path join tests

* Propagate column path changes to all path commands

* Update path command examples with columns paths

* Modernize path command examples by removing "echo"

* Improve structured path error message

* Fix typo
This commit is contained in:
Jakub Žádník 2021-09-15 21:03:51 +03:00 committed by GitHub
parent 7fc65067cf
commit cc3653cfd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 238 additions and 122 deletions

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -9,13 +9,13 @@ use std::path::Path;
pub struct PathBasename; pub struct PathBasename;
struct PathBasenameArguments { struct PathBasenameArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
replace: Option<Tagged<String>>, replace: Option<Tagged<String>>,
} }
impl PathSubcommandArguments for PathBasenameArguments { impl PathSubcommandArguments for PathBasenameArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -26,10 +26,11 @@ impl WholeStreamCommand for PathBasename {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path basename") Signature::build("path basename")
.rest( .named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
.named( .named(
"replace", "replace",
@ -46,7 +47,7 @@ impl WholeStreamCommand for PathBasename {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathBasenameArguments { let cmd_args = Arc::new(PathBasenameArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
replace: args.get_flag("replace")?, replace: args.get_flag("replace")?,
}); });
@ -58,12 +59,17 @@ impl WholeStreamCommand for PathBasename {
vec![ vec![
Example { Example {
description: "Get basename of a path", description: "Get basename of a path",
example: "echo 'C:\\Users\\joe\\test.txt' | path basename", example: "'C:\\Users\\joe\\test.txt' | path basename",
result: Some(vec![Value::from("test.txt")]), result: Some(vec![Value::from("test.txt")]),
}, },
Example {
description: "Get basename of a path in a column",
example: "ls .. | path basename -c [ name ]",
result: None,
},
Example { Example {
description: "Replace basename of a path", description: "Replace basename of a path",
example: "echo 'C:\\Users\\joe\\test.txt' | path basename -r 'spam.png'", example: "'C:\\Users\\joe\\test.txt' | path basename -r 'spam.png'",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
"C:\\Users\\joe\\spam.png", "C:\\Users\\joe\\spam.png",
))]), ))]),
@ -76,12 +82,17 @@ impl WholeStreamCommand for PathBasename {
vec![ vec![
Example { Example {
description: "Get basename of a path", description: "Get basename of a path",
example: "echo '/home/joe/test.txt' | path basename", example: "'/home/joe/test.txt' | path basename",
result: Some(vec![Value::from("test.txt")]), result: Some(vec![Value::from("test.txt")]),
}, },
Example {
description: "Get basename of a path in a column",
example: "ls .. | path basename -c [ name ]",
result: None,
},
Example { Example {
description: "Replace basename of a path", description: "Replace basename of a path",
example: "echo '/home/joe/test.txt' | path basename -r 'spam.png'", example: "'/home/joe/test.txt' | path basename -r 'spam.png'",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
"/home/joe/spam.png", "/home/joe/spam.png",
))]), ))]),

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -9,14 +9,14 @@ use std::path::Path;
pub struct PathDirname; pub struct PathDirname;
struct PathDirnameArguments { struct PathDirnameArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
replace: Option<Tagged<String>>, replace: Option<Tagged<String>>,
num_levels: Option<Tagged<u32>>, num_levels: Option<Tagged<u32>>,
} }
impl PathSubcommandArguments for PathDirnameArguments { impl PathSubcommandArguments for PathDirnameArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -27,10 +27,11 @@ impl WholeStreamCommand for PathDirname {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path dirname") Signature::build("path dirname")
.rest( .named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
.named( .named(
"replace", "replace",
@ -53,7 +54,7 @@ impl WholeStreamCommand for PathDirname {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathDirnameArguments { let cmd_args = Arc::new(PathDirnameArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
replace: args.get_flag("replace")?, replace: args.get_flag("replace")?,
num_levels: args.get_flag("num-levels")?, num_levels: args.get_flag("num-levels")?,
}); });
@ -66,20 +67,25 @@ impl WholeStreamCommand for PathDirname {
vec![ vec![
Example { Example {
description: "Get dirname of a path", description: "Get dirname of a path",
example: "echo 'C:\\Users\\joe\\code\\test.txt' | path dirname", example: "'C:\\Users\\joe\\code\\test.txt' | path dirname",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
"C:\\Users\\joe\\code", "C:\\Users\\joe\\code",
))]), ))]),
}, },
Example {
description: "Get dirname of a path in a column",
example: "ls ('.' | path expand) | path dirname -c [ name ]",
result: None,
},
Example { Example {
description: "Walk up two levels", description: "Walk up two levels",
example: "echo 'C:\\Users\\joe\\code\\test.txt' | path dirname -n 2", example: "'C:\\Users\\joe\\code\\test.txt' | path dirname -n 2",
result: Some(vec![Value::from(UntaggedValue::filepath("C:\\Users\\joe"))]), result: Some(vec![Value::from(UntaggedValue::filepath("C:\\Users\\joe"))]),
}, },
Example { Example {
description: "Replace the part that would be returned with a custom path", description: "Replace the part that would be returned with a custom path",
example: example:
"echo 'C:\\Users\\joe\\code\\test.txt' | path dirname -n 2 -r C:\\Users\\viking", "'C:\\Users\\joe\\code\\test.txt' | path dirname -n 2 -r C:\\Users\\viking",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
"C:\\Users\\viking\\code\\test.txt", "C:\\Users\\viking\\code\\test.txt",
))]), ))]),
@ -92,17 +98,22 @@ impl WholeStreamCommand for PathDirname {
vec![ vec![
Example { Example {
description: "Get dirname of a path", description: "Get dirname of a path",
example: "echo '/home/joe/code/test.txt' | path dirname", example: "'/home/joe/code/test.txt' | path dirname",
result: Some(vec![Value::from(UntaggedValue::filepath("/home/joe/code"))]), result: Some(vec![Value::from(UntaggedValue::filepath("/home/joe/code"))]),
}, },
Example {
description: "Get dirname of a path in a column",
example: "ls ('.' | path expand) | path dirname -c [ name ]",
result: None,
},
Example { Example {
description: "Walk up two levels", description: "Walk up two levels",
example: "echo '/home/joe/code/test.txt' | path dirname -n 2", example: "'/home/joe/code/test.txt' | path dirname -n 2",
result: Some(vec![Value::from(UntaggedValue::filepath("/home/joe"))]), result: Some(vec![Value::from(UntaggedValue::filepath("/home/joe"))]),
}, },
Example { Example {
description: "Replace the part that would be returned with a custom path", description: "Replace the part that would be returned with a custom path",
example: "echo '/home/joe/code/test.txt' | path dirname -n 2 -r /home/viking", example: "'/home/joe/code/test.txt' | path dirname -n 2 -r /home/viking",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
"/home/viking/code/test.txt", "/home/viking/code/test.txt",
))]), ))]),

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -8,12 +8,12 @@ use std::path::Path;
pub struct PathExists; pub struct PathExists;
struct PathExistsArguments { struct PathExistsArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
} }
impl PathSubcommandArguments for PathExistsArguments { impl PathSubcommandArguments for PathExistsArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -23,10 +23,11 @@ impl WholeStreamCommand for PathExists {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path exists").rest( Signature::build("path exists").named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
} }
@ -37,7 +38,7 @@ impl WholeStreamCommand for PathExists {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathExistsArguments { let cmd_args = Arc::new(PathExistsArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
}); });
Ok(operate(args.input, &action, tag.span, cmd_args)) Ok(operate(args.input, &action, tag.span, cmd_args))
@ -45,20 +46,34 @@ impl WholeStreamCommand for PathExists {
#[cfg(windows)] #[cfg(windows)]
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
description: "Check if a file exists", Example {
example: "echo 'C:\\Users\\joe\\todo.txt' | path exists", description: "Check if a file exists",
result: Some(vec![Value::from(UntaggedValue::boolean(false))]), example: "'C:\\Users\\joe\\todo.txt' | path exists",
}] result: Some(vec![Value::from(UntaggedValue::boolean(false))]),
},
Example {
description: "Check if a file exists in a column",
example: "ls | path exists -c [ name ]",
result: None,
},
]
} }
#[cfg(not(windows))] #[cfg(not(windows))]
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
description: "Check if a file exists", Example {
example: "echo '/home/joe/todo.txt' | path exists", description: "Check if a file exists",
result: Some(vec![Value::from(UntaggedValue::boolean(false))]), example: "'/home/joe/todo.txt' | path exists",
}] result: Some(vec![Value::from(UntaggedValue::boolean(false))]),
},
Example {
description: "Check if a file exists in a column",
example: "ls | path exists -c [ name ]",
result: None,
},
]
} }
} }

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -11,12 +11,12 @@ pub struct PathExpand;
struct PathExpandArguments { struct PathExpandArguments {
strict: bool, strict: bool,
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
} }
impl PathSubcommandArguments for PathExpandArguments { impl PathSubcommandArguments for PathExpandArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -32,10 +32,11 @@ impl WholeStreamCommand for PathExpand {
"Throw an error if the path could not be expanded", "Throw an error if the path could not be expanded",
Some('s'), Some('s'),
) )
.rest( .named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
} }
@ -47,7 +48,7 @@ impl WholeStreamCommand for PathExpand {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathExpandArguments { let cmd_args = Arc::new(PathExpandArguments {
strict: args.has_flag("strict"), strict: args.has_flag("strict"),
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
}); });
Ok(operate(args.input, &action, tag.span, cmd_args)) Ok(operate(args.input, &action, tag.span, cmd_args))
@ -63,6 +64,11 @@ impl WholeStreamCommand for PathExpand {
UntaggedValue::filepath(r"C:\Users\joe\bar").into_value(Span::new(0, 25)) UntaggedValue::filepath(r"C:\Users\joe\bar").into_value(Span::new(0, 25))
]), ]),
}, },
Example {
description: "Expand a path in a column",
example: "ls | path expand -c [ name ]",
result: None,
},
Example { Example {
description: "Expand a relative path", description: "Expand a relative path",
example: r"'foo\..\bar' | path expand", example: r"'foo\..\bar' | path expand",
@ -83,6 +89,11 @@ impl WholeStreamCommand for PathExpand {
UntaggedValue::filepath("/home/joe/bar").into_value(Span::new(0, 22)) UntaggedValue::filepath("/home/joe/bar").into_value(Span::new(0, 22))
]), ]),
}, },
Example {
description: "Expand a path in a column",
example: "ls | path expand -c [ name ]",
result: None,
},
Example { Example {
description: "Expand a relative path", description: "Expand a relative path",
example: "'foo/../bar' | path expand", example: "'foo/../bar' | path expand",

View File

@ -1,4 +1,6 @@
use super::{handle_value, join_path, operate_column_paths, PathSubcommandArguments}; use super::{
column_paths_from_args, handle_value, join_path, operate_column_paths, PathSubcommandArguments,
};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -9,13 +11,13 @@ use std::path::{Path, PathBuf};
pub struct PathJoin; pub struct PathJoin;
struct PathJoinArguments { struct PathJoinArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
append: Option<Tagged<PathBuf>>, append: Option<Tagged<PathBuf>>,
} }
impl PathSubcommandArguments for PathJoinArguments { impl PathSubcommandArguments for PathJoinArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -26,16 +28,16 @@ impl WholeStreamCommand for PathJoin {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path join") Signature::build("path join")
.rest(
"rest",
SyntaxShape::ColumnPath,
"Optionally operate by column path",
)
.named( .named(
"columns",
SyntaxShape::Table,
"Optionally operate by column path",
Some('c'),
)
.optional(
"append", "append",
SyntaxShape::FilePath, SyntaxShape::FilePath,
"Path to append to the input", "Path to append to the input",
Some('a'),
) )
} }
@ -50,9 +52,10 @@ the output of 'path parse' and 'path split' subcommands."#
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathJoinArguments { let cmd_args = Arc::new(PathJoinArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
append: args.get_flag("append")?, append: args.opt(0)?,
}); });
Ok(operate_join(args.input, &action, tag, cmd_args)) Ok(operate_join(args.input, &action, tag, cmd_args))
@ -63,21 +66,26 @@ the output of 'path parse' and 'path split' subcommands."#
vec![ vec![
Example { Example {
description: "Append a filename to a path", description: "Append a filename to a path",
example: r"echo 'C:\Users\viking' | path join -a spam.txt", example: r"'C:\Users\viking' | path join spam.txt",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"C:\Users\viking\spam.txt", r"C:\Users\viking\spam.txt",
))]), ))]),
}, },
Example {
description: "Append a filename to a path inside a column",
example: r"ls | path join spam.txt -c [ name ]",
result: None,
},
Example { Example {
description: "Join a list of parts into a path", description: "Join a list of parts into a path",
example: r"echo [ 'C:' '\' 'Users' 'viking' 'spam.txt' ] | path join", example: r"[ 'C:' '\' 'Users' 'viking' 'spam.txt' ] | path join",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"C:\Users\viking\spam.txt", r"C:\Users\viking\spam.txt",
))]), ))]),
}, },
Example { Example {
description: "Join a structured path into a path", description: "Join a structured path into a path",
example: r"echo [ [parent stem extension]; ['C:\Users\viking' 'spam' 'txt']] | path join", example: r"[ [parent stem extension]; ['C:\Users\viking' 'spam' 'txt']] | path join",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"C:\Users\viking\spam.txt", r"C:\Users\viking\spam.txt",
))]), ))]),
@ -90,21 +98,26 @@ the output of 'path parse' and 'path split' subcommands."#
vec![ vec![
Example { Example {
description: "Append a filename to a path", description: "Append a filename to a path",
example: r"echo '/home/viking' | path join -a spam.txt", example: r"'/home/viking' | path join spam.txt",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"/home/viking/spam.txt", r"/home/viking/spam.txt",
))]), ))]),
}, },
Example {
description: "Append a filename to a path inside a column",
example: r"ls | path join spam.txt -c [ name ]",
result: None,
},
Example { Example {
description: "Join a list of parts into a path", description: "Join a list of parts into a path",
example: r"echo [ '/' 'home' 'viking' 'spam.txt' ] | path join", example: r"[ '/' 'home' 'viking' 'spam.txt' ] | path join",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"/home/viking/spam.txt", r"/home/viking/spam.txt",
))]), ))]),
}, },
Example { Example {
description: "Join a structured path into a path", description: "Join a structured path into a path",
example: r"echo [[ parent stem extension ]; [ '/home/viking' 'spam' 'txt' ]] | path join", example: r"[[ parent stem extension ]; [ '/home/viking' 'spam' 'txt' ]] | path join",
result: Some(vec![Value::from(UntaggedValue::filepath( result: Some(vec![Value::from(UntaggedValue::filepath(
r"/home/viking/spam.txt", r"/home/viking/spam.txt",
))]), ))]),

View File

@ -61,7 +61,7 @@ fn encode_path(
ALLOWED_COLUMNS.join(", ") ALLOWED_COLUMNS.join(", ")
); );
return Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Invalid column name", "Expected structured path table",
msg, msg,
new_span, new_span,
"originates from here", "originates from here",
@ -216,3 +216,36 @@ where
operate_column_paths(input, action, span, args) operate_column_paths(input, action, span, args)
} }
} }
fn column_paths_from_args(args: &CommandArgs) -> Result<Vec<ColumnPath>, ShellError> {
let column_paths: Option<Vec<Value>> = args.get_flag("columns")?;
let has_columns = column_paths.is_some();
let column_paths = match column_paths {
Some(cols) => {
let mut c = Vec::new();
for col in cols {
let colpath = ColumnPath::build(&col.convert_to_string().spanned_unknown());
if !colpath.is_empty() {
c.push(colpath)
}
}
c
}
None => Vec::new(),
};
if has_columns && column_paths.is_empty() {
let colval: Option<Value> = args.get_flag("columns")?;
let colspan = match colval {
Some(v) => v.tag.span,
None => Span::unknown(),
};
return Err(ShellError::labeled_error(
"Requires a list of columns",
"must be a list of columns",
colspan,
));
}
Ok(column_paths)
}

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -11,13 +11,13 @@ use std::path::Path;
pub struct PathParse; pub struct PathParse;
struct PathParseArguments { struct PathParseArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
extension: Option<Tagged<String>>, extension: Option<Tagged<String>>,
} }
impl PathSubcommandArguments for PathParseArguments { impl PathSubcommandArguments for PathParseArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -28,10 +28,11 @@ impl WholeStreamCommand for PathParse {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path parse") Signature::build("path parse")
.rest( .named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
.named( .named(
"extension", "extension",
@ -53,7 +54,7 @@ On Windows, an extra 'prefix' column is added."#
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathParseArguments { let cmd_args = Arc::new(PathParseArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
extension: args.get_flag("extension")?, extension: args.get_flag("extension")?,
}); });
@ -65,22 +66,22 @@ On Windows, an extra 'prefix' column is added."#
vec![ vec![
Example { Example {
description: "Parse a single path", description: "Parse a single path",
example: r"echo 'C:\Users\viking\spam.txt' | path parse", example: r"'C:\Users\viking\spam.txt' | path parse",
result: None, result: None,
}, },
Example { Example {
description: "Replace a complex extension", description: "Replace a complex extension",
example: r"echo 'C:\Users\viking\spam.tar.gz' | path parse -e tar.gz | update extension { 'txt' }", example: r"'C:\Users\viking\spam.tar.gz' | path parse -e tar.gz | update extension { 'txt' }",
result: None, result: None,
}, },
Example { Example {
description: "Ignore the extension", description: "Ignore the extension",
example: r"echo 'C:\Users\viking.d' | path parse -e ''", example: r"'C:\Users\viking.d' | path parse -e ''",
result: None, result: None,
}, },
Example { Example {
description: "Parse all paths under the 'name' column", description: "Parse all paths under the 'name' column",
example: r"ls | path parse name", example: r"ls | path parse -c [ name ]",
result: None, result: None,
}, },
] ]
@ -91,22 +92,22 @@ On Windows, an extra 'prefix' column is added."#
vec![ vec![
Example { Example {
description: "Parse a path", description: "Parse a path",
example: r"echo '/home/viking/spam.txt' | path parse", example: r"'/home/viking/spam.txt' | path parse",
result: None, result: None,
}, },
Example { Example {
description: "Replace a complex extension", description: "Replace a complex extension",
example: r"echo '/home/viking/spam.tar.gz' | path parse -e tar.gz | update extension { 'txt' }", example: r"'/home/viking/spam.tar.gz' | path parse -e tar.gz | update extension { 'txt' }",
result: None, result: None,
}, },
Example { Example {
description: "Ignore the extension", description: "Ignore the extension",
example: r"echo '/etc/conf.d' | path parse -e ''", example: r"'/etc/conf.d' | path parse -e ''",
result: None, result: None,
}, },
Example { Example {
description: "Parse all paths under the 'name' column", description: "Parse all paths under the 'name' column",
example: r"ls | path parse name", example: r"ls | path parse -c [ name ]",
result: None, result: None,
}, },
] ]

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -10,12 +10,12 @@ pub struct PathRelativeTo;
struct PathRelativeToArguments { struct PathRelativeToArguments {
path: Tagged<PathBuf>, path: Tagged<PathBuf>,
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
} }
impl PathSubcommandArguments for PathRelativeToArguments { impl PathSubcommandArguments for PathRelativeToArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -31,10 +31,11 @@ impl WholeStreamCommand for PathRelativeTo {
SyntaxShape::FilePath, SyntaxShape::FilePath,
"Parent shared with the input path", "Parent shared with the input path",
) )
.rest( .named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
} }
@ -52,7 +53,7 @@ path."#
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathRelativeToArguments { let cmd_args = Arc::new(PathRelativeToArguments {
path: args.req(0)?, path: args.req(0)?,
rest: args.rest(1)?, columns: column_paths_from_args(&args)?,
}); });
Ok(operate(args.input, &action, tag.span, cmd_args)) Ok(operate(args.input, &action, tag.span, cmd_args))
@ -66,6 +67,11 @@ path."#
example: r"'C:\Users\viking' | path relative-to 'C:\Users'", example: r"'C:\Users\viking' | path relative-to 'C:\Users'",
result: Some(vec![Value::from(UntaggedValue::filepath(r"viking"))]), result: Some(vec![Value::from(UntaggedValue::filepath(r"viking"))]),
}, },
Example {
description: "Find a relative path from two absolute paths in a column",
example: "ls ~ | path relative-to ~ -c [ name ]",
result: None,
},
Example { Example {
description: "Find a relative path from two relative paths", description: "Find a relative path from two relative paths",
example: r"'eggs\bacon\sausage\spam' | path relative-to 'eggs\bacon\sausage'", example: r"'eggs\bacon\sausage\spam' | path relative-to 'eggs\bacon\sausage'",
@ -82,6 +88,11 @@ path."#
example: r"'/home/viking' | path relative-to '/home'", example: r"'/home/viking' | path relative-to '/home'",
result: Some(vec![Value::from(UntaggedValue::filepath(r"viking"))]), result: Some(vec![Value::from(UntaggedValue::filepath(r"viking"))]),
}, },
Example {
description: "Find a relative path from two absolute paths in a column",
example: "ls ~ | path relative-to ~ -c [ name ]",
result: None,
},
Example { Example {
description: "Find a relative path from two relative paths", description: "Find a relative path from two relative paths",
example: r"'eggs/bacon/sausage/spam' | path relative-to 'eggs/bacon/sausage'", example: r"'eggs/bacon/sausage/spam' | path relative-to 'eggs/bacon/sausage'",

View File

@ -1,4 +1,4 @@
use super::{handle_value, operate_column_paths, PathSubcommandArguments}; use super::{column_paths_from_args, handle_value, operate_column_paths, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -8,12 +8,12 @@ use std::path::Path;
pub struct PathSplit; pub struct PathSplit;
struct PathSplitArguments { struct PathSplitArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
} }
impl PathSubcommandArguments for PathSplitArguments { impl PathSubcommandArguments for PathSplitArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -23,10 +23,11 @@ impl WholeStreamCommand for PathSplit {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path split").rest( Signature::build("path split").named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
} }
@ -37,7 +38,7 @@ impl WholeStreamCommand for PathSplit {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathSplitArguments { let cmd_args = Arc::new(PathSplitArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
}); });
Ok(operate_split(args.input, &action, tag.span, cmd_args)) Ok(operate_split(args.input, &action, tag.span, cmd_args))
@ -48,7 +49,7 @@ impl WholeStreamCommand for PathSplit {
vec![ vec![
Example { Example {
description: "Split a path into parts", description: "Split a path into parts",
example: r"echo 'C:\Users\viking\spam.txt' | path split", example: r"'C:\Users\viking\spam.txt' | path split",
result: Some(vec![ result: Some(vec![
Value::from(UntaggedValue::string("C:")), Value::from(UntaggedValue::string("C:")),
Value::from(UntaggedValue::string(r"\")), Value::from(UntaggedValue::string(r"\")),
@ -59,7 +60,7 @@ impl WholeStreamCommand for PathSplit {
}, },
Example { Example {
description: "Split all paths under the 'name' column", description: "Split all paths under the 'name' column",
example: r"ls | path split name", example: r"ls ('.' | path expand) | path split -c [ name ]",
result: None, result: None,
}, },
] ]
@ -70,7 +71,7 @@ impl WholeStreamCommand for PathSplit {
vec![ vec![
Example { Example {
description: "Split a path into parts", description: "Split a path into parts",
example: r"echo '/home/viking/spam.txt' | path split", example: r"'/home/viking/spam.txt' | path split",
result: Some(vec![ result: Some(vec![
Value::from(UntaggedValue::string("/")), Value::from(UntaggedValue::string("/")),
Value::from(UntaggedValue::string("home")), Value::from(UntaggedValue::string("home")),
@ -80,7 +81,7 @@ impl WholeStreamCommand for PathSplit {
}, },
Example { Example {
description: "Split all paths under the 'name' column", description: "Split all paths under the 'name' column",
example: r"ls | path split name", example: r"ls ('.' | path expand) | path split -c [ name ]",
result: None, result: None,
}, },
] ]

View File

@ -1,4 +1,4 @@
use super::{operate, PathSubcommandArguments}; use super::{column_paths_from_args, operate, PathSubcommandArguments};
use crate::prelude::*; use crate::prelude::*;
use nu_engine::filesystem::filesystem_shell::get_file_type; use nu_engine::filesystem::filesystem_shell::get_file_type;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
@ -9,12 +9,12 @@ use std::path::Path;
pub struct PathType; pub struct PathType;
struct PathTypeArguments { struct PathTypeArguments {
rest: Vec<ColumnPath>, columns: Vec<ColumnPath>,
} }
impl PathSubcommandArguments for PathTypeArguments { impl PathSubcommandArguments for PathTypeArguments {
fn get_column_paths(&self) -> &Vec<ColumnPath> { fn get_column_paths(&self) -> &Vec<ColumnPath> {
&self.rest &self.columns
} }
} }
@ -24,10 +24,11 @@ impl WholeStreamCommand for PathType {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("path type").rest( Signature::build("path type").named(
"rest", "columns",
SyntaxShape::ColumnPath, SyntaxShape::Table,
"Optionally operate by column path", "Optionally operate by column path",
Some('c'),
) )
} }
@ -38,18 +39,25 @@ impl WholeStreamCommand for PathType {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone(); let tag = args.call_info.name_tag.clone();
let cmd_args = Arc::new(PathTypeArguments { let cmd_args = Arc::new(PathTypeArguments {
rest: args.rest(0)?, columns: column_paths_from_args(&args)?,
}); });
Ok(operate(args.input, &action, tag.span, cmd_args)) Ok(operate(args.input, &action, tag.span, cmd_args))
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
description: "Show type of a filepath", Example {
example: "echo '.' | path type", description: "Show type of a filepath",
result: Some(vec![Value::from("Dir")]), example: "'.' | path type",
}] result: Some(vec![Value::from("Dir")]),
},
Example {
description: "Show type of a filepath in a column",
example: "ls | path type -c [ name ]",
result: None,
},
]
} }
} }

View File

@ -8,7 +8,7 @@ fn returns_path_joined_with_column_path() {
cwd: "tests", pipeline( cwd: "tests", pipeline(
r#" r#"
echo [ [name]; [eggs] ] echo [ [name]; [eggs] ]
| path join -a spam.txt name | path join spam.txt -c [ name ]
| get name | get name
"# "#
)); ));
@ -37,7 +37,7 @@ fn appends_slash_when_joined_with_empty_path() {
cwd: "tests", pipeline( cwd: "tests", pipeline(
r#" r#"
echo "/some/dir" echo "/some/dir"
| path join -a '' | path join ''
"# "#
)); ));
@ -51,7 +51,7 @@ fn returns_joined_path_when_joining_empty_path() {
cwd: "tests", pipeline( cwd: "tests", pipeline(
r#" r#"
echo "" echo ""
| path join -a foo.txt | path join foo.txt
"# "#
)); ));

View File

@ -105,7 +105,7 @@ fn parses_column_path_extension() {
cwd: "tests", pipeline( cwd: "tests", pipeline(
r#" r#"
echo [[home, barn]; ['home/viking/spam.txt', 'barn/cow/moo.png']] echo [[home, barn]; ['home/viking/spam.txt', 'barn/cow/moo.png']]
| path parse home barn | path parse -c [ home barn ]
| get barn | get barn
| get extension | get extension
"# "#

View File

@ -37,7 +37,7 @@ fn splits_correctly_with_column_path() {
['home/viking/spam.txt', 'barn/cow/moo.png'] ['home/viking/spam.txt', 'barn/cow/moo.png']
['home/viking/eggs.txt', 'barn/goat/cheese.png'] ['home/viking/eggs.txt', 'barn/goat/cheese.png']
] ]
| path split home barn | path split -c [ home barn ]
| get barn | get barn
| length | length
"# "#

View File

@ -101,6 +101,7 @@ impl FromValue for i64 {
v.as_i64() v.as_i64()
} }
} }
impl FromValue for Tagged<i64> { impl FromValue for Tagged<i64> {
fn from_value(v: &Value) -> Result<Self, ShellError> { fn from_value(v: &Value) -> Result<Self, ShellError> {
let tag = v.tag.clone(); let tag = v.tag.clone();