File Coverage

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


line stmt bran cond sub pod time code
1             package Types::TypedCodeRef::Factory;
2 7     7   1119488 use 5.010001;
  7         47  
3 7     7   44 use strict;
  7         21  
  7         166  
4 7     7   40 use warnings;
  7         14  
  7         217  
5 7     7   43 use utf8;
  7         16  
  7         51  
6 7     7   3609 use Moo;
  7         43268  
  7         45  
7 7     7   9882 use overload ();
  7         20  
  7         115  
8 7     7   39 use Carp ();
  7         13  
  7         578  
9 7     7   717 use Type::Tiny ();
  7         23109  
  7         121  
10 7     7   2265 use Type::Coercion;
  7         19086  
  7         251  
11 7     7   4088 use Type::Params qw( compile compile_named multisig );
  7         327810  
  7         106  
12 7     7   3838 use Types::Standard -types;
  7         18  
  7         64  
13 7     7   37227 use Scalar::Util;
  7         22  
  7         422  
14 7     7   3900 use Sub::Meta;
  7         82210  
  7         480  
15 7     7   4051 use Sub::WrapInType qw( wrap_sub );
  7         430707  
  7         598  
16 7     7   68 use Carp qw( croak );
  7         17  
  7         345  
17 7     7   48 use namespace::autoclean;
  7         17  
  7         66  
18              
19             our @CARP_NOT;
20              
21             sub _is_callable {
22 40     40   116472 my $callable = shift;
23 40         120 my $reftype = Scalar::Util::reftype($callable);
24 40 100 100     260 ( 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   513 my ($type_name, @type_parameters) = @_;
47 16         34 $type_name . do {
48 16 100       58 if (@type_parameters == 2) {
    100          
49 11         32 my ($params_types, $return_types) = @type_parameters;
50              
51 11         17 my $params_types_name = do {
52 11 100       48 if (ref $params_types eq 'ARRAY') {
    100          
53 6         16 "[@{[ join(', ', @$params_types) ]}]"
  6         31  
54             }
55             elsif (ref $params_types eq 'HASH') {
56 3         6 "{ @{[ join( ', ', map { qq{$_ => $params_types->{$_}} } sort keys %$params_types) ]} }"
  3         14  
  6         98  
57             }
58             else {
59 2         6 $params_types
60             }
61             };
62              
63 11 100       179 my $return_types_name = ref $return_types eq 'ARRAY'
64 2         9 ? "[@{[ join(', ', @$return_types) ]}]"
65             : $return_types;
66              
67 11         73 "[ $params_types_name => $return_types_name ]";
68             }
69             elsif (@type_parameters == 1) {
70 2         5 my $submeta = $type_parameters[0];
71 2         13 sprintf('[%s=%s]', ref $submeta, $submeta->display);
72             }
73             else {
74 3         24 '[]';
75             }
76             };
77 11     11 0 111330 };
78             }
79              
80             sub constraint_generator {
81 18     18 0 160702 my $self = shift;
82              
83             sub {
84 23     23   146108 my $constraints_sub_meta = do {
85 23 100       125 if ( @_ == 0 ) {
    100          
    100          
86 4         29 create_unknown_sub_meta();
87             }
88             elsif ( @_ == 1 ) {
89 4         26 state $validator = compile(InstanceOf['Sub::Meta']);
90 4         10016 my ($constraints_sub_meta) = $validator->(@_);
91 4         57 $constraints_sub_meta;
92             }
93             elsif ( @_ == 2 ) {
94 14         30 state $validator = do {
95 11         60 my $TypeConstraint = HasMethods[qw( check get_message )];
96 11         1911 compile(
97             $TypeConstraint | ArrayRef([$TypeConstraint]) | HashRef([$TypeConstraint]),
98             $TypeConstraint | ArrayRef([$TypeConstraint])
99             );
100             };
101 14         155391 my ($params, $returns) = $validator->(@_);
102              
103 14         1286 Sub::Meta->new(
104             args => $params,
105             returns => $returns,
106             );
107             }
108             else {
109 1         230 Carp::croak 'Too many arguments.';
110             }
111             };
112              
113             sub {
114 32         52468 my $typed_code_ref = shift;
115 32         108 my $maybe_sub_meta = $self->find_sub_meta($typed_code_ref);
116 32   66     247 $constraints_sub_meta->is_same_interface($maybe_sub_meta // create_unknown_sub_meta());
117 22         3517 };
118 18         135 };
119             }
120              
121             sub find_sub_meta {
122 32     32 0 79 my ($self, $typed_code_ref) = @_;
123 32         58 for my $finder (@{ $self->sub_meta_finders }) {
  32         131  
124 28         94 my $meta = $finder->($typed_code_ref);
125 28 100       4455 return $meta if defined $meta;
126             }
127 9         24 return;
128             }
129              
130             sub create_unknown_sub_meta {
131 13     13 0 87 Sub::Meta->new(
132             slurpy => 1,
133             );
134             }
135              
136             sub coercion_generator {
137             sub {
138 3     3   1535 my (undef, $type, @type_parameters) = @_;
139            
140 3 100       20 if (@type_parameters == 0) {
141 1         6 local @CARP_NOT = (__PACKAGE__, 'Type::Tiny');
142 1         208 croak 'No coercion for this type constraint';
143             }
144              
145 2         7 my ($params_types, $return_types) = @type_parameters;
146             Type::Coercion->new(
147             display_name => "to_${type}",
148             type_constraint => $type,
149             type_coercion_map => [
150             $CallableType => sub {
151 2         26 my $coderef = shift;
152 2         16 wrap_sub($params_types, $return_types, $coderef);
153             },
154 2         12 ],
155             );
156 4     4 0 34 };
157             }
158              
159             sub create {
160 4     4 0 14 my $self = shift;
161 4         34 Type::Tiny->new(
162             parent => $CallableType,
163             name => $self->name,
164             name_generator => $self->name_generator,
165             constraint_generator => $self->constraint_generator,
166             coercion_generator => $self->coercion_generator,
167             );
168             }
169              
170             1;