Commit | Line | Data |
3b46bd49 |
1 | package Mouse::Util::TypeConstraints; |
d60c78b9 |
2 | use strict; |
3 | use warnings; |
9baf5d6b |
4 | |
61a02a3a |
5 | use Carp (); |
6c169c50 |
6 | use Scalar::Util qw/blessed looks_like_number openhandle/; |
d60c78b9 |
7 | |
cceb0e25 |
8 | my %TYPE; |
7dbebb1b |
9 | my %TYPE_SOURCE; |
8a7f2a8a |
10 | my %COERCE; |
11 | my %COERCE_KEYS; |
4188b837 |
12 | |
bc37d507 |
13 | #find_type_constraint register_type_constraint |
4188b837 |
14 | sub import { |
15 | my $class = shift; |
16 | my %args = @_; |
bc37d507 |
17 | my $caller = $args{callee} || caller(0); |
4188b837 |
18 | |
19 | no strict 'refs'; |
61a02a3a |
20 | *{"$caller\::as"} = \&_as; |
21 | *{"$caller\::where"} = \&_where; |
22 | *{"$caller\::message"} = \&_message; |
23 | *{"$caller\::from"} = \&_from; |
24 | *{"$caller\::via"} = \&_via; |
0d9fea22 |
25 | *{"$caller\::type"} = \&_type; |
4188b837 |
26 | *{"$caller\::subtype"} = \&_subtype; |
27 | *{"$caller\::coerce"} = \&_coerce; |
ecc6e3b1 |
28 | *{"$caller\::class_type"} = \&_class_type; |
47f36c05 |
29 | *{"$caller\::role_type"} = \&_role_type; |
4188b837 |
30 | } |
31 | |
61a02a3a |
32 | |
33 | sub _as ($) { |
34 | as => $_[0] |
35 | } |
36 | sub _where (&) { |
37 | where => $_[0] |
38 | } |
39 | sub _message ($) { |
40 | message => $_[0] |
41 | } |
42 | |
43 | sub _from { @_ } |
44 | sub _via (&) { |
45 | $_[0] |
46 | } |
47 | |
381f326a |
48 | my $optimized_constraints; |
49 | my $optimized_constraints_base; |
50 | { |
51 | no warnings 'uninitialized'; |
cceb0e25 |
52 | %TYPE = ( |
381f326a |
53 | Any => sub { 1 }, |
54 | Item => sub { 1 }, |
55 | Bool => sub { |
56 | !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' |
57 | }, |
58 | Undef => sub { !defined($_) }, |
59 | Defined => sub { defined($_) }, |
60 | Value => sub { defined($_) && !ref($_) }, |
61 | Num => sub { !ref($_) && looks_like_number($_) }, |
62 | Int => sub { defined($_) && !ref($_) && /^-?[0-9]+$/ }, |
63 | Str => sub { defined($_) && !ref($_) }, |
64 | ClassName => sub { Mouse::is_class_loaded($_) }, |
65 | Ref => sub { ref($_) }, |
66 | |
67 | ScalarRef => sub { ref($_) eq 'SCALAR' }, |
68 | ArrayRef => sub { ref($_) eq 'ARRAY' }, |
69 | HashRef => sub { ref($_) eq 'HASH' }, |
70 | CodeRef => sub { ref($_) eq 'CODE' }, |
71 | RegexpRef => sub { ref($_) eq 'Regexp' }, |
72 | GlobRef => sub { ref($_) eq 'GLOB' }, |
73 | |
74 | FileHandle => sub { |
abe4e887 |
75 | ref($_) eq 'GLOB' && openhandle($_) |
381f326a |
76 | or |
abe4e887 |
77 | blessed($_) && $_->isa("IO::Handle") |
78 | }, |
381f326a |
79 | |
80 | Object => sub { blessed($_) && blessed($_) ne 'Regexp' }, |
8a7f2a8a |
81 | ); |
d3982c7e |
82 | |
cceb0e25 |
83 | sub optimized_constraints { \%TYPE } |
84 | my @TYPE_KEYS = keys %TYPE; |
85 | sub list_all_builtin_type_constraints { @TYPE_KEYS } |
7dbebb1b |
86 | |
87 | @TYPE_SOURCE{@TYPE_KEYS} = (__PACKAGE__) x @TYPE_KEYS; |
381f326a |
88 | } |
d3982c7e |
89 | |
0d9fea22 |
90 | sub _type { |
91 | my $pkg = caller(0); |
92 | my($name, %conf) = @_; |
0d062abb |
93 | if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) { |
7dbebb1b |
94 | Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg"; |
0d9fea22 |
95 | }; |
7dbebb1b |
96 | my $constraint = $conf{where} || do { $TYPE{delete $conf{as} || 'Any' } }; |
97 | |
98 | $TYPE_SOURCE{$name} = $pkg; |
99 | $TYPE{$name} = $constraint; |
0d9fea22 |
100 | } |
101 | |
4188b837 |
102 | sub _subtype { |
103 | my $pkg = caller(0); |
61a02a3a |
104 | my($name, %conf) = @_; |
0d062abb |
105 | if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) { |
7dbebb1b |
106 | Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg"; |
61a02a3a |
107 | }; |
7dbebb1b |
108 | my $constraint = $conf{where} || do { $TYPE{delete $conf{as} || 'Any' } }; |
109 | my $as = $conf{as} || ''; |
110 | |
111 | $TYPE_SOURCE{$name} = $pkg; |
112 | |
cceb0e25 |
113 | if ($as = $TYPE{$as}) { |
7dbebb1b |
114 | $TYPE{$name} = sub { $as->($_) && $constraint->($_) }; |
1fbefea5 |
115 | } else { |
7dbebb1b |
116 | $TYPE{$name} = $constraint; |
1fbefea5 |
117 | } |
4188b837 |
118 | } |
119 | |
120 | sub _coerce { |
61a02a3a |
121 | my($name, %conf) = @_; |
122 | |
123 | Carp::croak "Cannot find type '$name', perhaps you forgot to load it." |
cceb0e25 |
124 | unless $TYPE{$name}; |
61a02a3a |
125 | |
8a7f2a8a |
126 | unless ($COERCE{$name}) { |
127 | $COERCE{$name} = {}; |
128 | $COERCE_KEYS{$name} = []; |
129 | } |
61a02a3a |
130 | while (my($type, $code) = each %conf) { |
131 | Carp::croak "A coercion action already exists for '$type'" |
8a7f2a8a |
132 | if $COERCE{$name}->{$type}; |
61a02a3a |
133 | |
134 | Carp::croak "Could not find the type constraint ($type) to coerce from" |
cceb0e25 |
135 | unless $TYPE{$type}; |
61a02a3a |
136 | |
8a7f2a8a |
137 | push @{ $COERCE_KEYS{$name} }, $type; |
138 | $COERCE{$name}->{$type} = $code; |
61a02a3a |
139 | } |
4188b837 |
140 | } |
141 | |
ecc6e3b1 |
142 | sub _class_type { |
143 | my $pkg = caller(0); |
ecc6e3b1 |
144 | my($name, $conf) = @_; |
145 | my $class = $conf->{class}; |
61a02a3a |
146 | _subtype( |
4a957f05 |
147 | $name => where => sub { $_->isa($class) } |
61a02a3a |
148 | ); |
ecc6e3b1 |
149 | } |
150 | |
47f36c05 |
151 | sub _role_type { |
47f36c05 |
152 | my($name, $conf) = @_; |
153 | my $role = $conf->{role}; |
61a02a3a |
154 | _subtype( |
155 | $name => where => sub { |
156 | return unless defined $_ && ref($_) && $_->isa('Mouse::Object'); |
157 | $_->meta->does_role($role); |
158 | } |
159 | ); |
47f36c05 |
160 | } |
161 | |
4188b837 |
162 | sub typecast_constraints { |
eec1bb49 |
163 | my($class, $pkg, $type_constraint, $types, $value) = @_; |
eec1bb49 |
164 | |
b3b74cc6 |
165 | local $_; |
eec1bb49 |
166 | for my $type (ref($types) eq 'ARRAY' ? @{ $types } : ( $types )) { |
8a7f2a8a |
167 | next unless $COERCE{$type}; |
8a7f2a8a |
168 | for my $coerce_type (@{ $COERCE_KEYS{$type}}) { |
b3b74cc6 |
169 | $_ = $value; |
cceb0e25 |
170 | next unless $TYPE{$coerce_type}->(); |
b3b74cc6 |
171 | $_ = $value; |
172 | $_ = $COERCE{$type}->{$coerce_type}->(); |
173 | return $_ if $type_constraint->(); |
4188b837 |
174 | } |
175 | } |
4188b837 |
176 | return $value; |
177 | } |
178 | |
d60c78b9 |
179 | 1; |
180 | |
6feb83f1 |
181 | __END__ |
182 | |
183 | =head1 NAME |
184 | |
3b46bd49 |
185 | Mouse::Util::TypeConstraints - simple type constraints |
6feb83f1 |
186 | |
187 | =head1 METHODS |
188 | |
189 | =head2 optimized_constraints -> HashRef[CODE] |
190 | |
191 | Returns the simple type constraints that Mouse understands. |
192 | |
193 | =cut |
194 | |
195 | |