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   942422 use 5.010001;
  7         45  
3 7     7   37 use strict;
  7         14  
  7         124  
4 7     7   30 use warnings;
  7         12  
  7         154  
5 7     7   33 use utf8;
  7         12  
  7         39  
6 7     7   3122 use Moo;
  7         39751  
  7         37  
7 7     7   8267 use overload ();
  7         14  
  7         84  
8 7     7   30 use Carp ();
  7         12  
  7         84  
9 7     7   607 use Type::Tiny ();
  7         18441  
  7         114  
10 7     7   1829 use Type::Coercion;
  7         16308  
  7         213  
11 7     7   3732 use Type::Params qw( compile compile_named multisig );
  7         269250  
  7         93  
12 7     7   3270 use Types::Standard -types;
  7         16  
  7         65  
13 7     7   31391 use Scalar::Util;
  7         15  
  7         303  
14 7     7   3119 use Sub::Meta;
  7         57561  
  7         232  
15 7     7   2710 use Sub::WrapInType qw( wrap_sub );
  7         347760  
  7         538  
16 7     7   56 use Carp qw( croak );
  7         13  
  7         300  
17 7     7   43 use namespace::autoclean;
  7         14  
  7         53  
18              
19             our @CARP_NOT;
20              
21             sub _is_callable {
22 38     38   74116 my $callable = shift;
23 38         102 my $reftype = Scalar::Util::reftype($callable);
24 38 100 100     213 ( 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 15     15   329 my ($type_name, @type_parameters) = @_;
47 15         30 $type_name . do {
48 15 100       52 if (@type_parameters == 2) {
    100          
49 11         26 my ($params_types, $return_types) = @type_parameters;
50              
51 11         20 my $params_types_name = do {
52 11 100       41 if (ref $params_types eq 'ARRAY') {
    100          
53 6         14 "[@{[ join(', ', @$params_types) ]}]"
  6         33  
54             }
55             elsif (ref $params_types eq 'HASH') {
56 3         7 "{ @{[ join( ', ', map { qq{$_ => $params_types->{$_}} } sort keys %$params_types) ]} }"
  3         14  
  6         95  
57             }
58             else {
59 2         6 $params_types
60             }
61             };
62              
63 11 100       166 my $return_types_name = ref $return_types eq 'ARRAY'
64 2         9 ? "[@{[ join(', ', @$return_types) ]}]"
65             : $return_types;
66              
67 11         69 "[ $params_types_name => $return_types_name ]";
68             }
69             elsif (@type_parameters == 1) {
70 1         13 "[$type_parameters[0]]";
71             }
72             else {
73 3         22 '[]';
74             }
75             };
76 11     11 0 116995 };
77             }
78              
79             sub constraint_generator {
80 18     18 0 130942 my $self = shift;
81              
82             sub {
83 22     22   98235 my $constraints_sub_meta = do {
84 22 100       100 if ( @_ == 0 ) {
    100          
    100          
85 4         18 create_unknown_sub_meta();
86             }
87             elsif ( @_ == 1 ) {
88 3         12 state $validator = compile(InstanceOf['Sub::Meta']);
89 3         4880 my ($constraints_sub_meta) = $validator->(@_);
90 3         35 $constraints_sub_meta;
91             }
92             elsif ( @_ == 2 ) {
93 14         21 state $validator = do {
94 11         56 my $TypeConstraint = HasMethods[qw( check get_message )];
95 11         3178 compile(
96             $TypeConstraint | ArrayRef([$TypeConstraint]) | HashRef([$TypeConstraint]),
97             $TypeConstraint | ArrayRef([$TypeConstraint])
98             );
99             };
100 14         137068 my ($params, $returns) = $validator->(@_);
101              
102 14         1068 Sub::Meta->new(
103             args => $params,
104             returns => $returns,
105             );
106             }
107             else {
108 1         171 Carp::croak 'Too many arguments.';
109             }
110             };
111              
112             sub {
113 30         30527 my $typed_code_ref = shift;
114 30         82 my $maybe_sub_meta = $self->find_sub_meta($typed_code_ref);
115 30   66     132 $constraints_sub_meta->is_same_interface($maybe_sub_meta // create_unknown_sub_meta());
116 21         2448 };
117 18         114 };
118             }
119              
120             sub find_sub_meta {
121 30     30 0 63 my ($self, $typed_code_ref) = @_;
122 30         43 for my $finder (@{ $self->sub_meta_finders }) {
  30         109  
123 26         76 my $meta = $finder->($typed_code_ref);
124 26 100       2506 return $meta if defined $meta;
125             }
126 9         19 return;
127             }
128              
129             sub create_unknown_sub_meta {
130 13     13 0 65 Sub::Meta->new(
131             slurpy => 1,
132             );
133             }
134              
135             sub coercion_generator {
136             sub {
137 3     3   1220 my (undef, $type, @type_parameters) = @_;
138            
139 3 100       13 if (@type_parameters == 0) {
140 1         5 local @CARP_NOT = (__PACKAGE__, 'Type::Tiny');
141 1         173 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         13 wrap_sub($params_types, $return_types, $coderef);
152             },
153 2         10 ],
154             );
155 4     4 0 31 };
156             }
157              
158             sub create {
159 4     4 0 10 my $self = shift;
160 4         36 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;