File Coverage

lib/Mail/AuthenticationResults/FoldableHeader.pm
Criterion Covered Total %
statement 132 138 95.6
branch 26 30 86.6
condition 7 9 77.7
subroutine 23 25 92.0
pod 17 17 100.0
total 205 219 93.6


line stmt bran cond sub pod time code
1             package Mail::AuthenticationResults::FoldableHeader;
2             # ABSTRACT: Class for modelling a foldable header string
3              
4             require 5.008;
5 28     28   177 use strict;
  28         88  
  28         796  
6 28     28   154 use warnings;
  28         52  
  28         1020  
7             our $VERSION = '2.20210915'; # VERSION
8 28     28   144 use Carp;
  28         399  
  28         1375  
9              
10 28     28   9779 use Mail::AuthenticationResults::Token::String;
  28         74  
  28         934  
11 28     28   10629 use Mail::AuthenticationResults::Token::Space;
  28         65  
  28         725  
12 28     28   9748 use Mail::AuthenticationResults::Token::Separator;
  28         69  
  28         1339  
13 28     28   9757 use Mail::AuthenticationResults::Token::Comment;
  28         67  
  28         864  
14 28     28   9791 use Mail::AuthenticationResults::Token::Assignment;
  28         64  
  28         36004  
15              
16              
17             sub new {
18 101     101 1 239 my ( $class, $args ) = @_;
19              
20 101         186 my $self = {};
21 101         196 bless $self, $class;
22              
23 101         285 $self->{ 'string' } = [];
24              
25 101         228 return $self;
26             }
27              
28              
29             sub eol {
30 101     101 1 177 my ( $self ) = @_;
31 101 100       255 return $self->{ 'eol' } if exists ( $self->{ 'eol' } );
32 72         132 return "\n";
33             }
34              
35              
36             sub set_eol {
37 29     29 1 62 my ( $self, $eol ) = @_;
38 29         112 $self->{ 'eol' } = $eol;
39 29         58 return $self;
40             }
41              
42              
43             sub indent {
44 101     101 1 171 my ( $self ) = @_;
45 101 100       286 return $self->{ 'indent' } if exists ( $self->{ 'indent' } );
46 72         135 return ' ';
47             }
48              
49              
50             sub set_indent {
51 29     29 1 69 my ( $self, $indent ) = @_;
52 29         70 $self->{ 'indent' } = $indent;
53 29         49 return $self;
54             }
55              
56              
57             sub sub_indent {
58 101     101 1 176 my ( $self ) = @_;
59 101 100       246 return $self->{ 'sub_indent' } if exists ( $self->{ 'sub_indent' } );
60 72         103 return ' ';
61             }
62              
63              
64             sub set_sub_indent {
65 29     29 1 66 my ( $self, $indent ) = @_;
66 29         65 $self->{ 'sub_indent' } = $indent;
67 29         63 return $self;
68             }
69              
70              
71             sub try_fold_at {
72 418     418 1 631 my ( $self ) = @_;
73 418 100       870 return $self->{ 'try_fold_at' } if exists ( $self->{ 'try_fold_at' } );
74 331         642 return 800;
75             }
76              
77              
78             sub set_try_fold_at {
79 3     3 1 6 my ( $self, $length ) = @_;
80 3         6 $self->{ 'try_fold_at' } = $length;
81 3         4 return $self;
82             }
83              
84              
85             sub force_fold_at {
86 0     0 1 0 my ( $self ) = @_;
87 0 0       0 return $self->{ 'force_fold_at' } if exists ( $self->{ 'force_fold_at' } );
88 0         0 return 900;
89             }
90              
91              
92             sub set_force_fold_at {
93 0     0 1 0 my ( $self, $length ) = @_;
94 0         0 $self->{ 'force_fold_at' } = $length;
95 0         0 return $self;
96             }
97              
98              
99             sub string {
100 485     485 1 824 my( $self, $string ) = @_;
101 485         656 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::String->new_from_value( $string );
  485         1188  
102 485         831 return $self;
103             }
104              
105              
106             sub space {
107 341     341 1 560 my ( $self, $string ) = @_;
108 341         441 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Space->new_from_value( $string );
  341         950  
109 341         551 return $self;
110             }
111              
112              
113             sub separator {
114 91     91 1 170 my ( $self, $string ) = @_;
115 91         124 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Separator->new_from_value( $string );
  91         281  
116 91         165 return $self;
117             }
118              
119              
120             sub comment {
121 73     73 1 137 my ( $self, $string ) = @_;
122 73         102 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Comment->new_from_value( $string );
  73         237  
123 73         131 return $self;
124             }
125              
126              
127             sub assignment {
128 209     209 1 378 my ( $self, $string ) = @_;
129 209         264 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Assignment->new_from_value( $string );
  209         573  
130 209         364 return $self;
131             }
132              
133              
134             sub as_string {
135 101     101 1 193 my ( $self ) = @_;
136              
137 101         166 my $string = q{};
138 101         149 my $string_length = 0;
139 101         137 my $content_added = 0;
140              
141 101         157 my $sections = [];
142 101         164 my $stack = [];
143 101         149 my $last_type;
144              
145 101         128 foreach my $part ( @{ $self->{ 'string' } } ) {
  101         205  
146 1199 100 100     2273 if ( $part->is() eq 'space' && $last_type ne 'space' ) {
147             # We have a folding space
148 260 50       552 push @$sections, $stack if @$stack;
149 260         372 $stack = [];
150             }
151 1199         1686 push @$stack, $part;
152 1199         1997 $last_type = $part->is();
153             }
154 101 100       306 push @$sections, $stack if @$stack;
155              
156 101         237 my $eol = $self->eol();;
157 101         232 my $indent = $self->indent();
158 101         233 my $sub_indent = $self->sub_indent();
159              
160 101         168 my $fold_length = 0;
161             SECTION:
162 101         275 while ( my $section = shift @$sections ) {
163 382 100 100     781 if ( $section->[0]->is() eq 'space' && $section->[0]->value() eq $eol ) {
164             # This section starts a new line
165 111         164 $fold_length = 0;
166 111 100       254 if ( ! exists( $section->[0]->{ '_folded' } ) ) {
167 84 100       181 if ( $section->[1]->is() eq 'space' ) {
168             # Take the last indent value for the fold indent
169 81         158 $indent = $section->[1]->value();
170             }
171             }
172             }
173              
174 382         753 my $section_string = join( q{}, map { $_->value() } @$section );
  1316         2353  
175 382         662 my $section_length = length( $section_string );
176              
177 382 100       759 if ( $fold_length + $section_length > $self->try_fold_at() ) {
178 21 100       40 if ( $fold_length > 0 ) {
179             # Remove whitespace tokens at beginning of section
180 15         25 while ( $section->[0]->is() eq 'space' ) {
181 15         35 shift @$section;
182             }
183             # Insert new folding whitespace at beginning of section
184 15         40 unshift @$section, Mail::AuthenticationResults::Token::Space->new_from_value( $indent . $sub_indent );
185 15         29 unshift @$section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
186 15         25 $section->[0]->{ '_folded' } = 1;
187 15         22 unshift @$sections, $section;
188 15         34 next SECTION;
189             }
190             else {
191             # ToDo:
192             # This section alone is over the line limit
193             # It already starts with a fold, so we need to remove
194             # some of it to a new line if we can.
195              
196             # Strategy 1: Fold at a relevant token boundary
197 6         10 my $first_section = [];
198 6         8 my $second_section = [];
199 6         13 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
200 6         17 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $indent . $sub_indent );
201 6         8 $second_section->[0]->{ '_folded' } = 1;
202 6         9 my $first_section_length = 0;
203 6         12 foreach my $part ( @$section ) {
204 30         48 my $part_length = length $part->value();
205 30 100       49 if ( $part_length + $first_section_length < $self->try_fold_at() ) {
206 24         32 push @$first_section, $part;
207 24         33 $first_section_length += $part_length;
208             }
209             else {
210 6         9 push @$second_section, $part;
211 6         11 $first_section_length = $self->try_fold_at() + 1; # everything from this point goes onto second
212             }
213             }
214             # Do we have a first and second section with actual content?
215 6 50 33     11 if ( ( grep { $_->is() ne 'space' } @$first_section ) &&
  24         41  
216 18         31 ( grep { $_->is() ne 'space' } @$second_section ) ) {
217 6         12 unshift @$sections, $second_section;
218 6         8 unshift @$sections, $first_section;
219 6         16 next SECTION;
220             }
221              
222             # We MUST fold at $self->force_fold_at();
223             # Strategy 2: Force fold at a space within a string
224             # Strategy 3: Force fold anywhere
225              
226             # We assume that force fold is greater than try fold
227             }
228             }
229              
230 361         670 $string .= $section_string;
231 361         923 $fold_length += $section_length;
232             }
233              
234 101         1214 return $string;
235             }
236              
237             1;
238              
239             __END__