File Coverage

blib/lib/Mouse/Util/TypeConstraints.pm
Criterion Covered Total %
statement 151 166 90.9
branch 70 84 83.3
condition 31 44 70.4
subroutine 36 38 94.7
pod 14 22 63.6
total 302 354 85.3


line stmt bran cond sub pod time code
1             package Mouse::Util::TypeConstraints;
2 283     283   183100 use Mouse::Util; # enables strict and warnings
  283         775  
  283         1784  
3              
4 283     283   5595 use Mouse::Meta::TypeConstraint;
  283         729  
  283         6531  
5 283     283   1833 use Mouse::Exporter;
  283         749  
  283         1750  
6              
7 283     283   1721 use Carp ();
  283         681  
  283         4579  
8 283     283   1495 use Scalar::Util ();
  283         705  
  283         689315  
9              
10             Mouse::Exporter->setup_import_methods(
11             as_is => [qw(
12             as where message optimize_as
13             from via
14              
15             type subtype class_type role_type maybe_type duck_type
16             enum
17             coerce
18              
19             find_type_constraint
20             register_type_constraint
21             )],
22             );
23              
24             our @CARP_NOT = qw(Mouse::Meta::Attribute);
25              
26             my %TYPE;
27              
28             # The root type
29             $TYPE{Any} = Mouse::Meta::TypeConstraint->new(
30             name => 'Any',
31             );
32              
33             my @builtins = (
34             # $name => $parent, $code,
35              
36             # the base type
37             Item => 'Any', undef,
38              
39             # the maybe[] type
40             Maybe => 'Item', undef,
41              
42             # value types
43             Undef => 'Item', \&Undef,
44             Defined => 'Item', \&Defined,
45             Bool => 'Item', \&Bool,
46             Value => 'Defined', \&Value,
47             Str => 'Value', \&Str,
48             Num => 'Str', \&Num,
49             Int => 'Num', \&Int,
50              
51             # ref types
52             Ref => 'Defined', \&Ref,
53             ScalarRef => 'Ref', \&ScalarRef,
54             ArrayRef => 'Ref', \&ArrayRef,
55             HashRef => 'Ref', \&HashRef,
56             CodeRef => 'Ref', \&CodeRef,
57             RegexpRef => 'Ref', \&RegexpRef,
58             GlobRef => 'Ref', \&GlobRef,
59              
60             # object types
61             FileHandle => 'GlobRef', \&FileHandle,
62             Object => 'Ref', \&Object,
63              
64             # special string types
65             ClassName => 'Str', \&ClassName,
66             RoleName => 'ClassName', \&RoleName,
67             );
68              
69             while (my ($name, $parent, $code) = splice @builtins, 0, 3) {
70             $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
71             name => $name,
72             parent => $TYPE{$parent},
73             optimized => $code,
74             );
75             }
76              
77             # parametarizable types
78             $TYPE{Maybe} {constraint_generator} = \&_parameterize_Maybe_for;
79             $TYPE{ArrayRef}{constraint_generator} = \&_parameterize_ArrayRef_for;
80             $TYPE{HashRef} {constraint_generator} = \&_parameterize_HashRef_for;
81              
82             # sugars
83 59     59 1 15907 sub as ($) { (as => $_[0]) } ## no critic
84 58     58 1 7352 sub where (&) { (where => $_[0]) } ## no critic
85 7     7 0 32 sub message (&) { (message => $_[0]) } ## no critic
86 0     0 0 0 sub optimize_as (&) { (optimize_as => $_[0]) } ## no critic
87              
88 37     37 1 204 sub from { @_ }
89 37     37 1 3051 sub via (&) { $_[0] } ## no critic
90              
91             # type utilities
92              
93             sub optimized_constraints { # DEPRECATED
94 0     0 0 0 Carp::cluck('optimized_constraints() has been deprecated');
95 0         0 return \%TYPE;
96             }
97              
98             undef @builtins; # free the allocated memory
99             @builtins = keys %TYPE; # reuse it
100 1     1 1 9 sub list_all_builtin_type_constraints { @builtins }
101 5     5 1 642 sub list_all_type_constraints { keys %TYPE }
102              
103             sub _define_type {
104 853     853   1934 my $is_subtype = shift;
105 853         1905 my $name;
106             my %args;
107              
108 853 50 33     6810 if(@_ == 1 && ref $_[0] ){ # @_ : { name => $name, where => ... }
    50 66        
    100          
109 0         0 %args = %{$_[0]};
  0         0  
110             }
111             elsif(@_ == 2 && ref $_[1]) { # @_ : $name => { where => ... }
112 0         0 $name = $_[0];
113 0         0 %args = %{$_[1]};
  0         0  
114             }
115             elsif(@_ % 2) { # @_ : $name => ( where => ... )
116 850         4033 ($name, %args) = @_;
117             }
118             else{ # @_ : (name => $name, where => ...)
119 3         10 %args = @_;
120             }
121              
122 853 100       2859 if(!defined $name){
123 7         18 $name = $args{name};
124             }
125              
126 853         2139 $args{name} = $name;
127              
128 853         2348 my $parent = delete $args{as};
129 853 100 100     5286 if($is_subtype && !$parent){
130 3         11 $parent = delete $args{name};
131 3         11 $name = undef;
132             }
133              
134 853 100       2738 if(defined $parent) {
135 838         2738 $args{parent} = find_or_create_isa_type_constraint($parent);
136             }
137              
138 852 100       2697 if(defined $name){
139             # set 'package_defined_in' only if it is not a core package
140 842         2035 my $this = $args{package_defined_in};
141 842 50       2500 if(!$this){
142 842         2777 $this = caller(1);
143 842 100       10132 if($this !~ /\A Mouse \b/xms){
144 79         224 $args{package_defined_in} = $this;
145             }
146             }
147              
148 842 100       3079 if(defined $TYPE{$name}){
149 10   100     119 my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
150 10 100       41 if($this ne $that) {
151 3         7 my $note = '';
152 3 100       12 if($that eq __PACKAGE__) {
153             $note = sprintf " ('%s' is %s type constraint)",
154             $name,
155 1 50       6 scalar(grep { $name eq $_ } list_all_builtin_type_constraints())
  21         70  
156             ? 'a builtin'
157             : 'an implicitly created';
158             }
159 3         558 Carp::croak("The type constraint '$name' has already been created in $that"
160             . " and cannot be created again in $this" . $note);
161             }
162             }
163             }
164              
165 849 100       2720 $args{constraint} = delete $args{where} if exists $args{where};
166 849 100       3131 $args{optimized} = delete $args{optimized_as} if exists $args{optimized_as};
167              
168 849         5601 my $constraint = Mouse::Meta::TypeConstraint->new(%args);
169              
170 849 100       2835 if(defined $name){
171 839         4235 return $TYPE{$name} = $constraint;
172             }
173             else{
174 10         59 return $constraint;
175             }
176             }
177              
178             sub type {
179 15     15 1 104 return _define_type 0, @_;
180             }
181              
182             sub subtype {
183 825     825 1 2834 return _define_type 1, @_;
184             }
185              
186             sub coerce { # coerce $type, from $from, via { ... }, ...
187 31     31 1 72 my $type_name = shift;
188 31 100       143 my $type = find_type_constraint($type_name)
189             or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it");
190              
191 30         168 $type->_add_type_coercions(@_);
192 28         72 return;
193             }
194              
195             sub class_type {
196 570     570 1 1631 my($name, $options) = @_;
197 570   33     3257 my $class = $options->{class} || $name;
198              
199             # ClassType
200 570         5429 return subtype $name => (
201             as => 'Object',
202             optimized_as => Mouse::Util::generate_isa_predicate_for($class),
203             class => $class,
204             );
205             }
206              
207             sub role_type {
208 193     193 1 545 my($name, $options) = @_;
209 193   66     1118 my $role = $options->{role} || $name;
210              
211             # RoleType
212             return subtype $name => (
213             as => 'Object',
214             optimized_as => sub {
215 23   100 23   6025 return Scalar::Util::blessed($_[0])
216             && Mouse::Util::does_role($_[0], $role);
217             },
218 193         1233 role => $role,
219             );
220             }
221              
222             sub maybe_type {
223 1     1 0 3 my $param = shift;
224 1         7 return _find_or_create_parameterized_type($TYPE{Maybe}, $param);
225             }
226              
227             sub duck_type {
228 6     6 1 40 my($name, @methods);
229              
230 6 100       20 if(ref($_[0]) ne 'ARRAY'){
231 4         9 $name = shift;
232             }
233              
234 6 100 66     37 @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
  4         56  
235              
236             # DuckType
237             return _define_type 1, $name => (
238             as => 'Object',
239             optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
240             message => sub {
241 1     1   3 my($object) = @_;
242 1         3 my @missing = grep { !$object->can($_) } @methods;
  1         6  
243 1         19 return ref($object)
244             . ' is missing methods '
245             . Mouse::Util::quoted_english_list(@missing);
246             },
247 6         68 methods => \@methods,
248             );
249             }
250              
251             sub enum {
252 7     7 1 1352 my($name, %valid);
253              
254 7 100 66     43 if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
255 5         12 $name = shift;
256             }
257              
258 78         170 %valid = map{ $_ => undef }
259 7 100 66     34 (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_);
  3         28  
260              
261             # EnumType
262             return _define_type 1, $name => (
263             as => 'Str',
264             optimized_as => sub{
265 120   66 120   36750 return defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]};
266             },
267 7         45 );
268             }
269              
270             sub _find_or_create_regular_type{
271 128     128   477 my($spec, $create) = @_;
272              
273 128 100       550 return $TYPE{$spec} if exists $TYPE{$spec};
274              
275 13         55 my $meta = Mouse::Util::get_metaclass_by_name($spec);
276              
277 13 50       46 if(!defined $meta){
278 13 100       60 return $create ? class_type($spec) : undef;
279             }
280              
281 0 0       0 if(Mouse::Util::is_a_metarole($meta)){
282 0         0 return role_type($spec);
283             }
284             else{
285 0         0 return class_type($spec);
286             }
287             }
288              
289             sub _find_or_create_parameterized_type{
290 45     45   113 my($base, $param) = @_;
291              
292 45         428 my $name = sprintf '%s[%s]', $base->name, $param->name;
293              
294 45   100     405 $TYPE{$name} ||= $base->parameterize($param, $name);
295             }
296              
297             sub _find_or_create_union_type{
298 22 50   22   59 return if grep{ not defined } @_; # all things must be defined
  48         163  
299             my @types = sort
300 22 100       56 map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
  48         405  
  2         7  
301              
302 22         86 my $name = join '|', @types;
303              
304             # UnionType
305 22   66     199 $TYPE{$name} ||= Mouse::Meta::TypeConstraint->new(
306             name => $name,
307             type_constraints => \@types,
308             );
309             }
310              
311             # The type parser
312              
313             # param : '[' type ']' | NOTHING
314             sub _parse_param {
315 128     128   273 my($c) = @_;
316              
317 128 100       485 if($c->{spec} =~ s/^\[//){
318 44         166 my $type = _parse_type($c, 1);
319              
320 44 50       213 if($c->{spec} =~ s/^\]//){
321 44         113 return $type;
322             }
323 0         0 Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
324             }
325              
326 84         194 return undef;
327             }
328              
329             # name : [\w.:]+
330             sub _parse_name {
331 128     128   362 my($c, $create) = @_;
332              
333 128 50       852 if($c->{spec} =~ s/\A ([\w.:]+) //xms){
334 128         431 return _find_or_create_regular_type($1, $create);
335             }
336 0         0 Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
337             }
338              
339             # single_type : name param
340             sub _parse_single_type {
341 128     128   334 my($c, $create) = @_;
342              
343 128         326 my $type = _parse_name($c, $create);
344 128         429 my $param = _parse_param($c);
345              
346 128 100       349 if(defined $type){
    50          
347 118 100       283 if(defined $param){
348 44         140 return _find_or_create_parameterized_type($type, $param);
349             }
350             else {
351 74         225 return $type;
352             }
353             }
354             elsif(defined $param){
355 0         0 Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
356             }
357             else{
358 10         25 return undef;
359             }
360             }
361              
362             # type : single_type ('|' single_type)*
363             sub _parse_type {
364 108     108   261 my($c, $create) = @_;
365              
366 108         346 my $type = _parse_single_type($c, $create);
367 107 100       350 if($c->{spec}){ # can be an union type
368 58         114 my @types;
369 58         239 while($c->{spec} =~ s/^\|//){
370 20         58 push @types, _parse_single_type($c, $create);
371             }
372 58 100       209 if(@types){
373 16         67 return _find_or_create_union_type($type, @types);
374             }
375             }
376 91         190 return $type;
377             }
378              
379              
380             sub find_type_constraint {
381 972     972 1 24014 my($spec) = @_;
382 972 100 66     6983 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
383              
384 971         3864 $spec =~ s/\s+//g;
385 971         6504 return $TYPE{$spec};
386             }
387              
388             sub register_type_constraint {
389 2     2 0 14 my($constraint) = @_;
390 2 50       10 Carp::croak("No type supplied / type is not a valid type constraint")
391             unless Mouse::Util::is_a_type_constraint($constraint);
392 2         11 return $TYPE{$constraint->name} = $constraint;
393             }
394              
395             sub find_or_parse_type_constraint {
396 1251     1251 0 12183 my($spec) = @_;
397 1251 100 66     8268 return $spec if Mouse::Util::is_a_type_constraint($spec) or not defined $spec;
398              
399 1233         3690 $spec =~ tr/ \t\r\n//d;
400              
401 1233         3284 my $tc = $TYPE{$spec};
402 1233 100       3519 if(defined $tc) {
403 1169         3301 return $tc;
404             }
405              
406 64         294 my %context = (
407             spec => $spec,
408             orig => $spec,
409             );
410 64         246 $tc = _parse_type(\%context);
411              
412 63 50       246 if($context{spec}){
413 0         0 Carp::croak("Syntax error: extra elements '$context{spec}' in '$context{orig}'");
414             }
415              
416 63         251 return $TYPE{$spec} = $tc;
417             }
418              
419             sub find_or_create_does_type_constraint{
420             # XXX: Moose does not register a new role_type, but Mouse does.
421 5     5 0 19 my $tc = find_or_parse_type_constraint(@_);
422 5 50       27 return defined($tc) ? $tc : role_type(@_);
423             }
424              
425             sub find_or_create_isa_type_constraint {
426             # XXX: Moose does not register a new class_type, but Mouse does.
427 1196     1196 0 3772 my $tc = find_or_parse_type_constraint(@_);
428 1195 100       4867 return defined($tc) ? $tc : class_type(@_);
429             }
430              
431             1;
432             __END__