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