forked from extern/nushell
Refactor path commands (#9687)
This commit is contained in:
parent
8c52b7a23a
commit
ba766de5d1
@ -11,15 +11,10 @@ use nu_protocol::{
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
replace: Option<Spanned<String>>,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -33,15 +28,11 @@ impl Command for SubCommand {
|
||||
Signature::build("path basename")
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
// TODO: Why do these commands not use CellPaths in a standard way?
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, convert strings in the given columns to their basename",
|
||||
Some('c'),
|
||||
)
|
||||
.named(
|
||||
"replace",
|
||||
SyntaxShape::String,
|
||||
@ -63,7 +54,6 @@ impl Command for SubCommand {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
replace: call.get_flag(engine_state, stack, "replace")?,
|
||||
};
|
||||
|
||||
@ -86,21 +76,12 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_string("test.txt")),
|
||||
},
|
||||
Example {
|
||||
description: "Get basename of a path in a column",
|
||||
example: "ls .. | path basename -c [ name ]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Get basename of a path in a column",
|
||||
example: "[[name];[C:\\Users\\Joe]] | path basename -c [ name ]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["name".to_string()],
|
||||
vals: vec![Value::test_string("Joe")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
description: "Get basename of a list of paths",
|
||||
example: r"[ C:\Users\joe, C:\Users\doe ] | path basename",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("joe"),
|
||||
Value::test_string("doe"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Replace basename of a path",
|
||||
@ -119,16 +100,12 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_string("test.txt")),
|
||||
},
|
||||
Example {
|
||||
description: "Get basename of a path by column",
|
||||
example: "[[name];[/home/joe]] | path basename -c [ name ]",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["name".to_string()],
|
||||
vals: vec![Value::test_string("joe")],
|
||||
span: Span::test_data(),
|
||||
}],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
description: "Get basename of a list of paths",
|
||||
example: "[ /home/joe, /home/doe ] | path basename",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("joe"),
|
||||
Value::test_string("doe"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Replace basename of a path",
|
||||
|
@ -11,16 +11,11 @@ use nu_protocol::{
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
replace: Option<Spanned<String>>,
|
||||
num_levels: Option<i64>,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -32,13 +27,13 @@ impl Command for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path dirname")
|
||||
.input_output_types(vec![(Type::String, Type::String)])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, convert strings at the given columns to their dirname",
|
||||
Some('c'),
|
||||
)
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
])
|
||||
.named(
|
||||
"replace",
|
||||
SyntaxShape::String,
|
||||
@ -66,7 +61,6 @@ impl Command for SubCommand {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
replace: call.get_flag(engine_state, stack, "replace")?,
|
||||
num_levels: call.get_flag(engine_state, stack, "num-levels")?,
|
||||
};
|
||||
@ -90,9 +84,12 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_string("C:\\Users\\joe\\code")),
|
||||
},
|
||||
Example {
|
||||
description: "Get dirname of a path in a column",
|
||||
example: "ls ('.' | path expand) | path dirname -c [ name ]",
|
||||
result: None,
|
||||
description: "Get dirname of a list of paths",
|
||||
example: r"[ C:\Users\joe\test.txt, C:\Users\doe\test.txt ] | path dirname",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string(r"C:\Users\joe"),
|
||||
Value::test_string(r"C:\Users\doe"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Walk up two levels",
|
||||
@ -117,9 +114,12 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_string("/home/joe/code")),
|
||||
},
|
||||
Example {
|
||||
description: "Get dirname of a path in a column",
|
||||
example: "ls ('.' | path expand) | path dirname -c [ name ]",
|
||||
result: None,
|
||||
description: "Get dirname of a list of paths",
|
||||
example: "[ /home/joe/test.txt, /home/doe/test.txt ] | path dirname",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("/home/joe"),
|
||||
Value::test_string("/home/doe"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Walk up two levels",
|
||||
|
@ -1,25 +1,20 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use nu_engine::{current_dir, CallExt};
|
||||
use nu_engine::current_dir;
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
pwd: PathBuf,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -30,14 +25,13 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path exists")
|
||||
.input_output_types(vec![(Type::String, Type::Bool)])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, check strings at the given columns, and replace with result",
|
||||
Some('c'),
|
||||
)
|
||||
Signature::build("path exists").input_output_types(vec![
|
||||
(Type::String, Type::Bool),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::Bool)),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -58,7 +52,6 @@ If you need to distinguish dirs and files, please use `path type`."#
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
pwd: current_dir(engine_state, stack)?,
|
||||
};
|
||||
// This doesn't match explicit nulls
|
||||
@ -80,9 +73,12 @@ If you need to distinguish dirs and files, please use `path type`."#
|
||||
result: Some(Value::test_bool(false)),
|
||||
},
|
||||
Example {
|
||||
description: "Check if a file exists in a column",
|
||||
example: "ls | path exists -c [ name ]",
|
||||
result: None,
|
||||
description: "Check if files in list exist",
|
||||
example: r"[ C:\joe\todo.txt, C:\Users\doe\todo.txt ] | path exists",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_bool(false),
|
||||
Value::test_bool(false),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -96,9 +92,12 @@ If you need to distinguish dirs and files, please use `path type`."#
|
||||
result: Some(Value::test_bool(false)),
|
||||
},
|
||||
Example {
|
||||
description: "Check if a file exists in a column",
|
||||
example: "ls | path exists -c [ name ]",
|
||||
result: None,
|
||||
description: "Check if files in list exist",
|
||||
example: "[ /home/joe/todo.txt, /home/doe/todo.txt ] | path exists",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_bool(false),
|
||||
Value::test_bool(false),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1,28 +1,22 @@
|
||||
use std::path::Path;
|
||||
|
||||
use nu_engine::env::current_dir_str;
|
||||
use nu_engine::CallExt;
|
||||
use nu_path::{canonicalize_with, expand_path_with};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
strict: bool,
|
||||
columns: Option<Vec<String>>,
|
||||
cwd: String,
|
||||
not_follow_symlink: bool,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -34,19 +28,19 @@ impl Command for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path expand")
|
||||
.input_output_types(vec![(Type::String, Type::String)])
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
])
|
||||
.switch(
|
||||
"strict",
|
||||
"Throw an error if the path could not be expanded",
|
||||
Some('s'),
|
||||
)
|
||||
.switch("no-symlink", "Do not resolve symbolic links", Some('n'))
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, expand strings at the given columns",
|
||||
Some('c'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -63,7 +57,6 @@ impl Command for SubCommand {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
strict: call.has_flag("strict"),
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
cwd: current_dir_str(engine_state, stack)?,
|
||||
not_follow_symlink: call.has_flag("no-symlink"),
|
||||
};
|
||||
@ -85,20 +78,18 @@ impl Command for SubCommand {
|
||||
example: r"'C:\Users\joe\foo\..\bar' | path expand",
|
||||
result: Some(Value::test_string(r"C:\Users\joe\bar")),
|
||||
},
|
||||
Example {
|
||||
description: "Expand a path in a column",
|
||||
example: "ls | path expand -c [ name ]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Expand a relative path",
|
||||
example: r"'foo\..\bar' | path expand",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Expand an absolute path without following symlink",
|
||||
example: r"'foo\..\bar' | path expand -n",
|
||||
result: None,
|
||||
description: "Expand a list of paths",
|
||||
example: r"[ C:\foo\..\bar, C:\foo\..\baz ] | path expand",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string(r"C:\bar"),
|
||||
Value::test_string(r"C:\baz"),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -111,16 +102,19 @@ impl Command for SubCommand {
|
||||
example: "'/home/joe/foo/../bar' | path expand",
|
||||
result: Some(Value::test_string("/home/joe/bar")),
|
||||
},
|
||||
Example {
|
||||
description: "Expand a path in a column",
|
||||
example: "ls | path expand -c [ name ]",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Expand a relative path",
|
||||
example: "'foo/../bar' | path expand",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Expand a list of paths",
|
||||
example: "[ /foo/../bar, /foo/../baz ] | path expand",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("/bar"),
|
||||
Value::test_string("/baz"),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,10 @@ use nu_protocol::{
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
append: Vec<Spanned<String>>,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -38,13 +33,6 @@ impl Command for SubCommand {
|
||||
(Type::Record(vec![]), Type::String),
|
||||
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
|
||||
])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, join strings at the given columns",
|
||||
Some('c'),
|
||||
)
|
||||
.allow_variants_without_examples(true)
|
||||
.rest("append", SyntaxShape::String, "Path to append to the input")
|
||||
}
|
||||
|
||||
@ -66,7 +54,6 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
append: call.rest(engine_state, stack, 0)?,
|
||||
};
|
||||
|
||||
@ -105,11 +92,6 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
example: r"'C:\Users\viking' | path join spams this_spam.txt",
|
||||
result: Some(Value::test_string(r"C:\Users\viking\spams\this_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 {
|
||||
description: "Join a list of parts into a path",
|
||||
example: r"[ 'C:' '\' 'Users' 'viking' 'spam.txt' ] | path join",
|
||||
@ -117,6 +99,11 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
},
|
||||
Example {
|
||||
description: "Join a structured path into a path",
|
||||
example: r"{ parent: 'C:\Users\viking', stem: 'spam', extension: 'txt' } | path join",
|
||||
result: Some(Value::test_string(r"C:\Users\viking\spam.txt")),
|
||||
},
|
||||
Example {
|
||||
description: "Join a table of structured paths into a list of paths",
|
||||
example: r"[ [parent stem extension]; ['C:\Users\viking' 'spam' 'txt']] | path join",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_string(r"C:\Users\viking\spam.txt")],
|
||||
@ -139,11 +126,6 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
example: r"'/home/viking' | path join spams this_spam.txt",
|
||||
result: Some(Value::test_string(r"/home/viking/spams/this_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 {
|
||||
description: "Join a list of parts into a path",
|
||||
example: r"[ '/' 'home' 'viking' 'spam.txt' ] | path join",
|
||||
@ -151,6 +133,11 @@ the output of 'path parse' and 'path split' subcommands."#
|
||||
},
|
||||
Example {
|
||||
description: "Join a structured path into a path",
|
||||
example: r"{ parent: '/home/viking', stem: 'spam', extension: 'txt' } | path join",
|
||||
result: Some(Value::test_string(r"/home/viking/spam.txt")),
|
||||
},
|
||||
Example {
|
||||
description: "Join a table of structured paths into a list of paths",
|
||||
example: r"[[ parent stem extension ]; [ '/home/viking' 'spam' 'txt' ]] | path join",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_string(r"/home/viking/spam.txt")],
|
||||
@ -209,24 +196,11 @@ fn join_list(parts: &[Value], head: Span, span: Span, args: &Arguments) -> Value
|
||||
}
|
||||
|
||||
fn join_record(cols: &[String], vals: &[Value], head: Span, span: Span, args: &Arguments) -> Value {
|
||||
if args.columns.is_some() {
|
||||
super::operate(
|
||||
&join_single,
|
||||
args,
|
||||
Value::Record {
|
||||
cols: cols.to_vec(),
|
||||
vals: vals.to_vec(),
|
||||
span,
|
||||
},
|
||||
span,
|
||||
)
|
||||
} else {
|
||||
match merge_record(cols, vals, head, span) {
|
||||
Ok(p) => join_single(p.as_path(), head, args),
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
}
|
||||
match merge_record(cols, vals, head, span) {
|
||||
Ok(p) => join_single(p.as_path(), head, args),
|
||||
Err(error) => Value::Error {
|
||||
error: Box::new(error),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,7 @@ const ALLOWED_COLUMNS: [&str; 4] = ["prefix", "parent", "stem", "extension"];
|
||||
#[cfg(not(windows))]
|
||||
const ALLOWED_COLUMNS: [&str; 3] = ["parent", "stem", "extension"];
|
||||
|
||||
trait PathSubcommandArguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>>;
|
||||
}
|
||||
trait PathSubcommandArguments {}
|
||||
|
||||
fn operate<F, A>(cmd: &F, args: &A, v: Value, name: Span) -> Value
|
||||
where
|
||||
@ -40,45 +38,6 @@ where
|
||||
{
|
||||
match v {
|
||||
Value::String { val, span } => cmd(StdPath::new(&val), span, args),
|
||||
Value::Record { cols, vals, span } => {
|
||||
let col = if let Some(col) = args.get_columns() {
|
||||
col
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
if col.is_empty() {
|
||||
return Value::Error {
|
||||
error: Box::new(ShellError::UnsupportedInput(
|
||||
String::from("when the input is a table, you must specify the columns"),
|
||||
"value originates from here".into(),
|
||||
name,
|
||||
span,
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
let mut output_cols = vec![];
|
||||
let mut output_vals = vec![];
|
||||
|
||||
for (k, v) in cols.iter().zip(vals) {
|
||||
output_cols.push(k.clone());
|
||||
if col.contains(k) {
|
||||
let new_val = match v {
|
||||
Value::String { val, span } => cmd(StdPath::new(&val), span, args),
|
||||
_ => return handle_invalid_values(v, name),
|
||||
};
|
||||
output_vals.push(new_val);
|
||||
} else {
|
||||
output_vals.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
Value::Record {
|
||||
cols: output_cols,
|
||||
vals: output_vals,
|
||||
span,
|
||||
}
|
||||
}
|
||||
_ => handle_invalid_values(v, name),
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,10 @@ use nu_protocol::{
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
extension: Option<Spanned<String>>,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -32,13 +27,10 @@ impl Command for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path parse")
|
||||
.input_output_types(vec![(Type::String, Type::Record(vec![]))])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, convert strings at the given columns",
|
||||
Some('c'),
|
||||
)
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::Record(vec![])),
|
||||
(Type::List(Box::new(Type::String)), Type::Table(vec![])),
|
||||
])
|
||||
.named(
|
||||
"extension",
|
||||
SyntaxShape::String,
|
||||
@ -65,7 +57,6 @@ On Windows, an extra 'prefix' column is added."#
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
extension: call.get_flag(engine_state, stack, "extension")?,
|
||||
};
|
||||
|
||||
@ -126,9 +117,40 @@ On Windows, an extra 'prefix' column is added."#
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Parse all paths under the 'name' column",
|
||||
example: r"ls | path parse -c [ name ]",
|
||||
result: None,
|
||||
description: "Parse all paths in a list",
|
||||
example: r"[ C:\Users\viking.d C:\Users\spam.txt ] | path parse",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
"stem".into(),
|
||||
"extension".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("C:"),
|
||||
Value::test_string(r"C:\Users"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("d"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
cols: vec![
|
||||
"prefix".into(),
|
||||
"parent".into(),
|
||||
"stem".into(),
|
||||
"extension".into(),
|
||||
],
|
||||
vals: vec![
|
||||
Value::test_string("C:"),
|
||||
Value::test_string(r"C:\Users"),
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -168,9 +190,28 @@ On Windows, an extra 'prefix' column is added."#
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Parse all paths under the 'name' column",
|
||||
example: r"ls | path parse -c [ name ]",
|
||||
result: None,
|
||||
description: "Parse all paths in a list",
|
||||
example: r"[ /home/viking.d /home/spam.txt ] | path parse",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/home"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("d"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Record {
|
||||
cols: vec!["parent".into(), "stem".into(), "extension".into()],
|
||||
vals: vec![
|
||||
Value::test_string("/home"),
|
||||
Value::test_string("spam"),
|
||||
Value::test_string("txt"),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
},
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -13,14 +13,9 @@ use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
path: Spanned<String>,
|
||||
columns: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -32,18 +27,18 @@ impl Command for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path relative-to")
|
||||
.input_output_types(vec![(Type::String, Type::String)])
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
])
|
||||
.required(
|
||||
"path",
|
||||
SyntaxShape::String,
|
||||
"Parent shared with the input path",
|
||||
)
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, convert strings at the given columns",
|
||||
Some('c'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -66,7 +61,6 @@ path."#
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
path: call.req(engine_state, stack, 0)?,
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
};
|
||||
|
||||
// This doesn't match explicit nulls
|
||||
@ -88,9 +82,12 @@ path."#
|
||||
result: Some(Value::test_string(r"viking")),
|
||||
},
|
||||
Example {
|
||||
description: "Find a relative path from two absolute paths in a column",
|
||||
example: "ls ~ | path relative-to ~ -c [ name ]",
|
||||
result: None,
|
||||
description: "Find a relative path from absolute paths in list",
|
||||
example: r"[ C:\Users\viking, C:\Users\spam ] | path relative-to C:\Users",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("spam"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Find a relative path from two relative paths",
|
||||
@ -109,9 +106,12 @@ path."#
|
||||
result: Some(Value::test_string(r"viking")),
|
||||
},
|
||||
Example {
|
||||
description: "Find a relative path from two absolute paths in a column",
|
||||
example: "ls ~ | path relative-to ~ -c [ name ]",
|
||||
result: None,
|
||||
description: "Find a relative path from absolute paths in list",
|
||||
example: r"[ /home/viking, /home/spam ] | path relative-to '/home'",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("spam"),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Find a relative path from two relative paths",
|
||||
|
@ -1,23 +1,16 @@
|
||||
use std::path::{Component, Path};
|
||||
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
}
|
||||
struct Arguments;
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -28,14 +21,13 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path split")
|
||||
.input_output_types(vec![(Type::String, Type::List(Box::new(Type::String)))])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, split strings at the given columns",
|
||||
Some('c'),
|
||||
)
|
||||
Signature::build("path split").input_output_types(vec![
|
||||
(Type::String, Type::List(Box::new(Type::String))),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::List(Box::new(Type::String)))),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -45,14 +37,12 @@ impl Command for SubCommand {
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
};
|
||||
let args = Arguments;
|
||||
|
||||
// This doesn't match explicit nulls
|
||||
if matches!(input, PipelineData::Empty) {
|
||||
@ -81,9 +71,25 @@ impl Command for SubCommand {
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Split all paths under the 'name' column",
|
||||
example: r"ls ('.' | path expand) | path split -c [ name ]",
|
||||
result: None,
|
||||
description: "Split paths in list into parts",
|
||||
example: r"[ C:\Users\viking\spam.txt C:\Users\viking\eggs.txt ] | path split",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_list(vec![
|
||||
Value::test_string(r"C:\"),
|
||||
Value::test_string("Users"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("spam.txt"),
|
||||
]),
|
||||
Value::test_list(vec![
|
||||
Value::test_string(r"C:\"),
|
||||
Value::test_string("Users"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("eggs.txt"),
|
||||
]),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -105,9 +111,25 @@ impl Command for SubCommand {
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Split all paths under the 'name' column",
|
||||
example: r"ls ('.' | path expand) | path split -c [ name ]",
|
||||
result: None,
|
||||
description: "Split paths in list into parts",
|
||||
example: r"[ /home/viking/spam.txt /home/viking/eggs.txt ] | path split",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_list(vec![
|
||||
Value::test_string("/"),
|
||||
Value::test_string("home"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("spam.txt"),
|
||||
]),
|
||||
Value::test_list(vec![
|
||||
Value::test_string("/"),
|
||||
Value::test_string("home"),
|
||||
Value::test_string("viking"),
|
||||
Value::test_string("eggs.txt"),
|
||||
]),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1,23 +1,16 @@
|
||||
use std::path::Path;
|
||||
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
engine::Command, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||
};
|
||||
|
||||
use super::PathSubcommandArguments;
|
||||
|
||||
struct Arguments {
|
||||
columns: Option<Vec<String>>,
|
||||
}
|
||||
struct Arguments;
|
||||
|
||||
impl PathSubcommandArguments for Arguments {
|
||||
fn get_columns(&self) -> Option<Vec<String>> {
|
||||
self.columns.clone()
|
||||
}
|
||||
}
|
||||
impl PathSubcommandArguments for Arguments {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
@ -29,13 +22,14 @@ impl Command for SubCommand {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("path type")
|
||||
.input_output_types(vec![(Type::String, Type::String)])
|
||||
.named(
|
||||
"columns",
|
||||
SyntaxShape::Table(vec![]),
|
||||
"For a record or table input, check strings at the given columns, and replace with result",
|
||||
Some('c'),
|
||||
)
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -50,14 +44,12 @@ If nothing is found, an empty string will be returned."#
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let args = Arguments {
|
||||
columns: call.get_flag(engine_state, stack, "columns")?,
|
||||
};
|
||||
let args = Arguments;
|
||||
|
||||
// This doesn't match explicit nulls
|
||||
if matches!(input, PipelineData::Empty) {
|
||||
@ -77,8 +69,8 @@ If nothing is found, an empty string will be returned."#
|
||||
result: Some(Value::test_string("dir")),
|
||||
},
|
||||
Example {
|
||||
description: "Show type of a filepath in a column",
|
||||
example: "ls | path type -c [ name ]",
|
||||
description: "Show type of a filepaths in a list",
|
||||
example: "ls | get name | path type",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
@ -2,21 +2,6 @@ use nu_test_support::{nu, pipeline};
|
||||
|
||||
use super::join_path_sep;
|
||||
|
||||
#[test]
|
||||
fn returns_path_joined_with_column_path() {
|
||||
let actual = nu!(
|
||||
cwd: "tests", pipeline(
|
||||
r#"
|
||||
echo [ [name]; [eggs] ]
|
||||
| path join spam.txt -c [ name ]
|
||||
| get name.0
|
||||
"#
|
||||
));
|
||||
|
||||
let expected = join_path_sep(&["eggs", "spam.txt"]);
|
||||
assert_eq!(actual.out, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_path_joined_from_list() {
|
||||
let actual = nu!(
|
||||
|
@ -99,21 +99,6 @@ fn parses_ignoring_extension_gets_stem() {
|
||||
assert_eq!(actual.out, "spam.tar.gz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_column_path_extension() {
|
||||
let actual = nu!(
|
||||
cwd: "tests", pipeline(
|
||||
r#"
|
||||
echo [[home, barn]; ['home/viking/spam.txt', 'barn/cow/moo.png']]
|
||||
| path parse -c [ home barn ]
|
||||
| get barn
|
||||
| get extension.0
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "png");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_into_correct_number_of_columns() {
|
||||
let actual = nu!(
|
||||
|
@ -25,24 +25,3 @@ fn splits_correctly_single_path() {
|
||||
|
||||
assert_eq!(actual.out, "spam.txt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn splits_correctly_with_column_path() {
|
||||
let actual = nu!(
|
||||
cwd: "tests", pipeline(
|
||||
r#"
|
||||
echo [
|
||||
[home, barn];
|
||||
|
||||
['home/viking/spam.txt', 'barn/cow/moo.png']
|
||||
['home/viking/eggs.txt', 'barn/goat/cheese.png']
|
||||
]
|
||||
| path split -c [ home barn ]
|
||||
| get barn
|
||||
| flatten
|
||||
| length
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "6");
|
||||
}
|
||||
|
@ -1740,6 +1740,15 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_list(vals: Vec<Value>) -> Value {
|
||||
Value::List {
|
||||
vals,
|
||||
span: Span::test_data(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_date(val: DateTime<FixedOffset>) -> Value {
|
||||
|
Loading…
Reference in New Issue
Block a user