File Coverage

blib/lib/Coerce/Types/Standard.pm
Criterion Covered Total %
statement 203 228 89.0
branch 42 54 77.7
condition 14 28 50.0
subroutine 61 62 98.3
pod 2 32 6.2
total 322 404 79.7


line stmt bran cond sub pod time code
1             package Coerce::Types::Standard;
2              
3 11     11   1581887 use 5.006;
  11         47  
4 11     11   69 use strict;
  11         25  
  11         419  
5 11     11   82 use warnings;
  11         21  
  11         721  
6 11     11   66 use Scalar::Util qw/blessed reftype refaddr/;
  11         44  
  11         843  
7              
8 11     11   4240 use parent 'Types::Standard';
  11         2945  
  11         69  
9            
10             our @EXPORT_OK = ( Types::Standard->type_names );
11              
12             our $meta = __PACKAGE__->meta;
13             our $VERSION = '0.000009';
14              
15             our (%entity, %recurse, %compare, $esc, $unesc, $path);
16             BEGIN {
17 11     11   1677592 %entity = (
18             encode => {
19             q{&} => q{&}, q{"} => q{"}, q{'} => q{'}, q{<} => q{<}, q{>} => q{>}
20             }
21             );
22 11         26 my @keys = keys %{$entity{encode}};
  11         87  
23             $entity{decode} = +{ map {
24 11         32 $entity{encode}->{$_} => $_
  55         232  
25             } @keys };
26 11         36 $entity{encode}->{regex} = join "|", map { quotemeta($_) } @keys;
  55         196  
27 11         95 $entity{decode}->{regex} = join "|", map { quotemeta($_) } keys %{$entity{decode}};
  55         137  
  11         64  
28             $entity{escape} = +{
29             map {
30 11         95 chr($_) => sprintf("%%%02X", $_)
  2816         10215  
31             } (0..255)
32             };
33             $entity{unescape} = +{
34             map {
35 2816         8252 $entity{escape}->{$_} => $_
36 11         327 } keys %{$entity{escape}}
  11         396  
37             };
38 11         631 $esc = qr/[^A-Za-z0-9\-\._~]/;
39 11         40 $unesc = qr/[0-9A-Fa-f]{2}/;
40 11         57 $path = qr|^(([a-z][a-z0-9+\-.]*):(!?\/\/([^\/?#]+))?)?([a-z0-9\-._~%!\$\&'()*+,;=:@\/]*)?(\?[a-z0-9\-._~%!\$\&'()*+,;=:@\/]*)?(#[a-z0-9\-._~%!\$\&'()*+,;=:@\/]*)|;
41             %recurse = (
42 0         0 ARRAY => sub { return map { recurse($_, $_[1], $_[2]) } @{ $_[0] } },
  0         0  
  0         0  
43 0         0 HASH => sub { do { $_[0]->{$_} = recurse($_[0]->{$_}, $_[1], $_[2]) } for keys %{ $_[0] }; $_[0] },
  0         0  
  0         0  
  0         0  
44 0 0       0 SCALAR => sub { ${$_[0]} =~ m/^[0-9.]+$/g ? $_[0] : do { ${$_[0]} =~ s/^(.*)$/recurse(${$_[1]})/e; $_[0]; }; },
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
45 11         160 );
46             %compare = (
47             ARRAY => sub {
48 6         39 my $recurse = shift;
49 6         13 my @length = sort { $a < $b } map { scalar @{ $_ } } (@_);
  6         21  
  12         20  
  12         39  
50 6 100       34 for my $i (0 .. $length[0] - 1) { compare($recurse, map { $_->[$i] } @_) or return 0; }
  8         17  
  16         40  
51 3         31 1;
52             },
53             HASH => sub {
54 0         0 my $recurse = shift;
55 0         0 for my $k (combine_keys(@_)) {
56 0 0       0 compare($recurse, map { $_->{$k} } @_) or return 0;
  0         0  
57             }
58 0         0 1;
59             },
60 0         0 SCALAR => sub { compare(shift, map {${$_}} @_) },
  0         0  
  0         0  
61 7         27 MAGIC => sub { my %t; shift; map { $t{$_}++ } @_; scalar keys %t == 1; },
  7         12  
  7         24  
  14         32  
  7         69  
62 11         760 );
63             }
64             {
65             # all powerfull
66 11     11   106 no strict 'refs';
  11         20  
  11         48309  
67             my $counter = 0;
68             *{"Type::Tiny::by"} = sub {
69 32     32   2102728 my ($pn, $parent, $hide, $act) = ($_[0]->name, shift, shift);
70 32 100   64   737 $act = ref $hide ? sub { compare(\%compare, @_) } : sub { $_[0] =~ m/$_[1]/; };
  11         235  
  76         1957  
71             my $self = do {
72             $_ =~ m/^$pn/ && exists $meta->{types}->{$_}->{abuse}
73             && $act->($meta->{types}->{$_}->{abuse}, $hide)
74             and return $meta->{types}->{$_} foreach $meta->type_names;
75             undef;
76             } || $meta->add_type({
77             name => $parent->name . $counter++,
78             parent => $parent->{abuse_parent} && $parent->{abuse_parent}->($hide) || $parent,
79             coercion => $parent->{abuse}->($hide),
80             abuse => $hide,
81 32   33     78 ($parent->{abuse_constraint} ? (constraint => $parent->{abuse_constraint}->($hide)) : ())
82             });
83 29         59847 return $self;
84             };
85             }
86              
87             $meta->add_type({
88             name => 'StrToArray',
89             parent => scalar $meta->ArrayRef,
90             abuse => \&_strToArray
91             });
92              
93             sub _strToArray {
94 3     3   40 my $hide = shift;
95             return sub {
96 5 50   5   47890 defined $_[0] ? [split $hide, $_[0]] : $_[0];
97             }
98 3         37 }
99              
100             $meta->add_type({
101             name => 'StrToHash',
102             parent => scalar $meta->HashRef,
103             abuse => \&_strToHash
104             });
105              
106             sub _strToHash {
107 3     3   49 my $hide = shift;
108             return sub {
109 3 50   3   41262 defined $_[0] ? +{split $hide, $_[0]} : $_[0];
110             }
111 3         40 }
112              
113             $meta->add_type({
114             name => 'StrSR',
115             parent => scalar $meta->Str,
116             abuse_constraint => \&search_replace_constraint,
117             abuse => \&search_replace
118             });
119              
120             sub search_replace_constraint {
121 2     2 0 6 my ($sr) = (quotemeta($_[0][0]));
122             return sub {
123 6     6   25186 my $str = shift;
124 6         40 ! ($str =~ m/$sr/);
125 2         18 };
126             }
127              
128             sub search_replace {
129 2     2 0 26 my (@sr) = (quotemeta($_[0][0]), $_[0][1]);
130             return sub {
131 3     3   19 my $str = shift;
132 3 50       40 ref $sr[1] eq 'CODE'
133 0         0 ? $str =~ s/($sr[0])/$sr[1]->($1)/ego
134             : $str =~ s/($sr[0])/$sr[1]/g;
135 3         57 return $str;
136 2         31 };
137             }
138              
139              
140              
141             $meta->add_type({
142             name => 'ArrayToHash',
143             parent => scalar $meta->HashRef,
144             coercion => sub {
145             +{
146             @{ $_[0] }
147             };
148             },
149             abuse => \&_hash
150             });
151              
152             sub _hash {
153 5     5   49 my $hide = sprintf "array_to_hash_%s", shift;
154 5         38 return \&$hide;
155             }
156              
157             # issues with the following is that arrays are not always flat *|o|*
158             sub array_to_hash_reverse {
159 1     1 0 1050 my @array = @{$_[0]};
  1         4  
160 1         3 my %hash;
161 1         4 while (@array) {
162 2         6 my ($even, $odd) = (shift @array, shift @array);
163 2         10 $hash{$odd} = $even
164             }
165 1         28 return \%hash;
166             }
167              
168             sub array_to_hash_odd {
169 1     1 0 1013 my @array = @{$_[0]};
  1         5  
170 1         5 return +{ (map {$array[$_]} grep {$_ & 1} 1 .. scalar @array - 1) };
  2         36  
  3         9  
171             }
172              
173             sub array_to_hash_even {
174 1     1 0 1046 my @array = @{$_[0]};
  1         5  
175 1         6 return +{ (map {$array[$_]} grep {not $_ & 1} 0 .. scalar @array - 1) };
  2         42  
  4         12  
176             }
177              
178             sub array_to_hash_flat {
179 1     1 0 1054 return +{ _flat($_[0]) };
180             }
181              
182             sub array_to_hash_merge {
183             return +{
184 1     1 0 1000 map { %{$_} } grep { ref $_ eq 'HASH' } @{$_}
  2         5  
  2         36  
  2         8  
  1         3  
185             }
186             }
187              
188             $meta->add_type({
189             name => 'HashToArray',
190             parent => scalar $meta->ArrayRef,
191             coercion => sub {
192             defined $_[0] ? [
193             map { $_, $_[0]->{$_} } sort keys %{ $_[0] }
194             ] : $_[0];
195             },
196             abuse => \&_arrays
197             });
198              
199             sub _arrays {
200 3     3   33 my $hide = sprintf ('hash_to_array_%s', shift);
201 3         63 \&$hide;
202             }
203              
204             sub hash_to_array_keys {
205 1     1 0 1055 return [ sort keys %{ $_[0] } ];
  1         39  
206             }
207              
208             sub hash_to_array_values {
209 1     1 0 1022 return [ sort values %{ $_[0] } ];
  1         39  
210             }
211              
212             sub hash_to_array_flat {
213 1     1 0 1033 return [_flat($_[0])];
214             }
215              
216             sub _flat {
217 2     2   6 my @lazy;
218             my %r = (
219 4     4   8 ARRAY => sub { map { recurse($_[0], $_) } @{ $_[1] } },
  6         17  
  4         13  
220 2 50   2   7 HASH => sub { do { recurse($_[0], $_) && recurse($_[0], $_[1]->{$_}); } for sort keys %{ $_[1] }; },
  2         14  
  3         37  
221 0     0   0 SCALAR => sub { recurse($_[0], ${$_[1]}) },
  0         0  
222 8     8   31 MAGIC => sub { push @lazy, $_[1] },
223 2         31 );
224 2         14 recurse(\%r, $_[0]);
225 2         138 return @lazy;
226             }
227              
228             $meta->add_type({
229             name => 'HTML',
230             parent => scalar $meta->Str,
231             abuse_constraint => \&_html_constraint,
232             abuse => \&_html
233             });
234              
235             sub _html_constraint {
236 2     2   5 my $hide = sprintf('constraint_%s', shift);
237 2         24 \&$hide;
238             }
239              
240             sub _html {
241 2     2   34 my $hide = sprintf('%s', shift);
242 2         18 \&$hide;
243             }
244              
245             sub constraint_encode_entity {
246 2     2 0 809 my ($str, %encode) = (shift, %{ $entity{encode} });
  2         20  
247 2 100       90 $str =~ m/($encode{regex})(?![a-z#]+;)/ ? 0 : 1;
248             }
249              
250             sub encode_entity {
251 1     1 1 11 my ($str, %encode) = (shift, %{ $entity{encode} });
  1         8  
252 1         57 $str =~ s/($encode{regex})/$encode{$1}/eg;
  7         28  
253 1         35 return $str;
254             }
255              
256             sub constraint_decode_entity {
257 2 100   2 0 36016 shift =~ m/&([a-z#]+;)/ ? 0 : 1;
258             }
259              
260             sub decode_entity {
261 1     1 1 10 my ($str, %decode) = (shift, %{ $entity{decode} });
  1         13  
262 1         57 $str =~ s/($decode{regex})/$decode{$1}/eg;
  7         46  
263 1         36 return $str;
264             }
265              
266             $meta->add_type({
267             name => 'URI',
268             parent => scalar $meta->Object,
269             constraint => sub {
270             my $obj = ref $_[0];
271             $obj =~ m!^URI! ? 1 : 0;
272             },
273             coercion => sub {
274             require URI;
275             my @args = ref $_[0] ? @{ $_[0] } : $_[0];
276             my $queryForm = pop @args if ref $args[scalar @args - 1] eq 'HASH';
277             my $uri = URI->new(@args);
278             $uri->query_form($queryForm) if $queryForm;
279             return $uri;
280             },
281             abuse_parent => \&_uri_change,
282             abuse_constraint => \&_uri_constraint,
283             abuse => \&_uri
284             });
285              
286             sub _uri_change {
287 8     8   71 my $hide = shift;
288 8 100       72 return scalar $meta->Str if $hide =~ m/^escape|unescape|schema|host|path|query_string|fragment$/;
289 1         10 return scalar $meta->HashRef;
290             }
291              
292             sub _uri_constraint {
293 8     8   16 my $hide = sprintf "constraint_uri_%s", shift;
294 8         69 \&$hide;
295             }
296              
297             # I don't know why, just don't ask
298             sub constraint_uri_schema {
299 2     2 0 816 $_[0] =~ m/$path/;
300 2 100 33     25 $4 || $5 || $6 || $7 ? 0 : 1;
301             }
302              
303             sub constraint_uri_host {
304 2     2 0 736 $_[0] =~ m/$path/;
305 2 100 33     22 $2 || $5 || $6 || $7 ? 0 : 1;
306             }
307              
308             sub constraint_uri_path {
309 2     2 0 758 $_[0] =~ m/$path/;
310 2 100 33     21 $2 || $4 || $6 || $7 ? 0 : 1;
311             }
312              
313             sub constraint_uri_query_string {
314 2     2 0 748 $_[0] =~ m/$path/;
315 2 100 33     21 $2 || $4 || $5 || $7 ? 0 : 1;
316             }
317              
318             sub constraint_uri_fragment {
319 2     2 0 761 $_[0] =~ m/$path/;
320 2 100 33     27 $2 || $4 || $5 || $6 ? 0 : 1;
321             }
322              
323             sub constraint_uri_query_form {
324 1 50   1 0 49 ref $_[0] eq 'HASH' ? 1 : 0;
325             }
326              
327             sub constraint_uri_escape {
328 2 100   2 0 884 $_[0] =~ m/($esc)(?!$unesc)/ ? 0 : 1;
329             }
330              
331             sub constraint_uri_unescape {
332 2 100   2 0 821 $_[0] =~ m/%$unesc/ ? 0 : 1;
333             }
334              
335             sub _uri {
336 8     8   169 my $hide = sprintf "uri_%s", shift;
337 8         55 \&$hide;
338             }
339              
340             sub uri_schema {
341 1     1 0 14 $_[0] =~ m/$path/;
342 1         39 return $2;
343             }
344              
345             sub uri_host {
346 1     1 0 12 $_[0] =~ m/$path/;
347 1         25 return $4;
348             }
349              
350             sub uri_path {
351 1     1 0 13 $_[0] =~ m/$path/;
352 1         26 return $5;
353             }
354              
355             sub uri_query_string {
356 1     1 0 14 $_[0] =~ m/$path/;
357 1         5 return uri_unescape($6);
358             }
359              
360             sub uri_fragment {
361 1     1 0 14 $_[0] =~ m/$path/;
362 1         35 return $7;
363             }
364              
365             sub uri_query_form {
366 1     1 0 705 $_[0] =~ m/$path/;
367 1         5 my $query_string = uri_unescape($6);
368 1         5 $query_string =~ s,^\?,,;
369             return +{
370 1         49 split '=', $query_string
371             };
372             }
373              
374             sub uri_escape {
375 1     1 0 12 my ($string, %escape) = (shift, %{ $entity{escape} });
  1         317  
376 1         62 $string =~ s/($esc)/$escape{$1}/eg;
  6         21  
377 1         62 $string;
378             }
379              
380             sub uri_unescape {
381 3     3 0 15 my ($string, %unescape) = (shift, %{ $entity{unescape} });
  3         415  
382 3         84 $string =~ s/(%$unesc)/$unescape{$1}/eg;
  6         19  
383 3         1313 $string;
384             }
385              
386             $meta->add_type({
387             name => 'Count',
388             parent => scalar $meta->Str,
389             coercion => sub {
390             my $ref = ref $_[0];
391             return $ref eq 'ARRAY' ? scalar @{$_[0]} : scalar keys %{$_[0]};
392             },
393             });
394              
395             $meta->add_type({
396             name => 'JSON',
397             parent => scalar $meta->Any,
398             constraint => sub {
399             my $ref = ref $_[0];
400             $ref ? 1 : 0;
401             },
402             coercion => sub {
403             require JSON;
404             my $json = JSON->new;
405             return $json->decode($_[0]);
406             },
407             abuse_parent => \&_json_change,
408             abuse => \&_json
409             });
410              
411             sub _json_change {
412 3     3   39 my $ref = ref $_[0];
413 3 50 66     15 return unless ! $ref || $ref eq 'ARRAY';
414 3 100       12 my $key = $ref eq 'ARRAY' ? $_[0]->[0] : $_[0];
415 3         7 my $type = eval{ $meta->$key };
  3         93  
416 3 50       26 $type ? shift @{$_[0]} : do { $type = $meta->Str if $key eq 'encode'; };
  1 100       10  
  2         17  
417 3         21 $type;
418             }
419              
420             sub _json {
421 3     3   51 require JSON;
422 3         38 my $json = JSON->new;
423 3         11 my $ref = ref $_[0];
424 3 100       11 my $type = $ref ? $_[0]->[0] : $_[0];
425 3 100 66     37 map { $json = $json->$_ } @{ $_[0]->[1] } if ( $ref && ref $_[0]->[1] eq 'ARRAY' );
  2         18  
  2         8  
426 3     4   46 return sub { $json->$type($_[0]) };
  4         3035  
427             }
428              
429             sub compare {
430 19     19 0 37 my ($recurse, %same) = shift;
431 19   100     122 $same{reftype $_ || 'MAGIC'}++ for @_;
432 19 100       103 return 0 if scalar keys %same != 1;
433 13         49 return $recurse->{[(keys %same)]->[0]}->($recurse, @_);
434             }
435              
436             sub recurse {
437 14     14 0 35 my ($recurse, $ref) = shift;
438 14   100     62 $ref = reftype($_[0]) || 'MAGIC';
439 14 50       94 $recurse->{$ref}->($recurse, $_[0]) if (exists $recurse->{$ref});
440 14         65 $_[0];
441             }
442              
443             1;
444              
445             __END__