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 98     98   3562 use 5.006;
  98         338  
2 98     98   574 use strict;
  98         182  
  98         2239  
3 98     98   507 use warnings;
  98         207  
  98         63877  
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 655     655   1098 my $class = shift;
26 655         2533 bless [
27             $_[0],
28             !!0,
29             [],
30             ], $class;
31             }
32            
33             sub FETCH {
34 1123     1123   2803 my $self = shift;
35 1123         1615 my ( $ix ) = @_;
36 1123         1470 my $cache = $self->[__VALUES];
37            
38 1123         2312 $self->extend_to( $ix );
39            
40 1123 100       4038 $ix >= @$cache ? undef : $cache->[$ix];
41             }
42            
43             sub fetch_ref {
44 1520     1520   2058 my $self = shift;
45 1520         2282 my ( $ix ) = @_;
46 1520         2078 my $cache = $self->[__VALUES];
47            
48 1520         3243 $self->extend_to( $ix );
49            
50 1518 100       3071 return if $ix > 0+ $#$cache;
51 1357 100       2697 return if $ix < 0 - @$cache;
52 1355         2296 \( $cache->[$ix] );
53             } #/ sub fetch_ref
54            
55             sub FETCHSIZE {
56 845     845   43059 my $self = shift;
57 845         1991 $self->extend_to( -1 );
58 828         1301 scalar @{ $self->[__VALUES] };
  828         2331  
59             }
60            
61             sub current_extension {
62 2     2   3 my $self = shift;
63 2         3 scalar @{ $self->[__VALUES] };
  2         9  
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 3488     3488   12081 require LINQ;
73            
74 3488         4801 my $self = shift;
75 3488         4807 my ( $ix ) = @_;
76 3488         4660 my $cache = $self->[__VALUES];
77            
78             EXTEND: {
79 3488 100       4275 return if $self->[__EXHAUSTED];
  5668         10144  
80 3876 100 100     10227 return if $ix >= 0 && $ix < @$cache;
81            
82 2644         4985 my @got = $self->[__GENERATOR]->();
83 2627         8419 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 2627 100 66     13223 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 2180         3595 redo EXTEND;
101             } #/ EXTEND:
102             } #/ sub extend_to
103             }
104              
105             package LINQ::Iterator;
106              
107             our $AUTHORITY = 'cpan:TOBYINK';
108             our $VERSION = '0.001';
109              
110 98     98   28719 use Role::Tiny::With ();
  98         304957  
  98         2350  
111 98     98   29085 use LINQ::Util::Internal ();
  98         255  
  98         13682  
112              
113             Role::Tiny::With::with( qw( LINQ::Collection ) );
114              
115             sub new {
116 525     525 1 10600 my $class = shift;
117 525 100       1325 if ( @_ ) {
118 524         1337 tie my ( @arr ), 'LINQ::Iterator::_LazyList',
119             LINQ::Util::Internal::assert_code( @_ );
120 524         2930 return bless \@arr, $class;
121             }
122             LINQ::Util::Internal::throw(
123 1         5 "CallerError",
124             message => "Expected a coderef"
125             );
126             } #/ sub new
127              
128             sub _guts {
129 431     431   1938 my $self = shift;
130 431         1593 tied( @$self );
131             }
132              
133             sub to_list {
134 254     254 1 544 my $self = shift;
135 254         886 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 237 100       831 if ( tied( @$self ) ) {
142 98     98   717 no warnings;
  98         216  
  98         34176  
143 173         1328 untie( @$self );
144 173         537 @$self = @list;
145             }
146            
147 237         1726 @list;
148             } #/ sub to_list
149              
150              
151             sub to_array {
152 134     134 1 27529 my $self = shift;
153            
154 134 100       300 if ( my $guts = $self->_guts ) {
155 131         372 tie ( my @tied, 'LINQ::Iterator::_LazyList', undef );
156 131         309 @{ tied( @tied ) } = @$guts;
  131         434  
157 131         986 return \@tied;
158             }
159            
160 3         22 $self->LINQ::Collection::to_array( @_ );
161             }
162              
163             sub element_at {
164 61     61 1 1368 my $self = shift;
165            
166 61 100       162 if ( my $guts = $self->_guts ) {
167 27         63 my $ref = $guts->fetch_ref( @_ );
168 27 100       91 return $$ref if $ref;
169 4         1099 require LINQ::Exception;
170 4         21 'LINQ::Exception::NotFound'->throw( collection => $self );
171             }
172            
173 34         132 $self->LINQ::Collection::element_at( @_ );
174             } #/ sub element_at
175              
176             sub to_iterator {
177 231     231 1 386 my $self = shift;
178            
179 231 100       490 if ( my $guts = $self->_guts ) {
180 229         377 my $idx = 0;
181 229         380 my $done = 0;
182             return sub {
183 1494 100   1494   2764 return if $done;
184 1493         2995 my $val = $guts->fetch_ref( $idx++ );
185 1491 100       3926 return $$val if $val;
186 159         285 ++$done;
187 159         371 return;
188 229         1154 };
189             } #/ if ( my $guts = $self->...)
190            
191 2         26 $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.