File Coverage

blib/lib/HiveJSO.pm
Criterion Covered Total %
statement 160 162 98.7
branch 73 82 89.0
condition 17 21 80.9
subroutine 27 28 96.4
pod 3 11 27.2
total 280 304 92.1


line stmt bran cond sub pod time code
1             package HiveJSO;
2             BEGIN {
3 7     7   146384 $HiveJSO::AUTHORITY = 'cpan:GETTY';
4             }
5             # ABSTRACT: HiveJSO Perl Implementation
6             $HiveJSO::VERSION = '0.013';
7 7     7   8661 use Moo;
  7         141639  
  7         47  
8 7     7   22709 use JSON::MaybeXS;
  7         69079  
  7         479  
9 7     7   4231 use HiveJSO::Error;
  7         25  
  7         313  
10 7     7   8055 use Digest::CRC qw( crc32 );
  7         52584  
  7         689  
11 7     7   89 use Carp qw( croak );
  7         15  
  7         17327  
12              
13             our %short_attributes = qw(
14             c checksum
15             d data
16             ma manufacturer
17             maw manufacturer_web
18             maf manufacturer_factory
19             mac manufacturer_country
20             n units
21             o command
22             p product
23             pn product_name
24             pw product_web
25             pt product_timestamp
26             q supports
27             r ranges
28             s sources
29             t timestamp
30             tz timestamp_timezone
31             u unit
32             w software
33             x error_code
34             xt error
35             );
36              
37             our @long_attributes = qw(
38             ok
39             path
40             );
41              
42             our @attributes = sort {
43             $a cmp $b
44             } ( @long_attributes, values %short_attributes );
45              
46             has [grep { $_ ne 'unit' } @attributes] => (
47             is => 'ro',
48             predicate => 1,
49             );
50              
51             has unit => (
52             is => 'ro',
53             required => 1,
54             );
55              
56             has command_cmd => (
57             is => 'lazy',
58             );
59 0     0 0 0 sub has_command_cmd { $_[0]->has_command }
60             sub _build_command_cmd {
61 2     2   6273 my ( $self ) = @_;
62 1         4 my ( $cmd ) = ref $self->command eq 'ARRAY'
63 2 100       13 ? @{$self->command} : $self->command;
64 2         11 return $cmd;
65             }
66              
67 3     3 0 4 sub command_args { @{$_[0]->command_args_ref} }
  3         52  
68             has command_args_ref => (
69             is => 'lazy',
70             );
71 2     2 0 8 sub has_command_args { scalar ( $_[0]->command_args ) }
72             sub _build_command_args_ref {
73 2     2   452 my ( $self ) = @_;
74 2 100       16 return [] unless ref $self->command eq 'ARRAY';
75 1         2 my ( undef, @args ) = @{$self->command};
  1         5  
76 1         8 return [ @args ];
77             }
78              
79             sub new_via_json {
80 26     26 0 31717 my ( $class, $json ) = @_;
81 26         48 my %obj = %{decode_json($json)};
  26         451  
82 24         710 return $class->new( %obj, original_json => $json );
83             }
84              
85             has original_json => (
86             is => 'ro',
87             predicate => 1,
88             );
89              
90             sub BUILDARGS {
91 27     27 0 33369 my ( $class, @args ) = @_;
92 27         50 my %orig;
93 27 100       94 if (scalar @args == 1) {
94 1         3 %orig = %{$args[0]};
  1         7  
95             } else {
96 26         106 %orig = @args;
97             }
98 27         46 my %attr;
99              
100             #
101             # only accept allowed attributes
102             # only accept short or long not mixed
103             #
104 27         43 my ( $short, $long );
105 27         61 my $short_long_error = __PACKAGE__." you can't mix short HiveJSO attribute names with long HiveJSO attributes";
106 27         159 for my $k (keys %orig) {
107 84 100       546 if (defined $short_attributes{$k}) {
  1512 100       2982  
    100          
108 12 50       33 croak $short_long_error if $long; $short = 1;
  12         16  
109 12         36 $attr{$short_attributes{$k}} = $orig{$k};
110             } elsif (grep {
111             $k eq $short_attributes{$_}
112 86         208 } keys %short_attributes) {
113 29 50       76 croak $short_long_error if $short; $long = 1;
  29         42  
114 29         111 $attr{$k} = $orig{$k};
115             } elsif (grep { $_ eq $k } @long_attributes) {
116 18         74 $attr{$k} = $orig{$k};
117             } else {
118 25 100       63 if ($k eq 'original_json') {
119 24         56 $attr{$k} = $orig{$k}; delete $orig{$k};
  24         85  
120             } else {
121 1         236 croak __PACKAGE__." '".$k."' is not a valid HiveJSO attribute";
122             }
123             }
124             }
125              
126             #
127             # remove checksum now of input attributes
128             #
129 26   100     171 my $checksum = delete $orig{c} || delete $orig{checksum};
130              
131             #
132             # we need at least 2 attributes without checksum (unit and one other)
133             #
134 26 100       94 if (keys %orig < 2) {
135 1         1360 croak __PACKAGE__." we need more attributes for a valid HiveJSO";
136             }
137              
138             #
139             # ok must be 1
140             #
141 25 100 100     195 if (defined $attr{ok} && $attr{ok} != 1) {
142 1         222 croak __PACKAGE__." ok attribute must be set to 1 for a valid HiveJSO";
143             }
144              
145             #
146             # data attribute validation
147             #
148 24 100       69 if (defined $attr{data}) {
149 4 50       15 croak __PACKAGE__." 'data' must be an array" unless ref $attr{data} eq 'ARRAY';
150 4         7 for my $data_set (@{$attr{data}}) {
  4         11  
151 6 100       180 croak __PACKAGE__." values inside the 'data' array must be arrays" unless ref $data_set eq 'ARRAY';
152 5 100       6 croak __PACKAGE__." array inside 'data' array needs at least one value" unless scalar @{$data_set};
  5         233  
153             }
154             }
155              
156             #
157             # novell check
158             #
159 22 100       63 if (defined $attr{error_code}) {
160 1 50       228 croak __PACKAGE__." error_code must be positive integer above 0" unless $attr{error_code} > 0;
161             }
162              
163             #
164             # checksum check result is just an attribute, doesn't lead to failure
165             #
166 21 100       56 if ($checksum) {
167 6         25 my $calced_checksum = $class->calc_checksum(%orig);
168 6 100       35002 unless($calced_checksum == $checksum) {
169 1         377 croak __PACKAGE__." invalid HiveJSO checksum, should be '".$calced_checksum."'";
170             };
171             }
172 20         672 return { %attr };
173             }
174              
175             sub calc_checksum {
176 10     10 0 54 my ( $class, %obj ) = @_;
177 22         82 my $checksum_string = join(',',map {
178 13         41 '"'.$_.'"', $class->_get_value_checksum($obj{$_})
179 22 50       143 } sort { $a cmp $b } grep {
180 10         29 $_ ne 'checksum' && $_ ne 'c'
181             } keys %obj);
182 10         60 return crc32($checksum_string);
183             }
184              
185             sub _get_value_checksum {
186 28     28   45 my ( $class, $value ) = @_;
187 28 100       67 if (ref $value eq 'ARRAY') {
188 6         17 return '['.join(',',map {
189 3         7 $class->_get_value_checksum($_)
190 3         5 } @{$value}).']';
191             }
192 25         116 return '"'.$value.'"';
193             }
194              
195             has hivejso => (
196             is => 'lazy',
197             init_arg => undef,
198             );
199              
200             sub _build_hivejso {
201 1     1   24213 my ( $self ) = @_;
202 1         7 return encode_json({
203 1         3 %{$self->hivejso_data},
204             checksum => $self->hivejso_checksum,
205             });
206             }
207              
208             has hivejso_short => (
209             is => 'lazy',
210             init_arg => undef,
211             );
212              
213             sub _build_hivejso_short {
214 1     1   3127 my ( $self ) = @_;
215 1         5 return encode_json({
216 1         3 %{$self->hivejso_data_short},
217             c => $self->hivejso_checksum_short,
218             });
219             }
220              
221             has hivejso_data => (
222             is => 'lazy',
223             init_arg => undef,
224             );
225              
226             sub _build_hivejso_data {
227 3     3   1577 my ( $self ) = @_;
228             return {
229 63 100       365 unit => $self->unit,
230             (map {
231 69 100 100     388 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
232             } grep {
233 3         24 $_ ne 'unit' && $_ ne 'checksum' && $_ ne 'c'
234             } @attributes),
235             };
236             }
237              
238             has hivejso_data_short => (
239             is => 'lazy',
240             init_arg => undef,
241             );
242              
243             sub _build_hivejso_data_short {
244 2     2   1078 my ( $self ) = @_;
245 42 100       211 my %short_data = (
246             u => $self->unit,
247             (map {
248 46 100 100     231 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
249             } grep {
250 2         12 $_ ne 'unit' && $_ ne 'checksum' && $_ ne 'c'
251             } @attributes),
252             );
253 2         20 for my $k (keys %short_attributes) {
254 42 50       87 if ($short_data{$short_attributes{$k}}) {
255 0         0 $short_data{$k} = delete $short_data{$short_attributes{$k}};
256             }
257             }
258 2         25 return { %short_data };
259             }
260              
261             has hivejso_checksum => (
262             is => 'lazy',
263             init_arg => undef,
264             );
265              
266             sub _build_hivejso_checksum {
267 2     2   2053 my ( $self ) = @_;
268 2         5 return $self->calc_checksum(%{$self->hivejso_data});
  2         24  
269             }
270              
271             has hivejso_checksum_short => (
272             is => 'lazy',
273             init_arg => undef,
274             );
275              
276             sub _build_hivejso_checksum_short {
277 1     1   462 my ( $self ) = @_;
278 1         4 return $self->calc_checksum(%{$self->hivejso_data_short});
  1         24  
279             }
280              
281             sub parse {
282 11     11 1 17058 my ( $class, $string ) = @_;
283 11         46 return $class->_parse(0,$string);
284             }
285              
286             sub parse_one {
287 3     3 1 4 my ( $class, $string ) = @_;
288 3         18 return $class->_parse(1,$string);
289             }
290              
291             sub parse_seek {
292 3     3 1 19030 my ( $class, $string ) = @_;
293 3         17 my @parsed = $class->parse_one($string);
294 3         5 my ( $obj, $post );
295 3 100       14 if (ref $parsed[0]) {
    100          
296 1         2 $obj = shift @parsed;
297 1         3 $post = shift @parsed;
298             } elsif (scalar @parsed == 1) {
299 1         3 $post = shift @parsed;
300             } else {
301 1         2 shift @parsed;
302 1         3 $obj = shift @parsed;
303 1         2 $post = shift @parsed;
304             }
305 3         58 return ( $obj, $post );
306             }
307              
308             sub _parse_first {
309 15     15   27 my ( $class, $string, $current ) = @_;
310 15 100       51 my $start = defined $current
311             ? index($string,'{',$current)
312             : index($string,'{');
313 15 100       77 return if $start == -1;
314 14         22 my $end = index($string,'}',$start);
315 14 100       43 return if $end == -1;
316 10         35 my $test = substr($string,$start,$end-$start+1);
317 10         21 my $another_start = index($test,'{',1);
318 10 100       65 if ($another_start == -1) {
319 9 100       67 my @result = (
320             $start == 0 ? "" : substr($string,0,$start),
321             substr($string,$start,$end-$start+1),
322             substr($string,$end+1),
323             );
324 9         42 return @result;
325             } else {
326 1 50 33     6 return if defined $current && $another_start == $current; # TODO
327 1         8 return $class->_parse_first($string,$another_start);
328             }
329             }
330              
331             sub _parse {
332 14     14   61 my ( $class, $one, $string ) = @_;
333 14         23 my @results;
334 14         41 my @parse_first = $class->_parse_first($string);
335 14 100       41 if (@parse_first) {
336 9         24 my ( $pre, $obj, $post ) = @parse_first;
337 9 100 66     57 push @results, $pre if $pre && length($pre);
338 9         11 my $object;
339 9         16 eval {
340 9         33 $object = $class->new_via_json($obj);
341             };
342 9 100       361 if ($@) {
343 2         61 $object = HiveJSO::Error->new(
344             garbage => $obj,
345             error => $@,
346             );
347             }
348 9         1439 push @results, $object;
349 9 100 66     72 push @results, ( $one ? $post : $class->parse($post) ) if $post && length($post);
    100          
350             } else {
351 5         12 push @results, $string;
352             }
353 14         56 return @results;
354             }
355              
356             sub add {
357 1     1 0 51 my ( $self, %newattr ) = @_;
358 1         5 my %newobj = (
359 1         2 %{$self->hivejso_data},
360             %newattr,
361             );
362 1 50       31 return $self->new(
363             %newobj,
364             $self->has_checksum ? ( checksum => $self->calc_checksum(%newobj) ) : (),
365             );
366             }
367              
368             sub add_short {
369 1     1 0 56 my ( $self, %newattr ) = @_;
370 1         7 my %newobj = (
371 1         2 %{$self->hivejso_data_short},
372             %newattr,
373             );
374 1 50       9 return $self->new(
375             %newobj,
376             $self->has_checksum ? ( c => $self->calc_checksum(%newobj) ) : (),
377             );
378             }
379              
380             1;
381              
382             __END__