File Coverage

blib/lib/IPC/PrettyPipe/Stream.pm
Criterion Covered Total %
statement 62 95 65.2
branch 11 34 32.3
condition 2 15 13.3
subroutine 16 25 64.0
pod 2 4 50.0
total 93 173 53.7


line stmt bran cond sub pod time code
1             package IPC::PrettyPipe::Stream;
2              
3             # ABSTRACT: An I/O stream for an B pipeline or command
4              
5 15     15   227222 use Carp;
  15         57  
  15         1089  
6              
7 15     15   669 use Types::Standard qw[ Bool Str ];
  15         83407  
  15         148  
8              
9 15     15   18155 use IPC::PrettyPipe::Stream::Utils qw[ parse_spec ];
  15         44  
  15         1119  
10              
11 15     15   636 use String::ShellQuote 'shell_quote';
  15         1067  
  15         698  
12 15     15   6819 use IO::ReStoreFH;
  15         218003  
  15         567  
13 15     15   119 use POSIX ();
  15         31  
  15         339  
14 15     15   79 use Fcntl qw[ O_RDONLY O_WRONLY O_CREAT O_TRUNC O_APPEND ];
  15         32  
  15         835  
15              
16              
17 15     15   618 use Moo;
  15         12326  
  15         140  
18              
19             our $VERSION = '0.11'; # TRIAL
20              
21              
22             with 'IPC::PrettyPipe::Queue::Element';
23              
24 15     15   7843 use namespace::clean;
  15         11538  
  15         120  
25              
26              
27             my %fh_map = (
28             0 => *STDIN,
29             1 => *STDOUT,
30             2 => *STDERR
31             );
32              
33             my %op_map = (
34              
35             '<' => O_RDONLY,
36             '>' => O_WRONLY | O_CREAT | O_TRUNC,
37             '>>' => O_WRONLY | O_CREAT | O_APPEND
38              
39             );
40              
41             has N => (
42             is => 'rwp',
43             predicate => 1,
44             init_arg => undef,
45             );
46             has M => (
47             is => 'rwp',
48             predicate => 1,
49             init_arg => undef,
50             );
51             has Op => (
52             is => 'rwp',
53             init_arg => undef,
54             );
55              
56             has spec => (
57             is => 'rwp',
58             isa => Str,
59             required => 1,
60             );
61              
62             has file => (
63             is => 'rw',
64             isa => Str,
65             predicate => 1,
66             );
67              
68             has _type => (
69             is => 'rwp',
70             isa => Str,
71             init_arg => undef,
72             );
73              
74             has requires_file => (
75             is => 'rwp',
76             init_arg => undef,
77             );
78              
79             has strict => (
80             is => 'ro',
81             isa => Bool,
82             default => sub { 1 } );
83              
84              
85              
86              
87              
88             sub BUILDARGS {
89              
90 156     156 0 36065 my $class = shift;
91              
92 156 100       490 if ( @_ == 1 ) {
93              
94 3 50       11 return $_[0] if 'HASH' eq ref( $_[0] );
95              
96             return { spec => $_[0][0], file => $_[0][1] }
97 3 50 33     11 if 'ARRAY' eq ref( $_[0] ) && @{ $_[0] } == 2;
  0         0  
98              
99 3         60 return { spec => $_[0] };
100              
101             }
102              
103 153         2748 return {@_};
104              
105             }
106              
107             sub BUILD {
108              
109 156     156 0 7519 my $self = shift;
110              
111             ## no critic (ProhibitAccessOfPrivateData)
112              
113 156         800 my $opc = parse_spec( $self->spec );
114              
115             croak( __PACKAGE__, ': ', "cannot parse stream specification: ",
116             $self->spec )
117 156 100       19672 unless defined $opc->{type};
118              
119 44         179 $self->_set_requires_file( $opc->{param} );
120 44         989 $self->_set__type( $opc->{type} );
121              
122 90         468 $self->${ \"_set_$_" }( $opc->{$_} )
123 44         1468 for grep { exists $opc->{$_} } qw[ N M Op ];
  132         344  
124              
125              
126 44 100       168 if ( $self->strict ) {
127              
128 2 50 33     236 croak( __PACKAGE__, ': ', "stream specification ",
129             $self->spec, "requires a file\n" )
130             if $self->requires_file && !$self->has_file;
131              
132 0 0 0     0 croak( __PACKAGE__, ': ', "stream specification ",
133             $self->spec, "should not have a file\n" )
134             if !$self->requires_file && $self->has_file;
135              
136             }
137              
138 42         219 return;
139             }
140              
141 22     22 1 9293 sub quoted_file { shell_quote( $_[0]->file ) }
142              
143             sub _redirect {
144              
145 4     4   11 my ( $self, $N ) = @_;
146              
147 4         93 my $file = $self->file;
148              
149 4         29 my $sub;
150              
151 4 50       16 if ( defined $N ) {
152              
153 4         17 my $op = $self->Op;
154              
155             $sub = sub {
156 4 50   4   364 open( $N, $op, $file ) or die( "unable to open ", $file, ": $!\n" );
157 4         71 };
158             }
159              
160             else {
161              
162 0         0 $N = $self->N;
163 0   0     0 my $op = $op_map{ $self->Op }
164             // croak( "error: unrecognized operator: ", $self->Op, "\n" );
165              
166             $sub = sub {
167 0 0   0   0 my $nfd = POSIX::open( $file, $op, oct( 644 ) )
168             or croak( 'error opening', $file, ": $!\n" );
169 0 0       0 POSIX::dup2( $nfd, $N )
170             or croak( "error in dup2( $nfd, $N ): $!\n" );
171 0         0 POSIX::close( $nfd );
172 0         0 };
173              
174             }
175              
176 4         31 return $sub, $N;
177             }
178              
179             sub _dup {
180              
181 0     0   0 my ( $self, $N, $M ) = @_;
182              
183 0   0     0 $M //= $self->M;
184              
185 0         0 my $sub;
186              
187             # if $N is a known filehandle, we're in luck
188 0 0       0 if ( defined $N ) {
189              
190             $sub = sub {
191 0 0   0   0 open( $N, '>&', $M )
192             or die( "error in open($N >& $M): $!\n" );
193 0         0 };
194              
195             }
196              
197             else {
198              
199 0         0 $N = $self->N;
200 0         0 $M = $self->M;
201              
202             $sub = sub {
203 0 0   0   0 POSIX::dup2( $N, $M )
204             or die( "error in dup2( $N, $M ): $!\n" );
205 0         0 };
206              
207             }
208              
209 0         0 return $sub, $N;
210             }
211              
212             sub _redirect_stdout_stderr {
213              
214 0     0   0 my $self = shift;
215              
216 0         0 ( undef, my $sub_redir ) = $self->_redirect( *STDOUT );
217 0         0 ( undef, my $sub_dup ) = $self->_dup( *STDERR, *STDOUT );
218 0     0   0 return sub { $sub_redir->(), $sub_dup->() }, *STDOUT, *STDERR;
  0         0  
219              
220             }
221              
222             sub _close {
223              
224 0     0   0 my ( $self, $N ) = @_;
225              
226 0         0 my $sub;
227              
228 0 0       0 if ( defined $N ) {
229              
230 0 0   0   0 $sub = sub { close( $N ) or die( "error in closing $N: $!\n" ); };
  0         0  
231              
232             }
233              
234             else {
235              
236 0         0 $N = $self->N;
237              
238             $sub = sub {
239 0 0   0   0 POSIX::close( $N )
240             or die( "error in closing $N: $!\n" );
241 0         0 };
242              
243             }
244              
245 0         0 return $sub, $N;
246              
247             }
248              
249             sub apply {
250              
251 4     4 1 12 my $self = shift;
252              
253 4         6 my ( $N, $M ) = do {
254              
255 15     15   24703 no warnings 'uninitialized';
  15         41  
  15         2115  
256              
257 4         24 map { $fh_map{$_} } $self->N, $self->M;
  8         36  
258              
259             };
260              
261 4         23 my $mth = '_' . $self->_type;
262 4         19 return $self->$mth( $N, $M );
263             }
264              
265              
266              
267             1;
268              
269             #
270             # This file is part of IPC-PrettyPipe
271             #
272             # This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
273             #
274             # This is free software, licensed under:
275             #
276             # The GNU General Public License, Version 3, June 2007
277             #
278              
279             __END__