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 29     29   199 use strict;
  29         99  
  29         891  
6 29     29   178 use warnings;
  29         86  
  29         1135  
7             our $VERSION = '2.20230112'; # VERSION
8 29     29   196 use Carp;
  29         60  
  29         1503  
9              
10 29     29   12005 use Mail::AuthenticationResults::Token::String;
  29         82  
  29         931  
11 29     29   10833 use Mail::AuthenticationResults::Token::Space;
  29         72  
  29         849  
12 29     29   11046 use Mail::AuthenticationResults::Token::Separator;
  29         81  
  29         838  
13 29     29   11112 use Mail::AuthenticationResults::Token::Comment;
  29         75  
  29         810  
14 29     29   10886 use Mail::AuthenticationResults::Token::Assignment;
  29         73  
  29         39362  
15              
16              
17             sub new {
18 104     104 1 236 my ( $class, $args ) = @_;
19              
20 104         186 my $self = {};
21 104         192 bless $self, $class;
22              
23 104         312 $self->{ 'string' } = [];
24              
25 104         252 return $self;
26             }
27              
28              
29             sub eol {
30 104     104 1 195 my ( $self ) = @_;
31 104 100       318 return $self->{ 'eol' } if exists ( $self->{ 'eol' } );
32 74         126 return "\n";
33             }
34              
35              
36             sub set_eol {
37 30     30 1 77 my ( $self, $eol ) = @_;
38 30         94 $self->{ 'eol' } = $eol;
39 30         52 return $self;
40             }
41              
42              
43             sub indent {
44 104     104 1 202 my ( $self ) = @_;
45 104 100       266 return $self->{ 'indent' } if exists ( $self->{ 'indent' } );
46 74         114 return ' ';
47             }
48              
49              
50             sub set_indent {
51 30     30 1 69 my ( $self, $indent ) = @_;
52 30         70 $self->{ 'indent' } = $indent;
53 30         61 return $self;
54             }
55              
56              
57             sub sub_indent {
58 104     104 1 188 my ( $self ) = @_;
59 104 100       246 return $self->{ 'sub_indent' } if exists ( $self->{ 'sub_indent' } );
60 74         117 return ' ';
61             }
62              
63              
64             sub set_sub_indent {
65 30     30 1 65 my ( $self, $indent ) = @_;
66 30         71 $self->{ 'sub_indent' } = $indent;
67 30         55 return $self;
68             }
69              
70              
71             sub try_fold_at {
72 453     453 1 647 my ( $self ) = @_;
73 453 100       930 return $self->{ 'try_fold_at' } if exists ( $self->{ 'try_fold_at' } );
74 366         692 return 800;
75             }
76              
77              
78             sub set_try_fold_at {
79 3     3 1 7 my ( $self, $length ) = @_;
80 3         5 $self->{ 'try_fold_at' } = $length;
81 3         6 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 534     534 1 860 my( $self, $string ) = @_;
101 534         654 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::String->new_from_value( $string );
  534         1281  
102 534         896 return $self;
103             }
104              
105              
106             sub space {
107 381     381 1 675 my ( $self, $string ) = @_;
108 381         442 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Space->new_from_value( $string );
  381         1021  
109 381         602 return $self;
110             }
111              
112              
113             sub separator {
114 101     101 1 194 my ( $self, $string ) = @_;
115 101         134 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Separator->new_from_value( $string );
  101         340  
116 101         169 return $self;
117             }
118              
119              
120             sub comment {
121 83     83 1 188 my ( $self, $string ) = @_;
122 83         121 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Comment->new_from_value( $string );
  83         247  
123 83         144 return $self;
124             }
125              
126              
127             sub assignment {
128 233     233 1 407 my ( $self, $string ) = @_;
129 233         293 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Assignment->new_from_value( $string );
  233         992  
130 233         387 return $self;
131             }
132              
133              
134             sub as_string {
135 104     104 1 200 my ( $self ) = @_;
136              
137 104         163 my $string = q{};
138 104         152 my $string_length = 0;
139 104         146 my $content_added = 0;
140              
141 104         164 my $sections = [];
142 104         174 my $stack = [];
143 104         133 my $last_type;
144              
145 104         143 foreach my $part ( @{ $self->{ 'string' } } ) {
  104         238  
146 1332 100 100     2431 if ( $part->is() eq 'space' && $last_type ne 'space' ) {
147             # We have a folding space
148 292 50       609 push @$sections, $stack if @$stack;
149 292         389 $stack = [];
150             }
151 1332         1933 push @$stack, $part;
152 1332         2132 $last_type = $part->is();
153             }
154 104 100       317 push @$sections, $stack if @$stack;
155              
156 104         335 my $eol = $self->eol();;
157 104         228 my $indent = $self->indent();
158 104         260 my $sub_indent = $self->sub_indent();
159              
160 104         177 my $fold_length = 0;
161             SECTION:
162 104         291 while ( my $section = shift @$sections ) {
163 417 100 100     833 if ( $section->[0]->is() eq 'space' && $section->[0]->value() eq $eol ) {
164             # This section starts a new line
165 121         159 $fold_length = 0;
166 121 100       245 if ( ! exists( $section->[0]->{ '_folded' } ) ) {
167 94 100       191 if ( $section->[1]->is() eq 'space' ) {
168             # Take the last indent value for the fold indent
169 89         198 $indent = $section->[1]->value();
170             }
171             }
172             }
173              
174 417         768 my $section_string = join( q{}, map { $_->value() } @$section );
  1449         2359  
175 417         706 my $section_length = length( $section_string );
176              
177 417 100       812 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         28 while ( $section->[0]->is() eq 'space' ) {
181 15         34 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         33 unshift @$section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
186 15         29 $section->[0]->{ '_folded' } = 1;
187 15         20 unshift @$sections, $section;
188 15         38 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         41 my $first_section = [];
198 6         9 my $second_section = [];
199 6         16 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
200 6         25 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $indent . $sub_indent );
201 6         9 $second_section->[0]->{ '_folded' } = 1;
202 6         22 my $first_section_length = 0;
203 6         17 foreach my $part ( @$section ) {
204 30         47 my $part_length = length $part->value();
205 30 100       54 if ( $part_length + $first_section_length < $self->try_fold_at() ) {
206 24         38 push @$first_section, $part;
207 24         31 $first_section_length += $part_length;
208             }
209             else {
210 6         12 push @$second_section, $part;
211 6         15 $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     13 if ( ( grep { $_->is() ne 'space' } @$first_section ) &&
  24         42  
216 18         32 ( grep { $_->is() ne 'space' } @$second_section ) ) {
217 6         13 unshift @$sections, $second_section;
218 6         8 unshift @$sections, $first_section;
219 6         17 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 396         672 $string .= $section_string;
231 396         943 $fold_length += $section_length;
232             }
233              
234 104         1383 return $string;
235             }
236              
237             1;
238              
239             __END__