File Coverage

lib/App/MtAws/ConfigEngine.pm
Criterion Covered Total %
statement 388 392 98.9
branch 262 276 94.9
condition 77 93 82.8
subroutine 50 51 98.0
pod 0 34 0.0
total 777 846 91.8


line stmt bran cond sub pod time code
1             # mt-aws-glacier - Amazon Glacier sync client
2             # Copyright (C) 2012-2014 Victor Efimov
3             # http://mt-aws.com (also http://vs-dev.com) vs@vs-dev.com
4             # License: GPLv3
5             #
6             # This file is part of "mt-aws-glacier"
7             #
8             # mt-aws-glacier is free software: you can redistribute it and/or modify
9             # it under the terms of the GNU General Public License as published by
10             # the Free Software Foundation, either version 3 of the License, or
11             # (at your option) any later version.
12             #
13             # mt-aws-glacier is distributed in the hope that it will be useful,
14             # but WITHOUT ANY WARRANTY; without even the implied warranty of
15             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16             # GNU General Public License for more details.
17             #
18             # You should have received a copy of the GNU General Public License
19             # along with this program. If not, see <http://www.gnu.org/licenses/>.
20              
21             package App::MtAws::ConfigEngine;
22              
23             our $VERSION = '1.114_2';
24              
25 113     113   98463 use Getopt::Long 2.24 qw/:config no_ignore_case/ ;
  113         1164559  
  113         3593  
26 113     113   24056 use Encode;
  113         213  
  113         10158  
27 113     113   644 use Carp;
  113         190  
  113         7240  
28 113     113   2552 use List::Util qw/first/;
  113         700  
  113         7005  
29 113     113   630 use strict;
  113         156  
  113         2656  
30 113     113   484 use warnings;
  113         164  
  113         3918  
31 113     113   499 use utf8;
  113         142  
  113         840  
32 113     113   75801 use App::MtAws::Exceptions;
  113         284  
  113         9138  
33 113     113   687 use App::MtAws::Utils;
  113         173  
  113         14520  
34              
35 113     113   592 use Exporter 'import';
  113         165  
  113         636524  
36              
37             our @EXPORT = qw/option options positional command validation message
38             mandatory optional seen deprecated validate scope
39             present valid value lists raw_option custom error warning impose explicit/;
40              
41             our $context; # it's a not a global. always localized in code
42              
43             # TODOS
44             #refactor messages %option a% vs %option option%
45             #options_encoding_error specify source of problem
46              
47             sub message($;$%)
48             {
49 30946     30946 0 109746 my ($message, $format, %opts) = @_;
50 30946 100       43196 $format = $message unless defined $format;
51 30946 100 100     64292 confess "message $message already defined" if defined $context->{messages}->{$message} and !$context->{messages}->{$message}->{allow_redefine};
52 30943         66678 $context->{messages}->{$message} = { %opts, format => $format };
53 30943         64224 $message;
54             }
55              
56              
57             sub new
58             {
59 910     910 0 380406 my ($class, %args) = @_;
60 910         2587 my $self = {
61             %args
62             };
63 910         1833 bless $self, $class;
64 910         1299 local $context = $self;
65             # TODO: replace "%s option% with "%option%" - will this work?
66 910         2181 message 'list_options_in_config', '"List" options (where order is important) like "%s option%" cannot appear in config currently', allow_redefine => 1;
67 910         1716 message 'unexpected_option', 'Unexpected option %option option%', allow_redefine=>1;
68 910         2181 message 'unknown_config_option', 'Unknown option in config: "%s option%"', allow_redefine=>1;
69 910         1675 message 'unknown_command', 'Unknown command %command a%', allow_redefine=>1;
70 910         1721 message 'no_command', 'No command specified', allow_redefine=>1;
71 910         1874 message 'deprecated_option', 'Option %option% is deprecated, use %option main% instead', allow_redefine=>1;
72 910         1617 message 'deprecated_command', 'Command %command command% is deprecated', allow_redefine=>1;
73 910         1578 message 'already_specified_in_alias', 'Both options %option a% and %option b% are specified. However they are aliases', allow_redefine=>1;
74 910         1629 message 'getopts_error', 'Error parsing options', allow_redefine=>1;
75 910         1650 message 'options_encoding_error', 'Invalid %encoding% character in command line', allow_redefine => 1;
76 910         1618 message 'config_encoding_error', 'Invalid %encoding% character in config file', allow_redefine => 1;
77 910         1431 message 'mandatory', "Option %option a% is mandatory", allow_redefine => 1;
78 910         1564 message 'positional_mandatory', 'Positional argument #%d n% (%a%) is mandatory', allow_redefine => 1;
79 910         1645 message 'unexpected_argument', "Unexpected argument in command line: %a%", allow_redefine => 1;
80 910         1697 message 'option_deprecated_for_command', "Option %option a% deprecated for this command", allow_redefine => 1;
81 910         1543 message 'unknown_encoding', 'Unknown encoding "%s value%" in option %option a%', allow_redefine => 1;
82 910         2767 return $self;
83             }
84              
85              
86             sub error_to_message
87             {
88 550     550 0 7130 my ($spec, %data) = @_;
89             my $rep = sub {
90 819     819   1363 my ($match) = @_;
91 819 100       2970 if (my ($format, $name) = $match =~ /^([\w]+)\s+([\w]+)$/) {
92 394 100       931 if (lc $format eq lc 'option') {
    100          
93 295 100       978 defined(my $value = $data{$name})||confess;
94 294         1289 qq{"--$value"};
95             } elsif (lc $format eq lc 'command') {
96 24 50       71 defined(my $value = $data{$name})||confess;
97 24         114 qq{"$value"};
98             } else {
99 75 100       355 defined(my $value = $data{$name})||confess;
100 74         505 sprintf("%$format", $value);
101             }
102             } else {
103 425 100       1163 defined(my $value = $data{$match})||confess $spec;
104 424         1225 $value;
105             }
106 550         2651 };
107              
108 550 100       3513 $spec =~ s{%([\w\s]+)%} {$rep->($1)}ge if %data; # in new perl versions \w also means unicode chars..
  819         1222  
109 547         5993 $spec;
110             }
111              
112              
113             sub errors_or_warnings_to_messages
114             {
115 1448     1448 0 2797 my ($self, $err) = @_;
116 1448 100       3939 return unless defined $err;
117             map {
118 553 100       1598 if (ref($_) eq ref({})) {
119 532   33     1278 my $name = $_->{format} || confess "format not defined";
120 532 50 33     2726 confess qq{message $name not defined} unless $self->{messages}->{$name} and my $format = $self->{messages}->{$name}->{format};
121 532         1876 error_to_message($format, %$_);
122             } else {
123 21         135 $_;
124             }
125 417         490 } @{$err};
  417         839  
126             }
127              
128             sub arrayref_or_undef($)
129             {
130 2890     2890 0 6393 my ($ref) = @_;
131 2890 100 100     35588 defined($ref) && @$ref > 0 ? $ref : undef;
132             }
133              
134              
135             sub define($&)
136             {
137 744     744 0 1871 my ($self, $block) = @_;
138 744         966 local $context = $self; # TODO: create wrapper like 'localize sub ..'
139 744         1727 $block->();
140             }
141              
142             sub decode_option_value
143             {
144 2670     2670 0 2883 my ($self, $val) = @_;
145 2670   33     4924 my $enc = $self->{cmd_encoding}||confess;
146 2670         3327 my $decoded = eval {decode($enc, $val, Encode::DIE_ON_ERR|Encode::LEAVE_SRC)};
  2670         10701  
147 2670 100       91308 error("options_encoding_error", encoding => $enc) unless defined $decoded;
148 2670         3880 $decoded;
149             }
150              
151             sub decode_config_value
152             {
153 1833     1833 0 1978 my ($self, $val) = @_;
154 1833   33     3361 my $enc = $self->{cfg_encoding}||confess;
155 1833         1641 my $decoded = eval {decode($enc, $val, Encode::DIE_ON_ERR|Encode::LEAVE_SRC)};
  1833         5340  
156 1833 100       47205 error("config_encoding_error", encoding => $enc) unless defined $decoded;
157 1833         2640 $decoded;
158             }
159              
160             sub get_encoding
161             {
162 1436     1436 0 1915 my ($name, $config, $options) = @_;
163 1436 100       2728 return undef unless defined $name;
164 1224         1173 my $res = undef;
165              
166 1224 100 100     5156 if (defined $config && defined($config->{$name})) {
167 5         13 my $new_enc_obj = find_encoding($config->{$name});
168 5 50       46 error('unknown_encoding', encoding => $config->{$name}, a => $name), return unless $new_enc_obj;
169 5         6 $res = $new_enc_obj;
170             }
171              
172 1224     6375   3920 my $new_encoding = first { $_->{name} eq $name } @$options;
  6375         5680  
173 1224 100 66     4040 if (defined $new_encoding && defined $new_encoding->{value}) {
174 7         24 my $new_enc_obj = find_encoding($new_encoding->{value});
175 7 50       118 error('unknown_encoding', encoding => $new_encoding->{value}, a => $name), return unless $new_enc_obj;
176 7         16 $res = $new_enc_obj;
177             }
178              
179             $res
180 1224         1776 }
181              
182             sub get_option_ref
183             {
184 5223     5223 0 5489 my ($self, $name) = @_;
185 5223 100       10627 if ($self->{options}->{$name}) {
    100          
186 5022         8854 return ($self->{options}->{$name}, 0);
187             } elsif (defined($self->{optaliasmap}->{$name})) {
188 200         475 return ($self->{options}->{ $self->{optaliasmap}->{$name} }, 1);
189             } else {
190 1         2 return (undef, undef);
191             }
192             }
193              
194             sub parse_options
195             {
196 742     742 0 18646 (my $self, local @ARGV) = @_; # we override @ARGV here, cause GetOptionsFromArray is not exported on perl 5.8.8
197              
198              
199 742 100 100     7601 return { command => 'help', map { $_ => undef } qw/errors error_texts warnings warning_texts options/}
  40         68  
200             if (@ARGV && $ARGV[0] =~ /\b(help|h)\b/i);
201              
202 734 100 100     4911 return { command => 'version', map { $_ => undef } qw/errors error_texts warnings warning_texts options/}
  25         49  
203             if (@ARGV && $ARGV[0] =~ /^\-?\-?version$/i);
204              
205 729         1025 local $context = $self;
206              
207 729         821 my @results;
208             my @getopts = map {
209             ($_ => sub {
210 3341     3341   1468027 my ($name, $value) = @_;
211 3341         10549 my $sname = "$name";# can be object instead of name.. object interpolates to string well
212 3341         22435 push @results, { name => $sname, value => $value };
213             })
214 24001         62422 } map {
215 22142 100       29770 my $type = defined($_->{type}) ? $_->{type} : 's';
216 22142 100       34784 $type = "=$type" unless $type eq '';
217 22142 100       17792 map { "$_$type" } $_->{name}, @{ $_->{alias} || [] }, @{ $_->{deprecated} || [] } # TODO: it's possible to implement aliasing using GetOpt itself
  24001 100       41577  
  22142         52416  
  22142         51739  
218 729         1087 } grep { !$_->{positional} } values %{$self->{options}};
  22781         23077  
  729         3453  
219              
220 729 100       5675 error('getopts_error') unless GetOptions(@getopts);
221              
222 729         28029 my $cfg = undef;
223 729         961 my $cfg_opt = undef;
224              
225 729 100       2262 unless ($self->{errors}) {
226 723 100       2104 if (defined(my $cmd_enc = $self->{CmdEncoding})) {
227 614 50       2116 if (my $cmd_ref = $self->{options}->{$cmd_enc}) {
228 614 100       2046 confess "CmdEncoding option should be declared as binary" unless $cmd_ref->{binary};
229             }
230             }
231              
232 722 100       1880 if (defined(my $cfg_enc = $self->{ConfigEncoding})) {
233 614 50       1756 if (my $cfg_ref = $self->{options}->{$cfg_enc}) {
234 614 100       1587 confess "ConfigEncoding option should be declared as binary" unless $cfg_ref->{binary};
235             }
236             }
237              
238 721 100 66     4496 if (defined($self->{ConfigOption}) and $cfg_opt = $self->{options}->{$self->{ConfigOption}}) {
239 631 100       1643 confess "ConfigOption option should be declared as binary" unless $cfg_opt->{binary};
240 630     744   5078 my $cfg_value = first { $_->{name} eq $self->{ConfigOption} } @results;
  744         1683  
241 630 100       3063 $cfg_value = $cfg_value->{value} if defined $cfg_value;
242 630 100       1577 $cfg_value = $cfg_opt->{default} unless defined $cfg_value;
243 630 100       1378 if (defined $cfg_value) { # we should also check that config is 'seen'. we can only check below (so it must be seen)
244 614         2749 $cfg = $self->read_config($cfg_value);
245 613 100       5072 confess unless defined $cfg;
246             }
247             }
248              
249 718         2471 my $cmd_encoding = get_encoding($self->{CmdEncoding}, $cfg, \@results);
250 718         1784 my $cfg_encoding = get_encoding($self->{ConfigEncoding}, $cfg, \@results);
251 718 100       3367 $self->{cmd_encoding} = defined($cmd_encoding) ? $cmd_encoding : 'UTF-8';
252 718 100       2128 $self->{cfg_encoding} = defined($cfg_encoding) ? $cfg_encoding : 'UTF-8';
253             }
254              
255              
256              
257 724 100       1747 unless ($self->{errors}) {
258 718         1430 for (@results) { # sort needed here to define a/b order for already_specified_in_alias
259 3325         6318 my ($optref, $is_alias) = $self->get_option_ref($_->{name});
260 3325 50       5533 $optref||confess;
261             warning('deprecated_option', option => $_->{name}, main => $self->{optaliasmap}->{$_->{name}})
262 3325 100 100     7099 if $is_alias && $self->{deprecated_options}->{$_->{name}};
263              
264             error('already_specified_in_alias', ($optref->{original_option} lt $_->{name}) ?
265             (a => $optref->{original_option}, b => $_->{name}) :
266             (b => $optref->{original_option}, a => $_->{name})
267             )
268 3325 100 100     6875 if ((defined $optref->{value}) && !$optref->{list} && $optref->{source} eq 'option' );
    100 66        
269              
270 3325         2582 my $decoded;
271 3325 100       4428 if ($optref->{binary}) {
272 655         1201 $decoded = $_->{value};
273             } else {
274 2670         4650 $decoded = $self->decode_option_value($_->{value});
275 2670 100       4304 last unless defined $decoded;
276             }
277              
278 3322 100       4815 if ($optref->{list}) {
279 165 100       330 if (defined $optref->{value}) {
280 39         49 push @{ $optref->{value} }, $decoded;
  39         97  
281             } else {
282 126         256 @{$optref}{qw/value source/} = ([ $decoded ], 'list');
  126         251  
283             }
284 165   100     164 push @{$self->{option_list} ||= []}, { name => $optref->{name}, value => $decoded };
  165         984  
285             } else {
286             # fill from options from command line
287 3157         4017 @{$optref}{qw/value source original_option is_alias/} = ($decoded, 'option', $_->{name}, $is_alias);
  3157         9967  
288             }
289             }
290             }
291 724         1040 my $command = undef;
292              
293 724 100       1654 unless ($self->{errors}) {
294 704         1459 my $original_command = $command = shift @ARGV;
295 704 100       1459 if (defined($command)) {
296             error("unknown_command", a => $original_command) unless
297             $self->{commands}->{$command} ||
298 701 100 66     2169 (defined($command = $self->{aliasmap}->{$command}) && $self->{commands}->{$command});
      66        
299 701 100       2449 warning('deprecated_command', command => $original_command) if ($self->{deprecated_commands}->{$original_command});
300             } else {
301 3 50       16 error("no_command") unless defined $command;
302             }
303             }
304              
305 724 100       1698 unless ($self->{errors}) {
306 698 100       1506 if (defined $cfg) {
307 611         2043 for (keys %$cfg) {
308 1898         3161 my ($optref, $is_alias) = $self->get_option_ref($_);
309 1898 100       2626 if ($optref) {
310 1897 100       5202 if ($optref->{list}) {
    100          
311 2         9 error('list_options_in_config', option => $_);
312             } elsif (!defined $optref->{value}) {
313             # fill from config
314 1837 100       4162 my $decoded = $optref->{binary} ? $cfg->{$_} : $self->decode_config_value($cfg->{$_});
315 1837 100       2802 last unless defined $decoded;
316 1836         1739 @{$optref}{qw/value source/} = ($decoded, 'config'); # TODO: support for array options??
  1836         4401  
317             }
318             } else {
319 1         4 error('unknown_config_option', option => $_);
320             }
321             }
322             }
323             }
324 724 100       2021 unless ($self->{errors}) {
325              
326 694         793 for (values %{$self->{options}}) {
  694         4024  
327             # fill from default values
328 22487 100 100     61263 @{$_}{qw/value source/} = ($_->{default}, 'default') if (!defined($_->{value}) && defined($_->{default}));#$_->{seen} &&
  6205         9891  
329             }
330              
331 694 100       3347 $self->{preinitialize}->() if $self->{preinitialize};
332              
333 694         2050 $self->{positional_tail} = \@ARGV; #[map { decode($self->{cmd_encoding}, $_, Encode::DIE_ON_ERR|Encode::LEAVE_SRC) } @ARGV];
334              
335 694         2947 $self->{commands}->{$command}->{cb}->(); # the callback!
336              
337 692         5836 for (qw/ConfigOption ConfigEncoding CmdEncoding/) {
338 2074 100 100     8857 confess "Special option '$_' must be seen" if $self->{$_} && !$self->{options}{$self->{$_}}{seen};
339             }
340              
341 691         770 for (values %{$self->{options}}) {
  691         2501  
342 22907 100 100     57653 error('unexpected_option', option => _real_option_name($_)) if defined($_->{value}) && ($_->{source} eq 'option') && !$_->{seen}; # TODO: test validation same way
      100        
343             }
344              
345 691 100       1757 unless ($self->{errors}) {
346 432 100       1002 if (@ARGV) {
347 11 100       22 unless (defined eval {
348 11         74 error('unexpected_argument', a => decode($self->{cmd_encoding}, shift @ARGV, Encode::DIE_ON_ERR|Encode::LEAVE_SRC)); 1;
  10         51  
349             }) {
350 1         31 error("options_encoding_error", encoding => $self->{cmd_encoding}); #TODO: not utf!
351             }
352             }
353             }
354              
355 691 100       1580 unless ($self->{errors}) {
356 421         1219 $self->unflatten_scope();
357             }
358             }
359              
360 721         3120 $self->{error_texts} = [ $self->errors_or_warnings_to_messages($self->{errors}) ];
361 721         2419 $self->{warning_texts} = [ $self->errors_or_warnings_to_messages($self->{warnings}) ];
362              
363             return {
364             errors => arrayref_or_undef $self->{errors},
365             error_texts => arrayref_or_undef $self->{error_texts},
366             warnings => arrayref_or_undef $self->{warnings},
367             warning_texts => arrayref_or_undef $self->{warning_texts},
368             command => $self->{errors} ? undef : $command,
369             options => $self->{data},
370             option_list => $self->{option_list},
371 721 100       2444 };
372             }
373              
374             sub unflatten_scope
375             {
376 427     427 0 697 my ($self) = @_;
377 427         844 my $options = {};
378 427         563 for my $k (keys %{$self->{options}}) {
  427         2543  
379 13633         10513 my $v = $self->{options}->{$k};
380 13633 100 100     31722 if ($v->{seen} && defined($v->{value})) {
381 6193         4181 my $dest = $options;
382 6193 100       3999 for (@{$v->{scope}||[]}) {
  6193         15634  
383 202   100     647 $dest = $dest->{$_} ||= {};
384             }
385 6193         10645 $dest->{$k} = $v->{value};
386             }
387             }
388 427         2249 $self->{data} = $options;
389             }
390              
391              
392 23222 100   23222 0 42051 sub assert_option { $context->{options}->{$_} or confess "undeclared option $_"; }
393              
394             sub option($;%) {
395 20802     20802 0 118209 my ($name, %opts) = @_;
396 20802 100       33724 confess "option already declared" if $context->{options}->{$name};
397 20798 100       26979 if (%opts) {
398              
399 16346 100       22477 if (defined $opts{alias}) {
400 28 100       128 $opts{alias} = [$opts{alias}] if ref $opts{alias} eq ref ''; # TODO: common code for two subs, move out
401             }
402              
403 16346 100       21198 if (defined $opts{deprecated}) {
404 1266 100       3624 $opts{deprecated} = [$opts{deprecated}] if ref $opts{deprecated} eq ref '';
405             }
406              
407 16346 100       11563 for (@{$opts{alias}||[]}, @{$opts{deprecated}||[]}) {
  16346 100       42769  
  16346         42799  
408 1930 100       4112 confess "option $_ already declared" if defined $context->{options}->{$_};
409 1928 100       5709 confess "alias $_ already declared" if defined $context->{optaliasmap}->{$_};
410 1920         3502 $context->{optaliasmap}->{$_} = $name;
411             }
412              
413 16336 100       15274 $context->{deprecated_options}->{$_} = 1 for (@{$opts{deprecated}||[]});
  16336         41934  
414             }
415 20788 50       70408 $context->{options}->{$name} = { %opts, name => $name } unless $context->{options}->{$name};
416 20788         51913 return $name;
417             };
418              
419             sub positional($;%)
420             {
421 663     663 0 14265 option shift, @_, positional => 1;
422             }
423              
424             sub options(@) {
425             map {
426 679 100   679 0 35736 confess "option already declared $_" if $context->{options}->{$_};
  2601         5090  
427 2599         4898 $context->{options}->{$_} = { name => $_ };
428 2599         3711 $_
429             } @_;
430             };
431              
432              
433             sub validation(@)
434             {
435 18723     18723 0 29361 my ($name, $message, $cb, %opts) = (shift, shift, pop @_, @_);
436 18723 100       31597 confess "undeclared option" unless defined $context->{options}->{$name};
437 15720         45492 push @{ $context->{options}->{$name}->{validations} }, { %opts, 'message' => $message, cb => $cb }
438 18721 100 66     44102 unless $context->{override_validations} && exists($context->{override_validations}->{$name});
439 18721         38942 $name;
440             }
441              
442             sub command($@)
443             {
444 6378     6378 0 99954 my ($name, $cb, %opts) = (shift, pop, @_); # firs arg is name, last is cb, optional middle is opt
445              
446 6378 100       11598 confess "command $name already declared" if defined $context->{commands}->{$name};
447 6377 100       9195 confess "alias $name already declared" if defined $context->{aliasmap}->{$name};
448 6376 100       8444 if (%opts) {
449 26 100 100     151 $opts{alias} = [$opts{alias}] if (defined $opts{alias}) && (ref $opts{alias} eq ref '');
450              
451 26 100 100     113 $opts{deprecated} = [$opts{deprecated}] if (defined $opts{deprecated}) && ref $opts{deprecated} eq ref '';
452              
453 26 100       23 for (@{$opts{alias}||[]}, @{$opts{deprecated}||[]}) {
  26 100       81  
  26         89  
454 35 100       619 confess "command $_ already declared" if defined $context->{commands}->{$_};
455 33 100       1802 confess "alias $_ already declared" if defined $context->{aliasmap}->{$_};
456 24         77 $context->{aliasmap}->{$_} = $name;
457             }
458              
459 15 100       23 $context->{deprecated_commands}->{$_} = 1 for (@{$opts{deprecated}||[]});
  15         68  
460             }
461 6365         13856 $context->{commands}->{$name} = { cb => $cb, %opts };
462 6365         10626 return;
463             };
464              
465             sub _real_option_name($)
466             {
467 258     258   1019 my ($opt) = @_;
468 258 100       1371 defined($opt->{original_option}) ? $opt->{original_option} : $opt->{name};
469             }
470              
471             sub seen
472             {
473 26322 100   26322 0 32749 my $o = @_ ? shift : $_;
474 26322 100       41995 my $option = $context->{options}->{$o} or confess "undeclared option $o";
475 26321 100       33845 unless ($option->{seen}) {
476 13148         11597 $option->{seen} = 1;
477 13148 100       17366 if ($option->{positional}) {
478 58         55 my $v = shift @{$context->{positional_tail}};
  58         111  
479 58 100       130 if (defined $v) {
480 44         50 push @{$context->{positional_backlog}}, $o;
  44         153  
481 44 100       68 unless (defined eval {
482 44   100     324 @{$option}{qw/value source/} = (decode($context->{cmd_encoding}||'UTF-8', $v, Encode::DIE_ON_ERR|Encode::LEAVE_SRC), 'positional');
  43         1706  
483             }) {
484 1   50     57 error("options_encoding_error", encoding => $context->{cmd_encoding}||'UTF-8'); # TODO: actually remove UTF and fix tests
485             }
486             }
487             }
488             }
489 26321         24676 $o;
490             }
491              
492             sub mandatory(@) {
493             return map {
494 1628     1628 0 2929 my $opt = assert_option;
  15750         14937  
495 15750 100       21566 unless ($opt->{seen}) {
496 8118         7861 seen;
497             confess "mandatory positional argument goes after optional one"
498 8118 100 100     13925 if ($opt->{positional} and ($context->{positional_level} ||= 'mandatory') ne 'mandatory');
      100        
499 8116 100       11257 unless (defined($opt->{value})) {
500             $opt->{positional} ?
501 76 100       212 error("positional_mandatory", a => $_, n => scalar @{$context->{positional_backlog}||[]}+1) :
  3 100       23  
502             error("mandatory", a => _real_option_name($opt)); # actually does not have much sense
503             }
504             }
505 15747         20356 $_;
506             } @_;
507             };
508              
509             sub optional(@)
510             {
511             return map {
512 2826     2826 0 4340 seen;
  4630         5686  
513 4630 100       7683 $context->{positional_level} = 'optional' if ($context->{options}->{$_}->{positional});
514 4630         10324 $_;
515             } @_;
516             };
517              
518             sub deprecated(@)
519             {
520             return map {
521 94     94 0 388 assert_option;
  98         176  
522 98         209 my $opt = $context->{options}->{ seen() };
523 98 100       482 confess "positional options can't be deprecated" if $opt->{positional};
524 97 100       228 if (defined $opt->{value}) {
525 23 100       81 warning('option_deprecated_for_command', a => _real_option_name $opt) if $opt->{source} eq 'option';
526 23         38 undef $opt->{value};
527             }
528 97         365 $_;
529             } @_;
530             };
531             sub validate(@)
532             {
533             return map {
534 681     681 0 1516 my $opt = $context->{options}->{seen()};
  13190         15395  
535 13190 100 100     36650 if (defined($opt->{value}) && !$opt->{validated}) {
536 10034         17301 $opt->{validated} = $opt->{valid} = 1;
537 10034         6512 VALIDATION: for my $v (@{ $opt->{validations} }) {
  10034         14106  
538 7469         11037 for ($opt->{value}) {
539             error ({ format => $v->{message}, a => _real_option_name $opt, value => $_}),
540             $opt->{valid} = 0,
541             $v->{stop} && last VALIDATION
542 7469 100 100     15266 unless $v->{cb}->();
543             }
544             }
545             };
546 13190         53411 $_;
547             } @_;
548             };
549              
550             sub scope($@)
551             {
552 489     489 0 3968 my $scopename = shift;
553             return map {
554 489         841 assert_option;
  1399         2214  
555 1399         1145 unshift @{$context->{options}->{$_}->{scope}}, $scopename;
  1399         3406  
556 1399         3204 $_;
557             } @_;
558             };
559              
560             sub present(@) # TODO: test that it works with arrays
561             {
562 4239 100   4239 0 6912 my $name = @_ ? shift : $_;
563 4239         5844 assert_option for $name;
564             return defined($context->{options}->{$name}->{value})
565 4239         17768 };
566              
567             # TODO: test
568             sub explicit(@) # TODO: test that it works with arrays
569             {
570 226 50   226 0 512 my $name = @_ ? shift : $_;
571 226   66     393 return present($name) && $context->{options}->{$name}->{source} eq 'option'
572             };
573              
574             sub valid($)
575             {
576 115     115 0 268 my ($name) = @_;
577 115         216 assert_option for $name;
578 115 100       406 confess "validation not performed yet" unless $context->{options}->{$name}->{validated};
579 114         449 return $context->{options}->{$name}->{valid};
580             };
581              
582             sub value($)
583             {
584 1488     1488 0 1911 my ($name) = @_;
585 1488         2480 assert_option for $name;
586 1488 100       3106 confess "option not present" unless defined($context->{options}->{$name}->{value});
587 1487         5350 return $context->{options}->{$name}->{value};
588             };
589              
590             sub impose(@)
591             {
592 141     141 0 224 my ($name, $value) = @_;
593 141         373 assert_option for $name;
594 141         232 my $opt = $context->{options}->{$name};
595 141         249 $opt->{source} = 'impose';
596 141         220 $opt->{value} = $value;
597 141         704 return $name;
598             };
599              
600              
601             sub lists(@)
602             {
603 87     87 0 160 my @a = @_;
604 87     355   102 grep { my $o = $_; first { $_ eq $o->{name} } @a; } @{$context->{option_list}};
  155         137  
  155         566  
  355         821  
  87         235  
605             }
606              
607             sub raw_option($)
608             {
609 0     0 0 0 my ($name) = @_;
610 0         0 assert_option for $name;
611 0 0       0 confess "option not present" unless defined($context->{options}->{$name}->{value});
612 0         0 return $context->{options}->{$name};
613             };
614              
615             sub custom($$)
616             {
617 430     430 0 1330 my ($name, $value) = @_;
618 430 100       1104 confess if ($context->{options}->{$name});
619 429         1599 $context->{options}->{$name} = {source => 'set', value => $value, name => $name, seen => 1 };
620 429         1168 return $name;
621             };
622              
623              
624             sub error($;%)
625             {
626 383     383 0 6726 my ($name, %data) = @_;
627 383         2952 push @{$context->{errors}},
628 383 100       472 defined($context->{messages}->{$name}) ?
    100          
629             { format => $name, %data } :
630             (%data ? confess("message '$name' is undefined") : $name);
631 381         1293 return;
632             };
633              
634             sub warning($;%)
635             {
636 204     204 0 2562 my ($name, %data) = @_;
637 204         1149 push @{$context->{warnings}},
638 204 50       201 defined($context->{messages}->{$name}) ?
    100          
639             { format => $name, %data } :
640             (%data ? confess("message '$name' is undefined") : $name);
641 204         353 return;
642             };
643              
644             sub read_config
645             {
646 158     158   482 my ($self, $filename) = @_;
647 158 100       2367 -f $filename or
648             die exception 'config_file_is_not_a_file' => "Config file is not a file: %config%",
649             config => hex_dump_string($filename);
650 155 50       4266 open (my $F, "<:crlf", $filename) or
651             die exception 'cannot_read_config' => "Cannot read config file: %config%, errno=%errno%",
652             config => hex_dump_string($filename), 'ERRNO';
653 155         150 my %newconfig;
654 155         144 local $_;
655 155         159 my $lineno = 0;
656 155         1623 while (<$F>) {
657 266         282 chomp;
658 266         180 ++$lineno;
659 266 100       934 next if /^\s*$/;
660 247 100       494 next if /^\s*\#/;
661 239         183 my ($name, $value);
662             # we have there non-unicode data, so [ \t] can be replaced with \s. however i'll leave it for clarity
663 239 100       1272 if (($name, $value) = /^[ \t]*([A-Za-z0-9][A-Za-z0-9-]*)[ \t]*=[ \t]*(.*?)[ \t]*$/) {
    100          
664 145         758 $newconfig{$name} = $value;
665             } elsif (($name) = /^[ \t]*([A-Za-z0-9][A-Za-z0-9-]*)[ \t]*$/) {
666 40         206 $newconfig{$name} = 1;
667             } else {
668 54         164 die exception 'invalid_config_line' => 'Cannot parse line in config file: %line% at %config% line %lineno%',
669             lineno => $lineno, line => hex_dump_string($_), config => hex_dump_string($filename);
670             }
671             }
672 101         642 close $F;
673 101         513 return \%newconfig;
674             }
675              
676             1;