File Coverage

blib/lib/Types/XSD/Lite.pm
Criterion Covered Total %
statement 70 90 77.7
branch 26 52 50.0
condition 12 18 66.6
subroutine 16 27 59.2
pod 0 5 0.0
total 124 192 64.5


line stmt bran cond sub pod time code
1             package Types::XSD::Lite;
2              
3 3     3   130177 use 5.008003;
  3         19  
4 3     3   12 use strict;
  3         6  
  3         48  
5 3     3   12 use warnings;
  3         4  
  3         78  
6 3     3   1500 use utf8;
  3         36  
  3         14  
7              
8             BEGIN {
9 3     3   116 $Types::XSD::Lite::AUTHORITY = 'cpan:TOBYINK';
10 3         60 $Types::XSD::Lite::VERSION = '0.007';
11             }
12              
13 3     3   15 use B qw(perlstring);
  3         5  
  3         137  
14 3     3   16 use Carp;
  3         4  
  3         230  
15 3         24 use Type::Library 2.000000 -base, -utils, -declare => qw(
16             AnyType AnySimpleType String NormalizedString Token Language Boolean
17             Base64Binary HexBinary Float Double AnyURI Decimal
18             Integer NonPositiveInteger NegativeInteger Long Int Short Byte
19             NonNegativeInteger PositiveInteger UnsignedLong UnsignedInt
20             UnsignedShort UnsignedByte
21 3     3   1218 );
  3         89602  
22 3     3   41517 use Types::Standard 2.000000;
  3         181004  
  3         26  
23              
24             sub create_range_check {
25 36     36 0 4493 my $class = $_[0]; eval "require $class";
  36         1916  
26 36 100       65737 my ($lower, $upper) = map(defined($_) ? $class->new($_) : $_, @_[1,2]);
27 36         61852 my ($lexcl, $uexcl) = map(!!$_, @_[3,4]);
28            
29             my $checker =
30             (defined $lower and defined $upper and $lexcl and $uexcl)
31 0 0   0   0 ? sub { my $n = $class->new($_); $n > $lower and $n < $upper } :
  0         0  
32             (defined $lower and defined $upper and $lexcl)
33 0 0   0   0 ? sub { my $n = $class->new($_); $n > $lower and $n <= $upper } :
  0         0  
34             (defined $lower and defined $upper and $uexcl)
35 0 0   0   0 ? sub { my $n = $class->new($_); $n >= $lower and $n < $upper } :
  0         0  
36             (defined $lower and defined $upper)
37 0 0   0   0 ? sub { my $n = $class->new($_); $n >= $lower and $n <= $upper } :
  0         0  
38             (defined $lower and $lexcl)
39 0     0   0 ? sub { $class->new($_) > $lower } :
40             (defined $upper and $uexcl)
41 0     0   0 ? sub { $class->new($_) < $upper } :
42             (defined $lower)
43 0     0   0 ? sub { $class->new($_) >= $lower } :
44             (defined $upper)
45 0     0   0 ? sub { $class->new($_) <= $upper } :
46 36 50 33 0   596 sub { !!1 };
  0 100 66     0  
    50 66        
    50 100        
    100 66        
    50 66        
    50          
    50          
47            
48             my $inlined = sub {
49 78     78   39825 my $var = $_[1];
50 78         109 my @checks;
51 78 50       440 push @checks, sprintf('$n >%s "%s"->new("%s")', $lexcl?'':'=', $class, $lower) if defined $lower;
    100          
52 78 50       1656 push @checks, sprintf('$n <%s "%s"->new("%s")', $uexcl?'':'=', $class, $upper) if defined $upper;
    100          
53 78         1346 my $code = sprintf(
54             '%s and do { my $n = "%s"->new(%s); %s }',
55             Types::Standard::Int()->inline_check($var),
56             $class,
57             $var,
58             join(" and ", @checks),
59             );
60 36         148 };
61            
62             return (
63 36         201 constraint => $checker,
64             inlined => $inlined,
65             );
66             }
67              
68             sub quick_range_check {
69 6     6 0 8 my $class = $_[0];
70 6         287 eval "require $class";
71 6 100       48 my ($lower, $upper) = map(defined($_) ? $class->new($_) : $_, @_[1,2]);
72 6         278 my ($lexcl, $uexcl) = map(!!$_, @_[3,4]);
73 6         10 my $var = $_[5];
74 6         34 my @checks;
75 6 0       15 push @checks, sprintf('$n >%s "%s"->new("%s")', $lexcl?'':'=', $class, $lower) if defined $lower;
    50          
76 6 50       36 push @checks, sprintf('$n <%s "%s"->new("%s")', $uexcl?'':'=', $class, $upper) if defined $upper;
    50          
77 6         199 my $code = sprintf(
78             'do { my $n = "%s"->new(%s); %s }',
79             $class,
80             $var,
81             join(" and ", @checks),
82             );
83             }
84              
85             sub hex_length {
86 0     0 0 0 my $str = shift;
87 0         0 my $len = ($str =~ tr/0-9A-Fa-f//);
88 0         0 $len / 2;
89             }
90              
91             sub b64_length {
92 0     0 0 0 my $str = shift;
93 0         0 $str =~ s/[^a-zA-Z0-9+\x{2f}=]//g;
94 0         0 my $padding = ($str =~ tr/=//);
95 0         0 (length($str) * 3 / 4) - $padding;
96             }
97              
98             our @patterns; my $pattern_i = -1;
99             our @assertions; my $assertion_i = -1;
100              
101             our %facets = (
102             assertions => sub {
103             my ($o, $var) = @_;
104             return unless exists $o->{assertions};
105             my $ass = delete $o->{assertions};
106             $ass = [$ass] unless ref($ass) eq q(ARRAY);
107             my @r;
108             for my $a (@$ass) {
109             require Types::TypeTiny;
110             if ( Types::TypeTiny::is_CodeLike($a) ) {
111             $assertion_i++;
112             $assertions[$assertion_i] = $a;
113             push @r,
114             ($var eq '$_')
115             ? sprintf('$Types::XSD::Lite::assertions[%d]->(%s)', $assertion_i, $var)
116             : sprintf('do { local $_ = %s; $Types::XSD::Lite::assertions[%d]->(%s) }', $var, $assertion_i, $var);
117             }
118             elsif ( Types::TypeTiny::is_StringLike($a) ) {
119             push @r,
120             ($var eq '$_')
121             ? "do { $a }"
122             : "do { local \$_ = $var; $a }";
123             }
124             else {
125             croak "assertions should be strings or coderefs";
126             }
127             }
128             join ' && ', map "($_)", @r;
129             },
130             length => sub {
131             my ($o, $var) = @_;
132             return unless exists $o->{length};
133             sprintf('length(%s)==%d', $var, delete $o->{length});
134             },
135             maxLength => sub {
136             my ($o, $var) = @_;
137             return unless exists $o->{maxLength};
138             sprintf('length(%s)<=%d', $var, delete $o->{maxLength});
139             },
140             minLength => sub {
141             my ($o, $var) = @_;
142             return unless exists $o->{minLength};
143             sprintf('length(%s)>=%d', $var, delete $o->{minLength});
144             },
145             lengthHex => sub {
146             my ($o, $var) = @_;
147             return unless exists $o->{length};
148             sprintf('Types::XSD::Lite::hex_length(%s)==%d', $var, delete $o->{length});
149             },
150             maxLengthHex => sub {
151             my ($o, $var) = @_;
152             return unless exists $o->{maxLength};
153             sprintf('Types::XSD::Lite::hex_length(%s)<=%d', $var, delete $o->{maxLength});
154             },
155             minLengthHex => sub {
156             my ($o, $var) = @_;
157             return unless exists $o->{minLength};
158             sprintf('Types::XSD::Lite::hex_length(%s)>=%d', $var, delete $o->{minLength});
159             },
160             lengthQName => sub {
161             my ($o, $var) = @_;
162             return unless exists $o->{length};
163             delete $o->{length};
164             "!!1"
165             },
166             maxLengthQName => sub {
167             my ($o, $var) = @_;
168             return unless exists $o->{maxLength};
169             delete $o->{maxLength};
170             "!!1"
171             },
172             minLengthQName => sub {
173             my ($o, $var) = @_;
174             return unless exists $o->{minLength};
175             delete $o->{minLength};
176             "!!1"
177             },
178             lengthB64 => sub {
179             my ($o, $var) = @_;
180             return unless exists $o->{length};
181             sprintf('Types::XSD::Lite::b64_length(%s)==%d', $var, delete $o->{length});
182             },
183             maxLengthB64 => sub {
184             my ($o, $var) = @_;
185             return unless exists $o->{maxLength};
186             sprintf('Types::XSD::Lite::b64_length(%s)<=%d', $var, delete $o->{maxLength});
187             },
188             minLengthB64 => sub {
189             my ($o, $var) = @_;
190             return unless exists $o->{minLength};
191             sprintf('Types::XSD::Lite::b64_length(%s)>=%d', $var, delete $o->{minLength});
192             },
193             pattern => sub {
194             my ($o, $var) = @_;
195             return unless exists $o->{pattern};
196             $patterns[++$pattern_i] = delete $o->{pattern};
197             sprintf('%s =~ $Types::XSD::Lite::patterns[%d]', $var, $pattern_i);
198             },
199             enumeration => sub {
200             my ($o, $var) = @_;
201             return unless exists $o->{enumeration};
202             my $re = join "|", map quotemeta, @{delete $o->{enumeration}};
203             sprintf('%s =~ m/^(?:%s)$/sm', $var, $re);
204             },
205             whiteSpace => sub {
206             my ($o, $var) = @_;
207             return unless exists $o->{whiteSpace};
208             delete($o->{whiteSpace});
209             "!!1";
210             },
211             maxInclusive => sub {
212             my ($o, $var) = @_;
213             return unless exists $o->{maxInclusive};
214             quick_range_check("Math::BigInt", undef, delete($o->{maxInclusive}), undef, undef, $var);
215             },
216             minInclusive => sub {
217             my ($o, $var) = @_;
218             return unless exists $o->{minInclusive};
219             quick_range_check("Math::BigInt", delete($o->{minInclusive}), undef, undef, undef, $var);
220             },
221             maxExclusive => sub {
222             my ($o, $var) = @_;
223             return unless exists $o->{maxExclusive};
224             quick_range_check("Math::BigInt", undef, delete($o->{maxExclusive}), undef, 1, $var);
225             },
226             minExclusive => sub {
227             my ($o, $var) = @_;
228             return unless exists $o->{minExclusive};
229             quick_range_check("Math::BigInt", delete($o->{minExclusive}), undef, 1, undef, $var);
230             },
231             maxInclusiveFloat => sub {
232             my ($o, $var) = @_;
233             return unless exists $o->{maxInclusive};
234             quick_range_check("Math::BigFloat", undef, delete($o->{maxInclusive}), undef, undef, $var);
235             },
236             minInclusiveFloat => sub {
237             my ($o, $var) = @_;
238             return unless exists $o->{minInclusive};
239             quick_range_check("Math::BigFloat", delete($o->{minInclusive}), undef, undef, undef, $var);
240             },
241             maxExclusiveFloat => sub {
242             my ($o, $var) = @_;
243             return unless exists $o->{maxExclusive};
244             quick_range_check("Math::BigFloat", undef, delete($o->{maxExclusive}), undef, 1, $var);
245             },
246             minExclusiveFloat => sub {
247             my ($o, $var) = @_;
248             return unless exists $o->{minExclusive};
249             quick_range_check("Math::BigFloat", delete($o->{minExclusive}), undef, 1, undef, $var);
250             },
251             maxInclusiveStr => sub {
252             my ($o, $var) = @_;
253             return unless exists $o->{maxInclusive};
254             sprintf('%s le %s', $var, perlstring delete $o->{maxInclusive});
255             },
256             minInclusiveStr => sub {
257             my ($o, $var) = @_;
258             return unless exists $o->{minInclusive};
259             sprintf('%s ge %s', $var, perlstring delete $o->{minInclusive});
260             },
261             maxExclusiveStr => sub {
262             my ($o, $var) = @_;
263             return unless exists $o->{maxExclusive};
264             sprintf('%s lt %s', $var, perlstring delete $o->{maxExclusive});
265             },
266             minExclusiveStr => sub {
267             my ($o, $var) = @_;
268             return unless exists $o->{minExclusive};
269             sprintf('%s gt %s', $var, perlstring delete $o->{minExclusive});
270             },
271             totalDigits => sub {
272             my ($o, $var) = @_;
273             return unless exists $o->{totalDigits};
274             sprintf('do { no warnings "uninitialized"; my $tmp = %s; ($tmp=~tr/0-9//) <= %d }', $var, delete $o->{totalDigits});
275             },
276             fractionDigits => sub {
277             my ($o, $var) = @_;
278             return unless exists $o->{fractionDigits};
279             sprintf('do { no warnings "uninitialized"; my (undef, $tmp) = split /\\./, %s; ($tmp=~tr/0-9//) <= %d }', $var, delete $o->{fractionDigits});
280             },
281             );
282              
283             sub with_facets {
284 72     72 0 42258 my ( $arg, @more ) = @_;
285 72         217 my @allowed_facets = ( "assertions", @$arg );
286 72         85 my $allowed_regexp = qr{^${\(join "|", map quotemeta, @allowed_facets)}$}ms;
  72         1968  
287            
288 72         173 my %return;
289             my $IG = $return{inline_generator} = sub {
290 2     2   23 my %p_not_destroyed = @_;
291             return sub {
292 6         27427 my %p = %p_not_destroyed; # copy;
293 6         13 my $var = $_[1];
294 6         21 my @r = map( $facets{$_}->( \%p, $var ), @allowed_facets );
295 6 0       14 croak sprintf(
    50          
296             'Attempt to parameterize type "%s" with unrecognised parameter%s %s',
297             $_[0]->name,
298             scalar( keys %p ) == 1 ? '' : 's',
299             Type::Utils::english_list( map qq["$_"], sort keys %p ),
300             ) if keys %p;
301 6         18 return ( undef, @r );
302 2         13 };
303 72         306 };
304            
305             $return{constraint_generator} = sub {
306 1     1   240 my $base = $Type::Tiny::parameterize_type;
307 1 50       6 my %params = @_ or return $base;
308 1         5 my @checks = $IG->( %params )->( $base, '$_[0]' );
309 1         10 $checks[0] = $base->inline_check( '$_[0]' );
310 1         64 my $sub = sprintf(
311             'sub { %s }',
312             join( ' and ', map "($_)", @checks ),
313             );
314 1 50       175 eval($sub) or croak "could not build sub: $@\n\nCODE: $sub\n";
315 72         241 };
316            
317             $return{name_generator} = sub {
318 1     1   36 my ($s, %a) = @_;
319             sprintf(
320             '%s[%s]',
321             $s,
322             join(
323             q[,],
324 1         14 map( sprintf( "%s=>%s", $_, perlstring $a{$_} ), sort keys %a )
325             ),
326             );
327 72         218 };
328            
329 72         555 return ( %return, @more );
330             }
331              
332             declare AnyType,
333             as Types::Standard::Any;
334              
335             declare AnySimpleType,
336             as Types::Standard::Value;
337              
338             declare String,
339             with_facets [qw( length minLength maxLength pattern enumeration whiteSpace )],
340             as Types::Standard::Str;
341              
342             declare NormalizedString,
343             with_facets [qw( length minLength maxLength pattern enumeration whiteSpace )],
344             as Types::Standard::StrMatch[qr{^[^\t\r\n]*$}sm];
345              
346             declare Token,
347             with_facets [qw( length minLength maxLength pattern enumeration whiteSpace )],
348             as intersection([
349             NormalizedString,
350             Types::Standard::StrMatch([qr{\A\s}sm])->complementary_type,
351             Types::Standard::StrMatch([qr{\s\z}sm])->complementary_type,
352             Types::Standard::StrMatch([qr{\s{2}}sm])->complementary_type,
353             ]);
354              
355             declare Language,
356             with_facets [qw( length minLength maxLength pattern enumeration whiteSpace )],
357             as Types::Standard::StrMatch[qr{\A[a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*\z}sm];
358              
359             declare Boolean,
360             with_facets [qw( pattern whiteSpace )],
361             as Types::Standard::StrMatch[qr{\A(?:true|false|0|1)\z}ism];
362              
363             declare Base64Binary,
364             with_facets [qw( lengthB64 minLengthB64 maxLengthB64 pattern enumeration whiteSpace )],
365             as Types::Standard::StrMatch[qr{\A[a-zA-Z0-9+\x{2f}=\s]+\z}ism];
366              
367             declare HexBinary,
368             with_facets [qw( lengthHex minLengthHex maxLengthHex pattern enumeration whiteSpace )],
369             as Types::Standard::StrMatch[qr{\A[a-fA-F0-9]+\z}ism];
370              
371             declare Float,
372             with_facets [qw( pattern enumeration whiteSpace maxInclusiveFloat maxExclusiveFloat minInclusiveFloat minExclusiveFloat )],
373             as Types::Standard::Num;
374              
375             declare Double,
376             with_facets [qw( pattern enumeration whiteSpace maxInclusiveFloat maxExclusiveFloat minInclusiveFloat minExclusiveFloat )],
377             as Types::Standard::Num;
378              
379             declare AnyURI,
380             with_facets [qw( length minLength maxLength pattern enumeration whiteSpace )],
381             as Types::Standard::Str;
382              
383             declare Decimal,
384             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusiveFloat maxExclusiveFloat minInclusiveFloat minExclusiveFloat )],
385             as Types::Standard::StrMatch[qr{\A(?:(?:[+-]?[0-9]+(?:\.[0-9]+)?)|(?:[+-]?\.[0-9]+))\z}ism];
386              
387             declare Integer,
388             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
389             as Types::Standard::Int;
390              
391             declare NonPositiveInteger,
392             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
393             as Integer,
394             create_range_check( "Math::BigInt", undef, 0 );
395              
396             declare NegativeInteger,
397             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
398             as NonPositiveInteger,
399             create_range_check( "Math::BigInt", undef, -1 );
400              
401             declare NonNegativeInteger,
402             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
403             as Integer,
404             create_range_check( "Math::BigInt", 0, undef );
405              
406             declare PositiveInteger,
407             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
408             as NonNegativeInteger,
409             create_range_check( "Math::BigInt", 1, undef );
410              
411             declare Long,
412             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
413             as Integer,
414             create_range_check( "Math::BigInt", q[-9223372036854775808], q[9223372036854775807] );
415              
416             declare Int,
417             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
418             as Long,
419             create_range_check( "Math::BigInt", q[-2147483648], q[2147483647] );
420              
421             declare Short,
422             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
423             as Int,
424             create_range_check( "Math::BigInt", q[-32768], q[32767] );
425              
426             declare Byte,
427             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
428             as Short,
429             create_range_check( "Math::BigInt", q[-128], q[127] );
430              
431             declare UnsignedLong,
432             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
433             as NonNegativeInteger,
434             create_range_check( "Math::BigInt", q[0], q[18446744073709551615] );
435              
436             declare UnsignedInt,
437             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
438             as UnsignedLong,
439             create_range_check( "Math::BigInt", q[0], q[4294967295] );
440              
441             declare UnsignedShort,
442             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
443             as UnsignedInt,
444             create_range_check( "Math::BigInt", q[0], q[65535] );
445              
446             declare UnsignedByte,
447             with_facets [qw( totalDigits fractionDigits pattern whiteSpace enumeration maxInclusive maxExclusive minInclusive minExclusive )],
448             as UnsignedShort,
449             create_range_check( "Math::BigInt", q[0], q[255] );
450              
451             __PACKAGE__->meta->make_immutable;
452              
453             __END__