File Coverage

blib/lib/Aion/Types.pm
Criterion Covered Total %
statement 432 447 96.6
branch 168 188 89.3
condition 31 38 81.5
subroutine 40 40 100.0
pod 17 20 85.0
total 688 733 93.8


line stmt bran cond sub pod time code
1             package Aion::Types;
2             # Типы-валидаторы для Aion
3              
4 7     7   374665 use common::sense;
  7         10  
  7         37  
5 7     7   535 use warnings FATAL => 'recursion';
  7         16  
  7         394  
6              
7 7     7   1612 use Aion::Meta::Util qw/subref_is_reachable val_to_str/;
  7         34  
  7         547  
8 7     7   1592 use Aion::Type;
  7         27  
  7         191  
9 7     7   53 use Aion::Type::Lim;
  7         11  
  7         136  
10 7     7   27 use List::Util qw/all any first/;
  7         8  
  7         410  
11 7     7   34 use Exporter qw/import/;
  7         40  
  7         249  
12             require overload;
13 7     7   3080 use POSIX qw//;
  7         38933  
  7         225  
14 7     7   46 use Scalar::Util qw/looks_like_number reftype refaddr blessed/;
  7         7  
  7         407  
15 7     7   2832 use Sub::Util qw//;
  7         1774  
  7         13672  
16              
17             our @EXPORT = our @EXPORT_OK = grep {
18             eval {*{$Aion::Types::{$_}}{CODE}} && !/^(_|(NaN|import|all|any|first|looks_like_number|reftype|refaddr|blessed|subref_is_reachable|val_to_str|DBL_MAX)\z)/n
19             } keys %Aion::Types::;
20              
21             # Обрабатываем атрибут :Isa
22             sub MODIFY_CODE_ATTRIBUTES {
23 11     11   130709 my ($pkg, $referent, @attributes) = @_;
24              
25 11 50       16 grep { /^Isa\((.*)\)\z/s? do { _Isa($pkg, $referent, $1); 0 }: 1 } @attributes
  11         68  
  11         26  
  11         49  
26             }
27              
28             sub _Isa {
29 11     11   67 my ($pkg, $referent, $data) = @_;
30 11         58 my $subname = Sub::Util::subname $referent;
31 11         47 $subname =~ s/^.*:://;
32              
33 11 50       48 die "Anonymous subroutine cannot use :Isa!" if $subname eq '__ANON__';
34            
35 11         1623 my @signature = eval "package $pkg; map { UNIVERSAL::isa(\$_, 'Aion::Type')? \$_: __PACKAGE__->can(\$_)? __PACKAGE__->can(\$_)->(): Aion::Types::External([\$_]) } ($data)";
36 11 50       69 die if $@;
37              
38 11 50       36 die "$pkg\::$subname has no return type!" if @signature == 0;
39              
40 11         411 require Aion::Meta::Subroutine;
41 11         53 my $subroutine = Aion::Meta::Subroutine->new(
42             pkg => $pkg,
43             subname => $subname,
44             signature => \@signature,
45             referent => $referent,
46             );
47            
48 11 100       31 if(!subref_is_reachable($referent)) {
49 2         6 $Aion::META{$pkg}{require}{$subname} = $subroutine;
50             } else {
51 9         22 my $require = delete $Aion::META{$pkg}{require}{$subname};
52 9 50       13 $require->compare($subroutine) if $require;
53              
54 9         15 my $overload = $Aion::META{$pkg}{subroutine}{$subname};
55 9 50       15 $overload->compare($subroutine) if $overload;
56            
57 9         19 $subroutine->wrap_sub;
58             }
59             }
60              
61 0         0 BEGIN {
62 7     7   41 my $INIT_ARGS = sub { @{&ARGS} = map External([$_]), &ARGS };
  1776         2432  
  1776         1962  
63 7         18 my $INIT_KW_ARGS = sub { @{&ARGS} = List::Util::pairmap { $a => External([$b]) } &ARGS };
  7         47  
  7         11  
  14         28  
64              
65             my $COMBINE_SUBS = sub {
66 0         0 my ($f1, $f2) = @_;
67 0         0 sub { $f1->(); $f2->() }
  0         0  
68 7         18 };
  0         0  
69              
70             my $COMBINE_WHERE = sub {
71 0         0 my ($f1, $f2) = @_;
72 0 0       0 sub { $f1->() && $f2->() }
73 7         44 };
  0         0  
74              
75             my $IS_PARAM = sub {
76 278         396 my @S = @_;
77 278         405 while(@S) {
78 292         352 my $arg = pop @S;
79 292 100 66     1022 return 1 if UNIVERSAL::isa($arg, 'Aion::Type') && $arg->{is_param};
80 282         304 push @S, @{$arg->{args}};
  282         655  
81             }
82             ""
83 7         29 };
  268         661  
84              
85 7         9 my $REPLACE_PARAM; $REPLACE_PARAM = sub {
86 128         143 my ($arg) = @_;
87              
88 128 100       228 return $arg unless UNIVERSAL::isa($arg, 'Aion::Type');
89              
90 116 100       200 if(my $param = $arg->{is_param}) {
91 36 100       127 return $Aion::Type::SELF->{args}->[$param - 1] if $param > 0;
92 2 50       5 return $Aion::Type::SELF->{N} if $param == -1;
93 2 50       4 return $Aion::Type::SELF->{M} if $param == -2;
94 2 50       4 return $Aion::Type::SELF if $param == -256;
95 2 50       6 return @{$Aion::Type::SELF->{args}} if $param == -1024;
  2         6  
96 0         0 die "Parameter number invalid!";
97             }
98              
99 80 100 66     222 return $arg if !$arg->{args} || !List::Util::first { UNIVERSAL::isa($_, 'Aion::Type') } @{$arg->{args}};
  76         160  
  80         218  
100              
101 64         333 $arg = bless {%$arg}, 'Aion::Type';
102 64         83 $arg->{args} = [map $REPLACE_PARAM->($_), @{$arg->{args}}];
  64         135  
103 64 50       172 $arg->init if $arg->{init};
104              
105 64         127 $arg
106 7         23 };
107              
108             my $INIT_REPLACE_PARAM = sub {
109 20         52 $Aion::Type::SELF->{as} = $REPLACE_PARAM->($Aion::Type::SELF->{as});
110 7         4361 };
111              
112             # Создание типа
113             sub subtype(@) {
114 623     623 1 2189 my $subtype = shift;
115 623         1335 my %o = @_;
116            
117 623         1641 my ($as, $init_where, $where, $awhere, $message) = delete @o{qw/as init_where where awhere message/};
118            
119 623 100       1126 die "subtype $subtype unused keys left: " . join ", ", keys %o if keys %o;
120            
121 622 50       2800 die "subtype format is Name or Name[args] or Name`[args]" if $subtype !~ /^([A-Z_]\w*)(?:(\`)?\[(.*)\])?$/i;
122 622         1570 my ($name, $is_maybe_arg, $is_arg) = ($1, $2, $3);
123              
124 622         974 my $pkg = scalar caller;
125 622 100       652 die "subtype $subtype: ${pkg}::$name exists!" if *{"${pkg}::$name"}{CODE};
  622         2719  
126              
127 621 100       914 if($is_maybe_arg) {
128 73 100       132 die "subtype $subtype: needs an awhere" if !$awhere;
129             } else {
130 548 100       815 die "subtype $subtype: awhere is excess" if $awhere;
131             }
132              
133 619 100       982 my @init = $init_where? $init_where: ();
134            
135 619         612 my $init_types = do { given($is_arg) {
  619         653  
136 619         865 $INIT_ARGS when /^[A-Z]\w*(,\s*[A-Z]\w*)?\.\.\.$/;
137 591         646 $INIT_KW_ARGS when /^[a-z]\w*\s*=>\s*[A-Z],?\s*\.\.\.$/;
138 584         1095 when(/\b[A-Z]\b/) {
139 85         206 my @args = split /\s*,\s*/, $is_arg;
140 85         202 my @typeno = grep { $args[$_] =~ /^[A-Z]/ } 0..@args-1;
  100         324  
141 85     85   106 (sub { my ($typeno) = @_; sub {
142 530         737 my $args = &ARGS;
143 530         1120 $args->[$_] = External([$args->[$_]]) for @$typeno;
144 85         368 } })->(\@typeno);
  85         353  
145             }
146             }};
147              
148 619 100       1018 unshift @init, $init_types if $init_types;
149            
150 619 100       1521 $as = External([$as]) if defined $as;
151            
152 619 100 100     1597 unshift @init, $INIT_REPLACE_PARAM if $as && $is_arg && $IS_PARAM->($as);
      100        
153              
154             # Тут coerce - прототип - единый для всех порождаемых типов одного типа с разными аргументами
155 619 100 100     1839 my $type = Aion::Type->new(
    100          
    100          
    100          
156             name => $name,
157             coerce => [], # prototype
158             test => $where // \&Aion::Type::true,
159             $as? (as => $as): (),
160             @init? (init => \@init): (),
161             $awhere? (a_test => $awhere): (),
162             $message? (message => $message): (),
163             );
164            
165 619 100 100     1686 if($is_maybe_arg) {
    100          
166 72         169 $type->make_maybe_arg($pkg)
167             } elsif($is_arg || @init) {
168 217         372 $type->make_arg($pkg, $is_arg)
169             } else {
170 330         566 $type->make($pkg)
171             }
172             }
173             }
174              
175 604     604 1 1661 sub as(@) { (as => @_) }
176 93     93 1 284 sub init_where(&@) { (init_where => @_) }
177 491     491 1 1713 sub where(&@) { (where => @_) }
178 73     73 1 855 sub awhere(&@) { (awhere => @_) }
179 2     2 1 12 sub message(&@) { (message => @_) }
180              
181 46     46 1 145 sub SELF() { $Aion::Type::SELF }
182             sub ARGS() {
183 4540 100   4540 1 6962 return $Aion::Type::SELF->{is_param_args} if $Aion::Type::SELF->{is_param_args};
184 2103         5348 wantarray? @{$Aion::Type::SELF->{args}}: $Aion::Type::SELF->{args}
185 4539 100       8642 }
186 411     411 1 1564 sub A() { $Aion::Type::SELF->{args}[0] }
187 208     208 1 1331 sub B() { $Aion::Type::SELF->{args}[1] }
188 2     2 1 9 sub C() { $Aion::Type::SELF->{args}[2] }
189 1     1 1 4 sub D() { $Aion::Type::SELF->{args}[3] }
190              
191 79     79 1 275 sub M() :lvalue { $Aion::Type::SELF->{M} }
192 9     9 1 45 sub N() :lvalue { $Aion::Type::SELF->{N} }
193              
194             # Создание транслятора. У типа может быть сколько угодно трансляторов из других типов
195             # coerce Type, from OtherType, via {...}
196             sub coerce(@) {
197 52     52 1 1084 my ($type, %o) = @_;
198 52         120 my ($from, $via) = delete @o{qw/from via/};
199              
200 52 100       132 die "coerce $type unused keys left: " . join ", ", keys %o if keys %o;
201 51 100       134 die "coerce $type not Aion::Type!" unless UNIVERSAL::isa($type, "Aion::Type");
202 50 100       92 die "coerce $type: from is'nt Aion::Type!" unless UNIVERSAL::isa($from, "Aion::Type");
203 48 100       99 die "coerce $type: via is not subroutine!" unless ref $via eq "CODE";
204              
205 46         46 push @{$type->{coerce}}, [$from, $via];
  46         102  
206 46         65 return;
207             }
208              
209 48     48 1 207 sub from($) { (from => $_[0]) }
210 46     46 1 121 sub via(&) { (via => $_[0]) }
211              
212 7         10 use constant DBL_MAX => do {
213 7         13 my $ieee_dbl_max_str = '1.7976931348623157e+308';
214 7 50       12917 ($ieee_dbl_max_str+0) =~ /inf/i? do {
215 0         0 require Math::BigFloat;
216 0         0 Math::BigFloat->new($ieee_dbl_max_str)
217             }: $ieee_dbl_max_str+0
218 7     7   47 };
  7         9  
219              
220             sub _8BITS() {
221 1     1   5 undef *_8BITS;
222 1         6 require Math::BigInt;
223 1         3 my $_8bits = Math::BigInt->new(8);
224 1         182 constant->import(_8BITS => $_8bits);
225 1         3 $_8bits
226             }
227              
228 0         0 BEGIN {
229              
230 7     7   26705 subtype "Any";
231 7         99 subtype "Control", as &Any;
232             subtype "Union[A, B...]", as &Control,
233 7         99 where { my $val = $_; any { $_->include($val) } ARGS };
  165         261  
  165         697  
  226         434  
234             subtype "Intersection[A, B...]", as &Control,
235 7         101 where { my $val = $_; all { $_->include($val) } ARGS };
  39         50  
  39         137  
  71         133  
236             subtype "Exclude[A]", as &Control,
237 7         101 where { !A->test };
  11         22  
238             subtype "Option[A]", as &Control,
239 4         9 init_where { SELF->{is_option} = 1 }
240 7         96 where { A->test };
  2         5  
241             subtype "Wantarray[A, S]", as &Control,
242 1         4 init_where { SELF->{is_wantarray} = 1 }
243 7         118 where { ... };
  0         0  
244              
245 7         102 subtype "Item", as &Any;
246             sub External($) {
247 6976     6976 0 12076 local $_ = $_[0][0];
248             UNIVERSAL::isa($_, 'Aion::Type')? $_:
249 6976 100 66     14227 defined($_) && ref $_ eq ""? Object([$_]): do {
    100          
250 6 50 66     28 die "Not External[${\val_to_str($_)}]" unless reftype($_) eq "CODE" || overload::Method($_, '&{}');
  0         0  
251             Aion::Type->new(
252             name => 'External',
253             as => &Item,
254             args => $_[0],
255             test => $_,
256             UNIVERSAL::can($_, 'coerce')
257 6 100   4   292 ? (coerce => [[&Any, (sub { my ($ex) = @_; sub { $ex->coerce } })->($_)]])
  4         10  
  4         16  
  3         10  
258             : (),
259             )
260             }
261             }
262 7 100       106 subtype "Bool", as &Item, where { ref $_ eq "" and /^(1|0|)\z/ };
  17         152  
263             subtype "BoolLike", as &Item, where {
264 8 100       32 return 1 if overload::Method($_, 'bool');
265 6         243 my $m = overload::Method($_, '0+');
266 7 100       90 Bool()->include($m ? $m->($_) : $_) };
  6         410  
267             subtype "Enum[e...]", as &Item,
268 56         110 init_where { M = +{ map {($_ => $_)} ARGS } }
  98         250  
269 7         104 where { exists M->{$_} };
  15         24  
270 7         107 subtype "Undef", as &Item, where { !defined $_ };
  46         125  
271 7         90 subtype "Maybe[A]", as &Undef | A;
272 7         105 subtype "Defined", as &Item, where { defined $_ };
  789         1763  
273 7         98 subtype "Value", as &Defined, where { "" eq ref $_ };
  522         1116  
274 7         114 subtype "Version", as &Value, where { "VSTRING" eq ref \$_ };
  7         38  
275 7         91 subtype "Str", as &Value, where { "SCALAR" eq ref \$_ };
  496         1455  
276 7 100       89 subtype "Uni", as &Str, where { utf8::is_utf8($_) || /[\x80-\xFF]/a };
  3         20  
277 7   100     96 subtype "Bin", as &Str, where { !utf8::is_utf8($_) && !/[\x80-\xFF]/a };
  4         83  
278 7         99 subtype "NonEmptyStr", as &Str, where { /\S/ };
  3         11  
279             subtype "StartsWith[start]", as &Str,
280 2         4 init_where { M = qr/^${\ quotemeta A}/ },
  2         6  
281 7         104 where { $_ =~ M };
  2         6  
282             subtype "EndsWith[end]", as &Str,
283 2         3 init_where { N = qr/${\ quotemeta A}$/ },
  2         6  
284 7         98 where { $_ =~ N };
  2         5  
285 7         99 subtype "Email", as &Str, where { /@/ };
  3         12  
286 7         144 subtype "Tel", as &Str, where { /^\+\d{7,}\z/ };
  11         48  
287 7         93 subtype "Url", as &Str, where { /^https?:\/\// };
  2         10  
288 7         92 subtype "Path", as &Str, where { /^\// };
  3         11  
289 7         92 subtype "Html", as &Str, where { /^\s*<(!doctype\s+html|html)\b/i };
  4         24  
290 7         96 subtype "StrDate", as &Str, where { /^\d{4}-\d{2}-\d{2}\z/ };
  2         10  
291 7         91 subtype "StrDateTime", as &Str, where { /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\z/ };
  2         12  
292 7         93 subtype "StrMatch[regexp]", as &Str, where { $_ =~ A };
  6         18  
293 7     7   3071 subtype "PackageName", as &Str, where { no utf8; use bytes; /^(?:[a-z]\w*(?:::[a-z]\w*)*)\z/ia };
  7     7   1676  
  7         30  
  7         204  
  7         9  
  7         40  
  7         105  
  19         97  
294 7         103 subtype "ClassName", as &PackageName, where { !!$_->can('new') };
  15         110  
295 7   100     90 subtype "RoleName", as &PackageName, where { !$_->can('new') && !!(@{"$_\::ISA"} || first { *{$_}{CODE} } values %{"$_\::"}) };
  4         36  
296 7 100 33     92 subtype "StrRat", as &Str, where { m!\s*/\s*!? &Num->include($`) && &Num->include($`): &Num->test };
  10         155  
297 7 100       87 subtype "Num", as &Str, where { looks_like_number($_) && /[\dfn]\z/i };
  192         1679  
298 7         102 subtype "Int", as &Num, where { /^[-+]?\d+\z/ };
  66         322  
299              
300 7         135 subtype "Ref", as &Defined, where { "" ne ref $_ };
  258         727  
301             subtype "Tied`[class]", as &Ref,
302 7         12 where { my $ref = reftype($_); !!(
303 7 100       70 $ref eq "HASH"? tied %$_:
    100          
    100          
304             $ref eq "ARRAY"? tied @$_:
305             $ref eq "SCALAR"? tied $$_:
306             0
307             ) }
308 7         10 awhere { my $ref = reftype($_);
309 7 100       31 $ref eq "HASH"? A eq ref tied %$_:
    100          
    100          
310             $ref eq "ARRAY"? A eq ref tied @$_:
311             $ref eq "SCALAR"? A eq ref tied $$_:
312             ""
313 7         92 };
314 7         103 subtype "LValueRef", as &Ref, where { ref $_ eq "LVALUE" };
  6         18  
315 7         97 subtype "FormatRef", as &Ref, where { ref $_ eq "FORMAT" };
  2         9  
316 7         92 subtype "CodeRef", as &Ref, where { ref $_ eq "CODE" };
  20         80  
317 7         91 subtype "NamedCode[subname]", as &CodeRef, where { Sub::Util::subname($_) ~~ A };
  3         18  
318 7         168 subtype "ProtoCode[prototype]", as &CodeRef, where { Sub::Util::prototype($_) ~~ A };
  3         13  
319 7         96 subtype "ForwardRef", as &CodeRef, where { !subref_is_reachable($_) };
  5         21  
320 7         97 subtype "ImplementRef", as &CodeRef, where { subref_is_reachable($_) };
  2         7  
321             subtype "Isa[type...]", as &CodeRef,
322             init_where {
323 5         17 my $pkg = caller(2);
324 5 100       17 SELF->{args} = [ map { External([UNIVERSAL::isa($_, 'Aion::Type')? $_: $pkg->can($_)? $pkg->can($_)->(): $_]) } ARGS ];
  13 100       181  
325             }
326             where {
327 5 100       31 my $subroutine = $Aion::Isa{pack "J", refaddr $_} or return "";
328 4         9 my $signature = $subroutine->{signature};
329 4         6 my $args = ARGS;
330 4 100       16 return "" if @$signature != @$args;
331 3         5 my $i = 0;
332 3         4 for my $type (@$args) {
333 8 100       19 return "" unless $signature->[$i++] eq $type;
334             }
335             1
336 7         95 };
  2         7  
337 7         120 subtype "RegexpRef", as &Ref, where { ref $_ eq "Regexp" };
  2         11  
338             subtype "ValueRef`[A]", as &Ref,
339 10         80 where { ref($_) ~~ ["SCALAR", "REF"] }
340 7 50       91 awhere { ref($_) ~~ ["SCALAR", "REF"] && A->include($$_) };
  3         19  
341             subtype "ScalarRef`[A]", as &ValueRef,
342 2         12 where { ref $_ eq "SCALAR" }
343 7 50       99 awhere { ref $_ eq "SCALAR" && A->include($$_) };
  2         9  
344             subtype "RefRef`[A]", as &ValueRef,
345 2         10 where { ref $_ eq "REF" }
346 7 100       104 awhere { ref $_ eq "REF" && A->include($$_) };
  2         12  
347 7         100 subtype "GlobRef", as &Ref, where { ref $_ eq "GLOB" };
  6         22  
348             subtype "FileHandle", as &GlobRef,
349 7         110 where { !!*$_{IO} };
  5         38  
350             subtype "ArrayRef`[A]", as &Ref,
351 30         81 where { ref $_ eq "ARRAY" }
352 7 100       93 awhere { my $A = A; ref $_ eq "ARRAY" && all { $A->test } @$_ };
  7         13  
  7         48  
  10         18  
353             subtype "Tuple[A...]", as &ArrayRef,
354             where {
355 17         25 my $k = 0;
356 17         36 for my $A (ARGS) {
357 24 100       66 return "" if $A->exclude($_->[$k++]);
358             }
359 13         39 $k == @$_
360 7         117 };
361             subtype "CycleTuple[A...]", as &ArrayRef,
362             where {
363 4         8 my $k = 0;
364 4         10 while($k < @$_) {
365 7         10 for my $A (ARGS) {
366 14 100       23 return "" if $A->exclude($_->[$k++]);
367             }
368             }
369 2         9 $k == @$_
370 7         127 };
371             subtype "HashRef`[A]", as &Ref,
372 14         39 where { ref $_ eq "HASH" }
373 7 100       99 awhere { my $A = A; ref $_ eq "HASH" && all { $A->test } values %$_ };
  45         90  
  45         292  
  148         269  
374             subtype "Dict[k => A, ...]", as &HashRef,
375             where {
376 7         9 my $count = 0; my $k;
  7         8  
377 7         14 for my $A (ARGS) {
378 28 100       42 $k = $A, next unless ref $A;
379 14 100       24 if(exists $_->{$k}) {
380 11 100       20 return "" if $A->exclude($_->{$k});
381 10         12 $count++;
382             } else {
383 3 100       13 return "" if !exists $A->{is_option};
384             }
385             }
386 5         22 $count == keys %$_
387 7         144 };
388             subtype "Map[K, V]", as &HashRef,
389             where {
390 5         14 my ($K, $V) = ARGS;
391 5         19 while(my ($k, $v) = each %$_) {
392 5 100 100     15 return "" unless $K->include($k) && $V->include($v);
393             }
394 3         12 return 1;
395 7         99 };
396             subtype "Object`[class]", as &Ref,
397 25         164 where { blessed($_) ne "" }
398 7 50       99 awhere { blessed($_) && $_->isa(A) };
  19         67  
399             subtype "Me", as &Object,
400 7         108 init_where { SELF->{as} = Object([caller(2)]) };
  8         158  
401 7         18 subtype "Rat", as 'Math::BigRat';
402             subtype "RegexpLike", as &Ref,
403 7 100       109 where { reftype($_) eq "REGEXP" || !!overload::Method($_, 'qr') };
  3         23  
404             subtype "CodeLike", as &Ref,
405 7 100       99 where { reftype($_) eq "CODE" || !!overload::Method($_, '&{}') };
  3         22  
406             subtype "ArrayLike`[A]", as &Ref,
407 13 100       69 where { reftype($_) eq "ARRAY" || !!overload::Method($_, '@{}') }
408 7 100       111 awhere { &ArrayLike->test && do { my $A = A; all { $A->test } @$_ }};
  3         63  
  2         105  
  2         41  
  3         15  
409             subtype "Lim[from, to?]", as &ArrayLike,
410 26 100       65 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  7         15  
  26         60  
411 7 100       104 where { A <= @$_ && @$_ <= B };
  7         15  
412             subtype "HashLike`[A]", as &Ref,
413 17 100       109 where { reftype($_) eq "HASH" || !!overload::Method($_, "%{}") }
414 7 100       108 awhere { &HashLike->test && do { my $A = A; all { $A->test } values %$_ }};
  3         78  
  2         73  
  2         183  
  2         18  
415             subtype "HasProp[p...]", as &HashLike,
416 7         104 where { my $x = $_; all { exists $x->{$_} } ARGS };
  4         6  
  4         22  
  8         33  
417             subtype "LimKeys[from, to?]", as &HashLike,
418 19 100       34 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  5         9  
  19         41  
419 7 100       100 where { A <= scalar keys %$_ && scalar keys %$_ <= B };
  6         13  
420            
421 7         106 subtype "Like", as &Str | &Object;
422             subtype "HasMethods[m...]", as &Like,
423 7         97 where { my $x = $_; all { $x->can($_) } ARGS };
  6         7  
  6         16  
  11         68  
424             subtype "Overload`[m...]", as &Like,
425 4         9 where { !!overload::Overloaded($_) }
426 7         107 awhere { my $x = $_; all { overload::Method($x, $_) } ARGS };
  2         3  
  2         8  
  2         5  
427 7         133 subtype "InstanceOf[class...]", as &Like, where { my $x = $_; all { $x->isa($_) } ARGS };
  3         6  
  3         9  
  5         31  
428 7         118 subtype "ConsumerOf[role...]", as &Like, where { my $x = $_; all { $x->DOES($_) } ARGS };
  4         5  
  4         12  
  5         32  
429 7   66     103 subtype "StrLike", as &Like, where { !blessed($_) or !!overload::Method($_, '""') };
  15         39  
430             subtype "Len[from, to?]", as &StrLike,
431 36 100       37 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  25         33  
  36         57  
432 7 100       92 where { A <= length($_) && length($_) <= B };
  13         31  
433            
434 7         109 subtype "NumLike", as &Num | &Object & Overload(["0+"]);
435 5 50   5 0 2285 sub Opened($) { Aion::Type::Lim->from(ref $_[0] eq "ARRAY"? $_[0][0]: $_[0]) };
436             subtype "Range[from, to]", as &NumLike,
437             init_where {
438 131 100       253 SELF->{args}[0] = A->inc if UNIVERSAL::isa(A, 'Aion::Type::Lim');
439 131 100       210 SELF->{args}[1] = B->dec if UNIVERSAL::isa(B, 'Aion::Type::Lim');
440             }
441 7 100       152 where { A <= $_ && $_ <= B };
  62         90  
442 7         109 subtype "Float", as Range([-(POSIX::FLT_MAX), POSIX::FLT_MAX]);
443 7         126 subtype "Double", as Range([-(DBL_MAX), DBL_MAX]);
444             subtype "Bytes[n]", as Range([]),
445             init_where {
446 12 100       29 my $_8bits = A < 8? 8: _8BITS;
447 12         19 my $N = 1 << ($_8bits * A - 1);
448 12         3364 SELF->{as} = Range([-$N, $N-1]);
449 7         121 };
450             subtype "PositiveBytes[n]", as Range([]),
451             init_where {
452 11 100       27 my $_8bits = A < 8? 8: _8BITS;
453 11         16 my $M = 1 << ($_8bits*A);
454 11         4042 SELF->{as} = Range([0, $M-1]);
455 7         108 };
456              
457 7         108 coerce &Str => from &Undef => via { "" };
  1         3  
458 7 100       106 coerce &Int => from &Num => via { int($_+($_ < 0? -.5: .5)) };
  4         43  
459 7         92 coerce &Bool => from &Any => via { !!$_ };
  2         7  
460              
461 7         85 subtype 'Join[separator]', as &Str;
462 7         94 coerce &Join, from &ArrayRef, via { join A, @$_ };
  2         5  
463              
464 7         167 subtype 'Split[separator]', as &ArrayRef;
465 7         111 coerce &Split, from &Str, via { [split A, $_] };
  2         5  
466              
467 7         126 coerce &Rat => from &StrRat => via { Math::BigRat->new($_) };
  0         0  
468              
469 7         85 subtype "PositiveNum", as &Num & Range([0, 'Inf']);
470 7         124 subtype "PositiveInt", as &Int & Range([0, 'Inf']);
471 7         110 subtype "Nat", as &Int & Range([1, 'Inf']);
472              
473 7         107 my $_none = ~&Any;
474 4087     4087 0 13243 sub None() { $_none }
475             };
476              
477             $_->keyfn(\&Aion::Type::typed_sorted_args_key) for Union[], Intersection[];
478             (Enum[])->keyfn(\&Aion::Type::sorted_args_key);
479              
480             %Aion::Type::range_lbound = map { (Scalar::Util::refaddr $_->{coerce} => $_->{name} eq 'Range'? '-Inf': 0) } Range[], Lim[], LimKeys[], Len[];
481              
482             1;
483              
484             __END__