File Coverage

lib/App/GitFind/Actions.pm
Criterion Covered Total %
statement 44 63 69.8
branch 4 14 28.5
condition 7 8 87.5
subroutine 14 22 63.6
pod 2 8 25.0
total 71 115 61.7


line stmt bran cond sub pod time code
1             package App::GitFind::Actions;
2              
3 4     4   62 use 5.010;
  4         12  
4 4     4   19 use strict;
  4         5  
  4         82  
5 4     4   15 use warnings;
  4         8  
  4         137  
6              
7             our $VERSION = '0.000002';
8              
9 4     4   25 use parent 'Exporter';
  4         6  
  4         25  
10 4     4   213 use vars::i '@EXPORT_OK' => qw(ARGTEST argdetails);
  4         17  
  4         22  
11 4     4   313 use vars::i '%EXPORT_TAGS' => { all => [@EXPORT_OK] };
  4         10  
  4         39  
12              
13             # Imports
14 4     4   1709 use App::GitFind::Base;
  4         5  
  4         383  
15 4     4   1937 use Math::Cartesian::Product;
  4         5661  
  4         6387  
16              
17             # === Documentation === {{{1
18              
19             =head1 NAME
20              
21             App::GitFind::Actions - Worker functions for App::GitFind
22              
23             =head1 SYNOPSIS
24              
25             TODO
26              
27             =head1 FUNCTIONS
28              
29             =cut
30              
31             # }}}1
32             # Definitions of supported command-line arguments {{{1
33              
34             # NOTE: import() also adds data to these hashrefs.
35              
36             # Helpers for defining these
37 60   100 60   372 sub _a { ($_[0] => { token => 'ACTION', nparam => ($_[1]||0) }) }
38 248   100 248   1837 sub _t { ($_[0] => { token => 'TEST', nparam => ($_[1]||0), index => ($_[2]||false) }) }
      100        
39              
40             # A map from argument name to a details hashref. Valid keys in the hashref are:
41             # token: The token type
42             # nparam: - if a regex, the argument ends with an @ARGV element matching
43             # that regex.
44             # - if an integer, the argument takes that many parameters (>=0).
45             # index: (for tests only) Whether that test can be evaluated using only
46             # information from the index
47             # code: A coderef --- the do_*() function that implements that test.
48             # The {code} field is added by _inflate(), called by import().
49              
50             my %ARGS=(
51             # TODO find(1) positional options, global options?
52              
53             # No-argument tests -- all happen to be index tests
54             map( { _t $_, 0, true }
55             qw(empty executable false nogroup nouser readable true writeable) ),
56              
57             # No-argument actions
58             map( { _a $_ } qw(delete ls print print0 prune quit) ),
59              
60             # One-argument index tests
61             map( { _t $_, 1, true } qw(
62             cmin cnewer ctime
63             gid group ilname iname inum ipath iregex iwholename level
64             mmin mtime name
65             path
66             regex
67             size type uid
68             user wholename
69             ) ),
70              
71             # One-argument detailed tests
72             map( { _t $_, 1 } _qwc <<'EOT' ),
73             amin anewer atime fstype
74             links lname # Actually index tests?
75             newer
76             perm # Actually index test?
77             ref rev # Maybe not detailed tests - TODO investigate this
78             samefile # Actually index test?
79             used
80             EOT
81              
82             # -newerXY forms - all are detailed tests
83             map( { _t('newer' . join('', @$_), 1) }
84             cartesian {1} [qw(a B c m)], [qw(a B c m t)] ),
85              
86             # -amin n
87             # -anewer file
88             # -atime n
89             # -cmin n
90             # -cnewer file
91             # -ctime n
92             # -fstype type
93             # -gid n
94             # -group gname
95             # -ilname pattern
96             # -iname pattern
97             # -inum n
98             # -ipath pattern
99             # -iregex pattern
100             # -iwholename pattern
101             # -level n # not in find(1) - succeed if the item is at level n
102             # -links n
103             # -lname pattern
104             # -mmin n
105             # -mtime n
106             # -name pattern
107             # -newer file
108             # -newerXY reference
109             # -path pattern
110             # -perm [-/+]?mode
111             # -ref revspec # not in find(1) - specify a git ref OR REV
112             # (identical to -rev so you don't have to
113             # remember which)
114             # -regex pattern
115             # -rev revspec # not in find(1) - specify a git rev OR REF
116             # (identical to -ref)
117             # -samefile name
118             # -size n
119             # -type c
120             # -uid n
121             # -used n
122             # -user uname
123             # -wholename pattern
124             # -xtype c # Not supported for now
125             # -context pattern # Not supported for now
126              
127             # Actions with a fixed number of arguments
128             map( { _a $_, 1 } qw(fls fprint fprint0 printf) ),
129             map( { _a $_, 2 } qw(fprintf) ),
130              
131             # -fls file
132             # -fprint file
133             # -fprint0 file
134             # -fprintf file format
135             # -printf format
136              
137             # Actions with a delimited argument list
138             # -exec command [;+]
139             # -execdir command [;+]
140             # -ok command ;
141             # -okdir command ;
142             map( { _a $_, qr/^[;+]$/ } qw(exec execdir) ),
143             map( { _a $_, qr/^;$/ } qw(ok okdir) ),
144             );
145              
146             # }}}1
147             # === Argument-validation functions === {{{1
148             # Special validators for ok, okdir, exec, and execdir.
149             # Validators return undefined if validation passes, and an error message
150             # otherwise. Validators take the command and the located parameters
151             # in @_.
152              
153             sub _validate_exec {
154 0 0   0   0 return "need at least a command name" unless $#_>1;
155 0 0       0 if($_[$#_] eq '+') {
156 0 0       0 return "need a {}" unless grep { $_ eq '{}' } @_;
  0         0  
157 0 0       0 return "{} can't be the first argument to $_[0]" if $_[1] eq '{}';
158             }
159 0         0 return undef;
160             }
161              
162             sub _validate_ok {
163 0 0   0   0 return "need at least a command name" unless $#_>1;
164 0         0 return undef;
165             }
166              
167             # }}}1
168             # === Accessors for argument information === {{{1
169              
170             =head2 ARGTEST
171              
172             Returns a regex that will match any arg, with C<-> or C<--> prefix. The arg
173             is captured into $1. Prototyped as C<()>.
174              
175             =cut
176              
177             sub ARGTEST ()
178             { # Make a regex that will match any arg, with - or --.
179 4     4 1 825 my $x = join '|', map { quotemeta } keys %ARGS;
  308         430  
180 4         670 return qr{^--?($x)$};
181             } #ARGTEST
182              
183             =head2 argdetails
184              
185             Returns a hashref of details about the arg, or undef. Example:
186              
187             my $hr = argdetails('true');
188              
189             =cut
190              
191             sub argdetails {
192 20   50 20 1 105 return $ARGS{$_[0]//''};
193             }
194              
195             # }}}1
196             # === Tests/actions === {{{1
197             # The order matches that in App::GitFind::Actions
198              
199             # No-argument tests {{{2
200              
201             # empty
202             # executable
203              
204 0     0 0 0 sub do_false { false }
205              
206             # nogroup
207             # nouser
208             # readable
209              
210 0     0 0 0 sub do_true { true }
211              
212             # writeable
213              
214             # }}}2
215             # No-argument actions {{{2
216              
217             # delete
218              
219             sub do_ls {
220 0     0 0 0 state $loaded = (require App::GitFind::FileStatLs, true);
221 0         0 print App::GitFind::FileStatLs::ls_stat($_[1]->path);
222 0         0 true
223             }
224             # TODO optimization? Pull the stat() results from $_[1] rather than
225             # re-statting. May not be an issue.
226              
227             sub do_print {
228 0     0 0 0 say $_[0]->dot_relative_path($_[1]);
229 0         0 true
230             }
231              
232 0     0 0 0 sub do_print0 { print $_[0]->dot_relative_path($_[1]), "\0"; true }
  0         0  
233              
234             # prune
235              
236             # quit
237             # This appears to be a GNU extension. It should:
238             # - Finish any child processes
239             # (empirical): do not kill -9 ---
240             # find . -name LICENSE -exec sh -c 'sleep 2' {} + -o -name README -quit
241             # does not terminate the `sleep` early.
242             # - Run any queued -execdir {} + commands
243             # - (empirical) Do not run any queued -exec {} + commands?
244             # E.g., GNU
245             # find . \( -name LICENSE -quit -o -name README \) -exec ls -l {} +
246             # prints nothing. However, POSIX
247             # (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html)
248             # says that "The utility ... shall be invoked ... after the last
249             # pathname in the set is aggregated, and shall be completed
250             # **before the find utility exits**" (emphasis added).
251              
252              
253             # }}}2
254             # One-argument index tests
255             # TODO
256              
257             # }}}2
258             # One-argument detailed tests
259             # TODO
260              
261             # }}}2
262             # -newerXY forms (all are one-argument detailed tests)
263             # TODO
264              
265             # }}}2
266             # -newerXY forms (all are one-argument detailed tests)
267             # TODO
268              
269             # }}}2
270             # Actions with a fixed number of arguments
271              
272             # fls file
273             # fprint file
274             # fprint0 file
275             # fprintf file format
276              
277             sub do_printf { # -printf format. No newline at the end.
278 0     0 0 0 my ($self, %args) = getparameters('self',[qw(entry format)], @_);
279 0         0 print "printf($args{format}, $args{entry})"; # TODO
280             } #do_printf()
281              
282             # }}}2
283             # Actions with a delimited argument list
284              
285             # exec
286             # execdir
287             # ok
288             # okdir
289              
290             # }}}2
291              
292             # }}}1
293              
294             # === Import === {{{1
295             sub import {
296 7     7   1503 state $inflated = false;
297 7         12 my $package = $_[0];
298 7         351 $package->export_to_level(1, @_);
299              
300 7 100       67 if(!$inflated) {
301 4         6 $inflated = true;
302 4         10 _inflate($package);
303              
304             }
305             } #import()
306              
307             # Inflate %ARGS. Arg is the package providing the actions; no return.
308             sub _inflate {
309 4     4   7 my $package = $_[0];
310             # Hook the validators into %ARGS
311 4         8 $ARGS{exec}->{validator} = \&_validate_exec;
312 4         62 $ARGS{execdir}->{validator} = $ARGS{exec}->{validator};
313 4         7 $ARGS{ok}->{validator} = \&_validate_ok;
314 4         7 $ARGS{okdir}->{validator} = $ARGS{ok}->{validator};
315              
316             # Hook the actions into %ARGS
317 4         33 while (my ($key, $hrValue) = each %ARGS) {
318 308         3631 my $fn = $package->can("do_$key");
319 308 100       1970 next unless $fn;
320 24         127 $hrValue->{code} = $fn;
321             }
322             } #_inflate()
323              
324             # }}}1
325              
326             1; # End of App::GitFind::Actions
327             __END__
328              
329             # === Rest of the docs === {{{1
330              
331             =head1 AUTHOR
332              
333             Christopher White, C<< <cxw at cpan.org> >>
334              
335             =head1 LICENSE AND COPYRIGHT
336              
337             Copyright 2019 Christopher White.
338             Portions copyright 2019 D3 Engineering, LLC.
339              
340             This program is distributed under the MIT (X11) License:
341             L<http://www.opensource.org/licenses/mit-license.php>
342              
343             Permission is hereby granted, free of charge, to any person
344             obtaining a copy of this software and associated documentation
345             files (the "Software"), to deal in the Software without
346             restriction, including without limitation the rights to use,
347             copy, modify, merge, publish, distribute, sublicense, and/or sell
348             copies of the Software, and to permit persons to whom the
349             Software is furnished to do so, subject to the following
350             conditions:
351              
352             The above copyright notice and this permission notice shall be
353             included in all copies or substantial portions of the Software.
354              
355             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
356             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
357             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
358             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
359             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
360             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
361             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
362             OTHER DEALINGS IN THE SOFTWARE.
363              
364             =cut
365              
366             # }}}1
367             # vi: set fdm=marker fdl=0: #