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 30     30   195 use strict;
  30         51  
  30         5369  
6 30     30   2033 use warnings;
  30         3939  
  30         5587  
7             our $VERSION = '2.20260216'; # VERSION
8 30     30   169 use Carp;
  30         2299  
  30         10552  
9              
10 30     30   23670 use Mail::AuthenticationResults::Token::String;
  30         80  
  30         5499  
11 30     30   11988 use Mail::AuthenticationResults::Token::Space;
  30         101  
  30         1035  
12 30     30   13662 use Mail::AuthenticationResults::Token::Separator;
  30         83  
  30         1023  
13 30     30   13367 use Mail::AuthenticationResults::Token::Comment;
  30         90  
  30         1109  
14 30     30   13116 use Mail::AuthenticationResults::Token::Assignment;
  30         137  
  30         41928  
15              
16              
17             sub new {
18 109     109 1 327 my ( $class, $args ) = @_;
19              
20 109         187 my $self = {};
21 109         220 bless $self, $class;
22              
23 109         345 $self->{ 'string' } = [];
24              
25 109         374 return $self;
26             }
27              
28              
29             sub eol {
30 109     109 1 218 my ( $self ) = @_;
31 109 100       297 return $self->{ 'eol' } if exists ( $self->{ 'eol' } );
32 76         116 return "\n";
33             }
34              
35              
36             sub set_eol {
37 33     33 1 380 my ( $self, $eol ) = @_;
38 33         81 $self->{ 'eol' } = $eol;
39 33         72 return $self;
40             }
41              
42              
43             sub indent {
44 109     109 1 176 my ( $self ) = @_;
45 109 100       324 return $self->{ 'indent' } if exists ( $self->{ 'indent' } );
46 76         143 return ' ';
47             }
48              
49              
50             sub set_indent {
51 33     33 1 90 my ( $self, $indent ) = @_;
52 33         108 $self->{ 'indent' } = $indent;
53 33         65 return $self;
54             }
55              
56              
57             sub sub_indent {
58 109     109 1 244 my ( $self ) = @_;
59 109 100       319 return $self->{ 'sub_indent' } if exists ( $self->{ 'sub_indent' } );
60 76         121 return ' ';
61             }
62              
63              
64             sub set_sub_indent {
65 33     33 1 92 my ( $self, $indent ) = @_;
66 33         139 $self->{ 'sub_indent' } = $indent;
67 33         94 return $self;
68             }
69              
70              
71             sub try_fold_at {
72 490     490 1 787 my ( $self ) = @_;
73 490 100       1111 return $self->{ 'try_fold_at' } if exists ( $self->{ 'try_fold_at' } );
74 403         905 return 800;
75             }
76              
77              
78             sub set_try_fold_at {
79 3     3 1 7 my ( $self, $length ) = @_;
80 3         9 $self->{ 'try_fold_at' } = $length;
81 3         7 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 589     589 1 1117 my( $self, $string ) = @_;
101 589         928 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::String->new_from_value( $string );
  589         1912  
102 589         1146 return $self;
103             }
104              
105              
106             sub space {
107 441     441 1 909 my ( $self, $string ) = @_;
108 441         628 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Space->new_from_value( $string );
  441         1432  
109 441         844 return $self;
110             }
111              
112              
113             sub separator {
114 117     117 1 239 my ( $self, $string ) = @_;
115 117         182 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Separator->new_from_value( $string );
  117         490  
116 117         219 return $self;
117             }
118              
119              
120             sub comment {
121 91     91 1 180 my ( $self, $string ) = @_;
122 91         145 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Comment->new_from_value( $string );
  91         384  
123 91         196 return $self;
124             }
125              
126              
127             sub assignment {
128 259     259 1 495 my ( $self, $string ) = @_;
129 259         408 push @{ $self->{ 'string' } }, Mail::AuthenticationResults::Token::Assignment->new_from_value( $string );
  259         973  
130 259         461 return $self;
131             }
132              
133              
134             sub as_string {
135 109     109 1 183 my ( $self ) = @_;
136              
137 109         173 my $string = q{};
138 109         139 my $string_length = 0;
139 109         164 my $content_added = 0;
140              
141 109         155 my $sections = [];
142 109         141 my $stack = [];
143 109         207 my $last_type;
144              
145 109         175 foreach my $part ( @{ $self->{ 'string' } } ) {
  109         310  
146 1497 100 100     3369 if ( $part->is() eq 'space' && $last_type ne 'space' ) {
147             # We have a folding space
148 324 50       916 push @$sections, $stack if @$stack;
149 324         643 $stack = [];
150             }
151 1497         2588 push @$stack, $part;
152 1497         2852 $last_type = $part->is();
153             }
154 109 100       272 push @$sections, $stack if @$stack;
155              
156 109         293 my $eol = $self->eol();;
157 109         317 my $indent = $self->indent();
158 109         305 my $sub_indent = $self->sub_indent();
159              
160 109         174 my $fold_length = 0;
161             SECTION:
162 109         368 while ( my $section = shift @$sections ) {
163 454 100 100     1152 if ( $section->[0]->is() eq 'space' && $section->[0]->value() eq $eol ) {
164             # This section starts a new line
165 149         235 $fold_length = 0;
166 149 100       452 if ( ! exists( $section->[0]->{ '_folded' } ) ) {
167 122 100       278 if ( $section->[1]->is() eq 'space' ) {
168             # Take the last indent value for the fold indent
169 117         253 $indent = $section->[1]->value();
170             }
171             }
172             }
173              
174 454         981 my $section_string = join( q{}, map { $_->value() } @$section );
  1614         3209  
175 454         899 my $section_length = length( $section_string );
176              
177 454 100       1044 if ( $fold_length + $section_length > $self->try_fold_at() ) {
178 21 100       46 if ( $fold_length > 0 ) {
179             # Remove whitespace tokens at beginning of section
180 15         42 while ( $section->[0]->is() eq 'space' ) {
181 15         40 shift @$section;
182             }
183             # Insert new folding whitespace at beginning of section
184 15         76 unshift @$section, Mail::AuthenticationResults::Token::Space->new_from_value( $indent . $sub_indent );
185 15         40 unshift @$section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
186 15         35 $section->[0]->{ '_folded' } = 1;
187 15         25 unshift @$sections, $section;
188 15         124 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         13 my $first_section = [];
198 6         7 my $second_section = [];
199 6         15 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $eol );
200 6         21 push @$second_section, Mail::AuthenticationResults::Token::Space->new_from_value( $indent . $sub_indent );
201 6         14 $second_section->[0]->{ '_folded' } = 1;
202 6         11 my $first_section_length = 0;
203 6         13 foreach my $part ( @$section ) {
204 30         62 my $part_length = length $part->value();
205 30 100       81 if ( $part_length + $first_section_length < $self->try_fold_at() ) {
206 24         39 push @$first_section, $part;
207 24         41 $first_section_length += $part_length;
208             }
209             else {
210 6         10 push @$second_section, $part;
211 6         14 $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         54  
216 18         35 ( grep { $_->is() ne 'space' } @$second_section ) ) {
217 6         26 unshift @$sections, $second_section;
218 6         11 unshift @$sections, $first_section;
219 6         23 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 433         932 $string .= $section_string;
231 433         1422 $fold_length += $section_length;
232             }
233              
234 109         1814 return $string;
235             }
236              
237             1;
238              
239             __END__