File Coverage

lib/Data/TagDB/Iterator.pm
Criterion Covered Total %
statement 17 77 22.0
branch 0 20 0.0
condition 0 5 0.0
subroutine 6 23 26.0
pod 10 10 100.0
total 33 135 24.4


line stmt bran cond sub pod time code
1             # Copyright (c) 2024-2025 Philipp Schafft
2              
3             # licensed under Artistic License 2.0 (see LICENSE file)
4              
5             # ABSTRACT: Work with Tag databases
6              
7             package Data::TagDB::Iterator;
8              
9 1     1   656 use v5.10;
  1         3  
10 1     1   5 use strict;
  1         2  
  1         26  
11 1     1   5 use warnings;
  1         2  
  1         64  
12              
13 1     1   5 use Carp;
  1         2  
  1         995  
14              
15             our $VERSION = v0.12;
16              
17              
18              
19             sub new {
20 0     0 1   my ($pkg, %opts) = @_;
21              
22 0 0         croak 'Missing required member: db' unless defined $opts{db};
23              
24 0           return bless \%opts, $pkg;
25             }
26              
27              
28             sub from_array {
29 0     0 1   my ($pkg, $array, %opts) = @_;
30 0           return Data::TagDB::Iterator::_Array->new(%opts, array => $array);
31             }
32              
33              
34             sub db {
35 0     0 1   my ($self) = @_;
36 0           return $self->{db};
37             }
38              
39              
40             sub next {
41 0     0 1   my ($self) = @_;
42 0           confess 'Not implemented';
43             }
44              
45              
46             sub finish {
47 0     0 1   my ($self) = @_;
48 0           confess 'Not implemented';
49             }
50              
51              
52              
53             sub foreach {
54 0     0 1   my ($self, $code) = @_;
55              
56 0           while (defined(my $ent = $self->next)) {
57 0           $code->($ent);
58             }
59              
60 0           $self->finish;
61             }
62              
63              
64             sub one {
65 0     0 1   my ($self) = @_;
66 0           my $ent = $self->next;
67 0           $self->finish;
68              
69 0   0       return $ent // croak 'No entry';
70             }
71              
72              
73             sub none {
74 0     0 1   my ($self) = @_;
75 0           my $ent = $self->next;
76              
77 0           $self->finish;
78              
79 0 0         croak 'Iterator non-empty' if defined $ent;
80             }
81              
82              
83             sub map {
84 0     0 1   my ($self, $apply, %opts) = @_;
85              
86 0           return Data::TagDB::Iterator::_Mapped->new($self, $apply);
87             }
88              
89              
90             sub collect {
91 0     0 1   my ($self, $apply, %opts) = @_;
92 0           my @ret;
93              
94 0 0         if (defined $apply) {
95 0 0         unless (ref $apply) {
96 0           my $funcname = $apply;
97 0     0     $apply = sub { $_[0]->can($funcname)->(@_) };
  0            
98             }
99             }
100              
101 0 0         if (defined($apply)) {
102 0 0         if ($opts{skip_died}) {
103 0           while (defined(my $ent = $self->next)) {
104 0           $ent = eval { $ent->$apply() };
  0            
105 0 0         push(@ret, $ent) unless $@;
106             }
107             } else {
108 0           while (defined(my $ent = $self->next)) {
109 0           push(@ret, $ent->$apply());
110             }
111             }
112             } else {
113 0           while (defined(my $ent = $self->next)) {
114 0           push(@ret, $ent);
115             }
116             }
117              
118 0 0         return \@ret if $opts{return_ref};
119              
120 0           return @ret;
121             }
122              
123             package Data::TagDB::Iterator::_Mapped {
124 1     1   35 use parent -norequire, 'Data::TagDB::Iterator';
  1         3  
  1         10  
125              
126             sub new {
127 0     0     my ($pkg, $parent, $apply) = @_;
128              
129 0 0         unless (ref $apply) {
130 0           my $funcname = $apply;
131 0     0     $apply = sub { $_[0]->can($funcname)->(@_) };
  0            
132             }
133              
134 0           return $pkg->SUPER::new(db => $parent->db, parent => $parent, apply => $apply);
135             }
136              
137             sub next {
138 0     0     my ($self, @args) = @_;
139 0           my $ent = $self->{parent}->next(@args);
140 0           my $apply = $self->{apply};
141              
142 0 0         return undef unless defined $ent;
143              
144 0           return $ent->$apply();
145             }
146              
147             sub finish {
148 0     0     my ($self, @args) = @_;
149 0           return $self->{parent}->finish(@args);
150             }
151             };
152              
153             package Data::TagDB::Iterator::_Array {
154 1     1   420 use parent -norequire, 'Data::TagDB::Iterator';
  1         2  
  1         6  
155              
156             sub next {
157 0     0     my ($self) = @_;
158 0   0       $self->{index} //= 0;
159              
160 0           return $self->{array}[$self->{index}++];
161             }
162              
163       0     sub finish {}
164             };
165              
166             1;
167              
168             __END__
169              
170             =pod
171              
172             =encoding UTF-8
173              
174             =head1 NAME
175              
176             Data::TagDB::Iterator - Work with Tag databases
177              
178             =head1 VERSION
179              
180             version v0.12
181              
182             =head1 SYNOPSIS
183              
184             use Data::TagDB;
185              
186             Generic iterator for database entries
187              
188             =head1 METHODS
189              
190             =head2 new
191              
192             my Data::TagDB::Iterator $iter = XXX->new(...);
193              
194             Returns a new iterator. Maybe called in sub-packages implementing actual iterators.
195              
196             =head2 from_array
197              
198             my $Data::TagDB::Iterator $iter = Data::TagDB::Iterator->from_array(\@array, ...);
199              
200             Creates an iterator from a simple array reference.
201             The reference becomes part of the object (so no copy is made).
202              
203             =head2 db
204              
205             my Data::TagDB $db = $db->db;
206              
207             Returns the current Data::TagDB object
208              
209             =head2 next
210              
211             my $entry = $iter->next;
212              
213             Returns the next element or C<undef> when there is no next element.
214              
215             Needs to be implemented.
216              
217             =head2 finish
218              
219             $iter->finish;
220              
221             Tells the iterator that you're done reading. May allow early freeing of backend data.
222              
223             Needs to be implemented.
224              
225             =head2 foreach
226              
227             $iter->foreach(sub {
228             my ($entry) = @_;
229             # ...
230             });
231              
232             Runs a function for each entry.
233             Automatically finishes the iterator.
234              
235             =head2 one
236              
237             my $entry = $iter->one;
238              
239             Returns one entry from the iterator and finishes.
240             This is most useful when you expect there to be exactly one entry.
241             This function dies if no entry is returned. So It is guaranteed that this function returns non-C<undef>.
242              
243             =head2 none
244              
245             $iter->none;
246              
247             This method dies if there is an entry left in the iterator.
248             This finishes the iterator.
249             This is most useful to assert that something is not present.
250              
251             =head2 map
252              
253             my Data::TagDB::Iterator $mapped = $iter->map('method');
254             # or:
255             my Data::TagDB::Iterator $mapped = $iter->map(sub { ... });
256              
257             Returns a new iterator that contains the entries mapped by a filter.
258             If the filter is a simple string it is as a method name to be called on the object.
259              
260             =head2 collect
261              
262             my @list = $iter->collect;
263             # or:
264             my @list = $iter->collect('method');
265             # or:
266             my @list = $iter->collect(sub { ... });
267              
268             Reads all entries from an iterator and finishes.
269             The entries are returned as a list.
270             Optionally a filter can be applied.
271             If the filter is a simple string it is as a method name to be called on the object.
272              
273             =head1 AUTHOR
274              
275             Philipp Schafft <lion@cpan.org>
276              
277             =head1 COPYRIGHT AND LICENSE
278              
279             This software is Copyright (c) 2024-2025 by Philipp Schafft <lion@cpan.org>.
280              
281             This is free software, licensed under:
282              
283             The Artistic License 2.0 (GPL Compatible)
284              
285             =cut