File Coverage

blib/lib/Commandable/Command.pm
Criterion Covered Total %
statement 54 54 100.0
branch 10 12 83.3
condition 7 8 87.5
subroutine 26 26 100.0
pod 6 7 85.7
total 103 107 96.2


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2021-2024 -- leonerd@leonerd.org.uk
5              
6             package Commandable::Command 0.14;
7              
8 11     11   816 use v5.26;
  11         54  
9 11     11   70 use warnings;
  11         20  
  11         686  
10 11     11   753 use experimental qw( signatures );
  11         2283  
  11         95  
11              
12             =head1 NAME
13              
14             C - represent metadata for an invokable command
15              
16             =head1 DESCRIPTION
17              
18             Objects in this class are returned by a L instance to
19             represent individual commands that exist.
20              
21             =cut
22              
23 28         50 sub new ( $class, %args )
24 28     28 0 81 {
  28         132  
  28         44  
25 28   100     134 $args{arguments} //= [];
26 28   100     141 $args{options} //= {};
27 28         255 bless [ @args{qw( name description arguments options package code )} ], $class;
28             }
29              
30             =head1 ACCESSORS
31              
32             The following simple methods return metadata fields about the command
33              
34             =cut
35              
36             =head2 name
37              
38             =head2 description
39              
40             $name = $command->name;
41             $desc = $command->description;
42              
43             Strings giving the short name (to be used on a commandline), and descriptive
44             text for the command.
45              
46             =head2 arguments
47              
48             @args = $command->arguments;
49              
50             A (possibly-empty) list of argument metadata structures.
51              
52             =head2 options
53              
54             %opts = $command->options;
55              
56             A (possibly-empty) kvlist of option metadata structures.
57              
58             =head2 package
59              
60             $pkg = $command->package;
61              
62             The package name as a plain string.
63              
64             =head2 code
65              
66             $sub = $command->code;
67              
68             A CODE reference to the code actually implementing the command.
69              
70             =cut
71              
72 19     19 1 195 sub name { shift->[0] }
73 6     6 1 1809 sub description { shift->[1] }
74 37     37 1 158 sub arguments { shift->[2]->@* }
75 40     40 1 298 sub options { shift->[3]->%* }
76 4     4 1 42 sub package { shift->[4] }
77 16     16 1 68 sub code { shift->[5] }
78              
79             =head1 METHODS
80              
81             =cut
82              
83             =head2 parse_invocation
84              
85             I this method has been moved to L.
86              
87             =cut
88              
89             package # hide
90             Commandable::Command::_Argument;
91              
92             =head1 ARGUMENT SPECIFICATIONS
93              
94             Each argument specification is given by an object having the following structure:
95              
96             =head2 name
97              
98             =head2 description
99              
100             $name = $argspec->name;
101              
102             $desc = $argspec->description;
103              
104             Text strings for the user, used to generate the help text.
105              
106             =head2 optional
107              
108             $bool = $argspec->optional;
109              
110             If false, the option is mandatory and an error is raised if no value is
111             provided for it. If true, it is optional and if absent an C will passed
112             instead.
113              
114             =head2 slurpy
115              
116             $bool = $argspec->slurpy;
117              
118             If true, the argument will be passed as an ARRAY reference containing the
119             entire remaining list of tokens provided by the user.
120              
121             =cut
122              
123 20         88 sub new ( $class, %args )
124 20     20   57 {
  20         81  
  20         56  
125 20         247 bless [ @args{qw( name description optional slurpy )} ], $class;
126             }
127              
128 4     4   83 sub name { shift->[0] }
129 2     2   30 sub description { shift->[1] }
130 7     7   42 sub optional { shift->[2] }
131 11     11   97 sub slurpy { shift->[3] }
132              
133             package # hide
134             Commandable::Command::_Option;
135              
136             =head1 OPTION SPECIFICATIONS
137              
138             Each option specification is given by an object having the following
139             structure:
140              
141             =head2 name
142              
143             $name = $optspec->name;
144              
145             A string giving the primary human-readable name of the option.
146              
147             =head2 keyname
148              
149             $keyname = $optspec->keyname;
150              
151             A string giving the name this option will be given in the options hash
152             provided to the command subroutine. This is generated from the human-readable
153             name, but hyphens are converted to underscores, to make it simpler to use as a
154             hash key in Perl code.
155              
156             =head2 names
157              
158             @names = $optspec->names;
159              
160             A list containing the name plus all the aliases this option is known by.
161              
162             =head2 description
163              
164             $desc = $optspec->description;
165              
166             A text string containing information for the user, used to generate the help
167             text.
168              
169             =head2 mode
170              
171             $mode = $optspec->mode;
172              
173             A string that describes the behaviour of the option.
174              
175             C options do not expect a value to be suppled by the user, and will store a
176             true value in the options hash if present.
177              
178             C options take a value from the rest of the token, or the next token.
179              
180             --opt=value
181             --opt value
182              
183             C options can be supplied more than once; values are pushed into
184             an ARRAY reference which is passed in the options hash.
185              
186             C options may be supplied more than once; each occurance will increment
187             the stored value by one.
188              
189             =head2 default
190              
191             $val = $optspec->default;
192              
193             A value to provide in the options hash if the user did not specify a different
194             one.
195              
196             =head2 negatable
197              
198             $bool = $optspec->negatable;
199              
200             If true, also accept a C<--no-OPT> option to reset the value of the option to
201             C.
202              
203             =head2 typespec
204              
205             I no longer supported.
206              
207             =head2 matches
208              
209             $re = $optspec->matches;
210              
211             If defined, gives a precompiled regexp that any user-supplied value must
212             conform to.
213              
214             A few shortcuts are provided, which are used if the provided name ends in
215             C<=i> (for "integer"), C<=u> (for "unsigned integer", i.e. non-negative) or
216             C<=f> (for "float").
217              
218             =cut
219              
220             my %typespecs = (
221             i => [ "be an integer", qr/^-?\d+$/ ],
222             u => [ "be a non-negative integer", qr/^\d+$/ ],
223             f => [ "be a floating-point number", qr/^-?\d+(?:\.\d+)?$/ ],
224             );
225              
226 30         52 sub new ( $class, %args )
227 30     30   49 {
  30         73  
  30         64  
228             warn "Use of $args{name} in a Commandable command option name; should be " . $args{name} =~ s/:$/=/r
229 30 50       115 if $args{name} =~ m/:$/;
230              
231 30 100       127 if( $args{name} =~ s/([=:])(.+?)$/$1/ ) {
232             # Convert a type abbreviation
233 3 50       11 my $typespec = $typespecs{$2} or
234             die "Unrecognised typespec $2";
235              
236 3         8 ( $args{match_msg}, $args{matches} ) = @$typespec;
237             }
238 30 100       165 $args{mode} = "value" if $args{name} =~ s/[=:]$//;
239 30 100       76 $args{mode} = "multi_value" if $args{multi};
240 30         101 my @names = split m/\|/, delete $args{name};
241 30   100     222 $args{mode} //= "set";
242 30 100 50     83 $args{negatable} //= 1 if $args{mode} eq "bool";
243 30         203 bless [ \@names, @args{qw( description mode default negatable matches match_msg )} ], $class;
244             }
245              
246 275     275   737 sub name { shift->[0]->[0] }
247 230     230   1052 sub keyname { shift->name =~ s/-/_/gr }
248 35     35   174 sub names { shift->[0]->@* }
249 5     5   53 sub description { shift->[1] }
250 123     123   492 sub mode { shift->[2] }
251 208     208   541 sub default { shift->[3] }
252 9     9   43 sub negatable { shift->[4] }
253 32     32   99 sub matches { shift->[5] }
254 3     3   31 sub match_msg { shift->[6] }
255              
256 46     46   95 sub mode_expects_value { shift->mode =~ m/value$/ }
257              
258             =head1 AUTHOR
259              
260             Paul Evans
261              
262             =cut
263              
264             0x55AA;