File Coverage

blib/lib/IPC/PrettyPipe/Cmd.pm
Criterion Covered Total %
statement 131 138 94.9
branch 37 58 63.7
condition 12 21 57.1
subroutine 29 29 100.0
pod 6 8 75.0
total 215 254 84.6


line stmt bran cond sub pod time code
1             package IPC::PrettyPipe::Cmd;
2              
3             # ABSTRACT: A command in an B pipeline
4              
5 17     17   471195 use Carp;
  17         49  
  17         1208  
6              
7 17     17   189 use List::Util qw[ sum pairs ];
  17         43  
  17         1019  
8 17     17   175 use Scalar::Util qw[ blessed ];
  17         42  
  17         777  
9              
10 17     17   1147 use Try::Tiny;
  17         4144  
  17         1044  
11              
12 17     17   1000 use Safe::Isa;
  17         985  
  17         2734  
13              
14 17     17   7797 use IPC::PrettyPipe::Arg;
  17         66  
  17         637  
15 17     17   8258 use IPC::PrettyPipe::Stream;
  17         64  
  17         905  
16              
17 17     17   127 use Types::Standard -all;
  17         37  
  17         262  
18 17     17   831196 use Type::Params qw[ validate ];
  17         23637  
  17         202  
19              
20 17     17   6150 use IPC::PrettyPipe::Types -all;
  17         52  
  17         185  
21 17     17   57088 use IPC::PrettyPipe::Queue;
  17         64  
  17         600  
22 17     17   157 use IPC::PrettyPipe::Arg::Format;
  17         74  
  17         499  
23              
24 17     17   101 use String::ShellQuote 'shell_quote';
  17         48  
  17         858  
25              
26 17     17   107 use Moo;
  17         48  
  17         95  
27              
28             our $VERSION = '0.13';
29              
30             with 'IPC::PrettyPipe::Queue::Element';
31              
32 17     17   14426 use namespace::clean;
  17         45  
  17         132  
33              
34 17     17   35039 use overload 'fallback' => 1;
  17         44  
  17         159  
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57             use overload '|' => sub {
58 3     3   11 my $swap = pop;
59 3         56 my $pipe = IPC::PrettyPipe->new;
60 3 50       23 $pipe->add( $_ ) for ( $swap ? reverse( @_ ) : @_ );
61 3         19 $pipe;
62 17     17   2717 };
  17         40  
  17         143  
63              
64             # need access to has method which will get removed at the end of the
65             # compilation of this module
66             BEGIN {
67 17     17   1603 IPC::PrettyPipe::Arg::Format->shadow_attrs( fmt => sub { 'arg' . shift } );
  34         18062  
68             }
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97              
98              
99             has cmd => (
100             is => 'ro',
101             isa => Str,
102             required => 1,
103             );
104              
105             # delay building args until all attributes have been specified
106             has _init_args => (
107             is => 'ro',
108             init_arg => 'args',
109             coerce => AutoArrayRef->coercion,
110             isa => ArrayRef,
111             predicate => 1,
112             clearer => 1,
113             );
114              
115              
116              
117              
118              
119              
120              
121              
122              
123              
124              
125              
126              
127              
128              
129              
130              
131              
132              
133              
134              
135              
136              
137              
138              
139              
140              
141              
142              
143              
144              
145              
146              
147              
148              
149              
150              
151              
152             has args => (
153             is => 'ro',
154             default => sub { IPC::PrettyPipe::Queue->new },
155             init_arg => undef,
156             );
157              
158              
159              
160              
161              
162              
163              
164              
165              
166              
167             has streams => (
168             is => 'ro',
169             default => sub { IPC::PrettyPipe::Queue->new },
170             init_arg => undef,
171             );
172              
173              
174              
175              
176              
177              
178              
179              
180              
181              
182              
183              
184              
185              
186              
187              
188              
189              
190              
191              
192              
193              
194              
195              
196              
197              
198              
199              
200              
201              
202              
203              
204              
205              
206              
207              
208              
209              
210             has argfmt => (
211             is => 'ro',
212             lazy => 1,
213             handles => IPC::PrettyPipe::Arg::Format->shadowed_attrs,
214             default => sub { IPC::PrettyPipe::Arg::Format->new_from_attrs( shift ) },
215             );
216              
217              
218              
219              
220              
221             sub BUILDARGS {
222              
223 141     141 0 119245 my $class = shift;
224              
225             my $args
226             = @_ == 1
227             ? (
228             'HASH' eq ref( $_[0] )
229             ? $_[0]
230             : 'ARRAY' eq ref( $_[0] )
231 141 0 0     772 && @{ $_[0] } == 2 ? { cmd => $_[0][0], args => $_[0][1] }
    0          
    50          
232             : { cmd => $_[0] } )
233             : {@_};
234              
235             ## no critic (ProhibitAccessOfPrivateData)
236 141         552 delete @{$args}{ grep { !defined $args->{$_} } keys %$args };
  141         332  
  279         791  
237              
238 141         2533 return $args;
239             }
240              
241              
242             sub BUILD {
243              
244              
245 140     140 0 840 my $self = shift;
246              
247 140 100       620 if ( $self->_has_init_args ) {
248              
249 13         25 $self->ffadd( @{ $self->_init_args } );
  13         57  
250 13         287 $self->_clear_init_args;
251             }
252              
253 140         841 return;
254             }
255              
256              
257              
258              
259              
260              
261              
262              
263              
264              
265 30     30 1 15268 sub quoted_cmd { shell_quote( $_[0]->cmd ) }
266              
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278              
279              
280              
281              
282              
283              
284              
285              
286              
287              
288              
289              
290              
291              
292              
293              
294              
295              
296              
297              
298              
299              
300              
301              
302              
303              
304              
305              
306              
307              
308              
309              
310              
311              
312              
313              
314              
315              
316              
317             sub add {
318              
319 206     206 1 629 my $self = shift;
320              
321 206 50       710 unshift @_, 'arg' if @_ == 1;
322              
323             ## no critic (ProhibitAccessOfPrivateData)
324              
325 206         3804 my $argfmt = $self->argfmt->clone;
326              
327 206         902 my $argfmt_attrs = IPC::PrettyPipe::Arg::Format->shadowed_attrs;
328              
329             my ( $attr ) = validate(
330             \@_,
331             slurpy Dict [
332             arg => Str | Arg | ArrayRef | HashRef,
333             value => Optional [Str],
334             argfmt => Optional [ InstanceOf ['IPC::PrettyPipe::Arg::Format'] ],
335 206         8398 ( map { $_ => Optional [Str] } keys %{$argfmt_attrs} ),
  412         20606  
  206         481551  
336             ] );
337              
338 205         2072778 my $arg = $attr->{arg};
339 205         52550 my $ref = ref $arg;
340              
341             croak( "cannot specify a value if arg is a hash, array, or Arg object\n" )
342 205 50 66     1108 if $ref && exists $attr->{value};
343              
344 205 100       1419 $argfmt->copy_from( $attr->{argfmt} ) if defined $attr->{argfmt};
345 205         869 $argfmt->copy_from( IPC::PrettyPipe::Arg::Format->new_from_hash( $attr ) );
346              
347 205 50       1367 if ( 'HASH' eq $ref ) {
    100          
    50          
348              
349 0         0 while ( my ( $name, $value ) = each %$arg ) {
350              
351 0         0 $self->args->push(
352             IPC::PrettyPipe::Arg->new(
353             name => $name,
354             value => $value,
355             fmt => $argfmt->clone
356             ) );
357              
358             }
359             }
360              
361             elsif ( 'ARRAY' eq $ref ) {
362              
363 73 100       490 croak( "missing value for argument ", $arg->[-1] )
364             if @$arg % 2;
365              
366 72         685 foreach ( pairs @$arg ) {
367              
368 72         230 my ( $name, $value ) = @$_;
369              
370 72         369 $self->args->push(
371             IPC::PrettyPipe::Arg->new(
372             name => $name,
373             value => $value,
374             fmt => $argfmt->clone
375             ) );
376              
377             }
378             }
379              
380             elsif ( $arg->$_isa( 'IPC::PrettyPipe::Arg' ) ) {
381              
382 0         0 $self->args->push( $arg );
383              
384             }
385              
386             # everything else
387             else {
388              
389             $self->args->push(
390             IPC::PrettyPipe::Arg->new(
391             name => $attr->{arg},
392 132 50       1743 exists $attr->{value} ? ( value => $attr->value ) : (),
393             fmt => $argfmt->clone
394             ) );
395             }
396              
397 204         2996 return;
398             }
399              
400              
401              
402              
403              
404              
405              
406              
407              
408              
409              
410              
411              
412              
413              
414              
415              
416              
417              
418              
419              
420              
421              
422              
423              
424              
425              
426              
427              
428              
429              
430              
431              
432              
433              
434              
435              
436              
437              
438              
439              
440              
441              
442              
443              
444              
445              
446              
447              
448              
449             sub ffadd {
450              
451 137     137 1 307 my $self = shift;
452 137         406 my @args = @_;
453              
454 137         2608 my $argfmt = $self->argfmt->clone;
455              
456 137         601 for ( my $idx = 0 ; $idx < @args ; $idx++ ) {
457              
458 271         1051 my $t = $args[$idx];
459              
460 271 100       781 if ( $t->$_isa( 'IPC::PrettyPipe::Arg::Format' ) ) {
    50          
    100          
    50          
461              
462 13         226 $t->copy_into( $argfmt );
463              
464             }
465              
466             elsif ( $t->$_isa( 'IPC::PrettyPipe::Arg' ) ) {
467              
468 0         0 $self->add( arg => $t );
469              
470              
471             }
472              
473             elsif ( ref( $t ) =~ /^(ARRAY|HASH)$/ ) {
474              
475 72         1612 $self->add( arg => $t, argfmt => $argfmt->clone );
476              
477             }
478              
479             elsif ( $t->$_isa( 'IPC::PrettyPipe::Stream' ) ) {
480              
481 0         0 $self->stream( $t );
482              
483             }
484              
485             else {
486              
487             try {
488              
489 186     186   12716 my $stream = IPC::PrettyPipe::Stream->new(
490             spec => $t,
491             strict => 0,
492             );
493              
494 54 100       189 if ( $stream->requires_file ) {
495              
496 52 50       152 croak( "arg[$idx]: stream operator $t requires a file\n" )
497             if ++$idx == @args;
498              
499 52         1069 $stream->file( $args[$idx] );
500             }
501              
502 54         1707 $self->stream( $stream );
503             }
504             catch {
505              
506 132 50   132   4008 die $_ if /requires a file/;
507              
508 132         682 $self->add( arg => $t, argfmt => $argfmt->clone );
509 186         5600 };
510              
511             }
512             }
513              
514 137         1173 return;
515             }
516              
517              
518              
519              
520              
521              
522              
523              
524              
525              
526              
527              
528              
529              
530              
531             sub stream {
532              
533 54     54 1 108 my $self = shift;
534              
535 54         93 my $spec = shift;
536              
537 54 50       165 if ( $spec->$_isa( 'IPC::PrettyPipe::Stream' ) ) {
    0          
538              
539 54 50       966 croak( "too many arguments\n" )
540             if @_;
541              
542 54         235 $self->streams->push( $spec );
543              
544             }
545              
546             elsif ( !ref $spec ) {
547              
548 0 0       0 $self->streams->push(
549             IPC::PrettyPipe::Stream->new(
550             spec => $spec,
551             +@_ ? ( file => @_ ) : () ) );
552             }
553              
554             else {
555              
556 0         0 croak( "illegal stream specification\n" );
557              
558             }
559              
560              
561 54         175 return;
562             }
563              
564              
565              
566              
567              
568              
569              
570              
571              
572              
573              
574             sub valmatch {
575 61     61 1 6896 my $self = shift;
576 61         105 my $pattern = shift;
577              
578             # find number of matches;
579 61         110 return sum 0, map { $_->valmatch( $pattern ) } @{ $self->args->elements };
  104         319  
  61         248  
580             }
581              
582              
583              
584              
585              
586              
587              
588              
589              
590              
591              
592              
593              
594              
595              
596              
597              
598              
599              
600              
601              
602              
603              
604              
605              
606              
607              
608             sub valsubst {
609 31     31 1 1120 my $self = shift;
610              
611 31 100       169 my @args = ( shift, shift, @_ > 1 ? {@_} : @_ );
612              
613              
614             ## no critic (ProhibitAccessOfPrivateData)
615              
616 31         148 my ( $pattern, $value, $args ) = validate(
617             \@args,
618             RegexpRef,
619             Str,
620             Optional [
621             Dict [
622             lastvalue => Optional [Str],
623             firstvalue => Optional [Str] ]
624             ],
625             );
626              
627 31         176220 my $nmatch = $self->valmatch( $pattern );
628              
629 31 100       143 if ( $nmatch == 1 ) {
630              
631 18   66     171 $args->{lastvalue} //= $args->{firstvalue} // $value;
      66        
632 18   66     83 $args->{firstvalue} //= $args->{lastvalue};
633              
634             }
635             else {
636 13   66     77 $args->{lastvalue} ||= $value;
637 13   66     60 $args->{firstvalue} ||= $value;
638             }
639              
640 31         69 my $match = 0;
641 31         51 foreach ( @{ $self->args->elements } ) {
  31         122  
642              
643             $match++
644             if $_->valsubst( $pattern,
645             $match == 0 ? $args->{firstvalue}
646             : $match == ( $nmatch - 1 ) ? $args->{lastvalue}
647 55 100       260 : $value );
    100          
    100          
648             }
649              
650 31         187 return $match;
651             }
652              
653              
654             1;
655              
656             #
657             # This file is part of IPC-PrettyPipe
658             #
659             # This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
660             #
661             # This is free software, licensed under:
662             #
663             # The GNU General Public License, Version 3, June 2007
664             #
665              
666             __END__