File Coverage

lib/Net/EGTS/SubRecord.pm
Criterion Covered Total %
statement 100 100 100.0
branch 13 20 65.0
condition 4 6 66.6
subroutine 23 23 100.0
pod 5 7 71.4
total 145 156 92.9


line stmt bran cond sub pod time code
1 15     15   58437 use utf8;
  15         32  
  15         64  
2              
3             package Net::EGTS::SubRecord;
4 15     15   837 use namespace::autoclean;
  15         14060  
  15         47  
5 15     15   1065 use Mouse;
  15         21572  
  15         53  
6              
7 15     15   4216 use Carp;
  15         23  
  15         792  
8 15     15   515 use List::MoreUtils qw(natatime);
  15         9642  
  15         90  
9 15     15   10258 use Module::Load qw(load);
  15         5264  
  15         235  
10              
11 15     15   1283 use Net::EGTS::Codes;
  15         209  
  15         4022  
12 15     15   458 use Net::EGTS::Util qw(usize);
  15         27  
  15         565  
13 15     15   1366 use Net::EGTS::Types;
  15         402  
  15         1275  
14              
15             =head1 NAME
16              
17             Net::EGTS::SubRecord - SubRecord common part
18              
19             =cut
20              
21             # Packet types and classes
22             our %TYPES = (
23             EGTS_SR_RECORD_RESPONSE, 'Net::EGTS::SubRecord::Auth::RecordResponse',
24             EGTS_SR_DISPATCHER_IDENTITY, 'Net::EGTS::SubRecord::Auth::DispatcherIdentity',
25             EGTS_SR_RESULT_CODE, 'Net::EGTS::SubRecord::Auth::ResultCode',
26             EGTS_SR_POS_DATA, 'Net::EGTS::SubRecord::Teledata::PosData',
27             );
28              
29             # Subrecord Туре
30             has SRT => is => 'rw', isa => 'BYTE';
31             # Subrecord Length
32             has SRL =>
33             is => 'rw',
34             isa => 'USHORT',
35             lazy => 1,
36             builder => sub {
37             my ($self) = @_;
38 15     15   76 use bytes;
  15         20  
  15         59  
39             return length($self->SRD);
40             },
41             ;
42             # Subrecord Data
43             has SRD =>
44             is => 'rw',
45             isa => 'Maybe[BINARY]',
46             trigger => sub {
47             my ($self, $value, $old) = @_;
48             die 'Subrecord Data too long' if length($value) > 65495;
49             }
50             ;
51              
52             # SubRecord binary
53             has bin => is => 'rw', isa => 'Str', default => '';
54              
55             around BUILDARGS => sub {
56             my $orig = shift;
57             my $class = shift;
58              
59             # simple scalar decoding support
60             my $bin = @_ % 2 ? shift : undef;
61             my %opts = @_;
62              
63             return $class->$orig( bin => $bin, %opts ) if $bin;
64             return $class->$orig( %opts );
65             };
66             sub BUILD {
67 28     28 1 32 my $self = shift;
68 28         34 my $args = shift;
69 28 100       156 $self->decode( \$self->bin ) if length $self->bin;
70             }
71              
72             # Get chunk from binary and store it
73             sub take {
74 54     54 0 76 my ($self, $bin, $mask, $length) = @_;
75 15     15   3452 use bytes;
  15         20  
  15         46  
76              
77 54   66     136 $length //= usize($mask);
78 54 50       84 confess "Can`t get chunk of length $length" if $length > length $$bin;
79              
80 54         85 my $chunk = substr $$bin, 0 => $length, '';
81 54         140 $self->bin( $self->bin . $chunk );
82              
83 54         266 return unpack $mask => $chunk;
84             }
85              
86             # Helper to get portion of data
87             sub nip {
88 58     58 0 86 my ($self, $bin, $mask, $length) = @_;
89 15     15   1551 use bytes;
  15         27  
  15         53  
90              
91 58   66     129 $length //= usize($mask);
92 58 50       88 confess "Can`t get chunk of length $length" if $length > length $$bin;
93              
94 58         88 my $chunk = substr $$bin, 0 => $length, '';
95 58         239 return unpack $mask => $chunk;
96             }
97              
98             =head2 encode
99              
100             Build subrecord as binary
101              
102             =cut
103              
104             sub encode {
105 10     10 1 708 my ($self) = @_;
106 15     15   1417 use bytes;
  15         28  
  15         47  
107              
108 10 50       30 croak 'Subrecord Туре required' unless defined $self->SRT;
109 10 50       28 croak 'Subrecord Data required' unless defined $self->SRD;
110 10 50       56 croak 'Subrecord Length roo big' unless $self->SRL <= 65495;
111              
112 10         70 my $bin = pack 'C S a*' => $self->SRT, $self->SRL, $self->SRD;
113 10         25 $self->bin( $bin );
114 10         37 return $bin;
115             }
116              
117             =head2 decode \$bin
118              
119             Decode binary I<$bin> into subrecord object.
120             The binary stream will be truncated!
121              
122             =cut
123              
124             sub decode {
125 18     18 1 129 my ($self, $bin) = @_;
126 15     15   1622 use bytes;
  15         24  
  15         39  
127              
128 18         58 $self->SRT( $self->take($bin => 'C') );
129 18         36 $self->SRL( $self->take($bin => 'S') );
130 18         46 $self->SRD( $self->take($bin => 'a*' => $self->SRL) );
131              
132 18         35 return $self;
133             }
134              
135             =head2 decode_all \$bin
136              
137             Parse all subrecords from record Record Data
138              
139             =cut
140              
141             sub decode_all {
142 7     7 1 19 my ($class, $bin) = @_;
143 15     15   1316 use bytes;
  15         18  
  15         38  
144              
145 7         9 my @result;
146 7         17 while( my $length = length $bin ) {
147 8         21 my $type = unpack 'C' => substr $bin, 0 => usize('C');
148              
149 8         26 my $subclass = $TYPES{ $type };
150 8         24 load $subclass;
151              
152 8         485 my $self = $subclass->new->decode( \$bin );
153 8 50       57 die 'Something wrong in subrecords decode' unless $self;
154              
155 8         31 push @result, $self;
156             }
157              
158 7 100       61 return wantarray ? @result : \@result;
159             }
160              
161             =head2 as_debug
162              
163             Return human readable string
164              
165             =cut
166              
167             sub as_debug {
168 18     18 1 4625 my ($self) = @_;
169 15     15   1978 use bytes;
  15         25  
  15         49  
170              
171 18         204 my @bytes = ((unpack('B*', $self->bin)) =~ m{.{8}}g);
172              
173 18         28 my @str;
174 18         68 push @str => sprintf('SRT: %s', splice @bytes, 0 => usize('C'));
175 18         52 push @str => sprintf('SRL: %s %s', splice @bytes, 0 => usize('S'));
176              
177 18 50       48 if( @bytes ) {
178 18 100       65 if( my @qualify = inner() ) {
179 15         22 push @str => sprintf('SRD =>');
180 15         72 push @str, @qualify;
181 15         31 push @str => sprintf('<======');
182             } else {
183 3         80 my $it = natatime 4, @bytes;
184 3         4 my @chunks;
185 3         13 while (my @vals = $it->()) {
186 3         11 push @chunks, join(' ', @vals);
187             }
188 3         39 push @str => sprintf('SRD: %s', join("\n ", @chunks));
189             }
190             }
191              
192 18         138 return join "\n", @str;
193             }
194              
195             __PACKAGE__->meta->make_immutable();