File Coverage

blib/lib/GDPR/IAB/TCFv2/PublisherTC.pm
Criterion Covered Total %
statement 70 81 86.4
branch 12 26 46.1
condition 4 12 33.3
subroutine 17 17 100.0
pod 6 7 85.7
total 109 143 76.2


line stmt bran cond sub pod time code
1             package GDPR::IAB::TCFv2::PublisherTC;
2 5     5   39 use strict;
  5         11  
  5         209  
3 5     5   27 use warnings;
  5         38  
  5         381  
4              
5 5     5   31 use Carp qw;
  5         10  
  5         405  
6              
7 5         546 use GDPR::IAB::TCFv2::BitUtils qw
8             get_uint3
9             get_uint6
10 5     5   33 >;
  5         10  
11              
12             use constant {
13 5         8396 SEGMENT_TYPE_PUBLISHER_TC => 3,
14             MAX_PURPOSE_ID => 24,
15             OFFSETS => {
16             SEGMENT_TYPE => 0,
17             PURPOSE_CONSENT_ALLOWED => 3,
18             PURPOSE_LIT_ALLOWED => 27,
19             NUM_CUSTOM_PURPOSES => 51,
20             CUSTOM_PURPOSES_CONSENT => 57,
21             },
22 5     5   34 };
  5         11  
23              
24             sub Parse {
25 7     7 0 30 my ( $klass, %args ) = @_;
26              
27 7 50       24 croak "missing 'data'" unless defined $args{data};
28 7 50       46 croak "missing 'data_size'" unless defined $args{data_size};
29              
30 7 50       31 croak "missing 'options'" unless defined $args{options};
31 7 50       21 croak "missing 'options.json'" unless defined $args{options}->{json};
32              
33 7         25 my $data = $args{data};
34 7         11 my $data_size = $args{data_size};
35 7         14 my $options = $args{options};
36              
37 7 50       20 croak "invalid min size" if $data_size < 57;
38              
39 7         27 my $segment_type = get_uint3( $data, OFFSETS->{SEGMENT_TYPE} );
40              
41 7 50       21 croak
42 0         0 "invalid segment type ${segment_type}: expected @{[ SEGMENT_TYPE_PUBLISHER_TC ]}"
43             if $segment_type != SEGMENT_TYPE_PUBLISHER_TC;
44              
45             my $num_custom_purposes =
46 7         25 get_uint6( $data, OFFSETS->{NUM_CUSTOM_PURPOSES} );
47              
48 7         19 my $total_expected_size = 2 * $num_custom_purposes + 57;
49              
50 7 50       34 croak "invalid size" if $data_size < $total_expected_size;
51              
52             my $self = {
53             data => $data,
54             options => $options,
55             num_custom_purposes => $num_custom_purposes,
56             custom_purpose_lit_offset => OFFSETS->{CUSTOM_PURPOSES_CONSENT}
57 7         39 + $num_custom_purposes,
58             };
59              
60 7         12 bless $self, $klass;
61              
62 7         37 return $self;
63             }
64              
65             sub num_custom_purposes {
66 2     2 1 944 my $self = shift;
67              
68 2         14 return $self->{num_custom_purposes};
69             }
70              
71             sub is_purpose_consent_allowed {
72 48     48 1 49646 my ( $self, $id ) = @_;
73              
74 48 50 33     248 croak "invalid purpose id $id: must be between 1 and @{[ MAX_PURPOSE_ID ]}"
  0         0  
75             if $id < 1 || $id > MAX_PURPOSE_ID;
76              
77 48         112 return $self->_safe_is_purpose_consent_allowed($id);
78             }
79              
80             sub is_purpose_legitimate_interest_allowed {
81 48     48 1 38611 my ( $self, $id ) = @_;
82              
83 48 50 33     219 croak "invalid purpose id $id: must be between 1 and @{[ MAX_PURPOSE_ID ]}"
  0         0  
84             if $id < 1 || $id > MAX_PURPOSE_ID;
85              
86 48         123 return $self->_safe_is_purpose_legitimate_interest_allowed($id);
87             }
88              
89             sub is_custom_purpose_consent_allowed {
90 2     2 1 1617 my ( $self, $id ) = @_;
91              
92             croak
93 0         0 "invalid custom purpose id $id: must be between 1 and @{[ $self->{num_custom_purposes} ]}"
94 2 50 33     13 if $id < 1 || $id > $self->{num_custom_purposes};
95              
96 2         6 return $self->_safe_is_custom_purpose_consent_allowed($id);
97             }
98              
99             sub is_custom_purpose_legitimate_interest_allowed {
100 2     2 1 1633 my ( $self, $id ) = @_;
101              
102             croak
103 0         0 "invalid custom purpose id $id: must be between 1 and @{[ $self->{num_custom_purposes} ]}"
104 2 50 33     14 if $id < 1 || $id > $self->{num_custom_purposes};
105              
106 2         6 return $self->_safe_is_custom_purpose_legitimate_interest_allowed($id);
107             }
108              
109             sub TO_JSON {
110 4     4 1 5 my $self = shift;
111              
112 4         11 my %consents = map { $_ => $self->_safe_is_purpose_consent_allowed($_) }
  96         132  
113             1 .. MAX_PURPOSE_ID;
114             my %legitimate_interests =
115 4         14 map { $_ => $self->_safe_is_purpose_legitimate_interest_allowed($_) }
  96         167  
116             1 .. MAX_PURPOSE_ID;
117             my %custom_purpose_consents =
118 4         13 map { $_ => $self->_safe_is_custom_purpose_consent_allowed($_) }
119 4         27 1 .. $self->{num_custom_purposes};
120             my %custom_purpose_legitimate_interests = map {
121 4         7 $_ => $self->_safe_is_custom_purpose_legitimate_interest_allowed($_)
122 4         13 } 1 .. $self->{num_custom_purposes};
123              
124             return {
125             consents =>
126             $self->_format_json_subsection( \%consents, MAX_PURPOSE_ID ),
127             legitimate_interests => $self->_format_json_subsection(
128             \%legitimate_interests, MAX_PURPOSE_ID
129             ),
130             custom_purposes => {
131             consents => $self->_format_json_subsection(
132             \%custom_purpose_consents, $self->{num_custom_purposes}
133             ),
134             legitimate_interests => $self->_format_json_subsection(
135             \%custom_purpose_legitimate_interests,
136             $self->{num_custom_purposes}
137 4         16 ),
138             },
139             };
140             }
141              
142             sub _format_json_subsection {
143 16     16   22 my ( $self, $data, $max ) = @_;
144              
145 16         22 my ( $false, $true ) = @{ $self->{options}->{json}->{boolean_values} };
  16         30  
146              
147 16 50       35 if ( !!$self->{options}->{json}->{compact} ) {
148             return [
149 16         59 grep { $data->{$_} } 1 .. $max,
  200         373  
150             ];
151             }
152              
153 0         0 my $verbose = !!$self->{options}->{json}->{verbose};
154              
155 0 0       0 return $data if $verbose;
156              
157 0         0 return { map { $_ => $true } grep { $data->{$_} } keys %{$data} };
  0         0  
  0         0  
  0         0  
158             }
159              
160             sub _safe_is_purpose_consent_allowed {
161 144     144   197 my ( $self, $id ) = @_;
162             return
163             scalar(
164 144         403 is_set( $self->{data}, OFFSETS->{PURPOSE_CONSENT_ALLOWED} + $id - 1 )
165             );
166             }
167              
168             sub _safe_is_purpose_legitimate_interest_allowed {
169 144     144   207 my ( $self, $id ) = @_;
170              
171             return
172             scalar(
173 144         362 is_set( $self->{data}, OFFSETS->{PURPOSE_LIT_ALLOWED} + $id - 1 ) );
174             }
175              
176             sub _safe_is_custom_purpose_consent_allowed {
177 6     6   10 my ( $self, $id ) = @_;
178             return
179             scalar(
180 6         21 is_set( $self->{data}, OFFSETS->{CUSTOM_PURPOSES_CONSENT} + $id - 1 )
181             );
182             }
183              
184             sub _safe_is_custom_purpose_legitimate_interest_allowed {
185 6     6   11 my ( $self, $id ) = @_;
186              
187             return
188             scalar(
189 6         18 is_set( $self->{data}, $self->{custom_purpose_lit_offset} + $id - 1 )
190             );
191             }
192              
193             1;
194             __END__