File Coverage

blib/lib/App/Spec/Argument.pm
Criterion Covered Total %
statement 62 66 93.9
branch 36 40 90.0
condition 18 21 85.7
subroutine 5 5 100.0
pod 2 2 100.0
total 123 134 91.7


line stmt bran cond sub pod time code
1             # ABSTRACT: App::Spec objects representing command line options or parameters
2 5     5   39 use strict;
  5         9  
  5         138  
3 5     5   28 use warnings;
  5         9  
  5         217  
4             package App::Spec::Argument;
5              
6             our $VERSION = '0.013'; # VERSION
7              
8 5     5   1607 use Moo;
  5         34330  
  5         28  
9              
10             has name => ( is => 'ro' );
11             has type => ( is => 'ro' );
12             has multiple => ( is => 'ro' );
13             has mapping => ( is => 'ro' );
14             has required => ( is => 'ro' );
15             has unique => ( is => 'ro' );
16             has summary => ( is => 'ro' );
17             has description => ( is => 'ro' );
18             has default => ( is => 'ro' );
19             has completion => ( is => 'ro' );
20             has enum => ( is => 'ro' );
21             has values => ( is => 'ro' );
22              
23             sub common {
24 641     641 1 2082 my ($class, %args) = @_;
25 641         1061 my %dsl;
26 641 100       1453 if (defined $args{spec}) {
27 14         60 %dsl = $class->from_dsl(delete $args{spec});
28             }
29 641         966 my $description = $args{description};
30 641         994 my $summary = $args{summary};
31 641   100     1364 $summary //= '';
32 641   100     2330 $description //= '';
33 641   100     1568 my $type = $args{type} // 'string';
34             my %hash = (
35             name => $args{name},
36             summary => $summary,
37             description => $description,
38             type => $type,
39             multiple => $args{multiple} ? 1 : 0,
40             mapping => $args{mapping} ? 1 : 0,
41             required => $args{required} ? 1 : 0,
42             unique => $args{unique} ? 1 : 0,
43             default => $args{default},
44             completion => $args{completion},
45             enum => $args{enum},
46             values => $args{values},
47 641 100       5117 %dsl,
    100          
    100          
    100          
48             );
49 641   66     6982 not defined $hash{ $_ } and delete $hash{ $_ } for keys %hash;
50 641         5050 return %hash;
51             }
52              
53             my $name_re = qr{[\w-]+};
54              
55             sub from_dsl {
56 14     14 1 36 my ($class, $dsl) = @_;
57 14         25 my %hash;
58              
59             my $name;
60 14         26 my $type = "flag";
61 14         17 my $multiple;
62 14         41 $dsl =~ s/^\s+//;
63              
64 14 100       50 if ($dsl =~ s/^\+//) {
65 1         3 my $required = 1;
66 1         10 $hash{required} = $required;
67             }
68              
69 14 50       479 if ($dsl =~ s/^ ($name_re) //x) {
70 14         45 $name = $1;
71 14         38 $hash{name} = $name;
72             }
73             else {
74 0         0 die "invalid spec: '$dsl'";
75             }
76              
77 14         27 my @aliases;
78 14         54 while ($dsl =~ s/^ \| (\w) //x) {
79 4         15 push @aliases, $1;
80             }
81 14 100       52 if (@aliases) {
82 4         10 $hash{aliases} = \@aliases;
83             }
84              
85 14         27 my $getopt_type = '';
86 14 100       49 if ($dsl =~ s/^=//) {
87             # not a flag, default string
88 7         13 $type = "string";
89 7 100       26 if ($dsl =~ s/^([isf])//) {
90 5         9 $getopt_type = $1;
91 5 100       19 if ($getopt_type eq "i") {
    100          
    50          
92 1         3 $type = "integer";
93             }
94             elsif ($getopt_type eq "f") {
95 1         2 $type = "float";
96             }
97             elsif ($getopt_type eq "s") {
98             }
99             else {
100 0         0 die "Option $name: type $getopt_type not supported";
101             }
102             }
103             }
104              
105 14 100 100     127 if ($type eq 'flag' and $dsl =~ s/^\+//) {
    100 100        
    100 100        
106             # incremental flag
107 1         3 $multiple = 1;
108 1         3 $hash{multiple} = 1;
109             }
110             elsif ($type eq 'string' and $dsl =~ s/^\@//) {
111 1         4 $hash{multiple} = 1;
112             }
113             elsif ($type eq 'string' and $dsl =~ s/^\%//) {
114 1         3 $hash{multiple} = 1;
115 1         2 $hash{mapping} = 1;
116             }
117              
118 14         46 $dsl =~ s/^\s+//;
119              
120 14         52 while ($dsl =~ s/^\s*([=+])(\S+)//) {
121 3 100       10 if ($1 eq '+') {
122 2         4 $type = $2;
123 2 50 33     11 if ($getopt_type and $type ne $getopt_type) {
124 0         0 die "Explicit type '$type' conflicts with getopt type '$getopt_type'";
125             }
126             }
127             else {
128 1         4 $hash{default} = $2;
129             }
130             }
131              
132 14 100       59 if ($dsl =~ s/^\s*--\s*(.*)//) {
133             # TODO only summary should be supported
134 9         26 $hash{summary} = $1;
135             }
136              
137 14 50       40 if (length $dsl) {
138 0         0 die "Invalid spec: trailing '$dsl'";
139             }
140              
141 14         30 $hash{type} = $type;
142 14         80 return %hash;
143             }
144              
145             1;
146              
147             =pod
148              
149             =head1 NAME
150              
151             App::Spec::Argument - App::Spec objects representing command line options or parameters
152              
153             =head1 SYNOPSIS
154              
155             =head1 EXAMPLES
156              
157             Options can be defined in a verbose way via key value pairs, but you can also
158             use a shorter syntax.
159              
160             The idea comes from Ingy's L.
161              
162             The first item of the string is the name of the option using a syntax
163             very similar to the one from L.
164              
165             Then you can optionally define a type, a default value and a summary.
166              
167             You can see a list of supported syntax in this example from C:
168              
169             =for comment
170             START INLINE t/data/12.dsl.yaml
171              
172             ---
173             # version with short dsl syntax
174             name: myapp
175             appspec: { version: 0.001 }
176             class: App::Spec::Example::MyApp
177             title: My Very Cool App
178             options:
179             - spec: foo --Foo
180             - spec: verbose|v+ --be verbose
181             - spec: +req --Some required flag
182             - spec: number=i --integer option
183             - spec: number2|n= +integer --integer option
184             - spec: fnumber=f --float option
185             - spec: fnumber2|f= +float --float option
186             - spec: date|d=s =today
187             - spec: items=s@ --multi option
188             - spec: set=s% --multiple key=value pairs
189            
190             ---
191             # version with verbose syntax
192             name: myapp
193             appspec: { version: 0.001 }
194             class: App::Spec::Example::MyApp
195             title: My Very Cool App
196             options:
197             - name: foo
198             type: flag
199             summary: Foo
200             - name: verbose
201             summary: be verbose
202             type: flag
203             multiple: true
204             aliases: ["v"]
205             - name: req
206             summary: Some required flag
207             required: true
208             type: flag
209             - name: number
210             summary: integer option
211             type: integer
212             - name: number2
213             summary: integer option
214             type: integer
215             aliases: ["n"]
216             - name: fnumber
217             summary: float option
218             type: float
219             - name: fnumber2
220             summary: float option
221             type: float
222             aliases: ["f"]
223             - name: date
224             type: string
225             default: today
226             aliases: ["d"]
227             - name: items
228             type: string
229             multiple: true
230             summary: multi option
231             - name: set
232             type: string
233             multiple: true
234             mapping: true
235             summary: multiple key=value pairs
236            
237              
238              
239             =for comment
240             STOP INLINE
241              
242             =head1 METHODS
243              
244             =over 4
245              
246             =item common
247              
248             Builds a hash with the given hashref and fills in some defaults.
249              
250             my %hash = $class->common($args);
251              
252             =item from_dsl
253              
254             Builds a hash from the dsl string
255              
256             %dsl = $class->from_dsl("verbose|v+ --Be verbose");
257              
258              
259             =item name, type, multiple, required, unique, summary, description, default, completion, enum, values, mapping
260              
261             Attributes which represent the ones from the spec.
262              
263             =back
264              
265             =cut