]> git.proxmox.com Git - pve-client.git/blob - PVE/APIClient/Helpers.pm
cleanup: use nested CLIHandler command definitions
[pve-client.git] / PVE / APIClient / Helpers.pm
1 package PVE::APIClient::Helpers;
2
3 use strict;
4 use warnings;
5
6 use Storable;
7 use JSON;
8 use PVE::APIClient::Exception qw(raise);
9 use Encode::Locale;
10 use Encode;
11 use HTTP::Status qw(:constants);
12
13 my $pve_api_definition;
14 my $pve_api_path_hash;
15
16 my $pve_api_definition_fn = "/usr/share/pve-client/pve-api-definition.dat";
17
18 my $method_map = {
19 create => 'POST',
20 set => 'PUT',
21 get => 'GET',
22 delete => 'DELETE',
23 };
24
25 my $build_pve_api_path_hash;
26 $build_pve_api_path_hash = sub {
27 my ($tree) = @_;
28
29 my $class = ref($tree);
30 return $tree if !$class;
31
32 if ($class eq 'ARRAY') {
33 foreach my $el (@$tree) {
34 $build_pve_api_path_hash->($el);
35 }
36 } elsif ($class eq 'HASH') {
37 if (defined($tree->{leaf}) && defined(my $path = $tree->{path})) {
38 $pve_api_path_hash->{$path} = $tree;
39 }
40 foreach my $k (keys %$tree) {
41 $build_pve_api_path_hash->($tree->{$k});
42 }
43 }
44 };
45
46 sub get_api_definition {
47
48 if (!defined($pve_api_definition)) {
49 open(my $fh, '<', $pve_api_definition_fn) ||
50 die "unable to open '$pve_api_definition_fn' - $!\n";
51 $pve_api_definition = Storable::fd_retrieve($fh);
52 $build_pve_api_path_hash->($pve_api_definition);
53 }
54
55 return $pve_api_definition;
56 }
57
58 sub lookup_api_method {
59 my ($path, $method, $noerr) = @_;
60
61 get_api_definition(); # make sure API data is loaded
62
63 my $info = $pve_api_path_hash->{$path};
64
65 if (!$info) {
66 return undef if $noerr;
67 die "unable to find API info for path '$path'\n";
68 }
69
70 my $data = $info->{info}->{$method};
71
72 if (!$data) {
73 return undef if $noerr;
74 die "unable to find API method '$method' for path '$path'\n";
75 }
76
77 return $data;
78 }
79
80 sub complete_api_call_options {
81 my ($cmd, $prop, $prev, $cur, $args) = @_;
82
83 my $print_result = sub {
84 foreach my $p (@_) {
85 print "$p\n" if $p =~ m/^$cur/;
86 }
87 };
88
89 my $print_parameter_completion = sub {
90 my ($pname) = @_;
91 my $d = $prop->{$pname};
92 if ($d->{completion}) {
93 my $vt = ref($d->{completion});
94 if ($vt eq 'CODE') {
95 my $res = $d->{completion}->($cmd, $pname, $cur, $args);
96 &$print_result(@$res);
97 }
98 } elsif ($d->{type} eq 'boolean') {
99 &$print_result('0', '1');
100 } elsif ($d->{enum}) {
101 &$print_result(@{$d->{enum}});
102 }
103 };
104
105 my @option_list = ();
106 foreach my $key (keys %$prop) {
107 push @option_list, "--$key";
108 }
109
110 if ($cur =~ m/^-/) {
111 &$print_result(@option_list);
112 return;
113 }
114
115 if ($prev =~ m/^--?(.+)$/ && $prop->{$1}) {
116 my $pname = $1;
117 &$print_parameter_completion($pname);
118 return;
119 }
120
121 &$print_result(@option_list);
122 }
123
124 sub complete_api_path {
125 my ($text) = @_;
126
127 get_api_definition(); # make sure API data is loaded
128
129 $text =~ s!^/!!;
130
131 my ($dir, $rest) = $text =~ m|^(?:(.*)/)?(?:([^/]*))?$|;
132
133 my $info;
134 if (!defined($dir)) {
135 $dir = '';
136 $info = { children => $pve_api_definition };
137 } else {
138 $info = $pve_api_path_hash->{"/$dir"};
139 }
140
141 my $res = [];
142 if ($info) {
143 if (my $children = $info->{children}) {
144 foreach my $c (@$children) {
145 if ($c->{path} =~ m!\Q$dir/$rest!) {
146 push @$res, $c->{path};
147 push @$res, "$c->{path}/" if $c->{children};
148 }
149 }
150 }
151 }
152 return $res;
153 }
154
155 # test for command lines with api calls (or similar bash completion calls):
156 # example1: pveclient api get remote1 /cluster
157 sub extract_path_info {
158
159 my $info;
160
161 my $test_path_properties = sub {
162 my ($args) = @_;
163
164 return if scalar(@$args) < 5;
165 return if $args->[1] ne 'api';
166
167 my $path = $args->[4];
168 if (my $method = $method_map->{$args->[2]}) {
169 $info = lookup_api_method($path, $method, 1);
170 }
171 };
172
173 if (defined(my $cmd = $ARGV[0])) {
174 if ($cmd eq 'api') {
175 $test_path_properties->([$0, @ARGV]);
176 } elsif ($cmd eq 'bashcomplete') {
177 my $cmdline = substr($ENV{COMP_LINE}, 0, $ENV{COMP_POINT});
178 my $args = PVE::Tools::split_args($cmdline);
179 $test_path_properties->($args);
180 }
181 }
182
183 return $info;
184 }
185
186 1;