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 8     8   557266 use common::sense;
  8         12  
  8         50  
5 8     8   662 use warnings FATAL => 'recursion';
  8         13  
  8         737  
6              
7 8     8   2571 use Aion::Meta::Util qw/subref_is_reachable val_to_str/;
  8         25  
  8         736  
8 8     8   2442 use Aion::Type;
  8         19  
  8         311  
9 8     8   50 use Aion::Type::Lim;
  8         11  
  8         201  
10 8     8   49 use List::Util qw/all any first/;
  8         26  
  8         685  
11 8     8   69 use Exporter qw/import/;
  8         25  
  8         279  
12             require overload;
13 8     8   4158 use POSIX qw//;
  8         52101  
  8         374  
14 8     8   75 use Scalar::Util qw/looks_like_number reftype refaddr blessed/;
  8         17  
  8         603  
15 8     8   3644 use Sub::Util qw//;
  8         2382  
  8         18351  
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   166196 my ($pkg, $referent, @attributes) = @_;
24              
25 11 50       27 grep { /^Isa\((.*)\)\z/s? do { _Isa($pkg, $referent, $1); 0 }: 1 } @attributes
  11         92  
  11         60  
  11         114  
26             }
27              
28             sub _Isa {
29 11     11   55 my ($pkg, $referent, $data) = @_;
30 11         80 my $subname = Sub::Util::subname $referent;
31 11         69 $subname =~ s/^.*:://;
32              
33 11 50       39 die "Anonymous subroutine cannot use :Isa!" if $subname eq '__ANON__';
34            
35 11         2346 my @signature = eval "package $pkg; map { UNIVERSAL::isa(\$_, 'Aion::Type')? \$_: __PACKAGE__->can(\$_)? __PACKAGE__->can(\$_)->(): Aion::Types::External([\$_]) } ($data)";
36 11 50       111 die if $@;
37              
38 11 50       37 die "$pkg\::$subname has no return type!" if @signature == 0;
39              
40 11         678 require Aion::Meta::Subroutine;
41 11         78 my $subroutine = Aion::Meta::Subroutine->new(
42             pkg => $pkg,
43             subname => $subname,
44             signature => \@signature,
45             referent => $referent,
46             );
47            
48 11 100       43 if(!subref_is_reachable($referent)) {
49 2         12 $Aion::META{$pkg}{require}{$subname} = $subroutine;
50             } else {
51 9         34 my $require = delete $Aion::META{$pkg}{require}{$subname};
52 9 50       25 $require->compare($subroutine) if $require;
53              
54 9         24 my $overload = $Aion::META{$pkg}{subroutine}{$subname};
55 9 50       22 $overload->compare($subroutine) if $overload;
56            
57 9         30 $subroutine->wrap_sub;
58             }
59             }
60              
61 0         0 BEGIN {
62 8     8   58 my $INIT_ARGS = sub { @{&ARGS} = map External([$_]), &ARGS };
  1784         3035  
  1784         2354  
63 8         27 my $INIT_KW_ARGS = sub { @{&ARGS} = List::Util::pairmap { $a => External([$b]) } &ARGS };
  7         65  
  7         13  
  14         94  
64              
65             my $COMBINE_SUBS = sub {
66 0         0 my ($f1, $f2) = @_;
67 0         0 sub { $f1->(); $f2->() }
  0         0  
68 8         61 };
  0         0  
69              
70             my $COMBINE_WHERE = sub {
71 0         0 my ($f1, $f2) = @_;
72 0 0       0 sub { $f1->() && $f2->() }
73 8         22 };
  0         0  
74              
75             my $IS_PARAM = sub {
76 317         560 my @S = @_;
77 317         621 while(@S) {
78 332         442 my $arg = pop @S;
79 332 100 66     1332 return 1 if UNIVERSAL::isa($arg, 'Aion::Type') && $arg->{is_param};
80 321         467 push @S, @{$arg->{args}};
  321         792  
81             }
82             ""
83 8         37 };
  306         767  
84              
85 8         13 my $REPLACE_PARAM; $REPLACE_PARAM = sub {
86 128         240 my ($arg) = @_;
87              
88 128 100       416 return $arg unless UNIVERSAL::isa($arg, 'Aion::Type');
89              
90 116 100       353 if(my $param = $arg->{is_param}) {
91 36 100       245 return $Aion::Type::SELF->{args}->[$param - 1] if $param > 0;
92 2 50       11 return $Aion::Type::SELF->{N} if $param == -1;
93 2 50       11 return $Aion::Type::SELF->{M} if $param == -2;
94 2 50       8 return $Aion::Type::SELF if $param == -256;
95 2 50       7 return @{$Aion::Type::SELF->{args}} if $param == -1024;
  2         12  
96 0         0 die "Parameter number invalid!";
97             }
98              
99 80 100 66     425 return $arg if !$arg->{args} || !List::Util::first { UNIVERSAL::isa($_, 'Aion::Type') } @{$arg->{args}};
  76         298  
  80         302  
100              
101 64         729 $arg = bless {%$arg}, 'Aion::Type';
102 64         151 $arg->{args} = [map $REPLACE_PARAM->($_), @{$arg->{args}}];
  64         268  
103 64 50       273 $arg->init if $arg->{init};
104              
105 64         223 $arg
106 8         30 };
107              
108             my $INIT_REPLACE_PARAM = sub {
109 20         94 $Aion::Type::SELF->{as} = $REPLACE_PARAM->($Aion::Type::SELF->{as});
110 8         6214 };
111              
112             # Создание типа
113             sub subtype(@) {
114 709     709 1 2708 my $subtype = shift;
115 709         1761 my %o = @_;
116            
117 709         2157 my ($as, $init_where, $where, $awhere, $message) = delete @o{qw/as init_where where awhere message/};
118            
119 709 100       1515 die "subtype $subtype unused keys left: " . join ", ", keys %o if keys %o;
120            
121 708 50       3856 die "subtype format is Name or Name[args] or Name`[args]" if $subtype !~ /^([A-Z_]\w*)(?:(\`)?\[(.*)\])?$/i;
122 708         2338 my ($name, $is_maybe_arg, $is_arg) = ($1, $2, $3);
123              
124 708         1323 my $pkg = scalar caller;
125 708 100       750 die "subtype $subtype: ${pkg}::$name exists!" if *{"${pkg}::$name"}{CODE};
  708         3779  
126              
127 707 100       1103 if($is_maybe_arg) {
128 83 100       197 die "subtype $subtype: needs an awhere" if !$awhere;
129             } else {
130 624 100       1031 die "subtype $subtype: awhere is excess" if $awhere;
131             }
132              
133 705 100       1191 my @init = $init_where? $init_where: ();
134            
135 705         709 my $init_types = do { given($is_arg) {
  705         780  
136 705         1173 $INIT_ARGS when /^[A-Z]\w*(,\s*[A-Z]\w*)?\.\.\.$/;
137 673         817 $INIT_KW_ARGS when /^[a-z]\w*\s*=>\s*[A-Z],?\s*\.\.\.$/;
138 665         1372 when(/\b[A-Z]\b/) {
139 97         298 my @args = split /\s*,\s*/, $is_arg;
140 97         242 my @typeno = grep { $args[$_] =~ /^[A-Z]/ } 0..@args-1;
  114         393  
141 97     97   146 (sub { my ($typeno) = @_; sub {
142 531         939 my $args = &ARGS;
143 531         1477 $args->[$_] = External([$args->[$_]]) for @$typeno;
144 97         435 } })->(\@typeno);
  97         425  
145             }
146             }};
147              
148 705 100       1290 unshift @init, $init_types if $init_types;
149            
150 705 100       2105 $as = External([$as]) if defined $as;
151            
152 705 100 100     2252 unshift @init, $INIT_REPLACE_PARAM if $as && $is_arg && $IS_PARAM->($as);
      100        
153              
154             # Тут coerce - прототип - единый для всех порождаемых типов одного типа с разными аргументами
155 705 100 100     2422 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 705 100 100     2225 if($is_maybe_arg) {
    100          
166 82         180 $type->make_maybe_arg($pkg)
167             } elsif($is_arg || @init) {
168 247         592 $type->make_arg($pkg, $is_arg)
169             } else {
170 376         852 $type->make($pkg)
171             }
172             }
173             }
174              
175 689     689 1 2171 sub as(@) { (as => @_) }
176 106     106 1 326 sub init_where(&@) { (init_where => @_) }
177 560     560 1 2657 sub where(&@) { (where => @_) }
178 83     83 1 1892 sub awhere(&@) { (awhere => @_) }
179 2     2 1 16 sub message(&@) { (message => @_) }
180              
181 46     46 1 232 sub SELF() { $Aion::Type::SELF }
182             sub ARGS() {
183 4567 100   4567 1 8336 return $Aion::Type::SELF->{is_param_args} if $Aion::Type::SELF->{is_param_args};
184 2118         6870 wantarray? @{$Aion::Type::SELF->{args}}: $Aion::Type::SELF->{args}
185 4566 100       10194 }
186 423     423 1 2420 sub A() { $Aion::Type::SELF->{args}[0] }
187 219     219 1 1988 sub B() { $Aion::Type::SELF->{args}[1] }
188 2     2 1 17 sub C() { $Aion::Type::SELF->{args}[2] }
189 1     1 1 8 sub D() { $Aion::Type::SELF->{args}[3] }
190              
191 80     80 1 329 sub M() :lvalue { $Aion::Type::SELF->{M} }
192 9     9 1 78 sub N() :lvalue { $Aion::Type::SELF->{N} }
193              
194             # Создание транслятора. У типа может быть сколько угодно трансляторов из других типов
195             # coerce Type, from OtherType, via {...}
196             sub coerce(@) {
197 58     58 1 3803 my ($type, %o) = @_;
198 58         188 my ($from, $via) = delete @o{qw/from via/};
199              
200 58 100       172 die "coerce $type unused keys left: " . join ", ", keys %o if keys %o;
201 57 100       206 die "coerce $type not Aion::Type!" unless UNIVERSAL::isa($type, "Aion::Type");
202 56 100       151 die "coerce $type: from is'nt Aion::Type!" unless UNIVERSAL::isa($from, "Aion::Type");
203 54 100       140 die "coerce $type: via is not subroutine!" unless ref $via eq "CODE";
204              
205 52         65 push @{$type->{coerce}}, [$from, $via];
  52         139  
206 52         92 return;
207             }
208              
209 54     54 1 293 sub from($) { (from => $_[0]) }
210 52     52 1 169 sub via(&) { (via => $_[0]) }
211              
212 8         14 use constant DBL_MAX => do {
213 8         14 my $ieee_dbl_max_str = '1.7976931348623157e+308';
214 8 50       18031 ($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 8     8   69 };
  8         13  
219              
220             sub _8BITS() {
221 1     1   6 undef *_8BITS;
222 1         11 require Math::BigInt;
223 1         7 my $_8bits = Math::BigInt->new(8);
224 1         307 constant->import(_8BITS => $_8bits);
225 1         5 $_8bits
226             }
227              
228 0         0 BEGIN {
229              
230 8     8   37242 subtype "Any";
231 8         152 subtype "Control", as &Any;
232             subtype "Union[A, B...]", as &Control,
233 8         134 where { my $val = $_; any { $_->include($val) } ARGS };
  168         324  
  168         855  
  229         556  
234             subtype "Intersection[A, B...]", as &Control,
235 8         182 where { my $val = $_; all { $_->include($val) } ARGS };
  42         79  
  42         245  
  77         225  
236             subtype "Exclude[A]", as &Control,
237 8         174 where { !A->test };
  11         33  
238             subtype "Option[A]", as &Control,
239 4         12 init_where { SELF->{is_option} = 1 }
240 8         132 where { A->test };
  2         6  
241             subtype "Wantarray[A, S]", as &Control,
242 1         5 init_where { SELF->{is_wantarray} = 1 }
243 8         156 where { ... };
  0         0  
244              
245 8         147 subtype "Item", as &Any;
246             sub External($) {
247 7074     7074 0 16721 local $_ = $_[0][0];
248             UNIVERSAL::isa($_, 'Aion::Type')? $_:
249 7074 100 66     18776 defined($_) && ref $_ eq ""? Object([$_]): do {
    100          
250 6 50 66     47 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   432 ? (coerce => [[&Any, (sub { my ($ex) = @_; sub { $ex->coerce } })->($_)]])
  4         12  
  4         30  
  3         17  
258             : (),
259             )
260             }
261             }
262 8 100       132 subtype "Bool", as &Item, where { ref $_ eq "" and /^(1|0|)\z/ };
  18         199  
263             subtype "BoolLike", as &Item, where {
264 8 100       25 return 1 if overload::Method($_, 'bool');
265 6         150 my $m = overload::Method($_, '0+');
266 8 100       141 Bool()->include($m ? $m->($_) : $_) };
  6         175  
267             subtype "Enum[e...]", as &Item,
268 57         137 init_where { M = +{ map {($_ => $_)} ARGS } }
  98         288  
269 8         162 where { exists M->{$_} };
  15         36  
270 8         156 subtype "Undef", as &Item, where { !defined $_ };
  46         124  
271 8         180 subtype "Maybe[A]", as &Undef | A;
272 8         181 subtype "Defined", as &Item, where { defined $_ };
  794         2226  
273 8         174 subtype "Value", as &Defined, where { "" eq ref $_ };
  527         1367  
274 8         152 subtype "Version", as &Value, where { "VSTRING" eq ref \$_ };
  7         44  
275 8         137 subtype "Str", as &Value, where { "SCALAR" eq ref \$_ };
  501         1674  
276 8 100       154 subtype "Uni", as &Str, where { utf8::is_utf8($_) || /[\x80-\xFF]/a };
  3         33  
277 8   100     135 subtype "Bin", as &Str, where { !utf8::is_utf8($_) && !/[\x80-\xFF]/a };
  4         87  
278 8         126 subtype "NonEmptyStr", as &Str, where { /\S/ };
  3         20  
279             subtype "StartsWith[start]", as &Str,
280 2         4 init_where { M = qr/^${\ quotemeta A}/ },
  2         8  
281 8         153 where { $_ =~ M };
  2         6  
282             subtype "EndsWith[end]", as &Str,
283 2         4 init_where { N = qr/${\ quotemeta A}$/ },
  2         10  
284 8         173 where { $_ =~ N };
  2         7  
285 8         161 subtype "Email", as &Str, where { /@/ };
  3         23  
286 8         127 subtype "Tel", as &Str, where { /^\+\d{7,}\z/ };
  11         71  
287 8         159 subtype "Url", as &Str, where { /^https?:\/\// };
  2         18  
288 8         164 subtype "Path", as &Str, where { /^\// };
  3         23  
289 8         133 subtype "Html", as &Str, where { /^\s*<(!doctype\s+html|html)\b/i };
  4         33  
290 8         135 subtype "StrDate", as &Str, where { /^\d{4}-\d{2}-\d{2}\z/ };
  2         20  
291 8         143 subtype "StrDateTime", as &Str, where { /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\z/ };
  2         31  
292 8         131 subtype "StrMatch[regexp]", as &Str, where { $_ =~ A };
  6         24  
293 8     8   4531 subtype "PackageName", as &Str, where { no utf8; use bytes; /^(?:[a-z]\w*(?:::[a-z]\w*)*)\z/ia };
  8     8   2065  
  8         40  
  8         251  
  8         12  
  8         56  
  8         165  
  19         149  
294 8         141 subtype "ClassName", as &PackageName, where { !!$_->can('new') };
  15         180  
295 8   100     144 subtype "RoleName", as &PackageName, where { !$_->can('new') && !!(@{"$_\::ISA"} || first { *{$_}{CODE} } values %{"$_\::"}) };
  4         77  
296 8 100 33     197 subtype "StrRat", as &Str, where { m!\s*/\s*!? &Num->include($`) && &Num->include($`): &Num->test };
  10         208  
297 8 100       191 subtype "Num", as &Str, where { looks_like_number($_) && /[\dfn]\z/i };
  197         2498  
298 8         132 subtype "Int", as &Num, where { /^[-+]?\d+\z/ };
  68         405  
299              
300 8         181 subtype "Ref", as &Defined, where { "" ne ref $_ };
  258         773  
301             subtype "Tied`[class]", as &Ref,
302 7         13 where { my $ref = reftype($_); !!(
303 7 100       78 $ref eq "HASH"? tied %$_:
    100          
    100          
304             $ref eq "ARRAY"? tied @$_:
305             $ref eq "SCALAR"? tied $$_:
306             0
307             ) }
308 7         15 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 8         158 };
314 8         163 subtype "LValueRef", as &Ref, where { ref $_ eq "LVALUE" };
  6         18  
315 8         186 subtype "FormatRef", as &Ref, where { ref $_ eq "FORMAT" };
  2         8  
316 8         147 subtype "CodeRef", as &Ref, where { ref $_ eq "CODE" };
  20         53  
317 8         127 subtype "NamedCode[subname]", as &CodeRef, where { Sub::Util::subname($_) ~~ A };
  3         17  
318 8         301 subtype "ProtoCode[prototype]", as &CodeRef, where { Sub::Util::prototype($_) ~~ A };
  3         12  
319 8         143 subtype "ForwardRef", as &CodeRef, where { !subref_is_reachable($_) };
  5         14  
320 8         173 subtype "ImplementRef", as &CodeRef, where { subref_is_reachable($_) };
  2         8  
321             subtype "Isa[type...]", as &CodeRef,
322             init_where {
323 5         31 my $pkg = caller(2);
324 5 100       14 SELF->{args} = [ map { External([UNIVERSAL::isa($_, 'Aion::Type')? $_: $pkg->can($_)? $pkg->can($_)->(): $_]) } ARGS ];
  13 100       117  
325             }
326             where {
327 5 100       45 my $subroutine = $Aion::Isa{pack "J", refaddr $_} or return "";
328 4         6 my $signature = $subroutine->{signature};
329 4         8 my $args = ARGS;
330 4 100       12 return "" if @$signature != @$args;
331 3         4 my $i = 0;
332 3         4 for my $type (@$args) {
333 8 100       15 return "" unless $signature->[$i++] eq $type;
334             }
335             1
336 8         138 };
  2         9  
337 8         174 subtype "RegexpRef", as &Ref, where { ref $_ eq "Regexp" };
  2         8  
338             subtype "ValueRef`[A]", as &Ref,
339 10         42 where { ref($_) ~~ ["SCALAR", "REF"] }
340 8 50       155 awhere { ref($_) ~~ ["SCALAR", "REF"] && A->include($$_) };
  3         13  
341             subtype "ScalarRef`[A]", as &ValueRef,
342 2         8 where { ref $_ eq "SCALAR" }
343 8 50       175 awhere { ref $_ eq "SCALAR" && A->include($$_) };
  2         8  
344             subtype "RefRef`[A]", as &ValueRef,
345 2         7 where { ref $_ eq "REF" }
346 8 100       161 awhere { ref $_ eq "REF" && A->include($$_) };
  2         12  
347 8         150 subtype "GlobRef", as &Ref, where { ref $_ eq "GLOB" };
  6         16  
348             subtype "FileHandle", as &GlobRef,
349 8         189 where { !!*$_{IO} };
  5         27  
350             subtype "ArrayRef`[A]", as &Ref,
351 30         132 where { ref $_ eq "ARRAY" }
352 8 100       159 awhere { my $A = A; ref $_ eq "ARRAY" && all { $A->test } @$_ };
  7         19  
  7         81  
  10         46  
353             subtype "Tuple[A...]", as &ArrayRef,
354             where {
355 17         30 my $k = 0;
356 17         49 for my $A (ARGS) {
357 24 100       75 return "" if $A->exclude($_->[$k++]);
358             }
359 13         54 $k == @$_
360 8         167 };
361             subtype "CycleTuple[A...]", as &ArrayRef,
362             where {
363 4         6 my $k = 0;
364 4         11 while($k < @$_) {
365 7         14 for my $A (ARGS) {
366 14 100       36 return "" if $A->exclude($_->[$k++]);
367             }
368             }
369 2         8 $k == @$_
370 8         148 };
371             subtype "HashRef`[A]", as &Ref,
372 14         59 where { ref $_ eq "HASH" }
373 8 100       163 awhere { my $A = A; ref $_ eq "HASH" && all { $A->test } values %$_ };
  45         69  
  45         283  
  147         191  
374             subtype "Dict[k => A, ...]", as &HashRef,
375             where {
376 7         11 my $count = 0; my $k;
  7         11  
377 7         18 for my $A (ARGS) {
378 28 100       48 $k = $A, next unless ref $A;
379 14 100       27 if(exists $_->{$k}) {
380 11 100       27 return "" if $A->exclude($_->{$k});
381 10         17 $count++;
382             } else {
383 3 100       13 return "" if !exists $A->{is_option};
384             }
385             }
386 5         30 $count == keys %$_
387 8         130 };
388             subtype "Map[K, V]", as &HashRef,
389             where {
390 5         13 my ($K, $V) = ARGS;
391 5         20 while(my ($k, $v) = each %$_) {
392 5 100 100     12 return "" unless $K->include($k) && $V->include($v);
393             }
394 3         15 return 1;
395 8         143 };
396             subtype "Object`[class]", as &Ref,
397 25         141 where { blessed($_) ne "" }
398 8 50       127 awhere { blessed($_) && $_->isa(A) };
  19         89  
399             subtype "Me", as &Object,
400 8         161 init_where { SELF->{as} = Object([caller(2)]) };
  8         261  
401 8         29 subtype "Rat", as 'Math::BigRat';
402             subtype "RegexpLike", as &Ref,
403 8 100       130 where { reftype($_) eq "REGEXP" || !!overload::Method($_, 'qr') };
  3         23  
404             subtype "CodeLike", as &Ref,
405 8 100       158 where { reftype($_) eq "CODE" || !!overload::Method($_, '&{}') };
  3         22  
406             subtype "ArrayLike`[A]", as &Ref,
407 13 100       54 where { reftype($_) eq "ARRAY" || !!overload::Method($_, '@{}') }
408 8 100       126 awhere { &ArrayLike->test && do { my $A = A; all { $A->test } @$_ }};
  3         85  
  2         80  
  2         48  
  3         14  
409             subtype "Lim[from, to?]", as &ArrayLike,
410 27 100       30 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  7         9  
  27         47  
411 8 100       131 where { A <= @$_ && @$_ <= B };
  7         13  
412             subtype "HashLike`[A]", as &Ref,
413 17 100       74 where { reftype($_) eq "HASH" || !!overload::Method($_, "%{}") }
414 8 100       160 awhere { &HashLike->test && do { my $A = A; all { $A->test } values %$_ }};
  3         66  
  2         72  
  2         40  
  2         37  
415             subtype "HasProp[p...]", as &HashLike,
416 8         136 where { my $x = $_; all { exists $x->{$_} } ARGS };
  4         7  
  4         16  
  8         28  
417             subtype "LimKeys[from, to?]", as &HashLike,
418 20 100       47 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  5         9  
  20         41  
419 8 100       149 where { A <= scalar keys %$_ && scalar keys %$_ <= B };
  6         13  
420            
421 8         163 subtype "Like", as &Str | &Object;
422             subtype "HasMethods[m...]", as &Like,
423 8         129 where { my $x = $_; all { $x->can($_) } ARGS };
  6         7  
  6         16  
  11         59  
424             subtype "Overload`[m...]", as &Like,
425 4         8 where { !!overload::Overloaded($_) }
426 8         132 awhere { my $x = $_; all { overload::Method($x, $_) } ARGS };
  2         5  
  2         7  
  2         5  
427 8         151 subtype "InstanceOf[class...]", as &Like, where { my $x = $_; all { $x->isa($_) } ARGS };
  3         5  
  3         7  
  5         196  
428 8         146 subtype "ConsumerOf[role...]", as &Like, where { my $x = $_; all { $x->DOES($_) } ARGS };
  4         6  
  4         11  
  5         32  
429 8   66     171 subtype "StrLike", as &Like, where { !blessed($_) or !!overload::Method($_, '""') };
  15         91  
430             subtype "Len[from, to?]", as &StrLike,
431 37 100       77 init_where { unshift @{&ARGS}, 0 if @{&ARGS} == 1; }
  25         61  
  37         98  
432 8 100       144 where { A <= length($_) && length($_) <= B };
  13         34  
433            
434 8         175 subtype "NumLike", as &Num | &Object & Overload(["0+"]);
435 5 50   5 0 2515 sub Opened($) { Aion::Type::Lim->from(ref $_[0] eq "ARRAY"? $_[0][0]: $_[0]) };
436             subtype "Range[from, to]", as &NumLike,
437             init_where {
438 139 100       318 SELF->{args}[0] = A->inc if UNIVERSAL::isa(A, 'Aion::Type::Lim');
439 139 100       332 SELF->{args}[1] = B->dec if UNIVERSAL::isa(B, 'Aion::Type::Lim');
440             }
441 8 100       142 where { A <= $_ && $_ <= B };
  65         179  
442 8         171 subtype "Float", as Range([-(POSIX::FLT_MAX), POSIX::FLT_MAX]);
443 8         161 subtype "Double", as Range([-(DBL_MAX), DBL_MAX]);
444             subtype "Bytes[n]", as Range([]),
445             init_where {
446 12 100       50 my $_8bits = A < 8? 8: _8BITS;
447 12         45 my $N = 1 << ($_8bits * A - 1);
448 12         6630 SELF->{as} = Range([-$N, $N-1]);
449 8         168 };
450             subtype "PositiveBytes[n]", as Range([]),
451             init_where {
452 11 100       40 my $_8bits = A < 8? 8: _8BITS;
453 11         30 my $M = 1 << ($_8bits*A);
454 11         6380 SELF->{as} = Range([0, $M-1]);
455 8         136 };
456              
457 8         165 coerce &Str => from &Undef => via { "" };
  1         5  
458 8 100       129 coerce &Int => from &Num => via { int($_+($_ < 0? -.5: .5)) };
  4         45  
459 8         138 coerce &Bool => from &Any => via { !!$_ };
  2         9  
460              
461 8         105 subtype 'Join[separator]', as &Str;
462 8         143 coerce &Join, from &ArrayRef, via { join A, @$_ };
  2         6  
463              
464 8         195 subtype 'Split[separator]', as &ArrayRef;
465 8         144 coerce &Split, from &Str, via { [split A, $_] };
  2         6  
466              
467 8         141 coerce &Rat => from &StrRat => via { Math::BigRat->new($_) };
  0         0  
468              
469 8         124 subtype "PositiveNum", as &Num & Range([0, 'Inf']);
470 8         138 subtype "PositiveInt", as &Int & Range([0, 'Inf']);
471 8         178 subtype "Nat", as &Int & Range([1, 'Inf']);
472              
473 8         153 my $_none = ~&Any;
474 4087     4087 0 18989 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__