File Coverage

blib/lib/Types/TypedCodeRef/Factory.pm
Criterion Covered Total %
statement 104 104 100.0
branch 22 22 100.0
condition 5 6 83.3
subroutine 26 26 100.0
pod 0 6 0.0
total 157 164 95.7


line stmt bran cond sub pod time code
1             package Types::TypedCodeRef::Factory;
2 7     7   1141284 use 5.010001;
  7         52  
3 7     7   44 use strict;
  7         15  
  7         153  
4 7     7   34 use warnings;
  7         15  
  7         198  
5 7     7   42 use utf8;
  7         17  
  7         56  
6 7     7   4067 use Moo;
  7         49184  
  7         45  
7 7     7   9970 use overload ();
  7         18  
  7         95  
8 7     7   36 use Carp ();
  7         16  
  7         100  
9 7     7   773 use Type::Tiny ();
  7         23897  
  7         125  
10 7     7   2737 use Type::Coercion;
  7         19972  
  7         270  
11 7     7   4637 use Type::Params qw( compile compile_named multisig );
  7         341602  
  7         103  
12 7     7   3880 use Types::Standard -types;
  7         21  
  7         70  
13 7     7   37501 use Scalar::Util;
  7         19  
  7         449  
14 7     7   4181 use Sub::Meta;
  7         75532  
  7         285  
15 7     7   3789 use Sub::WrapInType qw( wrap_sub );
  7         462360  
  7         761  
16 7     7   73 use Carp qw( croak );
  7         17  
  7         377  
17 7     7   59 use namespace::autoclean;
  7         17  
  7         77  
18              
19             our @CARP_NOT;
20              
21             sub _is_callable {
22 40     40   125982 my $callable = shift;
23 40         144 my $reftype = Scalar::Util::reftype($callable);
24 40 100 100     321 ( defined $reftype && $reftype eq 'CODE' ) || defined overload::Method($callable, '&{}');
25             }
26              
27             my $CallableType = Type::Tiny->new(
28             name => 'Callable',
29             constraint => \&_is_callable,
30             );
31              
32             has name => (
33             is => 'ro',
34             isa => Str,
35             default => 'TypedCodeRef',
36             );
37              
38             has sub_meta_finders => (
39             is => 'ro',
40             isa => ArrayRef[CodeRef],
41             required => 1,
42             );
43              
44             sub name_generator {
45             sub {
46 16     16   599 my ($type_name, @type_parameters) = @_;
47 16         37 $type_name . do {
48 16 100       92 if (@type_parameters == 2) {
    100          
49 11         44 my ($params_types, $return_types) = @type_parameters;
50              
51 11         25 my $params_types_name = do {
52 11 100       65 if (ref $params_types eq 'ARRAY') {
    100          
53 6         20 "[@{[ join(', ', @$params_types) ]}]"
  6         95  
54             }
55             elsif (ref $params_types eq 'HASH') {
56 3         11 "{ @{[ join( ', ', map { qq{$_ => $params_types->{$_}} } sort keys %$params_types) ]} }"
  3         19  
  6         72  
57             }
58             else {
59 2         7 $params_types
60             }
61             };
62              
63 11 100       222 my $return_types_name = ref $return_types eq 'ARRAY'
64 2         9 ? "[@{[ join(', ', @$return_types) ]}]"
65             : $return_types;
66              
67 11         102 "[ $params_types_name => $return_types_name ]";
68             }
69             elsif (@type_parameters == 1) {
70 2         28 "[$type_parameters[0]]";
71             }
72             else {
73 3         28 '[]';
74             }
75             };
76 11     11 0 128603 };
77             }
78              
79             sub constraint_generator {
80 18     18 0 160491 my $self = shift;
81              
82             sub {
83 23     23   110635 my $constraints_sub_meta = do {
84 23 100       140 if ( @_ == 0 ) {
    100          
    100          
85 4         17 create_unknown_sub_meta();
86             }
87             elsif ( @_ == 1 ) {
88 4         40 state $validator = compile(InstanceOf['Sub::Meta']);
89 4         10518 my ($constraints_sub_meta) = $validator->(@_);
90 4         53 $constraints_sub_meta;
91             }
92             elsif ( @_ == 2 ) {
93 14         31 state $validator = do {
94 11         64 my $TypeConstraint = HasMethods[qw( check get_message )];
95 11         3986 compile(
96             $TypeConstraint | ArrayRef([$TypeConstraint]) | HashRef([$TypeConstraint]),
97             $TypeConstraint | ArrayRef([$TypeConstraint])
98             );
99             };
100 14         166862 my ($params, $returns) = $validator->(@_);
101              
102 14         1286 Sub::Meta->new(
103             args => $params,
104             returns => $returns,
105             );
106             }
107             else {
108 1         245 Carp::croak 'Too many arguments.';
109             }
110             };
111              
112             sub {
113 32         49873 my $typed_code_ref = shift;
114 32         140 my $maybe_sub_meta = $self->find_sub_meta($typed_code_ref);
115 32   66     211 $constraints_sub_meta->is_same_interface($maybe_sub_meta // create_unknown_sub_meta());
116 22         2981 };
117 18         141 };
118             }
119              
120             sub find_sub_meta {
121 32     32 0 92 my ($self, $typed_code_ref) = @_;
122 32         61 for my $finder (@{ $self->sub_meta_finders }) {
  32         161  
123 28         122 my $meta = $finder->($typed_code_ref);
124 28 100       4620 return $meta if defined $meta;
125             }
126 9         29 return;
127             }
128              
129             sub create_unknown_sub_meta {
130 13     13 0 77 Sub::Meta->new(
131             slurpy => 1,
132             );
133             }
134              
135             sub coercion_generator {
136             sub {
137 3     3   1402 my (undef, $type, @type_parameters) = @_;
138            
139 3 100       28 if (@type_parameters == 0) {
140 1         8 local @CARP_NOT = (__PACKAGE__, 'Type::Tiny');
141 1         198 croak 'No coercion for this type constraint';
142             }
143              
144 2         7 my ($params_types, $return_types) = @type_parameters;
145             Type::Coercion->new(
146             display_name => "to_${type}",
147             type_constraint => $type,
148             type_coercion_map => [
149             $CallableType => sub {
150 2         17 my $coderef = shift;
151 2         16 wrap_sub($params_types, $return_types, $coderef);
152             },
153 2         10 ],
154             );
155 4     4 0 73 };
156             }
157              
158             sub create {
159 4     4 0 12 my $self = shift;
160 4         62 Type::Tiny->new(
161             parent => $CallableType,
162             name => $self->name,
163             name_generator => $self->name_generator,
164             constraint_generator => $self->constraint_generator,
165             coercion_generator => $self->coercion_generator,
166             );
167             }
168              
169             1;