schema_get_type_text: return format_description if available
[pve-common.git] / src / PVE / PodParser.pm
1 package PVE::PodParser;
2
3 use strict;
4 use warnings;
5 use Pod::Parser;
6 use base qw(Pod::Parser);
7
8 my $currentYear = (localtime(time))[5] + 1900;
9
10 my $stdinclude = {
11     pve_copyright => <<EODATA,
12 \=head1 COPYRIGHT AND DISCLAIMER
13
14 Copyright (C) 2007-$currentYear Proxmox Server Solutions GmbH
15
16 This program is free software: you can redistribute it and\/or modify
17 it under the terms of the GNU Affero General Public License as
18 published by the Free Software Foundation, either version 3 of the
19 License, or (at your option) any later version.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 GNU Affero General Public License for more details.
25
26 You should have received a copy of the GNU Affero General Public License
27 along with this program.  If not, see L<http://www.gnu.org/licenses/>.
28 EODATA
29 };
30
31 sub command { 
32     my ($self, $cmd, $text, $line_num, $pod_para)  = @_;
33
34     if (($cmd eq 'include' && $text =~ m/^\s*(\S+)\s/)) {
35         my $incl = $1;
36         my $data = $stdinclude->{$incl} ? $stdinclude->{$incl} :
37             $self->{include}->{$incl};
38         chomp $data;
39         $self->textblock("$data\n\n", $line_num, $pod_para);
40     } else {
41         $self->textblock($pod_para->raw_text(), $line_num, $pod_para);
42     }
43 }
44
45 # helpers used to generate our manual pages
46
47 sub generate_typetext {
48     my ($schema) = @_;
49     my $typetext = '';
50     my (@optional, @required);
51     foreach my $key (sort keys %$schema) {
52         my $entry = $schema->{$key};
53         next if $entry->{alias};
54         next if !$entry->{format_description} &&
55                 !$entry->{typetext} &&
56                 !$entry->{enum} &&
57                 $entry->{type} ne 'boolean';
58         if ($schema->{$key}->{optional}) {
59             push @optional, $key;
60         } else {
61             push @required, $key;
62         }
63     }
64     my ($pre, $post) = ('', '');
65     my $add = sub {
66         my ($key) = @_;
67         $typetext .= $pre;
68         my $entry = $schema->{$key};
69         if (my $alias = $entry->{alias}) {
70             $key = $alias;
71             $entry = $schema->{$key};
72         }
73         if (!defined($entry->{typetext})) {
74             $typetext .= $entry->{default_key} ? "[$key=]" : "$key=";
75         }
76         if (my $desc = $entry->{format_description}) {
77             $typetext .= "<$desc>";
78         } elsif (my $text = $entry->{typetext}) {
79             $typetext .= $text;
80         } elsif (my $enum = $entry->{enum}) {
81             $typetext .= '<' . join('|', @$enum) . '>';
82         } elsif ($entry->{type} eq 'boolean') {
83             $typetext .= '<1|0>';
84         } else {
85             die "internal error: neither format_description nor typetext found";
86         }
87         $typetext .= $post;
88     };
89     foreach my $key (@required) {
90         &$add($key);
91         $pre = ', ';
92     }
93     $pre = $pre ? ' [,' : '[';
94     $post = ']';
95     foreach my $key (@optional) {
96         &$add($key);
97         $pre = ' [,';
98     }
99     return $typetext;
100 }
101
102 sub schema_get_type_text {
103     my ($phash) = @_;
104
105     if ($phash->{typetext}) {
106         return $phash->{typetext};
107     } elsif ($phash->{format_description}) {
108         return "<$phash->{format_description}>";
109     } elsif ($phash->{enum}) {
110         return "(" . join(' | ', sort @{$phash->{enum}}) . ")";
111     } elsif ($phash->{pattern}) {
112         return $phash->{pattern};
113     } elsif ($phash->{type} eq 'integer' || $phash->{type} eq 'number') {
114         if (defined($phash->{minimum}) && defined($phash->{maximum})) {
115             return "$phash->{type} ($phash->{minimum} - $phash->{maximum})";
116         } elsif (defined($phash->{minimum})) {
117             return "$phash->{type} ($phash->{minimum} - N)";
118         } elsif (defined($phash->{maximum})) {
119             return "$phash->{type} (-N - $phash->{maximum})";
120         }
121     } elsif ($phash->{type} eq 'string') {
122         if (my $format = $phash->{format}) {
123             $format = PVE::JSONSchema::get_format($format) if ref($format) ne 'HASH';
124             if (ref($format) eq 'HASH') {
125                 return generate_typetext($format);
126             }
127         }
128     }
129
130     my $type = $phash->{type} || 'string';
131
132     return $type;
133 }
134
135 sub generate_property_text {
136     my ($schema) = @_;
137     my $data = '';
138     foreach my $key (sort keys %$schema) {
139         my $d = $schema->{$key};
140         next if $d->{alias};
141         my $desc = $d->{description};
142         my $typetext = schema_get_type_text($d);
143         $desc = 'No description available' if !$desc;
144         $data .= "=item $key: $typetext\n\n$desc\n\n";
145     }
146     return $data;
147 }
148
149 # generate pod from JSON schema properties
150 sub dump_properties {
151     my ($properties) = @_;
152
153     my $data = "=over 1\n\n";
154
155     my $idx_param = {}; # -vlan\d+ -scsi\d+
156
157     foreach my $key (sort keys %$properties) {
158         my $d = $properties->{$key};
159         my $base = $key;
160         if ($key =~ m/^([a-z]+)(\d+)$/) {
161             my $name = $1;
162             next if $idx_param->{$name};
163             $idx_param->{$name} = 1;
164             $base = "${name}[n]";
165         }
166
167         my $descr = $d->{description} || 'No description avalable.';
168         chomp $descr;
169
170         if (defined(my $dv = $d->{default})) {
171             my $multi = $descr =~ m/\n\n/; # multi paragraph ?
172             $descr .= $multi ? "\n\n" : " ";
173             $descr .= "Default value is '$dv'.";
174         }
175
176         my $typetext = schema_get_type_text($d);
177         $data .= "=item $base: $typetext\n\n";
178         $data .= "$descr\n\n";
179
180         if ($d->{type} eq 'string') {
181             my $format = $d->{format};
182             if ($format && ref($format) eq 'HASH') {
183                 $data .= "=over 1.1\n\n";
184                 $data .= generate_property_text($format);
185                 $data .= "=back\n\n";
186             }
187         }
188     }
189
190     $data .= "=back";
191
192     return $data;
193 }
194
195 1;