File Coverage

blib/lib/HTTP/Headers/Fancy.pm
Criterion Covered Total %
statement 121 128 94.5
branch 59 70 84.2
condition 10 14 71.4
subroutine 19 20 95.0
pod 14 14 100.0
total 223 246 90.6


line stmt bran cond sub pod time code
1 14     14   303954 use strictures 2;
  14         21517  
  14         617  
2              
3             package HTTP::Headers::Fancy;
4              
5 14     14   2994 use Exporter qw(import);
  14         25  
  14         471  
6 14     14   64 use Scalar::Util qw(blessed);
  14         25  
  14         15221  
7              
8             # ABSTRACT: Fancy naming schema of HTTP headers
9              
10             our $VERSION = '1.000'; # VERSION
11              
12             our @EXPORT_OK = qw(
13             decode_key
14             encode_key
15             decode_hash
16             encode_hash
17             split_field_hash
18             split_field_list
19             build_field_hash
20             build_field_list
21             );
22              
23             our %EXPORT_TAGS = ( all => \@EXPORT_OK, );
24              
25             sub _self {
26 189     189   424 my @args = @_;
27 189 100 66     1628 if ( blessed $args[0] and $args[0]->isa(__PACKAGE__) ) {
    100 100        
      100        
28 38         121 return @args;
29             }
30             elsif ( defined $args[0] and not ref $args[0] and $args[0] eq __PACKAGE__ )
31             {
32 18         57 return @args;
33             }
34             else {
35 133         354 return ( __PACKAGE__, @args );
36             }
37             }
38              
39             sub _encode_hash_deeply {
40 6     6   17 my ( $self, @args ) = _self(@_);
41 6 100       20 if ( ref $args[0] ) {
42 2         4 return { $self->_encode_hash_deeply( %{ $args[0] } ) };
  2         19  
43             }
44 4         13 my %hash = $self->encode_hash(@args);
45 4         15 foreach my $key ( keys %hash ) {
46 8 100       20 if ( ref $hash{$key} ) {
47 4         11 $hash{$key} = $self->build( $hash{$key} );
48             }
49             }
50 4         38 return %hash;
51             }
52              
53             sub decode_key {
54 50     50 1 92 my ( $self, $k ) = _self(@_);
55 50         185 $k =~ s{^([^-]+)}{ucfirst(lc($1))}se;
  50         137  
56 50         90 $k =~ s{-+([^-]+)}{ucfirst(lc($1))}sge;
  28         63  
57 50         81 $k =~ s{^X([A-Z])}{-$1}s;
58 50         257 return ucfirst($k);
59             }
60              
61             sub decode_hash {
62 2     2 1 27 my ( $self, @args ) = _self(@_);
63 2 100       8 my %headers = @args == 1 ? %{ $args[0] } : @args;
  1         5  
64 2         7 foreach my $old ( keys %headers ) {
65 14         24 my $new = decode_key($old);
66 14 100       29 if ( $old ne $new ) {
67 12         30 $headers{$new} = delete $headers{$old};
68             }
69             }
70 2 100       21 wantarray ? %headers : \%headers;
71             }
72              
73             sub encode_key {
74 64     64 1 129 my ( $self, $k ) = _self(@_);
75 64         128 $k =~ s{_}{-}sg;
76 64         114 $k =~ s{-+}{-}sg;
77 64         141 $k =~ s{^-(.)}{'X'.uc($1)}se;
  6         19  
78 64         360 $k =~ s{([^-])([A-Z])}{$1-$2}s while $k =~ m{([^-])([A-Z])}s;
79 64         308 return lc($k);
80             }
81              
82             sub encode_hash {
83 6     6 1 50 my ( $self, @args ) = _self(@_);
84 6 100       26 my %headers = @args == 1 ? %{ $args[0] } : @args;
  1         7  
85 6         20 foreach my $old ( keys %headers ) {
86 34 50       72 delete $headers{$old} unless defined $headers{$old};
87 34         54 my $new = encode_key($old);
88 34 100       73 if ( $old ne $new ) {
89 30         72 $headers{$new} = delete $headers{$old};
90             }
91             }
92 6 100       68 wantarray ? %headers : \%headers;
93             }
94              
95             sub split_field_hash {
96 20     20 1 53 my ( $self, $value, @rest ) = _self(@_);
97 20 100       63 return () unless defined $value;
98 18 100       41 if ( ref $value eq 'HASH' ) {
99 1         2 foreach my $key (@rest) {
100 2         14 $value->{$key} = { $self->split_field_hash( $value->{$key} ) };
101             }
102 1         4 return $value;
103             }
104 17         39 pos($value) = 0;
105 17         26 my %data;
106 17         27 $value .= ',';
107 17         123 while ( $value =~
108             m{ \G \s* (? [^=,]+? ) \s* (?: \s* = \s* (?: (?: " (? [^"]*? ) " ) | (? [^,]*? ) ) )? \s* ,+ \s* }gsx
109             )
110             {
111 14     14   10632 $data{ decode_key( $+{key} ) } = $+{value};
  14         6394  
  14         15812  
  29         186  
112             }
113 17         124 return %data;
114             }
115              
116             sub split_field_list {
117 11     11 1 31 my ( $self, $value, @rest ) = _self(@_);
118 11 50       27 return () unless defined $value;
119 11 100       27 if ( ref $value eq 'HASH' ) {
120 1         2 foreach my $key (@rest) {
121 2         18 $value->{$key} = [ $self->split_field_list( $value->{$key} ) ];
122             }
123 1         5 return $value;
124             }
125 10         26 pos($value) = 0;
126 10         17 my @data;
127 10         17 $value .= ',';
128 10         67 while ( $value =~
129             m{ \G \s* (? W/ )? " (? [^"]*? ) " \s* ,+ \s* }gsix )
130             {
131 17         89 my $value = $+{value};
132 17 100       134 push @data => $+{weak} ? \$value : $value;
133             }
134 10         59 return @data;
135             }
136              
137             sub build_field_hash {
138 15     15 1 41 my ( $self, @args ) = _self(@_);
139 15 100       44 if ( ref $args[0] eq 'HASH' ) {
140 4         7 return $self->build_field_hash( %{ $args[0] } );
  4         31  
141             }
142 11         26 my %data = @args;
143             return join ', ', sort map {
144 11         33 $self->encode_key($_)
145             . (
146             defined( $data{$_} )
147             ? '='
148             . (
149             ( $data{$_} =~ m{[=,]} )
150             ? '"' . $data{$_} . '"'
151 17 100       68 : $data{$_}
    100          
152             )
153             : ''
154             )
155             } keys %data;
156             }
157              
158             sub build_field_list {
159 15     15 1 49 my ( $self, @args ) = _self(@_);
160 15 100       57 if ( ref $args[0] eq 'ARRAY' ) {
161 6         13 return $self->build_field_list( @{ $args[0] } );
  6         35  
162             }
163 9 100       21 return join ', ', map { ref($_) ? 'W/"' . $$_ . '"' : qq{"$_"} } @args;
  25         140  
164             }
165              
166             sub new {
167 6   50 6 1 93 my $class = shift // __PACKAGE__;
168 6   33     53 return bless {@_} => ref $class || $class;
169             }
170              
171             sub encode {
172 6     6 1 17 my $self = shift;
173 6 100       31 return unless @_;
174 4 100       16 if ( @_ > 1 ) {
    50          
175 2         10 return $self->_encode_hash_deeply(@_);
176             }
177             elsif ( ref $_[0] eq 'HASH' ) {
178 2         7 return $self->_encode_hash_deeply( $_[0] );
179             }
180             else {
181 0         0 return $self->encode_key( $_[0] );
182             }
183             }
184              
185             sub decode {
186 0     0 1 0 my $self = shift;
187 0 0       0 return unless @_;
188 0 0       0 if ( @_ > 1 ) {
    0          
189 0         0 return $self->decode_hash(@_);
190             }
191             elsif ( ref $_[0] eq 'HASH' ) {
192 0         0 return $self->decode_hash( $_[0] );
193             }
194             else {
195 0         0 return $self->decode_key( $_[0] );
196             }
197             }
198              
199             sub split {
200 5     5 1 11 my $self = shift;
201 5 100       21 return unless @_;
202 4         5 my $val = shift;
203 4 100       24 if ( $val =~ m{^ \s* ( W/ ) ? " }six ) {
    100          
    50          
204 2         7 return $self->split_field_list($val);
205             }
206             elsif ( not ref $val ) {
207 1         4 return $self->split_field_hash($val);
208             }
209             elsif ( ref $val eq 'HASH' ) {
210 1         21 foreach my $key (@_) {
211 3 50       10 next unless $val->{$key};
212 3 100       14 if ( $val->{$key} =~ m{^ \s* ( W/ )? " }six ) {
213 2         7 $val->{$key} = [ $self->split_field_list( $val->{$key} ) ];
214             }
215             else {
216 1         3 $val->{$key} = { $self->split_field_hash( $val->{$key} ) };
217             }
218             }
219 1         4 return $val;
220             }
221             }
222              
223             sub build {
224 7     7 1 14 my $self = shift;
225 7 100       27 if ( ref $_[0] eq 'HASH' ) {
    100          
226 3         11 return $self->build_field_hash(@_);
227             }
228             elsif ( ref $_[0] eq 'ARRAY' ) {
229 3         9 return $self->build_field_list(@_);
230             }
231             else {
232 1         3 return $self->build_field_hash(@_);
233             }
234             }
235              
236             sub etags {
237 2     2 1 9 my $self = shift;
238 2         7 return $self->build_field_list(@_);
239             }
240              
241             1;
242              
243             __END__