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
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 {