File Coverage

blib/lib/Type/Params/Alternatives.pm
Criterion Covered Total %
statement 94 96 97.9
branch 20 22 90.9
condition 17 22 77.2
subroutine 26 26 100.0
pod 0 10 0.0
total 157 176 89.2


line stmt bran cond sub pod time code
1             # INTERNAL MODULE: OO backend for Type::Params multisig-type signatures.
2              
3             package Type::Params::Alternatives;
4              
5 4     4   96 use 5.008001;
  4         16  
6 4     4   22 use strict;
  4         11  
  4         97  
7 4     4   23 use warnings;
  4         11  
  4         174  
8              
9             BEGIN {
10 4 50   4   160 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
11             }
12              
13             BEGIN {
14 4     4   33 $Type::Params::Alternatives::AUTHORITY = 'cpan:TOBYINK';
15 4         142 $Type::Params::Alternatives::VERSION = '2.004000';
16             }
17              
18             $Type::Params::Alternatives::VERSION =~ tr/_//d;
19              
20 4     4   29 use B ();
  4         13  
  4         98  
21 4     4   24 use Eval::TypeTiny::CodeAccumulator;
  4         11  
  4         138  
22 4     4   26 use Types::Standard qw( -is -types -assert );
  4         10  
  4         47  
23 4     4   514 use Types::TypeTiny qw( -is -types to_TypeTiny );
  4         10  
  4         30  
24              
25             require Type::Params::Signature;
26             our @ISA = 'Type::Params::Signature';
27              
28             sub new {
29 19     19 0 50 my $class = shift;
30 19 50       98 my %self = @_ == 1 ? %{$_[0]} : @_;
  0         0  
31 19         58 my $self = bless \%self, $class;
32             exists( $self->{$_} ) || ( $self->{$_} = $self->{base_options}{$_} )
33 19   50     36 for keys %{ $self->{base_options} };
  19         220  
34 19   50     488 $self->{sig_class} ||= 'Type::Params::Signature';
35 19   100     105 $self->{message} ||= 'Parameter validation failed';
36 19         126 return $self;
37             }
38              
39 44   50 44 0 395 sub base_options { $_[0]{base_options} ||= {} }
40 19   50 19 0 64 sub alternatives { $_[0]{alternatives} ||= [] }
41 44     44 0 183 sub sig_class { $_[0]{sig_class} }
42 24   100 24 0 112 sub meta_alternatives { $_[0]{meta_alternatives} ||= $_[0]->_build_meta_alternatives }
43 36     36 0 189 sub parameters { [] }
44 37     37 0 172 sub goto_next { $_[0]{base_options}{goto_next} }
45 17     17 0 81 sub package { $_[0]{base_options}{package} }
46 17     17 0 152 sub subname { $_[0]{base_options}{subname} }
47              
48             sub _build_meta_alternatives {
49 19     19   40 my $self = shift;
50              
51 19         39 my $index = 0;
52             return [
53             map {
54 51         125 my $meta = $self->_build_meta_alternative( $_ );
55 50         183 $meta->{_index} = $index++;
56 50         182 $meta;
57 19         38 } @{ $self->alternatives }
  19         55  
58             ];
59             }
60              
61             sub _build_meta_alternative {
62 51     51   111 my ( $self, $alt ) = @_;
63              
64 51 100       236 if ( is_CodeRef $alt ) {
    100          
    100          
65 6         20 return { closure => $alt };
66             }
67             elsif ( is_HashRef $alt ) {
68             my %opts = (
69 31         48 %{ $self->base_options },
  31         79  
70             goto_next => !!0, # don't propagate
71             %$alt,
72             want_source => !!0,
73             want_object => !!0,
74             want_details => !!1,
75             );
76 31         91 my $sig = $self->sig_class->new_from_v2api( \%opts );
77 31         104 return $sig->return_wanted;
78             }
79             elsif ( is_ArrayRef $alt ) {
80             my %opts = (
81 13         22 %{ $self->base_options },
  13         30  
82             goto_next => !!0, # don't propagate
83             positional => $alt,
84             want_source => !!0,
85             want_object => !!0,
86             want_details => !!1,
87             );
88 13         41 my $sig = $self->sig_class->new_from_v2api( \%opts );
89 13         49 return $sig->return_wanted;
90             }
91             else {
92 1         9 $self->_croak( 'Alternative signatures must be CODE, HASH, or ARRAY refs' );
93             }
94             }
95              
96             sub _coderef_start_extra {
97 19     19   52 my ( $self, $coderef ) = ( shift, @_ );
98            
99 19         65 $coderef->add_line( 'my $r;' );
100 19         64 $coderef->add_line( 'undef ${^TYPE_PARAMS_MULTISIG};' );
101 19         62 $coderef->add_line( 'undef ${^_TYPE_PARAMS_MULTISIG};' );
102 19         73 $coderef->add_gap;
103              
104 19         39 for my $meta ( @{ $self->meta_alternatives } ) {
  19         54  
105 50         134 $self->_coderef_meta_alternative( $coderef, $meta );
106             }
107            
108 18         40 $self;
109             }
110              
111             sub _coderef_meta_alternative {
112 50     50   111 my ( $self, $coderef, $meta ) = ( shift, @_ );
113            
114 50         100 my @cond = '! $r';
115 50 100       220 push @cond, sprintf( '@_ >= %s', $meta->{min_args} ) if defined $meta->{min_args};
116 50 100       151 push @cond, sprintf( '@_ <= %s', $meta->{max_args} ) if defined $meta->{max_args};
117 50 100 66     182 if ( defined $meta->{max_args} and defined $meta->{min_args} ) {
118             splice @cond, -2, 2, sprintf( '@_ == %s', $meta->{min_args} )
119 26 100       124 if $meta->{max_args} == $meta->{min_args};
120             }
121            
122             # It is sometimes possible to inline $meta->{source} here
123 50 100 100     313 if ( $meta->{source}
      100        
124             and $meta->{source} !~ /return/
125 37         128 and ! keys %{ $meta->{environment} } ) {
126            
127 19         44 my $alt_code = $meta->{source};
128 19         126 $alt_code =~ s/^sub [{]/do {/;
129             $coderef->add_line( sprintf(
130             'eval { local @_ = @_; $r = [ %s ]; ${^TYPE_PARAMS_MULTISIG} = ${^_TYPE_PARAMS_MULTISIG} = %d }%sif ( %s );',
131             $alt_code,
132             $meta->{_index},
133 19         159 "\n\t",
134             join( ' and ', @cond ),
135             ) );
136 19         56 $coderef->add_gap;
137             }
138             else {
139            
140 31         103 my $callback_var = $coderef->add_variable( '$signature', \$meta->{closure} );
141             $coderef->add_line( sprintf(
142             'eval { $r = [ %s->(@_) ]; ${^TYPE_PARAMS_MULTISIG} = ${^_TYPE_PARAMS_MULTISIG} = %d }%sif ( %s );',
143             $callback_var,
144             $meta->{_index},
145 31         201 "\n\t",
146             join( ' and ', @cond ),
147             ) );
148 31         69 $coderef->add_gap;
149             }
150            
151 50         122 return $self;
152             }
153              
154             sub _coderef_end_extra {
155 18     18   47 my ( $self, $coderef ) = ( shift, @_ );
156            
157             $coderef->add_line( sprintf(
158             '%s unless $r;',
159 18         115 $self->_make_general_fail( message => B::perlstring( $self->{message} ) ),
160             ) );
161 18         64 $coderef->add_gap;
162            
163 18         39 return $self;
164             }
165              
166             sub _coderef_check_count {
167 18     18   49 shift;
168             }
169              
170             sub _make_return_list {
171 18     18   52 '@$r';
172             }
173              
174             sub make_class_pp_code {
175 5     5 0 12 my $self = shift;
176            
177             return join(
178             qq{\n},
179 13         190 grep { length $_ }
180 13 100       49 map { $_->{class_definition} || '' }
181 5         10 @{ $self->meta_alternatives }
  5         13  
182             );
183             }
184              
185             1;
186