File Coverage

blib/lib/LINQ/Iterator.pm
Criterion Covered Total %
statement 89 89 100.0
branch 28 28 100.0
condition 13 17 88.2
subroutine 20 20 100.0
pod 5 5 100.0
total 155 159 98.7


line stmt bran cond sub pod time code
1 99     99   3582 use 5.006;
  99         345  
2 99     99   515 use strict;
  99         165  
  99         2273  
3 99     99   493 use warnings;
  99         159  
  99         64422  
4              
5             if ( $] < 5.010000 ) {
6             require UNIVERSAL::DOES;
7             }
8              
9             {
10             package # hide from PAUSE
11             LINQ::Iterator::_LazyList;
12            
13             my $_throw_caller_error = sub {
14             shift;
15             require LINQ::Exception;
16             'LINQ::Exception::CallerError'->throw( message => @_ );
17             };
18            
19             sub __GENERATOR () { 0 }
20             sub __EXHAUSTED () { 1 }
21             sub __VALUES () { 2 }
22             sub __NEXT_SLOT () { 3 }
23            
24             sub TIEARRAY {
25 663     663   1103 my $class = shift;
26 663         2425 bless [
27             $_[0],
28             !!0,
29             [],
30             ], $class;
31             }
32            
33             sub FETCH {
34 1167     1167   2901 my $self = shift;
35 1167         1727 my ( $ix ) = @_;
36 1167         1828 my $cache = $self->[__VALUES];
37            
38 1167         2357 $self->extend_to( $ix );
39            
40 1167 100       3956 $ix >= @$cache ? undef : $cache->[$ix];
41             }
42            
43             sub fetch_ref {
44 1520     1520   1959 my $self = shift;
45 1520         2344 my ( $ix ) = @_;
46 1520         2064 my $cache = $self->[__VALUES];
47            
48 1520         3286 $self->extend_to( $ix );
49            
50 1518 100       3143 return if $ix > 0+ $#$cache;
51 1357 100       2732 return if $ix < 0 - @$cache;
52 1355         2380 \( $cache->[$ix] );
53             } #/ sub fetch_ref
54            
55             sub FETCHSIZE {
56 853     853   43890 my $self = shift;
57 853         1992 $self->extend_to( -1 );
58 836         1319 scalar @{ $self->[__VALUES] };
  836         2454  
59             }
60            
61             sub current_extension {
62 2     2   5 my $self = shift;
63 2         2 scalar @{ $self->[__VALUES] };
  2         10  
64             }
65            
66             sub is_fully_extended {
67 1     1   2 my $self = shift;
68 1         4 $self->[__EXHAUSTED];
69             }
70            
71             sub extend_to {
72 3540     3540   11993 require LINQ;
73            
74 3540         4887 my $self = shift;
75 3540         5085 my ( $ix ) = @_;
76 3540         4894 my $cache = $self->[__VALUES];
77            
78             EXTEND: {
79 3540 100       4406 return if $self->[__EXHAUSTED];
  5764         10483  
80 3928 100 100     10242 return if $ix >= 0 && $ix < @$cache;
81            
82 2696         5098 my @got = $self->[__GENERATOR]->();
83 2679         8192 my $got;
84            
85             # Crazy optimized loop to find and handle LINQ::END
86             # within @got
87             # uncoverable condition count:1
88             # uncoverable condition count:5
89 2679 100 66     14140 push( @$cache, shift @got )
      100        
      100        
      66        
      33        
90             and ref( $got = $cache->[-1] )
91             and $got == LINQ::END()
92             and ( $self->[__EXHAUSTED] = !!1 )
93             and pop( @$cache )
94             and (
95             @got
96             ? $self->$_throw_caller_error( 'Returned values after LINQ::END' )
97             : return ()
98             ) while @got;
99            
100 2224         3745 redo EXTEND;
101             } #/ EXTEND:
102             } #/ sub extend_to
103             }
104              
105             package LINQ::Iterator;
106              
107             our $AUTHORITY = 'cpan:TOBYINK';
108             our $VERSION = '0.002';
109              
110 99     99   29004 use Role::Tiny::With ();
  99         312373  
  99         2618  
111 99     99   29961 use LINQ::Util::Internal ();
  99         252  
  99         13569  
112              
113             Role::Tiny::With::with( qw( LINQ::Collection ) );
114              
115             sub new {
116 533     533 1 9725 my $class = shift;
117 533 100       1293 if ( @_ ) {
118 532         1431 tie my ( @arr ), 'LINQ::Iterator::_LazyList',
119             LINQ::Util::Internal::assert_code( @_ );
120 532         3010 return bless \@arr, $class;
121             }
122             LINQ::Util::Internal::throw(
123 1         6 "CallerError",
124             message => "Expected a coderef"
125             );
126             } #/ sub new
127              
128             sub _guts {
129 431     431   2561 my $self = shift;
130 431         1594 tied( @$self );
131             }
132              
133             sub to_list {
134 262     262 1 604 my $self = shift;
135 262         979 my @list = @$self;
136            
137             # We must have exhausted the iterator now,
138             # so remove all the magic and act like a
139             # plain old arrayref.
140             #
141 245 100       897 if ( tied( @$self ) ) {
142 99     99   741 no warnings;
  99         222  
  99         34225  
143 181         1428 untie( @$self );
144 181         570 @$self = @list;
145             }
146            
147 245         1299 @list;
148             } #/ sub to_list
149              
150              
151             sub to_array {
152 134     134 1 31334 my $self = shift;
153            
154 134 100       284 if ( my $guts = $self->_guts ) {
155 131         381 tie ( my @tied, 'LINQ::Iterator::_LazyList', undef );
156 131         316 @{ tied( @tied ) } = @$guts;
  131         408  
157 131         967 return \@tied;
158             }
159            
160 3         18 $self->LINQ::Collection::to_array( @_ );
161             }
162              
163             sub element_at {
164 61     61 1 996 my $self = shift;
165            
166 61 100       193 if ( my $guts = $self->_guts ) {
167 27         65 my $ref = $guts->fetch_ref( @_ );
168 27 100       92 return $$ref if $ref;
169 4         1094 require LINQ::Exception;
170 4         23 'LINQ::Exception::NotFound'->throw( collection => $self );
171             }
172            
173 34         153 $self->LINQ::Collection::element_at( @_ );
174             } #/ sub element_at
175              
176             sub to_iterator {
177 231     231 1 379 my $self = shift;
178            
179 231 100       491 if ( my $guts = $self->_guts ) {
180 229         346 my $idx = 0;
181 229         388 my $done = 0;
182             return sub {
183 1494 100   1494   2691 return if $done;
184 1493         3056 my $val = $guts->fetch_ref( $idx++ );
185 1491 100       4082 return $$val if $val;
186 159         290 ++$done;
187 159         354 return;
188 229         1215 };
189             } #/ if ( my $guts = $self->...)
190            
191 2         15 $self->LINQ::Collection::to_iterator( @_ );
192             } #/ sub to_iterator
193              
194             1;
195              
196             __END__
197              
198             =pod
199              
200             =encoding utf-8
201              
202             =head1 NAME
203              
204             LINQ::Iterator - a LINQ collection with an iterator backend
205              
206             =head1 SYNOPSIS
207              
208             use LINQ qw( LINQ );
209             use LINQ::Iterator;
210            
211             my $linq = LINQ( sub { ... } );
212            
213             # Same:
214             my $linq = 'LINQ::Iterator'->new( sub { ... } );
215              
216             =head1 METHODS
217              
218             LINQ::Iterator supports all the methods defined in L<LINQ::Collection>.
219              
220             =begin trustme
221              
222             =item new
223              
224             =item element_at
225              
226             =item to_list
227              
228             =item to_array
229              
230             =item to_iterator
231              
232             =end trustme
233              
234             =head1 BUGS
235              
236             Please report any bugs to
237             L<http://rt.cpan.org/Dist/Display.html?Queue=LINQ>.
238              
239             =head1 SEE ALSO
240              
241             L<LINQ>, L<LINQ::Collection>.
242              
243             L<https://en.wikipedia.org/wiki/Language_Integrated_Query>
244              
245             =head1 AUTHOR
246              
247             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
248              
249             =head1 COPYRIGHT AND LICENCE
250              
251             This software is copyright (c) 2021 by Toby Inkster.
252              
253             This is free software; you can redistribute it and/or modify it under
254             the same terms as the Perl 5 programming language system itself.
255              
256             =head1 DISCLAIMER OF WARRANTIES
257              
258             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
259             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
260             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.