File Coverage

blib/lib/HashDataRole/Source/LinesInDATA.pm
Criterion Covered Total %
statement 91 105 86.6
branch 17 24 70.8
condition 3 8 37.5
subroutine 15 20 75.0
pod 4 13 30.7
total 130 170 76.4


line stmt bran cond sub pod time code
1             package HashDataRole::Source::LinesInDATA;
2              
3 4     4   405965 use Role::Tiny;
  4         7708  
  4         32  
4 4     4   1635 use Role::Tiny::With;
  4         258  
  4         422  
5             with 'HashDataRole::Spec::Basic';
6              
7             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
8             our $DATE = '2024-11-04'; # DATE
9             our $DIST = 'HashDataRoles-Standard'; # DIST
10             our $VERSION = '0.005'; # VERSION
11              
12             sub new {
13 4     4   24 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  4         8  
  4         1688  
14              
15 2     2 1 385362 my ($class, %args) = @_;
16              
17 2         3 my $fh = \*{"$class\::DATA"};
  2         10  
18 2         5 my $fhpos_data_begin;
19 2 100       3 if (defined ${"$class\::_HashData_fhpos_data_begin_cache"}) {
  2         12  
20 1         24 $fhpos_data_begin = ${"$class\::_HashData_fhpos_data_begin_cache"};
  1         4  
21 1         8 seek $fh, $fhpos_data_begin, 0;
22             } else {
23 1         4 $fhpos_data_begin = ${"$class\::_HashData_fhpos_data_begin_cache"} = tell $fh;
  1         4  
24             }
25              
26             bless {
27             fh => $fh,
28 2   50     28 separator => $args{separator} // ':',
29             fhpos_data_begin => $fhpos_data_begin,
30             pos => 0, # iterator
31             }, $class;
32             }
33              
34             sub get_next_item {
35 18     18 0 71 my $self = shift;
36 18 100       84 die "StopIteration" if eof($self->{fh});
37 17         35 $self->{fhpos_cur_item} = tell($self->{fh});
38 17         39 chomp(my $line = readline($self->{fh}));
39 17 50       109 my ($key, $value) = split /\Q$self->{separator}\E/, $line, 2 or die "Invalid line at position $self->{pos}: no separator ':'";
40 17         27 $self->{pos}++;
41 17         71 [$key, $value];
42             }
43              
44             sub has_next_item {
45 12     12 0 14 my $self = shift;
46 12         39 !eof($self->{fh});
47             }
48              
49             sub get_iterator_pos {
50 0     0 0 0 my $self = shift;
51 0         0 $self->{pos};
52             }
53              
54             sub reset_iterator {
55 4     4 0 10 my $self = shift;
56 4         49 seek $self->{fh}, $self->{fhpos_data_begin}, 0;
57 4         10 $self->{pos} = 0;
58             }
59              
60             sub _get_pos_cache {
61 4     4   31 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  4         7  
  4         743  
62              
63 6     6   7 my $self = shift;
64              
65 6   33     23 my $class = $self->{orig_class} // ref($self);
66 5         12 return ${"$class\::_HashData_pos_cache"}
67 6 100       7 if defined ${"$class\::_HashData_pos_cache"};
  6         26  
68              
69             # build
70 1         2 my $pos_cache = [];
71 1         3 $self->reset_iterator;
72 1         3 while ($self->has_next_item) {
73 5         11 $self->get_next_item;
74 5         31 push @$pos_cache, $self->{fhpos_cur_item};
75             }
76             #use DD; dd $pos_cache;
77 1         2 ${"$class\::_HashData_pos_cache"} = $pos_cache;
  1         6  
78             }
79              
80             sub _get_hash_cache {
81 4     4   26 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  4         24  
  4         2526  
82              
83 4     4   6 my $self = shift;
84              
85 4   33     31 my $class = $self->{orig_class} // ref($self);
86 3         7 return ${"$class\::_HashData_hash_cache"}
87 4 100       5 if defined ${"$class\::_HashData_hash_cache"};
  4         21  
88              
89 1         3 my $hash_cache = {};
90 1         3 $self->reset_iterator;
91 1         4 while ($self->has_next_item) {
92 5         9 my $item = $self->get_next_item;
93 5         13 $hash_cache->{$item->[0]} = $self->{fhpos_cur_item};
94             }
95             #use DD; dd $hash_cache;
96 1         2 ${"$class\::_HashData_hash_cache"} = $hash_cache;
  1         6  
97             }
98              
99             sub get_item_at_pos {
100 3     3 0 31 my ($self, $pos) = @_;
101              
102 3         5 my $pos_cache = $self->_get_pos_cache;
103 3 50       7 if ($pos < 0) {
104 0 0       0 die "Out of range" unless -$pos <= @{ $pos_cache };
  0         0  
105             } else {
106 3 100       4 die "Out of range" unless $pos < @{ $pos_cache };
  3         13  
107             }
108              
109 2         3 my $oldfhpos = tell $self->{fh};
110 2         13 seek $self->{fh}, $pos_cache->[$pos], 0;
111 2         16 chomp(my $line = readline($self->{fh}));
112 2         20 my ($key, $value) = split /\Q$self->{separator}\E/, $line, 2;
113 2         10 seek $self->{fh}, $oldfhpos, 0;
114 2         12 [$key, $value];
115             }
116              
117             sub has_item_at_pos {
118 3     3 0 7 my ($self, $pos) = @_;
119              
120 3         8 my $pos_cache = $self->_get_pos_cache;
121 3 50       7 if ($pos < 0) {
122 0 0       0 return -$pos <= @{ $pos_cache } ? 1:0;
  0         0  
123             } else {
124 3 100       5 return $pos < @{ $pos_cache } ? 1:0;
  3         12  
125             }
126             }
127              
128             sub get_item_at_key {
129 2     2 0 29 my ($self, $key) = @_;
130              
131 2         5 my $hash_cache = $self->_get_hash_cache;
132 2 100       15 die "No such key '$key'" unless exists $hash_cache->{$key};
133              
134 1         3 my $oldfhpos = tell $self->{fh};
135 1         6 seek $self->{fh}, $hash_cache->{$key}, 0;
136 1         8 chomp(my $line = readline($self->{fh}));
137 1         18 my (undef, $value) = split /\Q$self->{separator}\E/, $line, 2;
138 1         6 seek $self->{fh}, $oldfhpos, 0;
139 1         6 $value;
140             }
141              
142             sub has_item_at_key {
143 2     2 0 7 my ($self, $key) = @_;
144              
145 2         7 my $hash_cache = $self->_get_hash_cache;
146 2         10 exists $hash_cache->{$key};
147             }
148              
149             sub get_all_keys {
150 0     0 0   my ($self, $key) = @_;
151              
152 0           my $hash_cache = $self->_get_hash_cache;
153 0           sort %$hash_cache;
154             }
155              
156              
157             sub fh {
158 0     0 1   my $self = shift;
159 0           $self->{fh};
160             }
161              
162             sub fh_min_offset {
163 0     0 1   my $self = shift;
164 0           $self->{fhpos_data_begin};
165             }
166              
167 0     0 1   sub fh_max_offset { undef }
168              
169             1;
170             # ABSTRACT: Role to access hash data from DATA section, one line per item
171              
172             __END__