File Coverage

blib/lib/IPC/PrettyPipe/Cmd.pm
Criterion Covered Total %
statement 121 128 94.5
branch 36 56 64.2
condition 12 21 57.1
subroutine 26 26 100.0
pod 6 8 75.0
total 201 239 84.1


line stmt bran cond sub pod time code
1             package IPC::PrettyPipe::Cmd;
2              
3             # ABSTRACT: A command in an B pipeline
4              
5 14     14   352805 use Carp;
  14         39  
  14         907  
6              
7 14     14   126 use List::Util qw[ sum pairs ];
  14         31  
  14         751  
8 14     14   127 use Scalar::Util qw[ blessed ];
  14         30  
  14         584  
9              
10 14     14   848 use Try::Tiny;
  14         3499  
  14         665  
11              
12 14     14   768 use Safe::Isa;
  14         812  
  14         1700  
13              
14 14     14   5706 use IPC::PrettyPipe::Arg;
  14         47  
  14         433  
15 14     14   5728 use IPC::PrettyPipe::Stream;
  14         47  
  14         614  
16              
17 14     14   100 use Types::Standard -all;
  14         25  
  14         188  
18 14     14   608473 use Type::Params qw[ validate ];
  14         19036  
  14         143  
19              
20 14     14   4309 use IPC::PrettyPipe::Types -all;
  14         31  
  14         135  
21 14     14   39761 use IPC::PrettyPipe::Queue;
  14         40  
  14         399  
22 14     14   88 use IPC::PrettyPipe::Arg::Format;
  14         29  
  14         350  
23              
24 14     14   65 use String::ShellQuote 'shell_quote';
  14         24  
  14         726  
25              
26 14     14   82 use Moo;
  14         27  
  14         50  
27              
28             our $VERSION = '0.12';
29              
30             with 'IPC::PrettyPipe::Queue::Element';
31              
32 14     14   9727 use namespace::clean;
  14         27  
  14         64  
33              
34              
35             # need access to has method which will get removed at the end of the
36             # compilation of this module
37             BEGIN {
38 14     14   24711 IPC::PrettyPipe::Arg::Format->shadow_attrs( fmt => sub { 'arg' . shift } );
  28         13044  
39             }
40              
41             has cmd => (
42             is => 'ro',
43             isa => Str,
44             required => 1,
45             );
46              
47             # delay building args until all attributes have been specified
48             has _init_args => (
49             is => 'ro',
50             init_arg => 'args',
51             coerce => AutoArrayRef->coercion,
52             isa => ArrayRef,
53             predicate => 1,
54             clearer => 1,
55             );
56              
57             has args => (
58             is => 'ro',
59             default => sub { IPC::PrettyPipe::Queue->new },
60             init_arg => undef,
61             );
62              
63             has streams => (
64             is => 'ro',
65             default => sub { IPC::PrettyPipe::Queue->new },
66             init_arg => undef,
67             );
68              
69             has argfmt => (
70             is => 'ro',
71             lazy => 1,
72             handles => IPC::PrettyPipe::Arg::Format->shadowed_attrs,
73             default => sub { IPC::PrettyPipe::Arg::Format->new_from_attrs( shift ) },
74             );
75              
76              
77              
78              
79              
80             sub BUILDARGS {
81              
82 93     93 0 98305 my $class = shift;
83              
84             my $args
85             = @_ == 1
86             ? (
87             'HASH' eq ref( $_[0] )
88             ? $_[0]
89             : 'ARRAY' eq ref( $_[0] )
90 93 0 0     494 && @{ $_[0] } == 2 ? { cmd => $_[0][0], args => $_[0][1] }
    0          
    50          
91             : { cmd => $_[0] } )
92             : {@_};
93              
94             ## no critic (ProhibitAccessOfPrivateData)
95 93         346 delete @{$args}{ grep { !defined $args->{$_} } keys %$args };
  93         199  
  183         491  
96              
97 93         1460 return $args;
98             }
99              
100              
101             sub BUILD {
102              
103              
104 92     92 0 443 my $self = shift;
105              
106 92 100       390 if ( $self->_has_init_args ) {
107              
108 13         23 $self->ffadd( @{ $self->_init_args } );
  13         68  
109 13         269 $self->_clear_init_args;
110             }
111              
112 92         545 return;
113             }
114              
115 28     28 1 14852 sub quoted_cmd { shell_quote( $_[0]->cmd ) }
116              
117             sub add {
118              
119 179     179 1 329 my $self = shift;
120              
121 179 50       482 unshift @_, 'arg' if @_ == 1;
122              
123             ## no critic (ProhibitAccessOfPrivateData)
124              
125 179         3118 my $argfmt = $self->argfmt->clone;
126              
127 179         739 my $argfmt_attrs = IPC::PrettyPipe::Arg::Format->shadowed_attrs;
128              
129             my ( $attr ) = validate(
130             \@_,
131             slurpy Dict [
132             arg => Str | Arg | ArrayRef | HashRef,
133             value => Optional [Str],
134             argfmt => Optional [ InstanceOf ['IPC::PrettyPipe::Arg::Format'] ],
135 179         6492 ( map { $_ => Optional [Str] } keys %{$argfmt_attrs} ),
  358         16888  
  179         392022  
136             ] );
137              
138 178         1696939 my $arg = $attr->{arg};
139 178         43519 my $ref = ref $arg;
140              
141             croak( "cannot specify a value if arg is a hash, array, or Arg object\n" )
142 178 50 66     900 if $ref && exists $attr->{value};
143              
144 178 100       1423 $argfmt->copy_from( $attr->{argfmt} ) if defined $attr->{argfmt};
145 178         821 $argfmt->copy_from( IPC::PrettyPipe::Arg::Format->new_from_hash( $attr ) );
146              
147 178 50       1163 if ( 'HASH' eq $ref ) {
    100          
    50          
148              
149 0         0 while ( my ( $name, $value ) = each %$arg ) {
150              
151 0         0 $self->args->push(
152             IPC::PrettyPipe::Arg->new(
153             name => $name,
154             value => $value,
155             fmt => $argfmt->clone
156             ) );
157              
158             }
159             }
160              
161             elsif ( 'ARRAY' eq $ref ) {
162              
163 67 100       386 croak( "missing value for argument ", $arg->[-1] )
164             if @$arg % 2;
165              
166 66         500 foreach ( pairs @$arg ) {
167              
168 66         184 my ( $name, $value ) = @$_;
169              
170 66         268 $self->args->push(
171             IPC::PrettyPipe::Arg->new(
172             name => $name,
173             value => $value,
174             fmt => $argfmt->clone
175             ) );
176              
177             }
178             }
179              
180             elsif ( $arg->$_isa( 'IPC::PrettyPipe::Arg' ) ) {
181              
182 0         0 $self->args->push( $arg );
183              
184             }
185              
186             # everything else
187             else {
188              
189             $self->args->push(
190             IPC::PrettyPipe::Arg->new(
191             name => $attr->{arg},
192 111 50       1661 exists $attr->{value} ? ( value => $attr->value ) : (),
193             fmt => $argfmt->clone
194             ) );
195             }
196              
197 177         2615 return;
198             }
199              
200              
201             # free form add
202             sub ffadd {
203              
204 89     89 1 190 my $self = shift;
205 89         241 my @args = @_;
206              
207 89         1518 my $argfmt = $self->argfmt->clone;
208              
209 89         336 for ( my $idx = 0 ; $idx < @args ; $idx++ ) {
210              
211 218         783 my $t = $args[$idx];
212              
213 218 100       733 if ( $t->$_isa( 'IPC::PrettyPipe::Arg::Format' ) ) {
    50          
    100          
    50          
214              
215 9         147 $t->copy_into( $argfmt );
216              
217             }
218              
219             elsif ( $t->$_isa( 'IPC::PrettyPipe::Arg' ) ) {
220              
221 0         0 $self->add( arg => $t );
222              
223              
224             }
225              
226             elsif ( ref( $t ) =~ /^(ARRAY|HASH)$/ ) {
227              
228 66         1340 $self->add( arg => $t, argfmt => $argfmt->clone );
229              
230             }
231              
232             elsif ( $t->$_isa( 'IPC::PrettyPipe::Stream' ) ) {
233              
234 0         0 $self->stream( $t );
235              
236             }
237              
238             else {
239              
240             try {
241              
242 143     143   10098 my $stream = IPC::PrettyPipe::Stream->new(
243             spec => $t,
244             strict => 0,
245             );
246              
247 32 100       106 if ( $stream->requires_file ) {
248              
249 30 50       115 croak( "arg[$idx]: stream operator $t requires a file\n" )
250             if ++$idx == @args;
251              
252 30         612 $stream->file( $args[$idx] );
253             }
254              
255 32         940 $self->stream( $stream );
256             }
257             catch {
258              
259 111 50   111   2455 die $_ if /requires a file/;
260              
261 111         471 $self->add( arg => $t, argfmt => $argfmt->clone );
262 143         4528 };
263              
264             }
265             }
266              
267 89         647 return;
268             }
269              
270             sub stream {
271              
272 32     32 1 64 my $self = shift;
273              
274 32         64 my $spec = shift;
275              
276 32 50       112 if ( $spec->$_isa( 'IPC::PrettyPipe::Stream' ) ) {
    0          
277              
278 32 50       584 croak( "too many arguments\n" )
279             if @_;
280              
281 32         131 $self->streams->push( $spec );
282              
283             }
284              
285             elsif ( !ref $spec ) {
286              
287 0 0       0 $self->streams->push(
288             IPC::PrettyPipe::Stream->new(
289             spec => $spec,
290             +@_ ? ( file => @_ ) : () ) );
291             }
292              
293             else {
294              
295 0         0 croak( "illegal stream specification\n" );
296              
297             }
298              
299              
300 32         97 return;
301             }
302              
303              
304             sub valmatch {
305 61     61 1 5764 my $self = shift;
306 61         91 my $pattern = shift;
307              
308             # find number of matches;
309 61         91 return sum 0, map { $_->valmatch( $pattern ) } @{ $self->args->elements };
  104         266  
  61         181  
310             }
311              
312             sub valsubst {
313 31     31 1 1246 my $self = shift;
314              
315 31 100       114 my @args = ( shift, shift, @_ > 1 ? {@_} : @_ );
316              
317              
318             ## no critic (ProhibitAccessOfPrivateData)
319              
320 31         122 my ( $pattern, $value, $args ) = validate(
321             \@args,
322             RegexpRef,
323             Str,
324             Optional [
325             Dict [
326             lastvalue => Optional [Str],
327             firstvalue => Optional [Str] ]
328             ],
329             );
330              
331 31         148125 my $nmatch = $self->valmatch( $pattern );
332              
333 31 100       99 if ( $nmatch == 1 ) {
334              
335 18   66     117 $args->{lastvalue} //= $args->{firstvalue} // $value;
      66        
336 18   66     64 $args->{firstvalue} //= $args->{lastvalue};
337              
338             }
339             else {
340 13   66     64 $args->{lastvalue} ||= $value;
341 13   66     47 $args->{firstvalue} ||= $value;
342             }
343              
344 31         44 my $match = 0;
345 31         55 foreach ( @{ $self->args->elements } ) {
  31         86  
346              
347             $match++
348             if $_->valsubst( $pattern,
349             $match == 0 ? $args->{firstvalue}
350             : $match == ( $nmatch - 1 ) ? $args->{lastvalue}
351 55 100       198 : $value );
    100          
    100          
352             }
353              
354 31         144 return $match;
355             }
356              
357              
358             1;
359              
360             #
361             # This file is part of IPC-PrettyPipe
362             #
363             # This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
364             #
365             # This is free software, licensed under:
366             #
367             # The GNU General Public License, Version 3, June 2007
368             #
369              
370             __END__