File Coverage

blib/lib/Sub/Meta/Parameters.pm
Criterion Covered Total %
statement 223 223 100.0
branch 110 110 100.0
condition 55 56 98.2
subroutine 39 39 100.0
pod 29 29 100.0
total 456 457 99.7


line stmt bran cond sub pod time code
1             package Sub::Meta::Parameters;
2 25     25   932682 use 5.010;
  25         112  
3 25     25   155 use strict;
  25         48  
  25         655  
4 25     25   138 use warnings;
  25         52  
  25         1385  
5              
6             our $VERSION = "0.13";
7              
8 25     25   167 use Carp ();
  25         54  
  25         462  
9 25     25   124 use Scalar::Util ();
  25         48  
  25         678  
10              
11 25     25   11703 use Sub::Meta::Param;
  25         69  
  25         1087  
12              
13             use overload
14 25         230 fallback => 1,
15             eq => \&is_same_interface
16 25     25   327 ;
  25         56  
17              
18 7     7   52 sub _croak { require Carp; goto &Carp::croak }
  7         907  
19              
20 96     96 1 414 sub param_class { return 'Sub::Meta::Param' }
21              
22             sub new {
23 148     148 1 188305 my ($class, @args) = @_;
24 148 100       449 my %args = @args == 1 ? %{$args[0]} : @args;
  109         426  
25              
26 146 100       452 _croak 'parameters reqruires args' unless exists $args{args};
27              
28 144         301 my $self = bless \%args => $class;
29 144         468 $self->set_args($args{args});
30              
31 144 100       371 $self->set_invocant(delete $args{invocant}) if exists $args{invocant};
32 144 100       418 $self->set_nshift(delete $args{nshift}) if exists $args{nshift};
33 144 100       401 $self->set_slurpy(delete $args{slurpy}) if $args{slurpy};
34              
35 144         426 return $self;
36             }
37              
38 468   100 468 1 14437 sub nshift() { my $self = shift; return $self->{nshift} // 0 }
  468         3050  
39 232     232 1 4610 sub slurpy() { my $self = shift; return $self->{slurpy} }
  232         1256  
40 1469     1469 1 7904 sub args() { my $self = shift; return $self->{args} }
  1469         8006  
41 137     137 1 7294 sub invocant() { my $self = shift; return $self->{invocant} }
  137         418  
42 1245 100   1245 1 7779 sub invocants() { my $self = shift; return defined $self->{invocant} ? [ $self->{invocant} ] : [] }
  1245         2788  
43 1162     1162 1 25655 sub all_args() { my $self = shift; return [ @{$self->invocants}, @{$self->args} ] }
  1162         1407  
  1162         1738  
  1162         1802  
44              
45 23     23 1 4491 sub has_invocant() { my $self = shift; return defined $self->{invocant} }
  23         66  
46 293     293 1 4802 sub has_slurpy() { my $self = shift; return defined $self->{slurpy} }
  293         1557  
47              
48             sub set_slurpy {
49 28     28 1 61 my ($self, $v) = @_;
50 28 100 100     195 $self->{slurpy} = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
51             ? $v
52             : $self->param_class->new($v);
53 28         72 return $self;
54             }
55              
56             sub set_args {
57 160     160 1 1425 my ($self, @args) = @_;
58 160         428 $self->{args} = $self->_normalize_args(@args);
59 157         360 return $self;
60             }
61              
62             sub set_nshift {
63 93     93 1 1312 my ($self, $v) = @_;
64              
65 93 100 100     432 unless (defined $v && ($v == 0 || $v == 1) ) {
      100        
66 2   100     19 _croak sprintf("Can't set this nshift: %s", $v//'');
67             }
68              
69 91         166 $self->{nshift} = $v;
70              
71 91 100 100     264 if ($v == 1 && !defined $self->invocant) {
72 31         81 my $default_invocant = $self->param_class->new(invocant => 1);
73 31         109 $self->set_invocant($default_invocant)
74             }
75              
76 91 100 100     256 if ($v == 0 && defined $self->invocant) {
77             delete $self->{invocant}
78 1         3 }
79              
80 91         158 return $self;
81             }
82              
83             sub set_invocant {
84 51     51 1 114 my ($self, $v) = @_;
85              
86 51 100 100     455 my $invocant = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
87             ? $v
88             : $self->param_class->new($v);
89              
90 51         232 $invocant->set_invocant(1);
91              
92 51         102 $self->{invocant} = $invocant;
93              
94 51 100       129 if ($self->nshift == 0) {
95 14         37 $self->set_nshift(1);
96             }
97              
98 51         108 return $self;
99             }
100              
101             sub _normalize_args {
102 160     160   304 my ($self, @args) = @_;
103 160         256 my $args = $args[0];
104 160 100 100     783 _croak 'args must be a single reference' unless @args == 1 && ref $args;
105              
106 157         264 my @normalized_args;
107 157 100       369 if (ref $args eq 'ARRAY') {
    100          
108 147         222 @normalized_args = @{$args};
  147         296  
109             }
110             elsif (ref $args eq 'HASH') {
111 5         10 for my $name (sort { $a cmp $b } keys %{$args}) {
  1         5  
  5         17  
112 5         11 my $v = $args->{$name};
113 5   66     13 my $f = ref $v && ref $v eq 'HASH';
114 5 100       23 push @normalized_args => {
115             name => $name,
116             named => 1,
117             ($f ? %$v : (type => $v) ),
118             }
119             }
120             }
121             else {
122 5         8 @normalized_args = ($args);
123             }
124              
125             return [
126             map {
127 157 100 100     564 Scalar::Util::blessed($_) && $_->isa('Sub::Meta::Param')
  130         872  
128             ? $_
129             : $self->param_class->new($_)
130             } @normalized_args
131             ]
132             }
133              
134             sub _all_positional_required() {
135 59     59   26159 my $self = shift;
136 59         111 return [ @{$self->invocants}, @{$self->positional_required} ];
  59         118  
  59         132  
137             }
138              
139              
140 23     23 1 17034 sub positional() { my $self = shift; return [ grep { $_->positional } @{$self->args} ] }
  23         46  
  26         64  
  23         54  
141 82 100   82 1 15873 sub positional_required() { my $self = shift; return [ grep { $_->positional && $_->required } @{$self->args} ] }
  82         109  
  88         197  
  82         153  
142 36 100   36 1 13676 sub positional_optional() { my $self = shift; return [ grep { $_->positional && $_->optional } @{$self->args} ] }
  36         60  
  36         86  
  36         82  
143              
144 44     44 1 7793 sub named() { my $self = shift; return [ grep { $_->named } @{$self->args} ] }
  44         74  
  51         112  
  44         85  
145 46 100   46 1 13124 sub named_required() { my $self = shift; return [ grep { $_->named && $_->required } @{$self->args} ] }
  46         79  
  52         105  
  46         97  
146 23 100   23 1 11014 sub named_optional() { my $self = shift; return [ grep { $_->named && $_->optional } @{$self->args} ] }
  23         38  
  26         90  
  23         62  
147              
148             sub args_min() {
149 23     23 1 7869 my $self = shift;
150 23         72 my $r = 0;
151 23         35 $r += @{$self->_all_positional_required};
  23         52  
152 23         46 $r += @{$self->named_required} * 2;
  23         47  
153 23         59 return $r
154             }
155              
156             sub args_max() {
157 23     23 1 4489 my $self = shift;
158 23 100 100     58 return 0 + 'Inf' if $self->slurpy || @{$self->named}; ## no critic (ProhibitMismatchedOperators)
  21         46  
159 13         27 my $r = 0;
160 13         22 $r += @{$self->_all_positional_required};
  13         29  
161 13         25 $r += @{$self->positional_optional};
  13         26  
162 13         61 return $r
163             }
164              
165             sub is_same_interface {
166 56     56 1 104 my ($self, $other) = @_;
167              
168 56 100 100     306 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
169              
170 52 100       133 if ($self->has_slurpy) {
171 11 100       24 return unless $self->slurpy->is_same_interface($other->slurpy)
172             }
173             else {
174 41 100       74 return if $other->has_slurpy;
175             }
176              
177 46 100       102 return unless $self->nshift == $other->nshift;
178              
179 40 100       65 return unless @{$self->all_args} == @{$other->all_args};
  40         75  
  40         68  
180              
181 32         71 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  55         97  
182 35 100       68 return unless $self->all_args->[$i]->is_same_interface($other->all_args->[$i]);
183             }
184              
185 20         59 return !!1;
186             }
187              
188             sub is_relaxed_same_interface {
189 58     58 1 107 my ($self, $other) = @_;
190              
191 58 100 100     311 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
192              
193 53 100       129 if ($self->has_slurpy) {
194 11 100       24 return unless $self->slurpy->is_same_interface($other->slurpy)
195             }
196              
197 50 100       121 return unless $self->nshift == $other->nshift;
198              
199 44 100       65 return unless @{$self->all_args} <= @{$other->all_args};
  44         90  
  44         72  
200              
201 42         84 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  78         123  
202 46 100       82 return unless $self->all_args->[$i]->is_relaxed_same_interface($other->all_args->[$i]);
203             }
204              
205 32         87 return !!1;
206             }
207              
208             sub is_same_interface_inlined {
209 12     12 1 31 my ($self, $v) = @_;
210              
211 12         20 my @src;
212              
213 12         55 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
214              
215 12 100       32 push @src => $self->has_slurpy ? $self->slurpy->is_same_interface_inlined(sprintf('%s->slurpy', $v))
216             : sprintf('!%s->has_slurpy', $v);
217              
218 12         36 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
219              
220 12         23 push @src => sprintf('%d == @{%s->all_args}', scalar @{$self->all_args}, $v);
  12         33  
221              
222 12         34 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  24         45  
223 12         27 push @src => $self->all_args->[$i]->is_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
224             }
225              
226 12         2052 return join "\n && ", @src;
227             }
228              
229             sub is_relaxed_same_interface_inlined {
230 12     12 1 34 my ($self, $v) = @_;
231              
232 12         19 my @src;
233              
234 12         52 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
235              
236 12 100       30 push @src => $self->slurpy->is_relaxed_same_interface_inlined(sprintf('%s->slurpy', $v)) if $self->has_slurpy;
237              
238 12         33 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
239              
240 12         23 push @src => sprintf('%d <= @{%s->all_args}', scalar @{$self->all_args}, $v);
  12         29  
241              
242 12         30 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  24         50  
243 12         27 push @src => $self->all_args->[$i]->is_relaxed_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
244             }
245              
246 12         1635 return join "\n && ", @src;
247             }
248              
249              
250             sub error_message {
251 23     23 1 48 my ($self, $other) = @_;
252              
253 23 100 100     156 return sprintf('must be Sub::Meta::Parameters. got: %s', $other // '')
      100        
254             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
255              
256 20 100       48 if ($self->has_slurpy) {
257 3 100       9 return sprintf('invalid slurpy. got: %s, expected: %s', $other->has_slurpy ? $other->slurpy->display : '', $self->slurpy->display)
    100          
258             unless $self->slurpy->is_same_interface($other->slurpy)
259             }
260             else {
261 17 100       35 return 'should not have slurpy' if $other->has_slurpy;
262             }
263              
264 17 100       40 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
265             unless $self->nshift == $other->nshift;
266              
267 2         5 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  2         4  
268 14 100       23 unless @{$self->all_args} == @{$other->all_args};
  14         30  
  14         26  
269              
270 12         29 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  17         31  
271 9         19 my $s = $self->all_args->[$i];
272 9         19 my $o = $other->all_args->[$i];
273 9 100       30 return sprintf('args[%d] is invalid. got: %s, expected: %s', $i, $o->display, $s->display)
274             unless $s->is_same_interface($o);
275             }
276              
277 8         21 return '';
278             }
279              
280             sub relaxed_error_message {
281 23     23 1 44 my ($self, $other) = @_;
282              
283 23 100 100     143 return sprintf('must be Sub::Meta::Parameters. got: %s', $other // '')
      100        
284             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
285              
286 20 100       44 if ($self->has_slurpy) {
287 3 100       7 return sprintf('invalid slurpy. got: %s, expected: %s', $other->has_slurpy ? $other->slurpy->display : '', $self->slurpy->display)
    100          
288             unless $self->slurpy->is_same_interface($other->slurpy)
289             }
290              
291 18 100       39 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
292             unless $self->nshift == $other->nshift;
293              
294 1         3 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  1         3  
295 15 100       22 unless @{$self->all_args} <= @{$other->all_args};
  15         30  
  15         28  
296              
297 14         27 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  19         36  
298 9         20 my $s = $self->all_args->[$i];
299 9         18 my $o = $other->all_args->[$i];
300 9 100       23 return sprintf('args[%d] is invalid. got: %s, expected: %s', $i, $o->display, $s->display)
301             unless $s->is_relaxed_same_interface($o);
302             }
303              
304 10         22 return '';
305             }
306             sub display {
307 11     11 1 15 my $self = shift;
308              
309 11         20 my $s = '';
310 11 100 100     24 $s .= $self->invocant->display . ': '
311             if $self->invocant && $self->invocant->display;
312              
313 11         20 $s .= join ', ', map { $_->display } @{$self->args};
  9         20  
  11         23  
314 11 100 100     32 $s .= ', ' if $s && $self->slurpy;
315 11 100       22 $s .= $self->slurpy->display if $self->slurpy;
316 11         30 return $s;
317             }
318              
319             1;
320             __END__