[pve-devel] [RFC common 6/9] cli: factor out generate usage string
Thomas Lamprecht
t.lamprecht at proxmox.com
Tue Dec 5 09:36:07 CET 2017
On 12/05/2017 09:01 AM, Dominik Csapak wrote:
> i am not sure if intended or not, but this patch makes a huge difference in how the help output is generated
>
> previous a command like
> pveum help asdf
>
> would result in an output like this:
>
> 400 Parameter verification failed.
> cmd: no such command 'asdf'
> pveum help [<cmd>] [OPTIONS]
>
> now it results in showing a verbose help for all (!) commands
>
> this is especially weird when using tab completion
>
> pveum help role<tab><tab>
> <list of roleadd,...>
> <enter>
>
> help for all commands
Hmm, that should not be the case, I'll look into this.
For the record, we probably want the following behavior:
* pveum help -> whole (meaning all commands but non-verbose) help
* pveum help non-existing -> error and whole help
* pveum help role -> help of all role sub-commands
* pveum help role existing-cmd -> help of specific role sub-command 'existing-cmd'
* pveum help role non-existing-cmd -> error & help of all role sub-commands
Anything else?
>
> On 11/06/2017 02:54 PM, Thomas Lamprecht wrote:
>> reduce code reuse and prepare for sub commands
>> ---
>> src/PVE/CLIHandler.pm | 137 ++++++++++++++++++++++++++++----------------------
>> 1 file changed, 76 insertions(+), 61 deletions(-)
>>
>> diff --git a/src/PVE/CLIHandler.pm b/src/PVE/CLIHandler.pm
>> index 4e3342f..64a35c1 100644
>> --- a/src/PVE/CLIHandler.pm
>> +++ b/src/PVE/CLIHandler.pm
>> @@ -40,6 +40,56 @@ my $complete_command_names = sub {
>> return [ sort keys %$cmddef ];
>> };
>> +sub generate_usage_str {
>> + my ($format, $cmd, $indent, $separator, $sortfunc) = @_;
>> +
>> + $assert_initialized->();
>> + die 'format required' if !$format;
>> +
>> + $sortfunc //= sub { sort keys %{$_[0]} };
>> + $separator //= '';
>> + $indent //= '';
>> +
>> + my $can_read_pass = $cli_handler_class->can('read_password');
>> + my $can_str_param_fmap = $cli_handler_class->can('string_param_file_mapping');
>> +
>> + my $def = $cmddef;
>> +
>> + my $generate;
>> + $generate = sub {
>> + my ($indent, $separator, $def, $prefix) = @_;
>> +
>> + my $str = '';
>> + if (ref($def) eq 'HASH') {
>> + my $oldclass = undef;
>> + foreach my $cmd (&$sortfunc($def)) {
>> +
>> + if (ref($def->{$cmd}) eq 'ARRAY') {
>> + my ($class, $name, $arg_param, $fixed_param) = @{$def->{$cmd}};
>> +
>> + $str .= $separator if $oldclass && $oldclass ne $class;
>> + $str .= $indent;
>> + $str .= $class->usage_str($name, "$prefix $cmd", $arg_param,
>> + $fixed_param, $format,
>> + $can_read_pass, $can_str_param_fmap);
>> + $oldclass = $class;
>> + }
>> +
>> + }
>> + } else {
>> + my ($class, $name, $arg_param, $fixed_param) = @$def;
>> + $abort->("unknown command '$cmd'") if !$class;
>> +
>> + $str .= $indent;
>> + $str .= $class->usage_str($name, $prefix, $arg_param, $fixed_param, $format,
>> + $can_read_pass, $can_str_param_fmap);
>> + }
>> + return $str;
>> + };
>> +
>> + return $generate->($indent, $separator, $def, $exename);
>> +}
>> +
>> __PACKAGE__->register_method ({
>> name => 'help',
>> path => 'help',
>> @@ -82,18 +132,14 @@ __PACKAGE__->register_method ({
>> return undef;
>> }
>> - $cmd = &$expand_command_name($cmddef, $cmd);
>> + my $str;
>> + if ($verbose) {
>> + $str = generate_usage_str('full', $cmd, '');
>> + } else {
>> + $str = generate_usage_str('short', $cmd, ' ' x 7);
>> + }
>> + $str =~ s/^\s+//;
>> - my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd} || []};
>> -
>> - raise_param_exc({ cmd => "no such command '$cmd'"}) if !$class;
>> -
>> - my $pwcallback = $cli_handler_class->can('read_password');
>> - my $stringfilemap = $cli_handler_class->can('string_param_file_mapping');
>> -
>> - my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param,
>> - $verbose ? 'full' : 'short', $pwcallback,
>> - $stringfilemap);
>> if ($verbose) {
>> print "$str\n";
>> } else {
>> @@ -105,43 +151,22 @@ __PACKAGE__->register_method ({
>> }});
>> sub print_simple_asciidoc_synopsis {
>> - my ($class, $name, $arg_param, $uri_param) = @_;
>> -
>> $assert_initialized->();
>> - my $pwcallback = $cli_handler_class->can('read_password');
>> - my $stringfilemap = $cli_handler_class->can('string_param_file_mapping');
>> -
>> - my $synopsis = "*${name}* `help`\n\n";
>> -
>> - $synopsis .= $class->usage_str($name, $name, $arg_param, $uri_param,
>> - 'asciidoc', $pwcallback, $stringfilemap);
>> + my $synopsis = "*${exename}* `help`\n\n";
>> + $synopsis .= generate_usage_str('asciidoc');
>> return $synopsis;
>> }
>> sub print_asciidoc_synopsis {
>> -
>> $assert_initialized->();
>> - my $pwcallback = $cli_handler_class->can('read_password');
>> - my $stringfilemap = $cli_handler_class->can('string_param_file_mapping');
>> -
>> my $synopsis = "";
>> $synopsis .= "*${exename}* `<COMMAND> [ARGS] [OPTIONS]`\n\n";
>> - my $oldclass;
>> - foreach my $cmd (sort keys %$cmddef) {
>> - my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}};
>> - my $str = $class->usage_str($name, "$exename $cmd", $arg_param,
>> - $uri_param, 'asciidoc', $pwcallback,
>> - $stringfilemap);
>> - $synopsis .= "\n" if $oldclass && $oldclass ne $class;
>> -
>> - $synopsis .= "$str\n\n";
>> - $oldclass = $class;
>> - }
>> + $synopsis .= generate_usage_str('asciidoc');
>> $synopsis .= "\n";
>> @@ -149,24 +174,13 @@ sub print_asciidoc_synopsis {
>> }
>> sub print_usage_verbose {
>> -
>> $assert_initialized->();
>> - my $pwcallback = $cli_handler_class->can('read_password');
>> - my $stringfilemap = $cli_handler_class->can('string_param_file_mapping');
>> -
>> print "USAGE: $exename <COMMAND> [ARGS] [OPTIONS]\n\n";
>> - foreach my $cmd (sort keys %$cmddef) {
>> - my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}};
>> - my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param,
>> - 'full', $pwcallback, $stringfilemap);
>> - print "$str\n\n";
>> - }
>> -}
>> + my $str = generate_usage_str('full');
>> -sub sorted_commands {
>> - return sort { ($cmddef->{$a}->[0] cmp $cmddef->{$b}->[0]) || ($a cmp $b)} keys %$cmddef;
>> + print "$str\n";
>> }
>> sub print_usage_short {
>> @@ -174,20 +188,21 @@ sub print_usage_short {
>> $assert_initialized->();
>> - my $pwcallback = $cli_handler_class->can('read_password');
>> - my $stringfilemap = $cli_handler_class->can('string_param_file_mapping');
>> -
>> print $fd "ERROR: $msg\n" if $msg;
>> print $fd "USAGE: $exename <COMMAND> [ARGS] [OPTIONS]\n";
>> - my $oldclass;
>> - foreach my $cmd (sorted_commands()) {
>> - my ($class, $name, $arg_param, $uri_param) = @{$cmddef->{$cmd}};
>> - my $str = $class->usage_str($name, "$exename $cmd", $arg_param, $uri_param, 'short', $pwcallback, $stringfilemap);
>> - print $fd "\n" if $oldclass && $oldclass ne $class;
>> - print $fd " $str";
>> - $oldclass = $class;
>> - }
>> + print {$fd} generate_usage_str('short', undef, ' ' x 7, "\n", sub {
>> + my ($h) = @_;
>> + return sort {
>> + if (ref($h->{$a}) eq 'ARRAY' && ref($h->{$b}) eq 'ARRAY') {
>> + # $a and $b are both real commands order them by their class
>> + return $h->{$a}->[0] cmp $h->{$b}->[0];
>> + } else {
>> + # both are from the same class
>> + return $a cmp $b;
>> + }
>> + } keys %$h;
>> + });
>> }
>> my $print_bash_completion = sub {
>> @@ -338,7 +353,7 @@ sub generate_asciidoc_synopsis {
>> $cmddef = $def;
>> if (ref($def) eq 'ARRAY') {
>> - print_simple_asciidoc_synopsis(@$def);
>> + print_simple_asciidoc_synopsis();
>> } else {
>> $cmddef->{help} = [ __PACKAGE__, 'help', ['cmd'] ];
>> @@ -398,7 +413,7 @@ my $handle_simple_cmd = sub {
>> if (scalar(@$args) >= 1) {
>> if ($args->[0] eq 'help') {
>> my $str = "USAGE: $name help\n";
>> - $str .= $class->usage_str($name, $name, $arg_param, $uri_param, 'long', $pwcallback, $stringfilemap);
>> + $str .= generate_usage_str('long');
>> print STDERR "$str\n\n";
>> return;
>> } elsif ($args->[0] eq 'verifyapi') {
>>
>
>
> _______________________________________________
> pve-devel mailing list
> pve-devel at pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
More information about the pve-devel
mailing list