File Coverage

lib/Pcore/Core/CLI/Opt.pm
Criterion Covered Total %
statement 6 143 4.2
branch 0 132 0.0
condition 0 36 0.0
subroutine 2 15 13.3
pod 0 2 0.0
total 8 328 2.4


line stmt bran cond sub pod time code
1             package Pcore::Core::CLI::Opt;
2              
3             # NOTE http://docopt.org/
4              
5 5     5   34 use Pcore -class;
  5         10  
  5         32  
6 5     5   40 use Pcore::Util::Scalar qw[is_ref is_plain_arrayref is_plain_hashref];
  5         11  
  5         34  
7              
8             with qw[Pcore::Core::CLI::Type];
9              
10             has name => ( is => 'ro', isa => Str, required => 1 );
11             has short => ( is => 'lazy', isa => Maybe [ StrMatch [qr/\A[[:alpha:]]\z/sm] ] ); # undef - disable short option
12             has desc => ( is => 'ro', isa => Str );
13              
14             has type => ( is => 'lazy', isa => Str ); # option type desc for usage help
15             has isa => ( is => 'ro', isa => CodeRef | RegexpRef | ArrayRef | Enum [ keys $Pcore::Core::CLI::Type::TYPE->%* ] );
16              
17             has default => ( is => 'ro', isa => Str | ArrayRef | HashRef ); # applied, when option is not exists, possible only for required options
18              
19             # NOTE !!!WARNING!!! default_val is not work as it should with Getopt::Long, don't use it now
20             has default_val => ( is => 'ro', isa => Maybe [Str] ); # applied, when option value is not defined
21              
22             has min => ( is => 'lazy', isa => PositiveOrZeroInt ); # 0 - option is not required
23             has max => ( is => 'lazy', isa => PositiveOrZeroInt ); # 0 - unlimited repeats
24              
25             has negated => ( is => 'lazy', isa => Bool ); # trigger can be used with --no prefix
26             has hash => ( is => 'ro', isa => Bool, default => 0 ); # option ia a hash, --opt key=val
27              
28             has getopt_name => ( is => 'lazy', isa => Str, init_arg => undef );
29             has is_trigger => ( is => 'lazy', isa => Bool, init_arg => undef );
30             has is_repeatable => ( is => 'lazy', isa => Bool, init_arg => undef );
31             has is_required => ( is => 'lazy', isa => Bool, init_arg => undef );
32             has getopt_spec => ( is => 'lazy', isa => Str, init_arg => undef );
33             has help_spec => ( is => 'lazy', isa => Str, init_arg => undef );
34              
35 0     0 0   sub BUILD ( $self, $args ) {
  0            
  0            
  0            
36 0           my $name = $self->name;
37              
38             # TODO remove, when this bug will be fixed
39 0 0         die qq[Option "$name", don't use default_val until bug with Getopt::Long will be fixed] if defined $self->default_val;
40              
41             # max
42 0 0 0       die qq[Option "$name", "max" must be >= "min" ] if $self->max && $self->max < $self->min;
43              
44             # default
45 0 0         if ( defined $self->default ) {
46 0 0         die qq[Option "$name", default value can be used only for required option (min > 0)] if $self->min == 0;
47              
48 0 0         if ( $self->is_trigger ) {
49 0 0         if ( $self->is_repeatable ) {
50 0 0         die qq[Option "$name", default value can be positive integer for incremental trigger] if $self->default !~ /\A\d+\z/sm;
51             }
52             else {
53 0 0         die qq[Option "$name", default value can be 0 or 1 for boolean trigger] if $self->default !~ /\A[01]\z/sm;
54             }
55             }
56             else {
57 0 0         if ( $self->hash ) {
    0          
58 0 0         die qq[Option "$name", default value must be a hash for hash option] if !is_plain_hashref $self->default;
59             }
60             elsif ( $self->is_repeatable ) {
61 0 0         die qq[Option "$name", default value must be a array for repeatable option] if !is_plain_arrayref $self->default;
62             }
63             else {
64 0 0         die qq[Option "$name", default value must be a string for plain option] if is_ref $self->default;
65             }
66             }
67             }
68              
69             # default_val
70 0 0         if ( defined $self->default_val ) {
71 0 0 0       die qq[Option "$name", "default_val" can not be used for trigger, hash or repeatable option] if $self->is_trigger || $self->hash || $self->is_repeatable;
      0        
72             }
73              
74 0 0         if ( $self->is_trigger ) {
75 0 0         die qq[Option "$name", trigger can't be a hash] if $self->hash;
76              
77 0 0         if ( $self->negated ) {
78 0 0         die qq[Option "$name", negated can't be used with short option] if defined $self->short;
79              
80 0 0         die qq[Option "$name", negated can't be used with incremental trigger] if $self->is_repeatable;
81              
82 0 0 0       die qq[Option "$name", negated is useless for the boolean trigger with default value = 0] if defined $self->default && $self->default == 0;
83             }
84             else {
85 0 0 0       die qq[Option "$name", negated should be enabled for the boolean trigger with default value = 1] if !$self->is_repeatable && defined $self->default && $self->default == 1;
      0        
86             }
87             }
88             else {
89 0 0         die qq[Option "$name", negated can be used only with triggers] if $self->negated;
90             }
91              
92 0           return;
93             }
94              
95 0     0     sub _build_min ($self) {
  0            
  0            
96 0 0         return defined $self->default ? 1 : 0;
97             }
98              
99 0     0     sub _build_max ($self) {
  0            
  0            
100 0 0         return $self->min ? $self->min : 1;
101             }
102              
103 0     0     sub _build_negated ($self) {
  0            
  0            
104 0 0 0       if ( $self->is_trigger && defined $self->default ) {
105 0 0         return 0 if $self->default == 0; # negated is useless if default value is already = 0
106              
107 0 0 0       return 1 if $self->default == 1 && !$self->is_repeatable; # negated is mandatory for boolean trigger with default value = 1
108             }
109              
110 0           return 0;
111             }
112              
113 0     0     sub _build_getopt_name ($self) {
  0            
  0            
114 0           return $self->name =~ s/_/-/smgr;
115             }
116              
117 0     0     sub _build_is_trigger ($self) {
  0            
  0            
118 0 0         return defined $self->isa ? 0 : 1;
119             }
120              
121 0     0     sub _build_is_repeatable ($self) {
  0            
  0            
122 0 0         return $self->max != 1 ? 1 : 0;
123             }
124              
125 0     0     sub _build_is_required ($self) {
  0            
  0            
126 0 0 0       return $self->min && !defined $self->default ? 1 : 0;
127             }
128              
129 0     0     sub _build_short ($self) {
  0            
  0            
130 0 0         return $self->negated ? undef : substr $self->name, 0, 1;
131             }
132              
133 0     0     sub _build_type ($self) {
  0            
  0            
134 0 0         if ( !$self->is_trigger ) {
135 0           my $ref = ref $self->isa;
136              
137 0 0         if ( !$ref ) {
    0          
    0          
    0          
138 0           return uc $self->isa;
139             }
140             elsif ( $ref eq 'ARRAY' ) {
141 0           return 'ENUM';
142             }
143             elsif ( $ref eq 'CODE' ) {
144 0           return 'STR';
145             }
146             elsif ( $ref eq 'Regexp' ) {
147 0           return 'STR';
148             }
149             }
150              
151 0           return q[];
152             }
153              
154 0     0     sub _build_getopt_spec ($self) {
  0            
  0            
155 0           my $spec = $self->getopt_name;
156              
157 0 0         $spec .= q[|] . $self->short if defined $self->short;
158              
159 0 0         if ( $self->is_trigger ) {
160 0 0         $spec .= q[!] if $self->negated;
161              
162 0 0         $spec .= q[+] if $self->is_repeatable;
163             }
164             else {
165 0 0         if ( defined $self->default_val ) {
166 0           $spec .= q[:s];
167             }
168             else {
169 0           $spec .= q[=s];
170              
171 0 0         if ( $self->hash ) {
    0          
172 0           $spec .= q[%];
173             }
174             elsif ( $self->is_repeatable ) {
175 0           $spec .= q[@];
176             }
177             }
178             }
179              
180 0           return $spec;
181             }
182              
183 0     0     sub _build_help_spec ($self) {
  0            
  0            
184 0 0         my $spec = $self->short ? q[-] . $self->short . q[ ] : q[ ] x 3;
185              
186 0           $spec .= q[--];
187              
188 0 0         $spec .= '[no[-]]' if $self->negated;
189              
190 0           $spec .= $self->getopt_name;
191              
192 0 0         if ( !$self->is_trigger ) {
193 0           my $type = uc $self->type;
194              
195 0 0         if ( $self->hash ) {
196 0           $spec .= " key=$type";
197             }
198             else {
199 0 0         if ( defined $self->default_val ) {
200 0           $spec .= "=[$type]";
201             }
202             else {
203 0           $spec .= " $type";
204             }
205             }
206             }
207              
208 0           my @attrs;
209              
210 0 0         push @attrs, q[+] if $self->is_repeatable;
211              
212 0 0         push @attrs, q[!] if $self->is_required;
213              
214 0 0         $spec .= q[ ] . join q[], map {"[$_]"} @attrs if @attrs;
  0            
215              
216 0           return $spec;
217             }
218              
219 0     0 0   sub validate ( $self, $opt ) {
  0            
  0            
  0            
220 0           my $name = $self->name;
221              
222             # remap getopt name
223 0 0         $opt->{$name} = delete $opt->{ $self->getopt_name } if exists $opt->{ $self->getopt_name };
224              
225 0 0 0       if ( !exists $opt->{$name} ) {
    0          
226 0 0         if ( $self->min ) { # option is required
227 0 0         if ( defined $self->default ) {
228              
229             # apply default value
230 0           $opt->{$name} = $self->default;
231             }
232             else {
233 0           return qq[option "$name" is required];
234             }
235             }
236             else {
237              
238             # option is not exists and is not required
239 0           return;
240             }
241             }
242             elsif ( defined $self->default_val && $opt->{$name} eq q[] ) {
243              
244             # apply default_val if opt is exists. but value is not specified
245 0           $opt->{$name} = $self->default_val;
246             }
247              
248             # min / max check for the repeatable opt
249 0 0         if ( $self->is_repeatable ) {
250 0           my $count;
251              
252 0 0         if ( $self->is_trigger ) {
    0          
    0          
    0          
253 0           $count = $opt->{$name};
254              
255             # do not check min / max for the repeatable trigger with default = 0
256 0 0         goto VALIDATE if $count == 0;
257             }
258             elsif ( !ref $opt->{$name} ) {
259 0           $count = 1;
260             }
261             elsif ( is_plain_arrayref $opt->{$name} ) {
262 0           $count = scalar $opt->{$name}->@*;
263             }
264             elsif ( is_plain_hashref $opt->{$name} ) {
265 0           $count = scalar keys $opt->{$name}->%*;
266             }
267              
268             # check min args num
269 0 0         return qq[option "$name" must be repeated at least @{[$self->min]} time(s)] if $count < $self->min;
  0            
270              
271             # check max args num
272 0 0 0       return qq[option "$name" can be repeated not more, than @{[$self->max]} time(s)] if $self->max && $count > $self->max;
  0            
273             }
274              
275             VALIDATE:
276              
277             # validate option value type
278 0 0 0       if ( defined $self->isa && ( my $error_msg = $self->_validate_isa( $opt->{$name} ) ) ) {
279 0           return qq[option "$name" $error_msg];
280             }
281              
282 0           return;
283             }
284              
285             1;
286             ## -----SOURCE FILTER LOG BEGIN-----
287             ##
288             ## PerlCritic profile "pcore-script" policy violations:
289             ## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
290             ## | Sev. | Lines | Policy |
291             ## |======+======================+================================================================================================================|
292             ## | 3 | 35 | Subroutines::ProhibitExcessComplexity - Subroutine "BUILD" with high complexity score (35) |
293             ## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
294             ##
295             ## -----SOURCE FILTER LOG END-----
296             __END__
297             =pod
298              
299             =encoding utf8
300              
301             =head1 NAME
302              
303             Pcore::Core::CLI::Opt
304              
305             =head1 SYNOPSIS
306              
307             =head1 DESCRIPTION
308              
309             =head1 ATTRIBUTES
310              
311             =head1 METHODS
312              
313             =head1 SEE ALSO
314              
315             =cut