File Coverage

blib/lib/Type/Nano.pm
Criterion Covered Total %
statement 49 111 44.1
branch 12 40 30.0
condition 7 62 11.2
subroutine 22 54 40.7
pod 3 23 13.0
total 93 290 32.0


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