File Coverage

blib/lib/Type/Params/Alternatives.pm
Criterion Covered Total %
statement 102 105 97.1
branch 32 36 88.8
condition 19 28 67.8
subroutine 26 27 96.3
pod 11 11 100.0
total 190 207 91.7


line stmt bran cond sub pod time code
1             package Type::Params::Alternatives;
2              
3 6     6   141 use 5.008001;
  6         27  
4 6     6   45 use strict;
  6         14  
  6         188  
5 6     6   32 use warnings;
  6         13  
  6         618  
6              
7             BEGIN {
8 6 50   6   267 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
9             }
10              
11             BEGIN {
12 6     6   21 $Type::Params::Alternatives::AUTHORITY = 'cpan:TOBYINK';
13 6         312 $Type::Params::Alternatives::VERSION = '2.010001';
14             }
15              
16             $Type::Params::Alternatives::VERSION =~ tr/_//d;
17              
18 6     6   63 use B ();
  6         13  
  6         162  
19 6     6   47 use Eval::TypeTiny::CodeAccumulator;
  6         15  
  6         283  
20 6     6   33 use Types::Standard qw( -is -types -assert );
  6         11  
  6         71  
21 6     6   1260 use Types::TypeTiny qw( -is -types to_TypeTiny );
  6         14  
  6         51  
22              
23             my $Attrs = Enum[ qw/
24             caller_level package subname description _is_signature_for ID
25             method head tail parameters slurpy
26             message on_die next fallback strictness is_named allow_dash method_invocant
27             bless class constructor named_to_list list_to_named oo_trace
28             class_prefix class_attributes
29             returns_scalar returns_list
30             want_details want_object want_source can_shortcut coderef
31            
32             sig_class base_options alternatives meta_alternatives
33            
34             quux mite_signature is_wrapper
35             / ]; # quux for reasons
36              
37             require Type::Params::Signature;
38             our @ISA = 'Type::Params::Signature';
39              
40             sub new {
41 22     22 1 70 my $class = shift;
42 22 50       167 my %self = @_ == 1 ? %{$_[0]} : @_;
  0         0  
43 22         67 my $self = bless \%self, $class;
44 22 50 0     136 $self->{next} ||= delete $self->{goto_next} if exists $self->{goto_next};
45             exists( $self->{$_} ) || ( $self->{$_} = $self->{base_options}{$_} )
46 22   50     47 for keys %{ $self->{base_options} };
  22         360  
47 22   50     100 $self->{sig_class} ||= 'Type::Params::Signature';
48 22   100     164 $self->{message} ||= 'Parameter validation failed';
49 22         106 delete $self->{base_options}{$_} for qw/ returns returns_list returns_scalar /;
50 22         158 $self->_rationalize_returns;
51 22 100       379 $Attrs->all( sort keys %$self ) or do {
52 1         7 require Carp;
53 1         5 require Type::Utils;
54 1         6 my @bad = ( ~ $Attrs )->grep( sort keys %$self );
55 1 50       4 Carp::carp( sprintf(
56             "Warning: unrecognized signature %s: %s, continuing anyway",
57             @bad == 1 ? 'option' : 'options',
58             Type::Utils::english_list( @bad ),
59             ) );
60             };
61 22         195 return $self;
62             }
63              
64 54   50 54 1 780 sub base_options { $_[0]{base_options} ||= {} }
65 22   50 22 1 98 sub alternatives { $_[0]{alternatives} ||= [] }
66 54     54 1 389 sub sig_class { $_[0]{sig_class} }
67 27   100 27 1 179 sub meta_alternatives { $_[0]{meta_alternatives} ||= $_[0]->_build_meta_alternatives }
68 42     42 1 222 sub parameters { [] }
69 48     48 1 273 sub next { $_[0]{base_options}{next} }
70 0     0 1 0 sub goto_next { $_[0]{base_options}{next} }
71 20     20 1 133 sub package { $_[0]{base_options}{package} }
72 20     20 1 210 sub subname { $_[0]{base_options}{subname} }
73              
74             sub _build_meta_alternatives {
75 22     22   47 my $self = shift;
76              
77 22         51 my $index = 0;
78             return [
79             map {
80 62         241 $self->_build_meta_alternative( $_, $index++ )
81 22         44 } @{ $self->alternatives }
  22         78  
82             ];
83             }
84              
85             sub _build_meta_alternative {
86 62     62   208 my ( $self, $alt, $index ) = @_;
87              
88 62         114 my $meta;
89 62 100 66     610 if ( is_CodeRef $alt ) {
    100          
    100          
    100          
90 6         22 $meta = { closure => $alt };
91             }
92             elsif ( is_HashRef $alt and exists $alt->{closure} ) {
93 1         5 $meta = { %$alt };
94             }
95             elsif ( is_HashRef $alt ) {
96             my %opts = (
97 39         95 %{ $self->base_options },
  39         135  
98             next => !!0, # don't propagate these next few
99             returns => undef,
100             returns_scalar => undef,
101             returns_list => undef,
102             %$alt,
103             want_source => !!0,
104             want_object => !!0,
105             want_details => !!1,
106             );
107 39         159 $meta = $self->sig_class->new_from_v2api( \%opts )->return_wanted;
108 39 100       1108 $meta->{ID} = $alt->{ID} if exists $alt->{ID};
109             }
110             elsif ( is_ArrayRef $alt ) {
111             my %opts = (
112 15         26 %{ $self->base_options },
  15         91  
113             next => !!0, # don't propagate these next few
114             returns => undef,
115             returns_scalar => undef,
116             returns_list => undef,
117             positional => $alt,
118             want_source => !!0,
119             want_object => !!0,
120             want_details => !!1,
121             );
122 15         62 $meta = $self->sig_class->new_from_v2api( \%opts )->return_wanted;
123             }
124             else {
125 1         13 $self->_croak( 'Alternative signatures must be CODE, HASH, or ARRAY refs' );
126             }
127            
128 61         477 $meta->{_index} = $index;
129 61         361 return $meta;
130             }
131              
132             sub _coderef_start_extra {
133 22     22   65 my ( $self, $coderef ) = ( shift, @_ );
134            
135 22         217 $coderef->add_line( 'my $r;' );
136 22         73 $coderef->add_line( 'undef ${^_TYPE_PARAMS_MULTISIG};' );
137 22         75 $coderef->add_gap;
138              
139 22         44 for my $meta ( @{ $self->meta_alternatives } ) {
  22         101  
140 61         231 $self->_coderef_meta_alternative( $coderef, $meta );
141             }
142            
143 21         416 $self;
144             }
145              
146             sub _coderef_meta_alternative {
147 61     61   169 my ( $self, $coderef, $meta ) = ( shift, @_ );
148            
149 61         153 my @cond = '! $r';
150 61 100       278 push @cond, sprintf( '@_ >= %s', $meta->{min_args} ) if defined $meta->{min_args};
151 61 100       257 push @cond, sprintf( '@_ <= %s', $meta->{max_args} ) if defined $meta->{max_args};
152 61 100 66     299 if ( defined $meta->{max_args} and defined $meta->{min_args} ) {
153             splice @cond, -2, 2, sprintf( '@_ == %s', $meta->{min_args} )
154 32 100       174 if $meta->{max_args} == $meta->{min_args};
155             }
156            
157             # It is sometimes possible to inline $meta->{source} here
158 61 100 100     474 if ( $meta->{source}
      100        
159             and $meta->{source} !~ /return/
160 47         215 and ! keys %{ $meta->{environment} } ) {
161            
162 25         70 my $alt_code = $meta->{source};
163 25         185 $alt_code =~ s/^sub [{]/do {/;
164             $coderef->add_line( sprintf(
165             'eval { local @_ = @_; $r = [ %s ]; ${^_TYPE_PARAMS_MULTISIG} = %s }%sif ( %s );',
166             $alt_code,
167             defined( $meta->{ID} )
168             ? B::perlstring( $meta->{ID} )
169 25 100       285 : ( 0 + $meta->{_index} ),
170             "\n\t",
171             join( ' and ', @cond ),
172             ) );
173 25         95 $coderef->add_gap;
174             }
175             else {
176            
177 36         199 my $callback_var = $coderef->add_variable( '$signature', \$meta->{closure} );
178             $coderef->add_line( sprintf(
179             'eval { $r = [ %s->(@_) ]; ${^_TYPE_PARAMS_MULTISIG} = %s }%sif ( %s );',
180             $callback_var,
181             defined( $meta->{ID} )
182             ? B::perlstring( $meta->{ID} )
183 36 100       458 : ( 0 + $meta->{_index} ),
184             "\n\t",
185             join( ' and ', @cond ),
186             ) );
187 36         125 $coderef->add_gap;
188             }
189            
190 61         263 return $self;
191             }
192              
193             sub _coderef_end_extra {
194 21     21   65 my ( $self, $coderef ) = ( shift, @_ );
195            
196             $coderef->add_line( sprintf(
197             '%s unless $r;',
198 21         197 $self->_make_general_fail( message => B::perlstring( $self->{message} ) ),
199             ) );
200 21         77 $coderef->add_gap;
201            
202 21         48 return $self;
203             }
204              
205             sub _coderef_check_count {
206 21     21   54 shift;
207             }
208              
209             sub _make_return_list {
210 21     21   86 '@$r';
211             }
212              
213             sub make_class_pp_code {
214 5     5 1 12 my $self = shift;
215            
216             return join(
217             qq{\n},
218 13         76 grep { length $_ }
219 13 100       62 map { $_->{class_definition} || '' }
220 5         14 @{ $self->meta_alternatives }
  5         23  
221             );
222             }
223              
224             1;
225              
226             __END__