File Coverage

blib/lib/Iterator/Flex/Gather.pm
Criterion Covered Total %
statement 79 79 100.0
branch 21 28 75.0
condition 14 23 60.8
subroutine 14 14 100.0
pod 1 2 50.0
total 129 146 88.3


line stmt bran cond sub pod time code
1             package Iterator::Flex::Gather;
2              
3             # ABSTRACT: Gather Iterator Class
4              
5 3     3   174368 use v5.28;
  3         9  
6 3     3   10 use strict;
  3         4  
  3         50  
7 3     3   12 use warnings;
  3         6  
  3         167  
8 3     3   406 use experimental 'signatures';
  3         1298  
  3         15  
9              
10             our $VERSION = '0.34';
11              
12 3     3   860 use Iterator::Flex::Factory 'to_iterator';
  3         5  
  3         191  
13 3     3   14 use Iterator::Flex::Utils qw[ THROW STATE EXHAUSTION :IterAttrs :IterStates throw_failure];
  3         5  
  3         427  
14 3     3   14 use Ref::Util;
  3         3  
  3         97  
15 3     3   16 use parent 'Iterator::Flex::Base';
  3         3  
  3         18  
16              
17 3     3   605 use Iterator::Flex::Gather::Constants ':all';
  3         5  
  3         382  
18              
19              
20 3     3   14 use namespace::clean;
  3         4  
  3         15  
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
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              
100              
101              
102              
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
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              
153              
154              
155              
156              
157              
158              
159              
160              
161              
162              
163              
164              
165              
166              
167              
168              
169              
170              
171 10     10 1 16 sub new ( $class, $code, $iterable, $pars = {} ) {
  10         16  
  10         12  
  10         11  
  10         12  
  10         10  
172              
173 10 50       22 throw_failure( parameter => q{'code' parameter is not a coderef} )
174             unless Ref::Util::is_coderef( $code );
175              
176 10         22 my %pars = $pars->%*;
177              
178             my %state = (
179             code => $code,
180 10   100     44 cycle_on_exhaustion => delete( $pars{cycle_on_exhaustion} ) // GATHER_CYCLE_STOP,
181             src => $iterable,
182             );
183              
184             throw_failure( parameter => q{'cycle_on_exhaustion': illegal value} )
185             if defined $pars{cycle_on_exhaustion}
186             and $pars{cycle_on_exhaustion} != GATHER_CYCLE_CHOOSE
187 10 0 33     23 and !$pars{cycle_on_exhaustion} & ( GATHER_CYCLE_STOP | GATHER_CYCLE_ABORT );
      33        
188              
189 10         31 $class->SUPER::new( \%state, \%pars );
190             }
191              
192              
193 10     10 0 13 sub construct ( $class, $state ) {
  10         11  
  10         12  
  10         11  
194              
195 10 50       20 throw_failure( parameter => q{'state' parameter must be a HASH reference} )
196             unless Ref::Util::is_hashref( $state );
197              
198 10         12 my ( $code, $src, $cycle_on_exhaustion ) = @{$state}{qw[ code src cycle_on_exhaustion ]};
  10         17  
199              
200 10         41 $src
201             = to_iterator( $src, { ( +EXHAUSTION ) => THROW } );
202              
203 10         19 my $self;
204              
205             # cached value if current element should be in
206             # next cycle
207 10         15 my $has_cache = !!0;
208 10         15 my $cache;
209              
210             my $iterator_state;
211              
212             # This iterator may have to delay signalling exhaustion for one cycle if the
213             # input iterator is exhausted, and it needs to return the last group of elements.
214             # exhaustion.
215 10         12 my $next_is_exhausted = !!0;
216              
217             return {
218             ( +_NAME ) => 'igather',
219              
220             ( +_SELF ) => \$self,
221              
222             ( +STATE ) => \$iterator_state,
223              
224             ( +NEXT ) => sub {
225 61 100 66 61   144 return $self->signal_exhaustion
226             if $iterator_state == IterState_EXHAUSTED || $next_is_exhausted;
227              
228 54         56 my $cycle;
229             my @gathered;
230 54         52 my $ret = eval {
231 54         43 while ( 1 ) {
232              
233 324 100       489 my $rv = $has_cache ? $cache : $src->();
234 312         305 $has_cache = !!0;
235              
236 312         277 local $_ = $rv;
237 312         357 my $result = $code->( \@gathered, GATHER_GATHERING );
238 312         1335 $cycle = $result & GATHER_CYCLE_MASK;
239              
240 312 50       364 throw_failure( parameter => 'cycle action (continue, stop, abort, restart) was not specified' )
241             unless $cycle;
242              
243 312 100       381 if ( ( $result & GATHER_ELEMENT_MASK ) == GATHER_ELEMENT_INCLUDE ) {
    100          
244 193         204 push @gathered, $rv;
245             }
246             elsif ( ( $result & GATHER_ELEMENT_MASK ) == GATHER_ELEMENT_CACHE ) {
247 7 50       11 throw_failure( parameter =>
248             'inconsistent return: element action GATHER_ELEMENT_CACHE requires cycle action GATHER_CYCLE_STOP',
249             ) unless $cycle & GATHER_CYCLE_RESTART;
250 7         8 $cache = $rv;
251 7         7 $has_cache = !!1;
252             }
253              
254 312 100       388 last if $cycle & ( GATHER_CYCLE_RESTART | GATHER_CYCLE_STOP | GATHER_CYCLE_ABORT );
255             }
256 42         47 1;
257             };
258 54 100 66     598 if ( !$ret && length $@ ) {
259              
260 12 50 33     803 die $@
261             unless Ref::Util::is_blessed_ref( $@ )
262             && $@->isa( 'Iterator::Flex::Failure::Exhausted' );
263              
264 12         21 local $_ = undef;
265 12 100       24 my $result
266             = $cycle_on_exhaustion == GATHER_CYCLE_CHOOSE
267             ? $code->( \@gathered, GATHER_SRC_EXHAUSTED )
268             : $cycle_on_exhaustion;
269 12 100 66     67 return $self->signal_exhaustion
      100        
270             if ( $result & GATHER_CYCLE_ABORT )
271             || ( $result & GATHER_CYCLE_STOP && !@gathered );
272              
273 7         10 $next_is_exhausted = !!1;
274             }
275              
276 49         99 return \@gathered;
277             },
278             (
279 10         85 map { ( ( +RESET ) => $_, ( +REWIND ) => $_, ) } sub {
280 4     4   7 $next_is_exhausted = $has_cache = !!0;
281             },
282 10         48 ),
283             ( +_DEPENDS ) => $src,
284             };
285             }
286              
287             __PACKAGE__->_add_roles( qw[
288             State::Closure
289             Next::ClosedSelf
290             Rewind::Closure
291             Reset::Closure
292             Current::Closure
293             ] );
294              
295             1;
296              
297             #
298             # This file is part of Iterator-Flex
299             #
300             # This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
301             #
302             # This is free software, licensed under:
303             #
304             # The GNU General Public License, Version 3, June 2007
305             #
306              
307             __END__