File Coverage

blib/lib/TAPx/Parser/Iterator.pm
Criterion Covered Total %
statement 69 72 95.8
branch 19 22 86.3
condition 5 8 62.5
subroutine 21 22 95.4
pod 1 1 100.0
total 115 125 92.0


line stmt bran cond sub pod time code
1             package TAPx::Parser::Iterator;
2              
3 13     13   2602 use strict;
  13         28  
  13         515  
4 13     13   71 use vars qw($VERSION);
  13         27  
  13         3621  
5              
6             =head1 NAME
7              
8             TAPx::Parser::Iterator - Internal TAPx::Parser Iterator
9              
10             =head1 VERSION
11              
12             Version 0.50_07
13              
14             =cut
15              
16             $VERSION = '0.50_07';
17              
18             =head1 SYNOPSIS
19              
20             use TAPx::Parser::Iterator;
21             my $it = TAPx::Parser::Iterator->new(\*TEST);
22             my $it = TAPx::Parser::Iterator->new(\@array);
23              
24             my $line = $it->next;
25             if ( $it->is_first ) { ... }
26             if ( $it->is_last ) { ... }
27              
28             Originally ripped off from C.
29              
30             =head1 DESCRIPTION
31              
32             B
33              
34             This is a simple iterator wrapper for arrays and filehandles.
35              
36             =head2 new()
37              
38             Create an iterator.
39              
40             =head2 next()
41              
42             Iterate through it, of course.
43              
44             =head2 next_raw()
45              
46             Iterate raw input without applying any fixes for quirky input syntax.
47              
48             =head2 is_first()
49              
50             Returns true if on the first line. Must be called I C.
51              
52             =head2 is_last()
53              
54             Returns true if on or after the last line. Must be called I C.
55              
56             =cut
57              
58             sub new {
59 81     81 1 1356 my ( $proto, $thing ) = @_;
60              
61 81         679 my $ref = ref $thing;
62 81 100 100     1211 if ( $ref eq 'GLOB' || $ref eq 'IO::Handle' ) {
    50          
63              
64             # we may eventually allow a 'fast' switch which can read the entire
65             # stream into an array. This seems to speed things up by 10 to 12
66             # per cent. Should not be used with infinite streams.
67 55         896 return TAPx::Parser::Iterator::FH->new($thing);
68             }
69             elsif ( $ref eq 'ARRAY' ) {
70 26         232 return TAPx::Parser::Iterator::ARRAY->new($thing);
71             }
72             else {
73 0         0 die "Can't iterate with a ", ref $thing;
74             }
75             }
76              
77             eval { require POSIX; &POSIX::WEXITSTATUS(0) };
78             if ($@) {
79             *_wait2exit = sub { $_[1] >> 8 };
80             }
81             else {
82 63     63   408 *_wait2exit = sub { POSIX::WEXITSTATUS( $_[1] ) }
83             }
84              
85             package TAPx::Parser::Iterator::FH;
86              
87 13     13   75 use vars qw($VERSION @ISA);
  13         34  
  13         7770  
88             @ISA = 'TAPx::Parser::Iterator';
89             $VERSION = '0.50_07';
90              
91             sub new {
92 55     55   326 my ( $class, $thing ) = @_;
93 55         1658 bless {
94             fh => $thing,
95             next => undef,
96             exit => undef,
97             is_first => 0,
98             is_last => 0,
99             }, $class;
100             }
101              
102             ##############################################################################
103              
104             =head3 C
105              
106             my $pid = $source->pid;
107             $source->pid($pid);
108              
109             Getter/Setter for the pid of the process the filehandle reads from. Only
110             makes sense when a filehandle is being used for the iterator.
111              
112             =cut
113              
114             sub pid {
115 53     53   131 my $self = shift;
116 53 50       215 return $self->{pid} unless @_;
117 53         251 $self->{pid} = shift;
118 53         163 return $self;
119             }
120              
121 83     83   2514 sub wait { $_[0]->{wait} }
122 85     85   1031 sub exit { $_[0]->{exit} }
123 259     259   16390 sub is_first { $_[0]->{is_first} }
124 509     509   13482 sub is_last { $_[0]->{is_last} }
125              
126             sub next_raw {
127 254     254   494 my $self = shift;
128 254         600 my $fh = $self->{fh};
129              
130 254         392 my $line;
131 254 100       1081 if ( defined( $line = $self->{next} ) ) {
132 186 100       2088 if ( defined( my $next = <$fh> ) ) {
133 139         430 chomp( $self->{next} = $next );
134 139         353 $self->{is_first} = 0;
135             }
136             else {
137 47         290 $self->_finish;
138             }
139             }
140             else {
141 68 100       364 $self->{is_first} = 1 unless $self->{is_last};
142 68         873 local $^W; # Don't want to chomp undef values
143 68         1312086 chomp( $line = <$fh> );
144 68 100       400 unless ( defined $line ) {
145 16         108 $self->_finish;
146             }
147             else {
148 52         29190 chomp( $self->{next} = <$fh> );
149             }
150             }
151              
152 254         940 return $line;
153             }
154              
155             sub next {
156 254     254   9331 my $self = shift;
157 254         878 my $line = $self->next_raw;
158              
159             # vms nit: When encountering 'not ok', vms often has the 'not' on a line
160             # by itself:
161             # not
162             # ok 1 - 'I hate VMS'
163 254 50 66     2927 if ( defined $line && $line =~ /^\s*not\s*$/ ) {
164 0   0     0 $line .= ( $self->next_raw || '' );
165             }
166 254         2604 return $line;
167             }
168              
169             sub _finish {
170 63     63   134 my $self = shift;
171              
172 63         338 my $status = $?;
173              
174             # If we have a subprocess we need to wait for it to terminate
175 63 100       290 if ( defined $self->{pid} ) {
176 60 100       2827 if ( $self->{pid} == waitpid( $self->{pid}, 0 ) ) {
177 52         129 $status = $?;
178             }
179             }
180              
181 63         1010 close $self->{fh};
182              
183 63         160 $self->{is_first} = 0; # need to reset it here in case we have no output
184 63         323 $self->{is_last} = 1;
185 63         122 $self->{next} = undef;
186 63         373 $self->{wait} = $status;
187 63         598 $self->{exit} = $self->_wait2exit($status);
188 63         191 return $self;
189             }
190              
191             package TAPx::Parser::Iterator::ARRAY;
192              
193 13     13   163 use vars qw($VERSION @ISA);
  13         24  
  13         3343  
194             @ISA = 'TAPx::Parser::Iterator';
195             $VERSION = '0.50_07';
196              
197             sub new {
198 26     26   54 my ( $class, $thing ) = @_;
199 26         131 chomp @$thing;
200 26         257 bless {
201             idx => 0,
202             array => $thing,
203             exit => undef,
204             }, $class;
205             }
206              
207 23     23   57 sub wait { shift->exit }
208 48 100   48   683 sub exit { shift->is_last ? 0 : () }
209 149     149   751 sub is_first { 1 == $_[0]->{idx} }
210 360     360   721 sub is_last { @{ $_[0]->{array} } <= $_[0]->{idx} }
  360         2142  
211              
212             sub next {
213 148     148   210 my $self = shift;
214 148         742 return $self->{array}->[ $self->{idx}++ ];
215             }
216              
217 0     0     sub next_raw { shift->next }
218              
219             1;