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   427470 use Carp;
  14         38  
  14         1047  
6              
7 14     14   142 use List::Util qw[ sum pairs ];
  14         31  
  14         852  
8 14     14   125 use Scalar::Util qw[ blessed ];
  14         28  
  14         609  
9              
10 14     14   1055 use Try::Tiny;
  14         4181  
  14         719  
11              
12 14     14   996 use Safe::Isa;
  14         991  
  14         1991  
13              
14 14     14   5978 use IPC::PrettyPipe::Arg;
  14         53  
  14         486  
15 14     14   6704 use IPC::PrettyPipe::Stream;
  14         65  
  14         699  
16              
17 14     14   116 use Types::Standard -all;
  14         28  
  14         257  
18 14     14   745448 use Type::Params qw[ validate ];
  14         23104  
  14         154  
19              
20 14     14   4970 use IPC::PrettyPipe::Types -all;
  14         35  
  14         142  
21 14     14   45027 use IPC::PrettyPipe::Queue;
  14         45  
  14         449  
22 14     14   102 use IPC::PrettyPipe::Arg::Format;
  14         29  
  14         379  
23              
24 14     14   73 use String::ShellQuote 'shell_quote';
  14         46  
  14         861  
25              
26 14     14   95 use Moo;
  14         31  
  14         58  
27              
28             our $VERSION = '0.11'; # TRIAL
29              
30             with 'IPC::PrettyPipe::Queue::Element';
31              
32 14     14   10843 use namespace::clean;
  14         33  
  14         62  
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   37391 IPC::PrettyPipe::Arg::Format->shadow_attrs( fmt => sub { 'arg' . shift } );
  28         14648  
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 117837 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     589 && @{ $_[0] } == 2 ? { cmd => $_[0][0], args => $_[0][1] }
    0          
    50          
91             : { cmd => $_[0] } )
92             : {@_};
93              
94             ## no critic (ProhibitAccessOfPrivateData)
95 93         393 delete @{$args}{ grep { !defined $args->{$_} } keys %$args };
  93         245  
  183         616  
96              
97 93         1628 return $args;
98             }
99              
100              
101             sub BUILD {
102              
103              
104 92     92 0 574 my $self = shift;
105              
106 92 100       460 if ( $self->_has_init_args ) {
107              
108 13         35 $self->ffadd( @{ $self->_init_args } );
  13         425  
109 13         309 $self->_clear_init_args;
110             }
111              
112 92         658 return;
113             }
114              
115 28     28 1 15156 sub quoted_cmd { shell_quote( $_[0]->cmd ) }
116              
117             sub add {
118              
119 179     179 1 366 my $self = shift;
120              
121 179 50       538 unshift @_, 'arg' if @_ == 1;
122              
123             ## no critic (ProhibitAccessOfPrivateData)
124              
125 179         3281 my $argfmt = $self->argfmt->clone;
126              
127 179         794 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         6797 ( map { $_ => Optional [Str] } keys %{$argfmt_attrs} ),
  358         17654  
  179         411424  
136             ] );
137              
138 178         1769614 my $arg = $attr->{arg};
139 178         44945 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     865 if $ref && exists $attr->{value};
143              
144 178 100       1241 $argfmt->copy_from( $attr->{argfmt} ) if defined $attr->{argfmt};
145 178         793 $argfmt->copy_from( IPC::PrettyPipe::Arg::Format->new_from_hash( $attr ) );
146              
147 178 50       1201 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       487 croak( "missing value for argument ", $arg->[-1] )
164             if @$arg % 2;
165              
166 66         670 foreach ( pairs @$arg ) {
167              
168 66         212 my ( $name, $value ) = @$_;
169              
170 66         328 $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       1525 exists $attr->{value} ? ( value => $attr->value ) : (),
193             fmt => $argfmt->clone
194             ) );
195             }
196              
197 177         2565 return;
198             }
199              
200              
201             # free form add
202             sub ffadd {
203              
204 89     89 1 219 my $self = shift;
205 89         251 my @args = @_;
206              
207 89         1705 my $argfmt = $self->argfmt->clone;
208              
209 89         389 for ( my $idx = 0 ; $idx < @args ; $idx++ ) {
210              
211 218         849 my $t = $args[$idx];
212              
213 218 100       733 if ( $t->$_isa( 'IPC::PrettyPipe::Arg::Format' ) ) {
    50          
    100          
    50          
214              
215 9         148 $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         1572 $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   9639 my $stream = IPC::PrettyPipe::Stream->new(
243             spec => $t,
244             strict => 0,
245             );
246              
247 32 100       111 if ( $stream->requires_file ) {
248              
249 30 50       94 croak( "arg[$idx]: stream operator $t requires a file\n" )
250             if ++$idx == @args;
251              
252 30         591 $stream->file( $args[$idx] );
253             }
254              
255 32         1095 $self->stream( $stream );
256             }
257             catch {
258              
259 111 50   111   2501 die $_ if /requires a file/;
260              
261 111         444 $self->add( arg => $t, argfmt => $argfmt->clone );
262 143         4349 };
263              
264             }
265             }
266              
267 89         675 return;
268             }
269              
270             sub stream {
271              
272 32     32 1 77 my $self = shift;
273              
274 32         67 my $spec = shift;
275              
276 32 50       113 if ( $spec->$_isa( 'IPC::PrettyPipe::Stream' ) ) {
    0          
277              
278 32 50       610 croak( "too many arguments\n" )
279             if @_;
280              
281 32         138 $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         85 return;
301             }
302              
303              
304             sub valmatch {
305 61     61 1 7106 my $self = shift;
306 61         114 my $pattern = shift;
307              
308             # find number of matches;
309 61         117 return sum 0, map { $_->valmatch( $pattern ) } @{ $self->args->elements };
  104         357  
  61         242  
310             }
311              
312             sub valsubst {
313 31     31 1 1323 my $self = shift;
314              
315 31 100       153 my @args = ( shift, shift, @_ > 1 ? {@_} : @_ );
316              
317              
318             ## no critic (ProhibitAccessOfPrivateData)
319              
320 31         143 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         173831 my $nmatch = $self->valmatch( $pattern );
332              
333 31 100       136 if ( $nmatch == 1 ) {
334              
335 18   66     152 $args->{lastvalue} //= $args->{firstvalue} // $value;
      66        
336 18   66     80 $args->{firstvalue} //= $args->{lastvalue};
337              
338             }
339             else {
340 13   66     71 $args->{lastvalue} ||= $value;
341 13   66     56 $args->{firstvalue} ||= $value;
342             }
343              
344 31         58 my $match = 0;
345 31         54 foreach ( @{ $self->args->elements } ) {
  31         128  
346              
347             $match++
348             if $_->valsubst( $pattern,
349             $match == 0 ? $args->{firstvalue}
350             : $match == ( $nmatch - 1 ) ? $args->{lastvalue}
351 55 100       280 : $value );
    100          
    100          
352             }
353              
354 31         186 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__