File Coverage

blib/lib/Type/Params/Alternatives.pm
Criterion Covered Total %
statement 94 96 97.9
branch 19 22 86.3
condition 17 22 77.2
subroutine 26 26 100.0
pod 0 10 0.0
total 156 176 88.6


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