File Coverage

blib/lib/Types/XSD.pm
Criterion Covered Total %
statement 77 90 85.5
branch 4 10 40.0
condition 4 9 44.4
subroutine 21 24 87.5
pod 4 5 80.0
total 110 138 79.7


line stmt bran cond sub pod time code
1             package Types::XSD;
2              
3 40     40   1599945 use 5.008003;
  40         155  
  40         1646  
4 40     40   226 use strict;
  40         77  
  40         1255  
5 40     40   196 use warnings;
  40         69  
  40         1059  
6 40     40   2767 use utf8;
  40         106  
  40         296  
7              
8             BEGIN {
9 40     40   3038 $Types::XSD::AUTHORITY = 'cpan:TOBYINK';
10 40         944 $Types::XSD::VERSION = '0.005';
11             }
12              
13 40     40   222 use B qw(perlstring);
  40         79  
  40         3042  
14 40     40   209 use Carp;
  40         72  
  40         3043  
15 40     40   34916 use DateTimeX::Auto qw( dt dur );
  40         15596562  
  40         786  
16 40     40   497118 use DateTime::Incomplete ();
  40         6896668  
  40         1549  
17 40     40   46139 use Type::Utils;
  40         1534527  
  40         402  
18 40         363 use Type::Library -base, -declare => qw(
19             Name NmToken NmTokens NCName Id IdRef IdRefs Entity Entities
20             QName Notation Duration DateTime Time Date GYearMonth
21             GYear GMonthDay GDay GMonth
22             DateTimeStamp YearMonthDuration DayTimeDuration
23 40     40   59036 );
  40         72  
24 40     40   173473 use Types::Standard;
  40         1892020  
  40         546  
25 40     40   82182 use Types::XSD::Lite 0.003 ();
  40         4302807  
  40         1509  
26 40     40   37018 use XML::RegExp;
  40         27939  
  40         11528  
27              
28             our @EXPORT_OK = qw( dt_cmp dur_cmp dt_parse dur_parse );
29              
30             BEGIN {
31 40     40   316 Type::Utils::extends('Types::XSD::Lite');
32            
33 40         488014 *create_range_check = \&Types::XSD::Lite::create_range_check;
34 40         164 *quick_range_check = \&Types::XSD::Lite::quick_range_check;
35 40         127 *hex_length = \&Types::XSD::Lite::hex_length;
36 40         116 *b64_length = \&Types::XSD::Lite::b64_length;
37 40         2260 *facet = \&Types::XSD::Lite::facet;
38             };
39              
40 40     40   373 use constant MAGIC_DATES => map dt($_), qw( 1696-09-01 1697-02-01 1903-03-01 1903-07-01 );
  40         91  
  40         294  
41 40     40   63295 use constant MAGIC_TABLE => +{ "-1-1-1-1" => -1, "0000" => 0, "1111" => 1 };
  40         84  
  40         119281  
42             sub dur_cmp
43             {
44 176 50   176 1 169789 my @durations = map ref($_) ? $_ : dur($_), @_[0,1];
45 176         237201 my $result = join q[], map "DateTime::Duration"->compare(@durations, $_), MAGIC_DATES;
46 176 50       1411989 return MAGIC_TABLE->{$result} if exists MAGIC_TABLE->{$result};
47 0         0 return undef;
48             }
49              
50             our @patterns; my $pattern_i = -1;
51             our @assertions; my $assertion_i = -1;
52             my %facets = (
53             explicitTimezone => sub {
54             my ($o, $var) = @_;
55             return unless exists $o->{explicitTimezone};
56             my $etz = delete $o->{explicitTimezone};
57             return sprintf('%s =~ m/(?:Z|(?:[+-]\d{2}:?\d{2}))$/xism', $var)
58             if lc($etz) eq 'required';
59             return sprintf('%s !~ m/(?:Z|(?:[+-]\d{2}:?\d{2}))$/xism', $var)
60             if lc($etz) eq 'prohibited';
61             return '!!1'
62             if lc($etz) eq 'optional';
63             croak "explicitTimezone facet expected to be 'required', 'prohibited' or 'optional'"
64             },
65             maxInclusiveDuration => sub {
66             my ($o, $var) = @_;
67             return unless exists $o->{maxInclusive};
68             sprintf('(Types::XSD::dur_cmp(%s, %s)||0) <= 0', $var, perlstring delete $o->{maxInclusive});
69             },
70             minInclusiveDuration => sub {
71             my ($o, $var) = @_;
72             return unless exists $o->{minInclusive};
73             sprintf('(Types::XSD::dur_cmp(%s, %s)||0) >= 0', $var, perlstring delete $o->{minInclusive});
74             },
75             maxExclusiveDuration => sub {
76             my ($o, $var) = @_;
77             return unless exists $o->{maxExclusive};
78             sprintf('(Types::XSD::dur_cmp(%s, %s)||0) < 0', $var, perlstring delete $o->{maxExclusive});
79             },
80             minExclusiveDuration => sub {
81             my ($o, $var) = @_;
82             return unless exists $o->{minExclusive};
83             sprintf('(Types::XSD::dur_cmp(%s, %s)||0) > 0', $var, perlstring delete $o->{minExclusive});
84             },
85             maxInclusiveDT => sub {
86             my ($o, $var) = @_;
87             return unless exists $o->{maxInclusive};
88             sprintf('(Types::XSD::dt_cmp(%s, %s, %s)||0) <= 0', perlstring($Types::XSD::Lite::T), $var, perlstring delete $o->{maxInclusive});
89             },
90             minInclusiveDT => sub {
91             my ($o, $var) = @_;
92             return unless exists $o->{minInclusive};
93             sprintf('(Types::XSD::dt_cmp(%s, %s, %s)||0) >= 0', perlstring($Types::XSD::Lite::T), $var, perlstring delete $o->{minInclusive});
94             },
95             maxExclusiveDT => sub {
96             my ($o, $var) = @_;
97             return unless exists $o->{maxExclusive};
98             sprintf('(Types::XSD::dt_cmp(%s, %s, %s)||0) < 0', perlstring($Types::XSD::Lite::T), $var, perlstring delete $o->{maxExclusive});
99             },
100             minExclusiveDT => sub {
101             my ($o, $var) = @_;
102             return unless exists $o->{minExclusive};
103             sprintf('(Types::XSD::dt_cmp(%s, %s, %s)||0) > 0', perlstring($Types::XSD::Lite::T), $var, perlstring delete $o->{minExclusive});
104             },
105             );
106              
107             $Types::XSD::Lite::facets{$_} = $facets{$_} for keys %facets;
108              
109             our @dtarr;
110             my $i = -1;
111             our $base_datetime = "DateTime"->new(year => 2000, month => 1, day => 1); # leap year, 31 day month
112             our %dt_regexps;
113             sub dt_maker
114             {
115 360     360 0 1181 my ($name, $regexp, @fields) = @_;
116 360         619 my $j = ++$i; $dtarr[$j] = $regexp;
  360         1573  
117            
118             my $inlined = sub
119             {
120 1738     1738   2688487 my $var = $_[1];
121 1738         2732 my @code;
122 1738         3172 push @code, "do { my \$ok = 1;";
123 1738         15003 push @code, sprintf(
124             'my (%s) = (%s =~ $Types::XSD::dtarr[%d]) or --$ok;',
125             join(', ', map "\$$_", @fields),
126             $var,
127             $j,
128             );
129 1738         16816 push @code, sprintf(
130             '$ok and eval { "DateTime::Incomplete"->new(%s)->to_datetime(base => $Types::XSD::base_datetime) };',
131             join(', ', map "$_ => \$$_", @fields),
132             );
133 1738         3556 push @code, "}";
134 1738         58759 "@code";
135 360         1549 };
136            
137 360         916 my $type = "Type::Tiny"->new(
138             name => $name,
139             library => __PACKAGE__,
140             constraint => eval sprintf('sub { %s }', $inlined->(undef, '$_')),
141             inlined => $inlined,
142             );
143 360         17706 __PACKAGE__->add_type($type);
144            
145 360         129897 facet(
146             qw( pattern whiteSpace enumeration maxInclusiveDT maxExclusiveDT minInclusiveDT minExclusiveDT explicitTimezone ),
147             $type,
148             );
149            
150 360         137846 $dt_regexps{$type} = [$regexp, @fields];
151             }
152              
153             sub dt_parse
154             {
155 0     0 1 0 my ($type, $a) = @_;
156 0         0 my ($re, @fields) = @{ $dt_regexps{$type} };
  0         0  
157 0         0 my %d;
158 0         0 @d{@fields} = ($a =~ $re);
159 0   0     0 !defined($d{$_}) && delete($d{$_}) for @fields;
160 0         0 "DateTime::Incomplete"->new(%d);
161             }
162              
163             sub dur_parse
164             {
165 0     0 1 0 goto \&DateTimeX::Auto::dur;
166             }
167              
168             {
169             my %cache;
170             sub _detect_type
171             {
172 0     0   0 my ($lib, $v) = @_;
173 0         0 for my $type (qw(DateTime Time Date GYearMonth GYear GMonthDay GDay GMonth)) {
174 0 0       0 return $type if $lib->get_type($type)->check($v);
175             }
176 0         0 return $lib->get_type('DateTime');
177             }
178             sub dt_cmp
179             {
180 1387     1387 1 1773481 my ($type, $a, $b) = @_;
181 1387 50       4656 $type = __PACKAGE__->_detect_type($a) unless $type;
182 1387 50       9669 $type = __PACKAGE__->get_type($type) unless ref $type;
183 1387   66     28842 my $A = eval($cache{"$type;a"} ||= $type->inline_check('$a'));
184 1387   66     617726 my $B = eval($cache{"$type;b"} ||= $type->inline_check('$b'));
185 1387         570672 $A <=> $B;
186             }
187             }
188              
189             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
190             declare Name, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::Name)$}sm];
191              
192             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
193             declare NmToken, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::NmToken)$}sm];
194              
195             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
196             declare NmTokens, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::NmToken)(?:\s+$XML::RegExp::NmToken)*$}sm];
197              
198             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
199             declare NCName, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::NCName)$}sm];
200              
201             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
202             declare Id, as NCName;
203              
204             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
205             declare IdRef, as NCName;
206              
207             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
208             declare IdRefs, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::NCName)(?:\s+$XML::RegExp::NCName)*$}sm];
209              
210             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
211             declare Entity, as NCName;
212              
213             facet qw( length minLength maxLength pattern enumeration whiteSpace ),
214             declare Entities, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::NCName)(?:\s+$XML::RegExp::NCName)*$}sm];
215              
216             facet qw( lengthQName minLengthQName maxLengthQName pattern enumeration whiteSpace ),
217             declare QName, as Types::Standard::StrMatch[qr{^(?:$XML::RegExp::QName)$}sm];
218              
219             facet qw( lengthQName minLengthQName maxLengthQName pattern enumeration whiteSpace ),
220             declare Notation, as QName;
221              
222             facet qw( pattern whiteSpace enumeration maxInclusiveDuration maxExclusiveDuration minInclusiveDuration minExclusiveDuration ),
223             declare Duration, as Types::Standard::StrMatch[
224             qr{^-?P
225             (?:[0-9]+Y)?
226             (?:[0-9]+M)?
227             (?:[0-9]+D)?
228             (?:T
229             (?:[0-9]+H)?
230             (?:[0-9]+M)?
231             (?:[0-9]+(?:\.[0-9]+)?S)?
232             )?
233             $}xism
234             ];
235              
236             facet qw( pattern whiteSpace enumeration maxInclusiveDuration maxExclusiveDuration minInclusiveDuration minExclusiveDuration ),
237             declare YearMonthDuration, as Duration->parameterize(pattern => qr{^[^DT]*$}i);
238              
239             facet qw( pattern whiteSpace enumeration maxInclusiveDuration maxExclusiveDuration minInclusiveDuration minExclusiveDuration ),
240             declare DayTimeDuration, as Duration->parameterize(pattern => qr{^[^YM]*[DT].*$}i);
241              
242             dt_maker(
243             DateTime => qr{^
244             (-?[0-9]{4,})
245             -
246             ([0-9]{2})
247             -
248             ([0-9]{2})
249             T
250             ([0-9]{2})
251             :
252             ([0-9]{2})
253             :
254             ([0-9]{2}(?:\.[0-9]+)?)
255             (Z | (?: [+-]\d{2}:?\d{2} ))?
256             $}xism,
257             qw( year month day hour minute second time_zone ),
258             );
259              
260             dt_maker(
261             DateTimeStamp => qr{^
262             (-?[0-9]{4,})
263             -
264             ([0-9]{2})
265             -
266             ([0-9]{2})
267             T
268             ([0-9]{2})
269             :
270             ([0-9]{2})
271             :
272             ([0-9]{2}(?:\.[0-9]+)?)
273             (Z | (?: [+-]\d{2}:?\d{2} ))
274             $}xism,
275             qw( year month day hour minute second time_zone ),
276             );
277              
278             dt_maker(
279             Time => qr{^
280             ([0-9]{2})
281             :
282             ([0-9]{2})
283             :
284             ([0-9]{2}(?:\.[0-9]+)?)
285             (Z | (?: [+-]\d{2}:?\d{2} ))?
286             $}xism,
287             qw( hour minute second time_zone ),
288             );
289              
290             dt_maker(
291             Date => qr{^
292             (-?[0-9]{4,})
293             -
294             ([0-9]{2})
295             -
296             ([0-9]{2})
297             (Z | (?: [+-]\d{2}:?\d{2} ))?
298             $}xism,
299             qw( year month day time_zone ),
300             );
301              
302             dt_maker(
303             GYearMonth => qr{^
304             (-?[0-9]{4,})
305             -
306             ([0-9]{2})
307             (Z | (?: [+-]\d{2}:?\d{2} ))?
308             $}xism,
309             qw( year month time_zone ),
310             );
311              
312             dt_maker(
313             GYear => qr{^
314             (-?[0-9]{4,})
315             (Z | (?: [+-]\d{2}:?\d{2} ))?
316             $}xism,
317             qw( year time_zone ),
318             );
319              
320             dt_maker(
321             GMonthDay => qr{^
322             -
323             -
324             ([0-9]{2})
325             -
326             ([0-9]{2})
327             (Z | (?: [+-]\d{2}:?\d{2} ))?
328             $}xism,
329             qw( month day time_zone ),
330             );
331              
332             dt_maker(
333             GDay => qr{^
334             -
335             -
336             -
337             ([0-9]{2})
338             (Z | (?: [+-]\d{2}:?\d{2} ))?
339             $}xism,
340             qw( day time_zone ),
341             );
342              
343             dt_maker(
344             GMonth => qr{^
345             -
346             -
347             ([0-9]{2})
348             (Z | (?: [+-]\d{2}:?\d{2} ))?
349             $}xism,
350             qw( month time_zone ),
351             );
352              
353             1;
354              
355             __END__