File Coverage

blib/lib/Type/Nano.pm
Criterion Covered Total %
statement 118 129 91.4
branch 33 44 75.0
condition 37 62 59.6
subroutine 52 56 92.8
pod 3 23 13.0
total 243 314 77.3


line stmt bran cond sub pod time code
1             package Type::Nano;
2              
3 9     9   522406 use strict;
  9         96  
  9         237  
4 9     9   38 use warnings;
  9         15  
  9         263  
5 9     9   44 use vars qw( %TYPES $AUTHORITY $VERSION @ISA @EXPORT_OK );
  9         14  
  9         713  
6              
7 9     9   52 use Scalar::Util ();
  9         30  
  9         15414  
8              
9             $AUTHORITY = 'cpan:TOBYINK';
10             $VERSION = '1.001';
11              
12             require Exporter;
13             @ISA = qw( Exporter );
14              
15             eval {
16             require Exporter::Tiny;
17             @ISA = qw( Exporter::Tiny );
18             };
19              
20             @EXPORT_OK = qw(
21             Any Defined Undef Ref ArrayRef HashRef CodeRef Object Str Bool Num Int Object
22             class_type role_type duck_type union intersection enum type
23             );
24              
25             # Built-in type constraints
26             #
27              
28             sub Any () {
29             $TYPES{Any} ||= __PACKAGE__->new(
30             name => 'Any',
31 250     250   533 constraint => sub { !!1 },
32 12   66 12 0 1150 );
33             }
34              
35             sub Defined () {
36             $TYPES{Defined} ||= __PACKAGE__->new(
37             name => 'Defined',
38             parent => Any,
39 194     194   352 constraint => sub { defined $_ },
40 10   66 10 0 1904 );
41             }
42              
43             sub Undef () {
44             $TYPES{Undef} ||= __PACKAGE__->new(
45             name => 'Undef',
46             parent => Any,
47 16     16   31 constraint => sub { !defined $_ },
48 1   33 1 0 2102 );
49             }
50              
51             sub Ref () {
52             $TYPES{Ref} ||= __PACKAGE__->new(
53             name => 'Ref',
54             parent => Defined,
55 107     107   252 constraint => sub { ref $_ },
56 10   66 10 0 1852 );
57             }
58              
59             sub ArrayRef () {
60             $TYPES{ArrayRef} ||= __PACKAGE__->new(
61             name => 'ArrayRef',
62             parent => Ref,
63 12     12   42 constraint => sub { ref $_ eq 'ARRAY' },
64 3   66 3 0 2127 );
65             }
66              
67             sub HashRef () {
68             $TYPES{HashRef} ||= __PACKAGE__->new(
69             name => 'HashRef',
70             parent => Ref,
71 10     10   30 constraint => sub { ref $_ eq 'HASH' },
72 3   66 3 0 1869 );
73             }
74              
75             sub CodeRef () {
76             $TYPES{CodeRef} ||= __PACKAGE__->new(
77             name => 'CodeRef',
78             parent => Ref,
79 6     6   17 constraint => sub { ref $_ eq 'CODE' },
80 1   33 1 0 1851 );
81             }
82              
83             sub Object () {
84             $TYPES{Object} ||= __PACKAGE__->new(
85             name => 'Object',
86             parent => Ref,
87 24     24   76 constraint => sub { Scalar::Util::blessed($_) },
88 4   33 4 0 1846 );
89             }
90              
91             sub Bool () {
92             $TYPES{Bool} ||= __PACKAGE__->new(
93             name => 'Bool',
94             parent => Any,
95 24 100 66 24   147 constraint => sub { !defined($_) or (!ref($_) and { 1 => 1, 0 => 1, '' => 1 }->{$_}) },
96 3   66 3 0 1918 );
97             }
98              
99             sub Str () {
100             $TYPES{Str} ||= __PACKAGE__->new(
101             name => 'Str',
102             parent => Defined,
103 63     63   138 constraint => sub { !ref $_ },
104 5   66 5 0 2084 );
105             }
106              
107             sub Num () {
108             $TYPES{Num} ||= __PACKAGE__->new(
109             name => 'Num',
110             parent => Str,
111 28     28   105 constraint => sub { Scalar::Util::looks_like_number($_) },
112 4   66 4 0 1839 );
113             }
114              
115             sub Int () {
116             $TYPES{Int} ||= __PACKAGE__->new(
117             name => 'Int',
118             parent => Num,
119 13     13   79 constraint => sub { /\A-?[0-9]+\z/ },
120 4   66 4 0 1907 );
121             }
122              
123             sub class_type ($) {
124 1     1 0 103 my $class = shift;
125             $TYPES{CLASS}{$class} ||= __PACKAGE__->new(
126             name => $class,
127             parent => Object,
128 4     4   40 constraint => sub { $_->isa($class) },
129 1   33     9 class => $class,
130             );
131             }
132              
133             sub role_type ($) {
134 1     1 0 71 my $role = shift;
135             $TYPES{ROLE}{$role} ||= __PACKAGE__->new(
136             name => $role,
137             parent => Object,
138 2   33 2   13 constraint => sub { my $meth = $_->can('DOES') || $_->can('isa'); $_->$meth($role) },
  2         13  
139 1   33     7 role => $role,
140             );
141             }
142              
143             sub duck_type {
144 3 100   3 0 104 my $name = ref($_[0]) ? '__ANON__' : shift;
145 3 100       8 my @methods = sort( ref($_[0]) ? @{+shift} : @_ );
  2         7  
146 3         7 my $methods = join "|", @methods;
147             $TYPES{DUCK}{$methods} ||= __PACKAGE__->new(
148             name => $name,
149             parent => Object,
150 12   100 12   13 constraint => sub { my $obj = $_; $obj->can($_)||return !!0 for @methods; !!1 },
  12         57  
  9         34  
151 3   66     11 methods => \@methods,
152             );
153             }
154              
155             sub enum {
156 2 100   2 0 80 my $name = ref($_[0]) ? '__ANON__' : shift;
157 2 50       6 my @values = sort( ref($_[0]) ? @{+shift} : @_ );
  2         8  
158 2         10 my $values = join "|", map quotemeta, @values;
159 2         26 my $regexp = qr/\A(?:$values)\z/;
160             $TYPES{ENUM}{$values} ||= __PACKAGE__->new(
161             name => $name,
162             parent => Str,
163 6     6   43 constraint => sub { $_ =~ $regexp },
164 2   66     12 values => \@values,
165             );
166             }
167              
168             sub union {
169 2 100   2 0 8 my $name = ref($_[0]) ? '__ANON__' : shift;
170 2 50       6 my @types = ref($_[0]) ? @{+shift} : @_;
  2         4  
171             __PACKAGE__->new(
172             name => $name,
173 6   100 6   7 constraint => sub { my $val = $_; $_->check($val) && return !!1 for @types; !!0 },
  6         32  
  2         10  
174 2         28 types => \@types,
175             );
176             }
177              
178             sub intersection {
179 2 100   2 0 5 my $name = ref($_[0]) ? '__ANON__' : shift;
180 2 50       5 my @types = ref($_[0]) ? @{+shift} : @_;
  2         4  
181             __PACKAGE__->new(
182             name => $name,
183 8   100 8   9 constraint => sub { my $val = $_; $_->check($val) || return !!0 for @types; !!1 },
  8         18  
  4         25  
184 2         18 types => \@types,
185             );
186             }
187              
188             sub type {
189 2 100   2 0 78 my $name = ref($_[0]) ? '__ANON__' : shift;
190 2         4 my $coderef = shift;
191 2         8 __PACKAGE__->new(
192             name => $name,
193             constraint => $coderef,
194             );
195             }
196              
197             # OO interface
198             #
199              
200             sub DOES {
201 2     2 0 5 my ( $proto, $role ) = ( shift, @_ );
202            
203             return !!1 if {
204             'Type::API::Constraint' => 1,
205             'Type::API::Constraint::Constructor' => 1,
206 2 50       17 }->{$role};
207 0 0       0 "UNIVERSAL"->can("DOES") ? $proto->SUPER::DOES(@_) : $proto->isa(@_);
208             }
209              
210             sub new { # Type::API::Constraint::Constructor
211 54 50   54 1 121 my $class = ref($_[0]) ? ref(shift) : shift;
212 54 50       175 my $self = bless { @_ == 1 ? %{+shift} : @_ } => $class;
  0         0  
213            
214 54   50 0   404 $self->{constraint} ||= sub { !!1 };
  0         0  
215 54 50       98 unless ($self->{name}) {
216 0         0 require Carp;
217 0         0 Carp::croak("Requires both `name` and `constraint`");
218             }
219            
220 54         112 $self;
221             }
222              
223             sub check { # Type::API::Constraint
224 882     882 1 55441 my ( $self, $value ) = ( shift, @_ );
225            
226 882 100       1434 if ($self->{parent}) {
227 610 100       848 return unless $self->{parent}->check($value);
228             }
229            
230 796         1002 local $_ = $value;
231 796         1132 $self->{constraint}->($value);
232             }
233              
234             sub get_message { # Type::API::Constraint
235 8     8 1 480 my ( $self, $value ) = ( shift, @_ );
236            
237 8         32 require B;
238             !defined($value)
239             ? sprintf("Undef did not pass type constraint %s", $self->{name})
240             : ref($value)
241             ? sprintf("Reference %s did not pass type constraint %s", $value, $self->{name})
242 8 100       310 : sprintf("Value %s did not pass type constraint %s", B::perlstring($value), $self->{name});
    100          
243             }
244              
245             # Overloading
246             #
247              
248             {
249             my $nil = sub {};
250             sub _install_overloads
251             {
252 9     9   65 no strict 'refs';
  9         16  
  9         330  
253 9     9   47 no warnings 'redefine', 'once';
  9         14  
  9         7338  
254 9 50   9   42 if ($] < 5.010) {
255 0         0 require overload;
256 0         0 push @_, fallback => 1;
257 0         0 goto \&overload::OVERLOAD;
258             };
259 9         18 my $class = shift;
260 9     0   19 *{$class . '::(('} = sub {};
  9         67  
261 9     0   27 *{$class . '::()'} = sub {};
  9         28  
262 9         16 *{$class . '::()'} = do { my $x = 1; \$x };
  9         22  
  9         12  
  9         15  
263 9         45 while (@_)
264             {
265 27         39 my $f = shift;
266             #*{$class . '::(' . $f} = $nil; # cargo culting overload.pm
267             #*{$class . '::(' . $f} = shift;
268 27 50   0   60 *{$class . '::(' . $f} = ref $_[0] ? shift : do { my $m = shift; sub { shift->$m(@_) } };
  27         99  
  0         0  
  0         0  
  0         0  
269             }
270             }
271             }
272              
273             __PACKAGE__ ->_install_overloads(
274 1319     1319   2798 'bool' => sub { 1 },
275 2     2   11 '""' => sub { shift->{name} },
276             '&{}' => sub {
277 4     4   9 my $self = shift;
278             sub {
279 4     4   6 my ($value) = @_;
280 4 100       10 $self->check($value) or do {
281 2         14 require Carp;
282 2         4 Carp::croak($self->get_message($value));
283             };
284 4         18 };
285             },
286             );
287              
288             1;
289              
290             __END__