File Coverage

blib/lib/Iterator/Flex/Cat.pm
Criterion Covered Total %
statement 54 57 94.7
branch 7 12 58.3
condition 2 3 66.6
subroutine 17 19 89.4
pod 1 1 100.0
total 81 92 88.0


line stmt bran cond sub pod time code
1             package Iterator::Flex::Cat;
2              
3             # ABSTRACT: An iterator which concatenates a set of iterators
4              
5 4     4   183418 use v5.28;
  4         12  
6 4     4   15 use strict;
  4         6  
  4         75  
7 4     4   11 use warnings;
  4         127  
  4         251  
8 4     4   429 use experimental qw( signatures declared_refs refaliasing );
  4         1353  
  4         22  
9              
10             our $VERSION = '0.34';
11              
12             use Iterator::Flex::Utils
13 4     4   1313 qw( RETURN STATE EXHAUSTION :IterAttrs :IterStates can_meth throw_failure );
  4         9  
  4         851  
14 4     4   475 use Iterator::Flex::Factory 'to_iterator';
  4         9  
  4         188  
15 4     4   19 use parent 'Iterator::Flex::Base';
  4         5  
  4         29  
16 4     4   204 use List::Util 'all';
  4         6  
  4         203  
17              
18 4     4   16 use namespace::clean;
  4         5  
  4         25  
19              
20              
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 3     3 1 5 sub new ( $class, @args ) {
  3         5  
  3         5  
  3         4  
57 3 50       9 my $pars = Ref::Util::is_hashref( $args[-1] ) ? pop @args : {};
58              
59             @args
60 3 50       8 or throw_failure( parameter => 'not enough parameters' );
61              
62 3         17 $class->SUPER::new( {
63             depends => \@args,
64             current_iterator_index => undef,
65             },
66             $pars,
67             );
68             }
69              
70             sub construct ( $class, $state ) {
71             throw_failure( parameter => q{state must be a HASH reference} )
72             unless Ref::Util::is_hashref( $state );
73              
74             $state->{value} //= [];
75              
76             my ( \@depends, $current_iterator, $prev, $current, $next, undef )
77             = @{$state}{ 'depends', 'current_iterator', 'prev', 'current', 'next', 'thaw' };
78              
79             # transform into iterators if required.
80             my @iterators
81             = map { to_iterator( $_, { ( +EXHAUSTION ) => RETURN } ) } @depends;
82              
83             # -- not sure if this is just cargo-culted
84             # my $value;
85             # $value = $current
86             # if $thaw;
87              
88             my $self;
89             my $iterator_state;
90             my $iter;
91             my $is_exhausted;
92              
93             my sub init {
94             $current_iterator //= 0;
95             if ( $current_iterator >= @iterators ) {
96             $iterator_state = IterState_EXHAUSTED;
97             }
98             else {
99             $iter = $iterators[$current_iterator];
100             $is_exhausted = $iter->can( 'is_exhausted' );
101             }
102             }
103              
104             init();
105              
106             my %params = (
107              
108             ( +_SELF ) => \$self,
109              
110             ( +STATE ) => \$iterator_state,
111              
112             ( +NEXT ) => sub {
113 65 50   65   95 return $self->signal_exhaustion if $iterator_state == IterState_EXHAUSTED;
114              
115 65         55 while ( 1 ) {
116 73         150 my $value = $iter->();
117 73 100 66     125 if ( defined $value || !$iter->$is_exhausted ) {
118 58         54 $prev = $current;
119 58         51 $current = $value;
120 58         140 return $current;
121             }
122              
123 15 100       34 last if ++$current_iterator >= @iterators;
124 8         11 $iter = $iterators[$current_iterator];
125 8         22 $is_exhausted = $iter->can( 'is_exhausted' );
126             }
127              
128             # if we haven't returned, we've exhausted things
129 7         11 $current_iterator = undef;
130 7         22 $prev = $current;
131 7         12 return $current = $self->signal_exhaustion;
132             },
133              
134             ( +PREV ) => sub {
135 6     6   21 return $prev;
136             },
137              
138             ( +CURRENT ) => sub {
139 0 0   0   0 return $self->signal_exhaustion if $iterator_state eq IterState_EXHAUSTED;
140 0         0 return $current;
141             },
142              
143             ( +_ROLES ) => [],
144              
145             ( +_DEPENDS ) => \@iterators,
146             );
147              
148             # can only freeze if the iterators support a current method
149 7     7   13 if ( all { defined can_meth( $_, 'current' ) } @iterators ) {
150             $params{ +FREEZE } = sub {
151             return [
152 0     0   0 $class,
153             {
154             current_iterator => $current_iterator,
155             prev => $prev,
156             current => $current,
157             next => $next,
158             } ];
159             };
160             push $params{ +_ROLES }->@*, 'Freeze';
161             }
162              
163 7     7   9 if ( all { defined can_meth( $_, 'reset' ) } @iterators ) {
164             $params{ +RESET } = sub {
165 2     2   4 $prev = $current = $current_iterator = undef;
166 2         6 init();
167             };
168             push $params{ +_ROLES }->@*, 'Reset::Closure';
169             }
170              
171 7     7   11 if ( all { defined can_meth( $_, 'rewind' ) } @iterators ) {
172             $params{ +REWIND } = sub {
173 2     2   3 $current_iterator = undef;
174 2         5 init();
175             };
176             push $params{ +_ROLES }->@*, 'Rewind::Closure';
177             }
178              
179             $params{ +_NAME } = 'icat';
180             return \%params;
181             }
182              
183              
184             __PACKAGE__->_add_roles( qw[
185             State::Closure
186             Next::ClosedSelf
187             Current::Closure
188             Prev::Closure
189             ] );
190              
191             1;
192              
193             #
194             # This file is part of Iterator-Flex
195             #
196             # This software is Copyright (c) 2018 by Smithsonian Astrophysical Observatory.
197             #
198             # This is free software, licensed under:
199             #
200             # The GNU General Public License, Version 3, June 2007
201             #
202              
203             __END__