Add subcommands. Switch from-* and to-* to them (#1708)

This commit is contained in:
Jonathan Turner 2020-05-04 20:44:33 +12:00 committed by GitHub
parent 453087248a
commit a9968046ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 278 additions and 152 deletions

View file

@ -320,6 +320,7 @@ pub fn create_default_context(
whole_stream_command(Histogram),
whole_stream_command(Sum),
// File format output
whole_stream_command(To),
whole_stream_command(ToBSON),
whole_stream_command(ToCSV),
whole_stream_command(ToHTML),
@ -332,6 +333,7 @@ pub fn create_default_context(
whole_stream_command(ToURL),
whole_stream_command(ToYAML),
// File format input
whole_stream_command(From),
whole_stream_command(FromCSV),
whole_stream_command(FromEML),
whole_stream_command(FromTSV),

View file

@ -31,6 +31,7 @@ pub(crate) mod evaluate_by;
pub(crate) mod exit;
pub(crate) mod first;
pub(crate) mod format;
pub(crate) mod from;
pub(crate) mod from_bson;
pub(crate) mod from_csv;
pub(crate) mod from_eml;
@ -101,6 +102,7 @@ pub(crate) mod sum;
pub(crate) mod t_sort_by;
pub(crate) mod table;
pub(crate) mod tags;
pub(crate) mod to;
pub(crate) mod to_bson;
pub(crate) mod to_csv;
pub(crate) mod to_html;
@ -150,6 +152,7 @@ pub(crate) use evaluate_by::EvaluateBy;
pub(crate) use exit::Exit;
pub(crate) use first::First;
pub(crate) use format::Format;
pub(crate) use from::From;
pub(crate) use from_bson::FromBSON;
pub(crate) use from_csv::FromCSV;
pub(crate) use from_eml::FromEML;
@ -219,6 +222,7 @@ pub(crate) use sum::Sum;
pub(crate) use t_sort_by::TSortBy;
pub(crate) use table::Table;
pub(crate) use tags::Tags;
pub(crate) use to::To;
pub(crate) use to_bson::ToBSON;
pub(crate) use to_csv::ToCSV;
pub(crate) use to_html::ToHTML;

View file

@ -51,7 +51,7 @@ pub(crate) fn run_internal_command(
}
CommandAction::AutoConvert(tagged_contents, extension) => {
let contents_tag = tagged_contents.tag.clone();
let command_name = format!("from-{}", extension);
let command_name = format!("from {}", extension);
let command = command.clone();
if let Some(converter) = context.registry.get_command(&command_name) {
let new_args = RawCommandArgs {

View file

@ -407,7 +407,7 @@ impl Command {
pub fn run(&self, args: CommandArgs, registry: &CommandRegistry) -> OutputStream {
if args.call_info.switch_present("help") {
get_help(self.name(), self.usage(), self.signature()).into()
get_help(self.name(), self.usage(), self.signature(), registry).into()
} else {
match self.0.run(args, registry) {
Ok(stream) => stream,

View file

@ -95,7 +95,7 @@ fn enter(
let tagged_contents = contents.into_value(&contents_tag);
if let Some(extension) = file_extension {
let command_name = format!("from-{}", extension);
let command_name = format!("from {}", extension);
if let Some(converter) =
registry.get_command(&command_name)
{

View file

@ -0,0 +1,31 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::Signature;
pub struct From;
impl WholeStreamCommand for From {
fn name(&self) -> &str {
"from"
}
fn signature(&self) -> Signature {
Signature::build("from")
}
fn usage(&self) -> &str {
"Parse content (string or binary) as a table."
}
fn run(
&self,
_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
Ok(
crate::commands::help::get_help(self.name(), self.usage(), self.signature(), registry)
.into(),
)
}
}

View file

@ -10,15 +10,15 @@ pub struct FromBSON;
impl WholeStreamCommand for FromBSON {
fn name(&self) -> &str {
"from-bson"
"from bson"
}
fn signature(&self) -> Signature {
Signature::build("from-bson")
Signature::build("from bson")
}
fn usage(&self) -> &str {
"Parse text as .bson and create table."
"Parse binary as .bson and create table."
}
fn run(

View file

@ -14,11 +14,11 @@ pub struct FromCSVArgs {
impl WholeStreamCommand for FromCSV {
fn name(&self) -> &str {
"from-csv"
"from csv"
}
fn signature(&self) -> Signature {
Signature::build("from-csv")
Signature::build("from csv")
.named(
"separator",
SyntaxShape::String,

View file

@ -18,11 +18,11 @@ pub struct FromEMLArgs {
impl WholeStreamCommand for FromEML {
fn name(&self) -> &str {
"from-eml"
"from eml"
}
fn signature(&self) -> Signature {
Signature::build("from-eml").named(
Signature::build("from eml").named(
"preview-body",
SyntaxShape::Int,
"How many bytes of the body to preview",

View file

@ -11,11 +11,11 @@ pub struct FromIcs;
impl WholeStreamCommand for FromIcs {
fn name(&self) -> &str {
"from-ics"
"from ics"
}
fn signature(&self) -> Signature {
Signature::build("from-ics")
Signature::build("from ics")
}
fn usage(&self) -> &str {

View file

@ -8,11 +8,11 @@ pub struct FromINI;
impl WholeStreamCommand for FromINI {
fn name(&self) -> &str {
"from-ini"
"from ini"
}
fn signature(&self) -> Signature {
Signature::build("from-ini")
Signature::build("from ini")
}
fn usage(&self) -> &str {

View file

@ -12,11 +12,11 @@ pub struct FromJSONArgs {
impl WholeStreamCommand for FromJSON {
fn name(&self) -> &str {
"from-json"
"from json"
}
fn signature(&self) -> Signature {
Signature::build("from-json").switch(
Signature::build("from json").switch(
"objects",
"treat each line as a separate value",
Some('o'),

View file

@ -15,11 +15,11 @@ pub struct FromODSArgs {
impl WholeStreamCommand for FromODS {
fn name(&self) -> &str {
"from-ods"
"from ods"
}
fn signature(&self) -> Signature {
Signature::build("from-ods").switch(
Signature::build("from ods").switch(
"headerless",
"don't treat the first row as column names",
None,

View file

@ -10,11 +10,11 @@ pub struct FromSQLite;
impl WholeStreamCommand for FromSQLite {
fn name(&self) -> &str {
"from-sqlite"
"from sqlite"
}
fn signature(&self) -> Signature {
Signature::build("from-sqlite")
Signature::build("from sqlite")
}
fn usage(&self) -> &str {
@ -34,11 +34,11 @@ pub struct FromDB;
impl WholeStreamCommand for FromDB {
fn name(&self) -> &str {
"from-db"
"from db"
}
fn signature(&self) -> Signature {
Signature::build("from-db")
Signature::build("from db")
}
fn usage(&self) -> &str {

View file

@ -17,7 +17,7 @@ pub struct FromSSVArgs {
minimum_spaces: Option<Tagged<usize>>,
}
const STRING_REPRESENTATION: &str = "from-ssv";
const STRING_REPRESENTATION: &str = "from ssv";
const DEFAULT_MINIMUM_SPACES: usize = 2;
impl WholeStreamCommand for FromSSV {

View file

@ -7,11 +7,11 @@ pub struct FromTOML;
impl WholeStreamCommand for FromTOML {
fn name(&self) -> &str {
"from-toml"
"from toml"
}
fn signature(&self) -> Signature {
Signature::build("from-toml")
Signature::build("from toml")
}
fn usage(&self) -> &str {

View file

@ -13,11 +13,11 @@ pub struct FromTSVArgs {
impl WholeStreamCommand for FromTSV {
fn name(&self) -> &str {
"from-tsv"
"from tsv"
}
fn signature(&self) -> Signature {
Signature::build("from-tsv").switch(
Signature::build("from tsv").switch(
"headerless",
"don't treat the first row as column names",
None,

View file

@ -7,11 +7,11 @@ pub struct FromURL;
impl WholeStreamCommand for FromURL {
fn name(&self) -> &str {
"from-url"
"from url"
}
fn signature(&self) -> Signature {
Signature::build("from-url")
Signature::build("from url")
}
fn usage(&self) -> &str {

View file

@ -11,11 +11,11 @@ pub struct FromVcf;
impl WholeStreamCommand for FromVcf {
fn name(&self) -> &str {
"from-vcf"
"from vcf"
}
fn signature(&self) -> Signature {
Signature::build("from-vcf")
Signature::build("from vcf")
}
fn usage(&self) -> &str {

View file

@ -15,11 +15,11 @@ pub struct FromXLSXArgs {
impl WholeStreamCommand for FromXLSX {
fn name(&self) -> &str {
"from-xlsx"
"from xlsx"
}
fn signature(&self) -> Signature {
Signature::build("from-xlsx").switch(
Signature::build("from xlsx").switch(
"headerless",
"don't treat the first row as column names",
None,

View file

@ -7,11 +7,11 @@ pub struct FromXML;
impl WholeStreamCommand for FromXML {
fn name(&self) -> &str {
"from-xml"
"from xml"
}
fn signature(&self) -> Signature {
Signature::build("from-xml")
Signature::build("from xml")
}
fn usage(&self) -> &str {

View file

@ -7,11 +7,11 @@ pub struct FromYAML;
impl WholeStreamCommand for FromYAML {
fn name(&self) -> &str {
"from-yaml"
"from yaml"
}
fn signature(&self) -> Signature {
Signature::build("from-yaml")
Signature::build("from yaml")
}
fn usage(&self) -> &str {
@ -31,11 +31,11 @@ pub struct FromYML;
impl WholeStreamCommand for FromYML {
fn name(&self) -> &str {
"from-yml"
"from yml"
}
fn signature(&self) -> Signature {
Signature::build("from-yml")
Signature::build("from yml")
}
fn usage(&self) -> &str {

View file

@ -14,7 +14,7 @@ pub struct Help;
#[derive(Deserialize)]
pub struct HelpArgs {
command: Option<Tagged<String>>,
rest: Vec<Tagged<String>>,
}
impl WholeStreamCommand for Help {
@ -23,11 +23,7 @@ impl WholeStreamCommand for Help {
}
fn signature(&self) -> Signature {
Signature::build("help").optional(
"command",
SyntaxShape::String,
"the name of command(s) to get help on",
)
Signature::build("help").rest(SyntaxShape::String, "the name of command to get help on")
}
fn usage(&self) -> &str {
@ -44,15 +40,19 @@ impl WholeStreamCommand for Help {
}
fn help(
HelpArgs { command }: HelpArgs,
HelpArgs { rest }: HelpArgs,
RunnableContext { registry, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
if let Some(document) = command {
if let Some(document) = rest.get(0) {
let mut help = VecDeque::new();
if document.item == "commands" {
let mut sorted_names = registry.names();
sorted_names.sort();
for cmd in sorted_names {
// If it's a subcommand, don't list it during the commands list
if cmd.contains(' ') {
continue;
}
let mut short_desc = TaggedDictBuilder::new(name.clone());
let document_tag = document.tag.clone();
let value = command_dict(
@ -82,13 +82,31 @@ fn help(
help.push_back(ReturnSuccess::value(short_desc.into_value()));
}
} else if rest.len() == 2 {
// Check for a subcommand
let command_name = format!("{} {}", rest[0].item, rest[1].item);
if let Some(command) = registry.get_command(&command_name) {
return Ok(get_help(
&command.name(),
&command.usage(),
command.signature(),
&registry,
)
.into());
}
} else if let Some(command) = registry.get_command(&document.item) {
return Ok(get_help(&command.name(), &command.usage(), command.signature()).into());
return Ok(get_help(
&command.name(),
&command.usage(),
command.signature(),
&registry,
)
.into());
} else {
return Err(ShellError::labeled_error(
"Can't find command (use 'help commands' for full list)",
"can't find command",
document.tag,
document.tag.span,
));
}
let help = futures::stream::iter(help);
@ -128,6 +146,7 @@ pub(crate) fn get_help(
cmd_name: &str,
cmd_usage: &str,
cmd_sig: Signature,
registry: &CommandRegistry,
) -> impl Into<OutputStream> {
let mut help = VecDeque::new();
let mut long_desc = String::new();
@ -137,6 +156,15 @@ pub(crate) fn get_help(
let signature = cmd_sig;
let mut subcommands = String::new();
for name in registry.names() {
if name.starts_with(&format!("{} ", cmd_name)) {
let subcommand = registry.get_command(&name).expect("This shouldn't happen");
subcommands.push_str(&format!(" {} - {}\n", name, subcommand.usage()));
}
}
let mut one_liner = String::new();
one_liner.push_str(&signature.name);
one_liner.push_str(" ");
@ -156,14 +184,23 @@ pub(crate) fn get_help(
one_liner.push_str(" ...args");
}
if !subcommands.is_empty() {
one_liner.push_str("<subcommand> ");
}
if !signature.named.is_empty() {
one_liner.push_str("{flags} ");
}
long_desc.push_str(&format!("\nUsage:\n > {}\n", one_liner));
if !subcommands.is_empty() {
long_desc.push_str("\nSubcommands:\n");
long_desc.push_str(&subcommands);
}
if !signature.positional.is_empty() || signature.rest_positional.is_some() {
long_desc.push_str("\nparameters:\n");
long_desc.push_str("\nParameters:\n");
for positional in signature.positional {
match positional.0 {
PositionalType::Mandatory(name, _m) => {
@ -180,7 +217,7 @@ pub(crate) fn get_help(
}
}
if !signature.named.is_empty() {
long_desc.push_str("\nflags:\n");
long_desc.push_str("\nFlags:\n");
for (flag, ty) in signature.named {
let msg = match ty.0 {
NamedType::Switch(s) => {

View file

@ -221,7 +221,7 @@ fn save(
let content : Result<Vec<u8>, ShellError> = 'scope: loop {
break if !save_raw {
if let Some(extension) = full_path.extension() {
let command_name = format!("to-{}", extension.to_string_lossy());
let command_name = format!("to {}", extension.to_string_lossy());
if let Some(converter) = registry.get_command(&command_name) {
let new_args = RawCommandArgs {
host,

View file

@ -0,0 +1,31 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::Signature;
pub struct To;
impl WholeStreamCommand for To {
fn name(&self) -> &str {
"to"
}
fn signature(&self) -> Signature {
Signature::build("to")
}
fn usage(&self) -> &str {
"Convert table into an output format."
}
fn run(
&self,
_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
Ok(
crate::commands::help::get_help(self.name(), self.usage(), self.signature(), registry)
.into(),
)
}
}

View file

@ -12,11 +12,11 @@ pub struct ToBSON;
impl WholeStreamCommand for ToBSON {
fn name(&self) -> &str {
"to-bson"
"to bson"
}
fn signature(&self) -> Signature {
Signature::build("to-bson")
Signature::build("to bson")
}
fn usage(&self) -> &str {

View file

@ -14,11 +14,11 @@ pub struct ToCSVArgs {
impl WholeStreamCommand for ToCSV {
fn name(&self) -> &str {
"to-csv"
"to csv"
}
fn signature(&self) -> Signature {
Signature::build("to-csv")
Signature::build("to csv")
.named(
"separator",
SyntaxShape::String,

View file

@ -10,11 +10,11 @@ pub struct ToHTML;
impl WholeStreamCommand for ToHTML {
fn name(&self) -> &str {
"to-html"
"to html"
}
fn signature(&self) -> Signature {
Signature::build("to-html")
Signature::build("to html")
}
fn usage(&self) -> &str {

View file

@ -7,11 +7,11 @@ pub struct ToJSON;
impl WholeStreamCommand for ToJSON {
fn name(&self) -> &str {
"to-json"
"to json"
}
fn signature(&self) -> Signature {
Signature::build("to-json")
Signature::build("to json")
}
fn usage(&self) -> &str {

View file

@ -9,11 +9,11 @@ pub struct ToMarkdown;
impl WholeStreamCommand for ToMarkdown {
fn name(&self) -> &str {
"to-md"
"to md"
}
fn signature(&self) -> Signature {
Signature::build("to-md")
Signature::build("to md")
}
fn usage(&self) -> &str {

View file

@ -10,11 +10,11 @@ pub struct ToSQLite;
impl WholeStreamCommand for ToSQLite {
fn name(&self) -> &str {
"to-sqlite"
"to sqlite"
}
fn signature(&self) -> Signature {
Signature::build("to-sqlite")
Signature::build("to sqlite")
}
fn usage(&self) -> &str {
@ -38,11 +38,11 @@ pub struct ToDB;
impl WholeStreamCommand for ToDB {
fn name(&self) -> &str {
"to-db"
"to db"
}
fn signature(&self) -> Signature {
Signature::build("to-db")
Signature::build("to db")
}
fn usage(&self) -> &str {

View file

@ -7,11 +7,11 @@ pub struct ToTOML;
impl WholeStreamCommand for ToTOML {
fn name(&self) -> &str {
"to-toml"
"to toml"
}
fn signature(&self) -> Signature {
Signature::build("to-toml")
Signature::build("to toml")
}
fn usage(&self) -> &str {

View file

@ -13,11 +13,11 @@ pub struct ToTSVArgs {
impl WholeStreamCommand for ToTSV {
fn name(&self) -> &str {
"to-tsv"
"to tsv"
}
fn signature(&self) -> Signature {
Signature::build("to-tsv").switch(
Signature::build("to tsv").switch(
"headerless",
"do not output the column names as the first row",
None,

View file

@ -7,11 +7,11 @@ pub struct ToURL;
impl WholeStreamCommand for ToURL {
fn name(&self) -> &str {
"to-url"
"to url"
}
fn signature(&self) -> Signature {
Signature::build("to-url")
Signature::build("to url")
}
fn usage(&self) -> &str {

View file

@ -7,11 +7,11 @@ pub struct ToYAML;
impl WholeStreamCommand for ToYAML {
fn name(&self) -> &str {
"to-yaml"
"to yaml"
}
fn signature(&self) -> Signature {
Signature::build("to-yaml")
Signature::build("to yaml")
}
fn usage(&self) -> &str {

View file

@ -8,7 +8,7 @@ fn alias_args_work() {
cwd: dirs.root(),
r#"
alias double_echo [a b] {echo $a $b}
double_echo 1 2 | to-json
double_echo 1 2 | to json
"#
);

View file

@ -40,7 +40,7 @@ fn discards_empty_rows_by_default() {
cwd: dirs.test(), pipeline(
r#"
echo "[1,2,3,14,null]"
| from-json
| from json
| compact
| count
| echo $it

View file

@ -4,7 +4,7 @@ use nu_test_support::nu;
fn drop_rows() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"echo '[{"foo": 3}, {"foo": 8}, {"foo": 4}]' | from-json | drop 2 | get foo | sum | echo $it"#
r#"echo '[{"foo": 3}, {"foo": 8}, {"foo": 4}]' | from json | drop 2 | get foo | sum | echo $it"#
);
assert_eq!(actual, "3");

View file

@ -5,7 +5,7 @@ fn each_works_separately() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo [1 2 3] | each { echo $it 10 | sum } | to-json | echo $it
echo [1 2 3] | each { echo $it 10 | sum } | to json | echo $it
"#
));

View file

@ -240,7 +240,7 @@ fn errors_fetching_by_index_out_of_bounds() {
fn quoted_column_access() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"echo '[{"foo bar": {"baz": 4}}]' | from-json | get "foo bar".baz | echo $it"#
r#"echo '[{"foo bar": {"baz": 4}}]' | from json | get "foo bar".baz | echo $it"#
);
assert_eq!(actual, "4");

View file

@ -9,7 +9,7 @@ fn headers_uses_first_row_as_header() {
| get Sheet1
| headers
| get header0
| from-json"#
| from json"#
));
assert_eq!(actual, "r1c0r2c0")
@ -24,7 +24,7 @@ fn headers_adds_missing_column_name() {
| get Sheet1
| headers
| get Column1
| from-json"#
| from json"#
));
assert_eq!(actual, "r1c1r2c1")

View file

@ -125,7 +125,7 @@ fn compound_where() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from-json | where a == 2 && b == 1 | to-json
echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from json | where a == 2 && b == 1 | to json
"#
));
@ -137,7 +137,7 @@ fn compound_where_paren() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from-json | where (a == 2 && b == 1) || b == 2 | to-json
echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from json | where (a == 2 && b == 1) || b == 2 | to json
"#
));

View file

@ -131,7 +131,7 @@ fn uniq_when_keys_out_of_order() {
cwd: "tests/fixtures/formats", pipeline(
r#"
echo '[{"a": "a", "b": [1,2,3]},{"b": [1,2,3], "a": "a"}]'
| from-json
| from json
| uniq
| count
| echo $it

View file

@ -14,7 +14,7 @@ fn filters_by_unit_size_comparison() {
fn filters_with_nothing_comparison() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"echo '[{"foo": 3}, {"foo": null}, {"foo": 4}]' | from-json | where foo > 1 | get foo | sum | echo $it"#
r#"echo '[{"foo": 3}, {"foo": null}, {"foo": 4}]' | from json | where foo > 1 | get foo | sum | echo $it"#
);
assert_eq!(actual, "7");
@ -24,7 +24,7 @@ fn filters_with_nothing_comparison() {
fn where_in_table() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from-json | where name in: ["foo"] | get size | sum | echo $it"#
r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in: ["foo"] | get size | sum | echo $it"#
);
assert_eq!(actual, "5");
@ -34,7 +34,7 @@ fn where_in_table() {
fn where_not_in_table() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from-json | where name not-in: ["foo"] | get size | sum | echo $it"#
r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in: ["foo"] | get size | sum | echo $it"#
);
assert_eq!(actual, "4");

View file

@ -19,7 +19,7 @@ fn wrap_rows_into_a_row() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from-csv
| from csv
| wrap caballeros
| get caballeros
| nth 0
@ -49,7 +49,7 @@ fn wrap_rows_into_a_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from-csv
| from csv
| get last_name
| wrap caballero
| nth 2

View file

@ -6,8 +6,8 @@ fn table_to_bson_and_back_into_table() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open sample.bson
| to-bson
| from-bson
| to bson
| from bson
| get root
| get 1.b
| echo $it

View file

@ -6,7 +6,7 @@ use nu_test_support::{nu, pipeline};
fn table_to_csv_text_and_from_csv_text_back_into_table() {
let actual = nu!(
cwd: "tests/fixtures/formats",
"open caco3_plastics.csv | to-csv | from-csv | first 1 | get origin | echo $it"
"open caco3_plastics.csv | to csv | from csv | first 1 | get origin | echo $it"
);
assert_eq!(actual, "SPAIN");
@ -32,7 +32,7 @@ fn table_to_csv_text() {
| trim
| split-column "," a b c d origin
| last 1
| to-csv
| to csv
| lines
| nth 1
| echo $it
@ -63,7 +63,7 @@ fn table_to_csv_text_skipping_headers_after_conversion() {
| trim
| split-column "," a b c d origin
| last 1
| to-csv --headerless
| to csv --headerless
| echo $it
"#
));
@ -117,7 +117,7 @@ fn from_csv_text_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from-csv
| from csv
| get rusty_luck
| count
| echo $it
@ -145,7 +145,7 @@ fn from_csv_text_with_separator_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from-csv --separator ';'
| from csv --separator ';'
| get rusty_luck
| count
| echo $it
@ -173,7 +173,7 @@ fn from_csv_text_with_tab_separator_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from-csv --separator '\t'
| from csv --separator '\t'
| get rusty_luck
| count
| echo $it
@ -200,7 +200,7 @@ fn from_csv_text_skipping_headers_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_amigos.txt
| from-csv --headerless
| from csv --headerless
| get Column3
| count
| echo $it

View file

@ -5,7 +5,7 @@ fn out_html_simple() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo 3 | to-html
echo 3 | to html
"#
));
@ -17,7 +17,7 @@ fn out_html_table() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo '{"name": "jason"}' | from-json | to-html
echo '{"name": "jason"}' | from json | to html
"#
));

View file

@ -86,7 +86,7 @@ fn from_ics_text_to_table() {
cwd: dirs.test(), pipeline(
r#"
open calendar.txt
| from-ics
| from ics
| get events
| get properties
| where name == "SUMMARY"

View file

@ -8,8 +8,8 @@ fn table_to_json_text_and_from_json_text_back_into_table() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open sgml_description.json
| to-json
| from-json
| to json
| from json
| get glossary.GlossDiv.GlossList.GlossEntry.GlossSee
| echo $it
"#
@ -37,7 +37,7 @@ fn from_json_text_to_table() {
let actual = nu!(
cwd: dirs.test(),
"open katz.txt | from-json | get katz | get rusty_luck | count | echo $it"
"open katz.txt | from json | get katz | get rusty_luck | count | echo $it"
);
assert_eq!(actual, "4");
@ -61,7 +61,7 @@ fn from_json_text_recognizing_objects_independently_to_table() {
cwd: dirs.test(), pipeline(
r#"
open katz.txt
| from-json -o
| from json -o
| where name == "GorbyPuff"
| get rusty_luck
| echo $it
@ -90,8 +90,8 @@ fn table_to_json_text() {
| lines
| split-column "," name luck
| pick name
| to-json
| from-json
| to json
| from json
| nth 0
| get name
| echo $it

View file

@ -5,7 +5,7 @@ fn out_md_simple() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo 3 | to-md
echo 3 | to md
"#
));
@ -17,7 +17,7 @@ fn out_md_table() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo '{"name": "jason"}' | from-json | to-md
echo '{"name": "jason"}' | from json | to md
"#
));

View file

@ -6,8 +6,8 @@ fn table_to_sqlite_and_back_into_table() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open sample.db
| to-sqlite
| from-sqlite
| to sqlite
| from sqlite
| get table_values
| nth 2
| get x

View file

@ -19,7 +19,7 @@ fn from_ssv_text_to_table() {
cwd: dirs.test(), pipeline(
r#"
open oc_get_svc.txt
| from-ssv
| from ssv
| nth 0
| get IP
| echo $it
@ -47,7 +47,7 @@ fn from_ssv_text_to_table_with_separator_specified() {
cwd: dirs.test(), pipeline(
r#"
open oc_get_svc.txt
| from-ssv --minimum-spaces 3
| from ssv --minimum-spaces 3
| nth 0
| get IP
| echo $it
@ -74,7 +74,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
cwd: dirs.test(), pipeline(
r#"
open oc_get_svc.txt
| from-ssv --headerless -a
| from ssv --headerless -a
| first
| get Column1
| echo $it
@ -85,7 +85,7 @@ fn from_ssv_text_treating_first_line_as_data_with_flag() {
cwd: dirs.test(), pipeline(
r#"
open oc_get_svc.txt
| from-ssv --headerless
| from ssv --headerless
| first
| get Column1
| echo $it

View file

@ -6,8 +6,8 @@ fn table_to_toml_text_and_from_toml_text_back_into_table() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open cargo_sample.toml
| to-toml
| from-toml
| to toml
| from toml
| get package.name
| echo $it
"#

View file

@ -6,7 +6,7 @@ use nu_test_support::{nu, pipeline};
fn table_to_tsv_text_and_from_tsv_text_back_into_table() {
let actual = nu!(
cwd: "tests/fixtures/formats",
"open caco3_plastics.tsv | to-tsv | from-tsv | first 1 | get origin | echo $it"
"open caco3_plastics.tsv | to tsv | from tsv | first 1 | get origin | echo $it"
);
assert_eq!(actual, "SPAIN");
@ -16,7 +16,7 @@ fn table_to_tsv_text_and_from_tsv_text_back_into_table() {
fn table_to_tsv_text_and_from_tsv_text_back_into_table_using_csv_separator() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r"open caco3_plastics.tsv | to-tsv | from-csv --separator '\t' | first 1 | get origin | echo $it"
r"open caco3_plastics.tsv | to tsv | from csv --separator '\t' | first 1 | get origin | echo $it"
);
assert_eq!(actual, "SPAIN");
@ -41,7 +41,7 @@ fn table_to_tsv_text() {
| lines
| split-column "\t" a b c d origin
| last 1
| to-tsv
| to tsv
| lines
| nth 1
| echo $it
@ -71,7 +71,7 @@ fn table_to_tsv_text_skipping_headers_after_conversion() {
| lines
| split-column "\t" a b c d origin
| last 1
| to-tsv --headerless
| to tsv --headerless
| echo $it
"#
));
@ -97,7 +97,7 @@ fn from_tsv_text_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_amigos.txt
| from-tsv
| from tsv
| get rusty_luck
| count
| echo $it
@ -124,7 +124,7 @@ fn from_tsv_text_skipping_headers_to_table() {
cwd: dirs.test(), pipeline(
r#"
open los_tres_amigos.txt
| from-tsv --headerless
| from tsv --headerless
| get Column3
| count
| echo $it

View file

@ -6,8 +6,8 @@ fn can_encode_and_decode_urlencoding() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open sample.url
| to-url
| from-url
| to url
| from url
| get cheese
| echo $it
"#

View file

@ -70,7 +70,7 @@ fn from_vcf_text_to_table() {
cwd: dirs.test(), pipeline(
r#"
open contacts.txt
| from-vcf
| from vcf
| get properties
| where name == "EMAIL"
| first

View file

@ -6,8 +6,8 @@ fn table_to_yaml_text_and_from_yaml_text_back_into_table() {
cwd: "tests/fixtures/formats", pipeline(
r#"
open appveyor.yml
| to-yaml
| from-yaml
| to yaml
| from yaml
| get environment.global.PROJECT_NAME
| echo $it
"#

View file

@ -848,16 +848,22 @@ fn parse_internal_command(
lite_cmd: &LiteCommand,
registry: &dyn SignatureRegistry,
signature: &Signature,
mut idx: usize,
) -> (InternalCommand, Option<ParseError>) {
// This is a known internal command, so we need to work with the arguments and parse them according to the expected types
let mut internal_command = InternalCommand::new(
lite_cmd.name.item.clone(),
lite_cmd.name.span,
lite_cmd.span(),
);
let (name, name_span) = if idx == 0 {
(lite_cmd.name.item.clone(), lite_cmd.name.span)
} else {
(
format!("{} {}", lite_cmd.name.item, lite_cmd.args[0].item),
Span::new(lite_cmd.name.span.start(), lite_cmd.args[0].span.end()),
)
};
let mut internal_command = InternalCommand::new(name, name_span, lite_cmd.span());
internal_command.args.set_initial_flags(&signature);
let mut idx = 0;
let mut current_positional = 0;
let mut named = NamedArguments::new();
let mut positional = vec![];
@ -1051,12 +1057,31 @@ fn classify_pipeline(
garbage(lite_cmd.span())
};
commands.push(ClassifiedCommand::Expr(Box::new(expr)))
} else if let Some(signature) = registry.get(&lite_cmd.name.item) {
let (internal_command, err) = parse_internal_command(&lite_cmd, registry, &signature);
} else {
if !lite_cmd.args.is_empty() {
// Check if it's a sub-command
if let Some(signature) =
registry.get(&format!("{} {}", lite_cmd.name.item, lite_cmd.args[0].item))
{
let (internal_command, err) =
parse_internal_command(&lite_cmd, registry, &signature, 1);
error = error.or(err);
commands.push(ClassifiedCommand::Internal(internal_command))
} else {
commands.push(ClassifiedCommand::Internal(internal_command));
continue;
}
}
// Check if it's an internal command
if let Some(signature) = registry.get(&lite_cmd.name.item) {
let (internal_command, err) =
parse_internal_command(&lite_cmd, registry, &signature, 0);
error = error.or(err);
commands.push(ClassifiedCommand::Internal(internal_command));
continue;
}
let name = lite_cmd.name.clone().map(|v| {
let trimmed = trim_quotes(&v);
expand_path(&trimmed).to_string()

View file

@ -33,10 +33,13 @@ pub struct InternalCommand {
impl InternalCommand {
pub fn new(name: String, name_span: Span, full_span: Span) -> InternalCommand {
InternalCommand {
name: name.clone(),
name,
name_span,
args: crate::hir::Call::new(
Box::new(SpannedExpression::new(Expression::string(name), name_span)),
Box::new(SpannedExpression::new(
Expression::Command(name_span),
name_span,
)),
full_span,
),
}

View file

@ -21,10 +21,10 @@ You can save the name of files in a directory like this:
> ls | where type == File | pick name | save filenames.csv
```
Or you can format it in supported formats using one of the `to-*` commands:
Or you can format it in supported formats using one of the `to` commands:
```shell
> ls | where type == File | pick name | to-csv | save filenames
> ls | where type == File | pick name | to csv | save filenames
```
`filename.csv` and `filenames` are both `csv` formatted files. Nu auto-converts the format if a supported file extension is given.

View file

@ -119,7 +119,7 @@ fn converts_to_int() {
cwd: "tests/fixtures/formats", pipeline(
r#"
echo '{number_as_string: "1"}'
| from-json
| from json
| str number_as_string --to-int
| rename number
| where number == 1

View file

@ -48,13 +48,6 @@ fn automatically_change_directory_with_trailing_slash_and_same_name_as_command()
})
}
// #[test]
// fn correctly_escape_external_arguments() {
// let actual = nu!(cwd: ".", r#"^echo '[{"foo": "bar"}]' | from-json | to-json"#);
// assert_eq!(actual, "{\"foo\":\"bar\"}");
// }
#[test]
fn correctly_escape_external_arguments() {
let actual = nu!(cwd: ".", r#"^echo '$0'"#);

View file

@ -152,7 +152,7 @@ mod tilde_expansion {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls | sort-by name | group-by type | each { get File.name | echo $it } | to-json
ls | sort-by name | group-by type | each { get File.name | echo $it } | to json
"#
));