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 14     14   61251 use utf8;
  14         28  
  14         68  
2              
3             package Net::EGTS::SubRecord;
4 14     14   929 use namespace::autoclean;
  14         15154  
  14         58  
5 14     14   1162 use Mouse;
  14         23625  
  14         60  
6              
7 14     14   4322 use Carp;
  14         22  
  14         800  
8 14     14   548 use List::MoreUtils qw(natatime);
  14         11040  
  14         87  
9 14     14   10112 use Module::Load qw(load);
  14         5511  
  14         84  
10              
11 14     14   1539 use Net::EGTS::Codes;
  14         172  
  14         4028  
12 14     14   376 use Net::EGTS::Util qw(usize);
  14         130  
  14         577  
13 14     14   1438 use Net::EGTS::Types;
  14         478  
  14         1343  
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 14     14   77 use bytes;
  14         18  
  14         67  
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 22     22 1 34 my $self = shift;
68 22         22 my $args = shift;
69 22 100       133 $self->decode( \$self->bin ) if length $self->bin;
70             }
71              
72             # Get chunk from binary and store it
73             sub take {
74 45     45 0 70 my ($self, $bin, $mask, $length) = @_;
75 14     14   3506 use bytes;
  14         18  
  14         46  
76              
77 45   66     133 $length //= usize($mask);
78 45 50       75 confess "Can`t get chunk of length $length" if $length > length $$bin;
79              
80 45         78 my $chunk = substr $$bin, 0 => $length, '';
81 45         129 $self->bin( $self->bin . $chunk );
82              
83 45         235 return unpack $mask => $chunk;
84             }
85              
86             # Helper to get portion of data
87             sub nip {
88 52     52 0 70 my ($self, $bin, $mask, $length) = @_;
89 14     14   1531 use bytes;
  14         27  
  14         47  
90              
91 52   66     112 $length //= usize($mask);
92 52 50       72 confess "Can`t get chunk of length $length" if $length > length $$bin;
93              
94 52         87 my $chunk = substr $$bin, 0 => $length, '';
95 52         171 return unpack $mask => $chunk;
96             }
97              
98             =head2 encode
99              
100             Build subrecord as binary
101              
102             =cut
103              
104             sub encode {
105 7     7 1 806 my ($self) = @_;
106 14     14   1401 use bytes;
  14         23  
  14         49  
107              
108 7 50       29 croak 'Subrecord Туре required' unless defined $self->SRT;
109 7 50       32 croak 'Subrecord Data required' unless defined $self->SRD;
110 7 50       39 croak 'Subrecord Length roo big' unless $self->SRL <= 65495;
111              
112 7         54 my $bin = pack 'C S a*' => $self->SRT, $self->SRL, $self->SRD;
113 7         29 $self->bin( $bin );
114 7         22 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 15     15 1 118 my ($self, $bin) = @_;
126 14     14   1700 use bytes;
  14         27  
  14         61  
127              
128 15         62 $self->SRT( $self->take($bin => 'C') );
129 15         36 $self->SRL( $self->take($bin => 'S') );
130 15         41 $self->SRD( $self->take($bin => 'a*' => $self->SRL) );
131              
132 15         36 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 4     4 1 15 my ($class, $bin) = @_;
143 14     14   1360 use bytes;
  14         20  
  14         67  
144              
145 4         8 my @result;
146 4         13 while( my $length = length $bin ) {
147 5         17 my $type = unpack 'C' => substr $bin, 0 => usize('C');
148              
149 5         15 my $subclass = $TYPES{ $type };
150 5         23 load $subclass;
151              
152 5         464 my $self = $subclass->new->decode( \$bin );
153 5 50       41 die 'Something wrong in subrecords decode' unless $self;
154              
155 5         17 push @result, $self;
156             }
157              
158 4 100       44 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 4996 my ($self) = @_;
169 14     14   2175 use bytes;
  14         30  
  14         48  
170              
171 18         196 my @bytes = ((unpack('B*', $self->bin)) =~ m{.{8}}g);
172              
173 18         24 my @str;
174 18         53 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       44 if( @bytes ) {
178 18 100       60 if( my @qualify = inner() ) {
179 15         27 push @str => sprintf('SRD =>');
180 15         25 push @str, @qualify;
181 15         99 push @str => sprintf('<======');
182             } else {
183 3         40 my $it = natatime 4, @bytes;
184 3         56 my @chunks;
185 3         17 while (my @vals = $it->()) {
186 3         11 push @chunks, join(' ', @vals);
187             }
188 3         16 push @str => sprintf('SRD: %s', join("\n ", @chunks));
189             }
190             }
191              
192 18         164 return join "\n", @str;
193             }
194              
195             __PACKAGE__->meta->make_immutable();