File Coverage

blib/lib/Coerce/Types/Standard.pm
Criterion Covered Total %
statement 185 228 81.1
branch 32 54 59.2
condition 10 28 35.7
subroutine 58 62 93.5
pod 2 32 6.2
total 287 404 71.0


line stmt bran cond sub pod time code
1             package Coerce::Types::Standard;
2              
3 10     10   802913 use 5.006;
  10         67  
4 10     10   57 use strict;
  10         21  
  10         207  
5 10     10   57 use warnings;
  10         20  
  10         288  
6 10     10   76 use Scalar::Util qw/blessed reftype refaddr/;
  10         19  
  10         675  
7              
8 10     10   3887 use parent 'Types::Standard';
  10         2650  
  10         55  
9            
10             our @EXPORT_OK = ( Types::Standard->type_names );
11              
12             our $meta = __PACKAGE__->meta;
13             our $VERSION = '0.000008';
14              
15             our (%entity, %recurse, %compare, $esc, $unesc, $path);
16             BEGIN {
17 10     10   777470 %entity = (
18             encode => {
19             q{&} => q{&}, q{"} => q{"}, q{'} => q{'}, q{<} => q{<}, q{>} => q{>}
20             }
21             );
22 10         32 my @keys = keys %{$entity{encode}};
  10         61  
23             $entity{decode} = +{ map {
24 10         38 $entity{encode}->{$_} => $_
  50         160  
25             } @keys };
26 10         63 $entity{encode}->{regex} = join "|", map { quotemeta($_) } @keys;
  50         125  
27 10         38 $entity{decode}->{regex} = join "|", map { quotemeta($_) } keys %{$entity{decode}};
  50         129  
  10         66  
28             $entity{escape} = +{
29             map {
30 10         45 chr($_) => sprintf("%%%02X", $_)
  2560         8309  
31             } (0..255)
32             };
33             $entity{unescape} = +{
34             map {
35 2560         6409 $entity{escape}->{$_} => $_
36 10         229 } keys %{$entity{escape}}
  10         242  
37             };
38 10         386 $esc = qr/[^A-Za-z0-9\-\._~]/;
39 10         44 $unesc = qr/[0-9A-Fa-f]{2}/;
40 10         30 $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 10         105 );
46             %compare = (
47             ARRAY => sub {
48 2         5 my $recurse = shift;
49 2         5 my @length = sort { $a < $b } map { scalar @{ $_ } } (@_);
  2         7  
  4         5  
  4         14  
50 2 100       9 for my $i (0 .. $length[0] - 1) { compare($recurse, map { $_->[$i] } @_) or return 0; }
  3         7  
  6         16  
51 1         13 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 3         5 MAGIC => sub { my %t; shift; map { $t{$_}++ } @_; scalar keys %t == 1; },
  3         5  
  3         7  
  6         14  
  3         21  
62 10         445 );
63             }
64             {
65             # all powerfull
66 10     10   100 no strict 'refs';
  10         24  
  10         34533  
67             my $counter = 0;
68             *{"Type::Tiny::by"} = sub {
69 28     28   832 my ($pn, $parent, $hide, $act) = ($_[0]->name, shift, shift);
70 28 100   51   522 $act = ref $hide ? sub { compare(\%compare, @_) } : sub { $_[0] =~ m/$_[1]/; };
  5         111  
  74         1824  
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 28   33     51 ($parent->{abuse_constraint} ? (constraint => $parent->{abuse_constraint}->($hide)) : ())
82             });
83 26         22010 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   31052 defined $_[0] ? [split $hide, $_[0]] : $_[0];
97             }
98 3         30 }
99              
100             $meta->add_type({
101             name => 'StrToHash',
102             parent => scalar $meta->HashRef,
103             abuse => \&_strToHash
104             });
105              
106             sub _strToHash {
107 3     3   38 my $hide = shift;
108             return sub {
109 3 50   3   28019 defined $_[0] ? +{split $hide, $_[0]} : $_[0];
110             }
111 3         28 }
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 5 my ($sr) = (quotemeta($_[0][0]));
122             return sub {
123 6     6   26295 my $str = shift;
124 6         47 ! ($str =~ m/$sr/);
125 2         16 };
126             }
127              
128             sub search_replace {
129 2     2 0 36 my (@sr) = (quotemeta($_[0][0]), $_[0][1]);
130             return sub {
131 3     3   26 my $str = shift;
132 3 50       55 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         64 return $str;
136 2         15 };
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   70 my $hide = sprintf "array_to_hash_%s", shift;
154 5         46 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 689 my @array = @{$_[0]};
  1         4  
160 1         2 my %hash;
161 1         5 while (@array) {
162 2         6 my ($even, $odd) = (shift @array, shift @array);
163 2         8 $hash{$odd} = $even
164             }
165 1         21 return \%hash;
166             }
167              
168             sub array_to_hash_odd {
169 1     1 0 767 my @array = @{$_[0]};
  1         4  
170 1         5 return +{ (map {$array[$_]} grep {$_ & 1} 1 .. scalar @array - 1) };
  2         24  
  3         7  
171             }
172              
173             sub array_to_hash_even {
174 1     1 0 711 my @array = @{$_[0]};
  1         6  
175 1         5 return +{ (map {$array[$_]} grep {not $_ & 1} 0 .. scalar @array - 1) };
  2         26  
  4         11  
176             }
177              
178             sub array_to_hash_flat {
179 1     1 0 701 return +{ _flat($_[0]) };
180             }
181              
182             sub array_to_hash_merge {
183             return +{
184 1     1 0 745 map { %{$_} } grep { ref $_ eq 'HASH' } @{$_}
  2         5  
  2         28  
  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   49 my $hide = sprintf ('hash_to_array_%s', shift);
201 3         37 \&$hide;
202             }
203              
204             sub hash_to_array_keys {
205 1     1 0 712 return [ sort keys %{ $_[0] } ];
  1         25  
206             }
207              
208             sub hash_to_array_values {
209 1     1 0 695 return [ sort values %{ $_[0] } ];
  1         24  
210             }
211              
212             sub hash_to_array_flat {
213 1     1 0 710 return [_flat($_[0])];
214             }
215              
216             sub _flat {
217 2     2   5 my @lazy;
218             my %r = (
219 4     4   9 ARRAY => sub { map { recurse($_[0], $_) } @{ $_[1] } },
  6         14  
  4         10  
220 2 50   2   7 HASH => sub { do { recurse($_[0], $_) && recurse($_[0], $_[1]->{$_}); } for sort keys %{ $_[1] }; },
  2         12  
  3         11  
221 0     0   0 SCALAR => sub { recurse($_[0], ${$_[1]}) },
  0         0  
222 8     8   17 MAGIC => sub { push @lazy, $_[1] },
223 2         26 );
224 2         13 recurse(\%r, $_[0]);
225 2         68 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   8 my $hide = sprintf('constraint_%s', shift);
237 2         15 \&$hide;
238             }
239              
240             sub _html {
241 2     2   30 my $hide = sprintf('%s', shift);
242 2         13 \&$hide;
243             }
244              
245             sub constraint_encode_entity {
246 2     2 0 645 my ($str, %encode) = (shift, %{ $entity{encode} });
  2         15  
247 2 100       57 $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         6  
252 1         28 $str =~ s/($encode{regex})/$encode{$1}/eg;
  7         25  
253 1         25 return $str;
254             }
255              
256             sub constraint_decode_entity {
257 2 100   2 0 24689 shift =~ m/&([a-z#]+;)/ ? 0 : 1;
258             }
259              
260             sub decode_entity {
261 1     1 1 10 my ($str, %decode) = (shift, %{ $entity{decode} });
  1         18  
262 1         46 $str =~ s/($decode{regex})/$decode{$1}/eg;
  7         26  
263 1         26 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   391 my $hide = shift;
288 8 100       66 return scalar $meta->Str if $hide =~ m/^escape|unescape|schema|host|path|query_string|fragment$/;
289 1         7 return scalar $meta->HashRef;
290             }
291              
292             sub _uri_constraint {
293 8     8   21 my $hide = sprintf "constraint_uri_%s", shift;
294 8         55 \&$hide;
295             }
296              
297             # I don't know why, just don't ask
298             sub constraint_uri_schema {
299 2     2 0 636 $_[0] =~ m/$path/;
300 2 100 33     26 $4 || $5 || $6 || $7 ? 0 : 1;
301             }
302              
303             sub constraint_uri_host {
304 2     2 0 642 $_[0] =~ m/$path/;
305 2 100 33     19 $2 || $5 || $6 || $7 ? 0 : 1;
306             }
307              
308             sub constraint_uri_path {
309 2     2 0 662 $_[0] =~ m/$path/;
310 2 100 33     24 $2 || $4 || $6 || $7 ? 0 : 1;
311             }
312              
313             sub constraint_uri_query_string {
314 2     2 0 643 $_[0] =~ m/$path/;
315 2 100 33     23 $2 || $4 || $5 || $7 ? 0 : 1;
316             }
317              
318             sub constraint_uri_fragment {
319 2     2 0 641 $_[0] =~ m/$path/;
320 2 100 33     26 $2 || $4 || $5 || $6 ? 0 : 1;
321             }
322              
323             sub constraint_uri_query_form {
324 1 50   1 0 39 ref $_[0] eq 'HASH' ? 1 : 0;
325             }
326              
327             sub constraint_uri_escape {
328 2 100   2 0 676 $_[0] =~ m/($esc)(?!$unesc)/ ? 0 : 1;
329             }
330              
331             sub constraint_uri_unescape {
332 2 100   2 0 693 $_[0] =~ m/%$unesc/ ? 0 : 1;
333             }
334              
335             sub _uri {
336 8     8   131 my $hide = sprintf "uri_%s", shift;
337 8         42 \&$hide;
338             }
339              
340             sub uri_schema {
341 1     1 0 14 $_[0] =~ m/$path/;
342 1         21 return $2;
343             }
344              
345             sub uri_host {
346 1     1 0 20 $_[0] =~ m/$path/;
347 1         36 return $4;
348             }
349              
350             sub uri_path {
351 1     1 0 15 $_[0] =~ m/$path/;
352 1         23 return $5;
353             }
354              
355             sub uri_query_string {
356 1     1 0 15 $_[0] =~ m/$path/;
357 1         6 return uri_unescape($6);
358             }
359              
360             sub uri_fragment {
361 1     1 0 14 $_[0] =~ m/$path/;
362 1         21 return $7;
363             }
364              
365             sub uri_query_form {
366 1     1 0 623 $_[0] =~ m/$path/;
367 1         5 my $query_string = uri_unescape($6);
368 1         6 $query_string =~ s,^\?,,;
369             return +{
370 1         28 split '=', $query_string
371             };
372             }
373              
374             sub uri_escape {
375 1     1 0 10 my ($string, %escape) = (shift, %{ $entity{escape} });
  1         275  
376 1         33 $string =~ s/($esc)/$escape{$1}/eg;
  6         19  
377 1         50 $string;
378             }
379              
380             sub uri_unescape {
381 3     3 0 12 my ($string, %unescape) = (shift, %{ $entity{unescape} });
  3         287  
382 3         64 $string =~ s/(%$unesc)/$unescape{$1}/eg;
  6         19  
383 3         107 $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 0     0   0 my $ref = ref $_[0];
413 0 0 0     0 return unless ! $ref || $ref eq 'ARRAY';
414 0 0       0 my $key = $ref eq 'ARRAY' ? $_[0]->[0] : $_[0];
415 0         0 my $type = eval{ $meta->$key };
  0         0  
416 0 0       0 $type ? shift @{$_[0]} : do { $type = $meta->Str if $key eq 'encode'; };
  0 0       0  
  0         0  
417 0         0 $type;
418             }
419              
420             sub _json {
421 0     0   0 require JSON;
422 0         0 my $json = JSON->new;
423 0         0 my $ref = ref $_[0];
424 0 0       0 my $type = $ref ? $_[0]->[0] : $_[0];
425 0 0 0     0 map { $json = $json->$_ } @{ $_[0]->[1] } if ( $ref && ref $_[0]->[1] eq 'ARRAY' );
  0         0  
  0         0  
426 0     0   0 return sub { $json->$type($_[0]) };
  0         0  
427             }
428              
429             sub compare {
430 8     8 0 15 my ($recurse, %same) = shift;
431 8   100     46 $same{reftype $_ || 'MAGIC'}++ for @_;
432 8 100       54 return 0 if scalar keys %same != 1;
433 5         23 return $recurse->{[(keys %same)]->[0]}->($recurse, @_);
434             }
435              
436             sub recurse {
437 14     14 0 27 my ($recurse, $ref) = shift;
438 14   100     61 $ref = reftype($_[0]) || 'MAGIC';
439 14 50       51 $recurse->{$ref}->($recurse, $_[0]) if (exists $recurse->{$ref});
440 14         36 $_[0];
441             }
442              
443             1;
444              
445             __END__