File Coverage

blib/lib/Type/Alias.pm
Criterion Covered Total %
statement 118 122 96.7
branch 26 26 100.0
condition 14 16 87.5
subroutine 31 33 93.9
pod 0 4 0.0
total 189 201 94.0


line stmt bran cond sub pod time code
1             package Type::Alias;
2 13     13   2075656 use strict;
  13         171  
  13         396  
3 13     13   137 use warnings;
  13         25  
  13         595  
4              
5             our $VERSION = "0.06";
6              
7 13     13   73 use feature qw(state);
  13         26  
  13         1737  
8 13     13   94 use Carp qw(croak);
  13         26  
  13         847  
9 13     13   95 use Scalar::Util qw(blessed);
  13         25  
  13         618  
10 13     13   3794 use Types::Standard qw(Dict Tuple Undef Bool);
  13         648525  
  13         140  
11 13     13   40460 use Types::Equal qw( Eq NumEq );
  13         165545  
  13         121  
12              
13 13     13   8085 use constant AVAILABLE_BUILTIN => $] >= 5.036;
  13         30  
  13         2034  
14              
15 13     13   111 use constant True => Type::Tiny->new(name => 'True', parent => Bool, constraint => sub { $_ eq !!1 });
  13         27  
  13         78  
  0         0  
16 13     13   3348 use constant False => Type::Tiny->new(name => 'False', parent => Bool, constraint => sub { $_ eq !!0 });
  13         33  
  13         49  
  0         0  
17              
18             if (AVAILABLE_BUILTIN) {
19             eval q!
20             use experimental qw(builtin);
21             sub is_bool { builtin::is_bool($_[0]) }
22             sub created_as_number { builtin::created_as_number($_[0]) }
23             !;
24             }
25             else {
26 0     0 0 0 eval q!
  0     0 0 0  
27             sub is_bool { die 'This perl version does not support builtin::is_bool' }
28             sub created_as_number { die 'This perl version does not support builtin::created_as_number' }
29             !;
30             }
31              
32             sub import {
33 16     16   4060 my ($class, %args) = @_;
34              
35 16         48 my $target_package = caller;
36              
37 16         80 $class->_define_type($target_package, $args{type});
38 15         62 $class->_predefine_type_aliases($target_package, $args{'-alias'});
39 14         84 $class->_predefine_type_functions($target_package, $args{'-fun'});
40             }
41              
42             sub _define_type {
43 16     16   62 my ($class, $target_package, $options) = @_;
44 16   100     134 $options //= {};
45 16   100     94 my $type_name = $options->{'-as'} // 'type';
46              
47 16 100       181 if ($target_package->can($type_name)) {
48 1         111 croak "Alreay exists function '${target_package}::${type_name}'. Can specify another name: type => { -as => 'XXX' }.";
49             }
50              
51 13     13   5472 no strict qw(refs);
  13         31  
  13         524  
52 13     13   91 no warnings qw(once);
  13         44  
  13         1144  
53 15         79 *{"${target_package}::${type_name}"} = sub {
54 21     21   61880 my ($alias_name, $type_args) = @_;
55              
56 13     13   88 no strict qw(refs);
  13         29  
  13         459  
57 13     13   91 no warnings qw(redefine); # Already define empty type alias at _import_type_aliases
  13         24  
  13         2649  
58 21         68 *{"${target_package}::${alias_name}"} = generate_type_alias($type_args);
  21         147  
59             }
60 15         73 }
61              
62             sub _predefine_type_aliases {
63 15     15   42 my ($class, $target_package, $type_aliases) = @_;
64 15   100     63 $type_aliases //= [];
65              
66 15         56 for my $alias_name (@$type_aliases) {
67 17 100       175 if ($target_package->can($alias_name)) {
68 1         145 croak "Cannot predeclare type alias '${target_package}::${alias_name}'.";
69             }
70              
71 13     13   97 no strict qw(refs);
  13         27  
  13         2726  
72 16         56 *{"${target_package}::${alias_name}"} = sub :prototype() {
73 1     1   2444 croak "You should define type alias '$alias_name' before using it."
74             }
75 16         57 }
76             }
77              
78             sub _predefine_type_functions {
79 14     14   40 my ($class, $target_package, $type_functions) = @_;
80 14   100     82 $type_functions //= [];
81              
82 14         2582 for my $type_function (@$type_functions) {
83 5 100       30 if ($target_package->can($type_function)) {
84 1         129 croak "Cannot predeclare type function '${target_package}::${type_function}'.";
85             }
86              
87 13     13   114 no strict qw(refs);
  13         40  
  13         11499  
88 4         2316 *{"${target_package}::${type_function}"} = sub :prototype(;$) {
89 1     1   5001 croak "You should define type function '$type_function' before using it."
90             }
91 4         15 }
92             }
93              
94             sub to_type {
95 121     121 0 129320 my $v = shift;
96              
97 121 100       413 if (blessed($v)) {
    100          
98 86         162 _to_type_object($v);
99             }
100             elsif (ref $v) {
101 23         92 _to_type_reference($v);
102             }
103             else {
104 12         35 _to_type_scalar($v);
105             }
106             }
107              
108             sub _to_type_object {
109 86     86   137 my $v = $_[0];
110              
111 86 100 66     257 if ($v->can('check') && $v->can('get_message')) {
112 84         1799 return $v;
113             }
114             else {
115 2         292 croak 'This object is not supported: '. ref $v;
116             }
117             }
118              
119             sub _to_type_reference {
120 23     23   40 my $v = $_[0];
121              
122 23 100       93 if (ref $v eq 'ARRAY') {
    100          
    100          
123 8         27 return Tuple[ map { to_type($_) } @$v ];
  14         30  
124             }
125             elsif (ref $v eq 'HASH') {
126             return Dict[
127 9         48 map { $_ => to_type($v->{$_}) } sort { $a cmp $b } keys %$v
  20         1775  
  15         36  
128             ];
129             }
130             elsif (ref $v eq 'CODE') {
131             return sub {
132 23     23   4493 my @args;
133 23 100       67 if (@_) {
134 18 100 66     120 unless (@_ == 1 && ref $_[0] eq 'ARRAY') {
135 1         131 croak 'This type requires an array reference';
136             }
137 17         32 @args = map { to_type($_) } @{$_[0]};
  15         63  
  17         47  
138             }
139              
140 22         229 to_type($v->(@args));
141             }
142 5         31 }
143             else {
144 1         97 croak 'This reference is not supported: ' . ref $v ;
145             }
146             }
147              
148             sub _to_type_scalar {
149 12     12   22 my $v = $_[0];
150              
151 12         17 if (AVAILABLE_BUILTIN) {
152             if (!defined $v) {
153             return Undef;
154             }
155             elsif (is_bool($v)) {
156             $v ? True : False;
157             }
158             elsif (created_as_number($v)) {
159             NumEq[$v];
160             }
161             else { # string
162             Eq[$v];
163             }
164             }
165             else {
166 12 100       29 if (!defined $v) {
167 1         15 return Undef;
168             }
169             else { # string, number, bool
170 11         49 Eq[$v];
171             }
172             }
173             }
174              
175             sub generate_type_alias {
176 25     25 0 12349 my ($type_args) = @_;
177              
178 25 100 100     128 if ( (ref $type_args||'') eq 'CODE') {
179             return sub :prototype(;$) {
180 18     18   8357 state $type = to_type($type_args);
181 18         48 $type->(@_);
182 4         28 };
183             }
184             else {
185             return sub :prototype() {
186 50     50   75406 state $type = to_type($type_args);
187 50         42515 $type;
188             }
189 21         117 }
190             }
191              
192             1;
193             __END__