our $AUTOLOAD; # it's a package global
+our $standard_output_options = {
+ 'output-format' => PVE::JSONSchema::get_standard_option('pve-output-format'),
+ noheader => {
+ description => "Do not show column headers (for 'text' format).",
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ },
+ noborder => {
+ description => "Do not draw borders (for 'text' format).",
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ },
+ quiet => {
+ description => "Suppress printing results.",
+ type => 'boolean',
+ optional => 1,
+ },
+ 'human-readable' => {
+ description => "Call output rendering functions to produce human readable text.",
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ }
+};
+
sub api_clone_schema {
- my ($schema) = @_;
+ my ($schema, $no_typetext) = @_;
my $res = {};
my $ref = ref($schema);
my ($name, $idx) = ($1, $2);
if ($idx == 0 && defined($d->{"${name}1"})) {
$p = "${name}[n]";
- } elsif (defined($d->{"${name}0"})) {
+ } elsif ($idx > 0 && defined($d->{"${name}0"})) {
next; # only handle once for -xx0, but only if -xx0 exists
}
}
my $tmp = ref($pd) ? clone($pd) : $pd;
# NOTE: add typetext property for more complex types, to
# make the web api viewer code simpler
- if (!(defined($tmp->{enum}) || defined($tmp->{pattern}))) {
+ if (!$no_typetext && !(defined($tmp->{enum}) || defined($tmp->{pattern}))) {
my $typetext = PVE::JSONSchema::schema_get_type_text($tmp);
if ($tmp->{type} && ($tmp->{type} ne $typetext)) {
$tmp->{typetext} = $typetext;
} else {
if ($k eq 'parameters') {
$data->{$k} = api_clone_schema($d);
+ } elsif ($k eq 'returns') {
+ $data->{$k} = api_clone_schema($d, 1);
} else {
$data->{$k} = ref($d) ? clone($d) : $d;
}
}
sub handle {
- my ($self, $info, $param, $output_options) = @_;
+ my ($self, $info, $param) = @_;
my $func = $info->{code};
$param->{'extra-args'} = [map { /^(.*)$/ } @$extra] if $extra;
}
- $output_options //= {};
- my $result = &$func($param, $output_options);
+ my $result = &$func($param);
# todo: this is only to be safe - disable?
if (my $schema = $info->{returns}) {
# 'short' ... command line only (text, one line)
# 'full' ... text, include description
# 'asciidoc' ... generate asciidoc for man pages (like 'full')
-# $param_cb ... mapping for string parameters to file path parameters
+# $param_cb ... mapping for string parameters to file path parameters
+# $formatter_properties ... additional property definitions (passed to output formatter)
sub getopt_usage {
- my ($info, $prefix, $arg_param, $fixed_param, $format, $param_cb) = @_;
+ my ($info, $prefix, $arg_param, $fixed_param, $format, $param_cb, $formatter_properties) = @_;
$format = 'long' if !$format;
my $schema = $info->{parameters};
my $name = $info->{name};
- my $prop = $schema->{properties};
+ my $prop = { %{$schema->{properties}} }; # copy
+
+ my $has_output_format_option = $formatter_properties->{'output-format'} ? 1 : 0;
+
+ if ($formatter_properties) {
+ foreach my $key (keys %$formatter_properties) {
+ if (!$standard_output_options->{$key}) {
+ $prop->{$key} = $formatter_properties->{$key};
+ }
+ }
+ }
+
+ # also remove $standard_output_options from $prop (pvesh, pveclient)
+ if ($prop->{'output-format'}) {
+ $has_output_format_option = 1;
+ foreach my $key (keys %$prop) {
+ if ($standard_output_options->{$key}) {
+ delete $prop->{$key};
+ }
+ }
+ }
my $out = '';
if ($format eq 'asciidoc') {
$out .= "*${prefix}*";
$out .= " `$args`" if $args;
- $out .= $opts ? " `[OPTIONS]`\n" : "\n";
+ $out .= " `[OPTIONS]`" if $opts;
+ $out .= " `[FORMAT_OPTIONS]`" if $has_output_format_option;
+ $out .= "\n";
} else {
$out .= "USAGE: " if $format ne 'short';
$out .= "$prefix $args";
- $out .= $opts ? " [OPTIONS]\n" : "\n";
+ $out .= " [OPTIONS]" if $opts;
+ $out .= " [FORMAT_OPTIONS]" if $has_output_format_option;
+ $out .= "\n";
}
return $out if $format eq 'short';
}
sub usage_str {
- my ($self, $name, $prefix, $arg_param, $fixed_param, $format, $param_cb) = @_;
+ my ($self, $name, $prefix, $arg_param, $fixed_param, $format, $param_cb, $formatter_properties) = @_;
my $info = $self->map_method_by_name($name);
- return getopt_usage($info, $prefix, $arg_param, $fixed_param, $format, $param_cb);
+ return getopt_usage($info, $prefix, $arg_param, $fixed_param, $format, $param_cb, $formatter_properties);
}
# generate docs from JSON schema properties
return $param;
};
-our $standard_output_options = {
- format => PVE::JSONSchema::get_standard_option('pve-output-format'),
- noheader => {
- description => "Do not show column headers (for 'text' format).",
- type => 'boolean',
- optional => 1,
- default => 1,
- },
- noborder => {
- description => "Do not draw borders (for 'text' format).",
- type => 'boolean',
- optional => 1,
- default => 1,
- },
- quiet => {
- description => "Suppress printing results.",
- type => 'boolean',
- optional => 1,
- },
- 'human-readable' => {
- description => "Call output rendering functions to produce human readable text.",
- type => 'boolean',
- optional => 1,
- default => 1,
+sub add_standard_output_properties {
+ my ($propdef, $list) = @_;
+
+ $propdef //= {};
+
+ $list //= [ keys %$standard_output_options ];
+
+ my $res = { %$propdef }; # copy
+
+ foreach my $opt (@$list) {
+ die "no such standard output option '$opt'\n" if !defined($standard_output_options->{$opt});
+ die "detected overwriten standard CLI parameter '$opt'\n" if defined($res->{$opt});
+ $res->{$opt} = $standard_output_options->{$opt};
}
-};
-sub add_standard_output_parameters {
- my ($org_schema) = @_;
+ return $res;
+}
- my $schema = { %$org_schema };
- $schema->{properties} = { %{$schema->{properties}}, %$standard_output_options };
+sub extract_standard_output_properties {
+ my ($data) = @_;
- return $schema;
-};
+ my $options = {};
+ foreach my $opt (keys %$standard_output_options) {
+ $options->{$opt} = delete $data->{$opt} if defined($data->{$opt});
+ }
+
+ return $options;
+}
sub cli_handler {
- my ($self, $prefix, $name, $args, $arg_param, $fixed_param, $param_cb, $options) = @_;
+ my ($self, $prefix, $name, $args, $arg_param, $fixed_param, $param_cb, $formatter_properties) = @_;
my $info = $self->map_method_by_name($name);
- $options //= {};
-
- my $add_stdopts = $options->{'std-output-opts'};
-
my $res;
+ my $fmt_param = {};
+
eval {
my $param_map = {};
$param_map = $compute_param_mapping_hash->($param_cb->($name)) if $param_cb;
- my $schema = $add_stdopts ? add_standard_output_parameters($info->{parameters}) : $info->{properties} ;
+ my $schema = { %{$info->{parameters}} }; # copy
+ $schema->{properties} = { %{$schema->{properties}}, %$formatter_properties } if $formatter_properties;
my $param = PVE::JSONSchema::get_options($schema, $args, $arg_param, $fixed_param, $param_map);
- if ($add_stdopts) {
- foreach my $opt (keys %$standard_output_options) {
- $options->{$opt} = delete $param->{$opt} if defined($param->{$opt});
+ if ($formatter_properties) {
+ foreach my $opt (keys %$formatter_properties) {
+ $fmt_param->{$opt} = delete $param->{$opt} if defined($param->{$opt});
}
}
$replace_file_names_with_contents->($param, $param_map);
}
- $res = $self->handle($info, $param, $options);
+ $res = $self->handle($info, $param);
};
if (my $err = $@) {
my $ec = ref($err);
die $err if !$ec || $ec ne "PVE::Exception" || !$err->is_param_exc();
- $err->{usage} = $self->usage_str($name, $prefix, $arg_param, $fixed_param, 'short', $param_cb);
+ $err->{usage} = $self->usage_str($name, $prefix, $arg_param, $fixed_param, 'short', $param_cb, $formatter_properties);
die $err;
}
- return $res;
+ return wantarray ? ($res, $fmt_param) : $res;
}
# utility methods