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