File Coverage

blib/lib/Sub/Meta/Parameters.pm
Criterion Covered Total %
statement 230 230 100.0
branch 116 116 100.0
condition 54 55 98.1
subroutine 40 40 100.0
pod 30 30 100.0
total 470 471 99.7


line stmt bran cond sub pod time code
1             package Sub::Meta::Parameters;
2 34     34   724221 use 5.010;
  34         120  
3 34     34   156 use strict;
  34         62  
  34         583  
4 34     34   132 use warnings;
  34         56  
  34         1165  
5              
6             our $VERSION = "0.15";
7              
8 34     34   167 use Carp ();
  34         63  
  34         498  
9 34     34   151 use Scalar::Util ();
  34         68  
  34         685  
10              
11 34     34   11737 use Sub::Meta::Param;
  34         84  
  34         1223  
12              
13             use overload
14 34         273 fallback => 1,
15             eq => \&is_same_interface
16 34     34   323 ;
  34         65  
17              
18 5     5   30 sub _croak { require Carp; goto &Carp::croak }
  5         472  
19              
20 132     132 1 554 sub param_class { return 'Sub::Meta::Param' }
21              
22             sub new {
23 296     296 1 155599 my ($class, @args) = @_;
24 296 100       679 my %args = @args == 1 ? %{$args[0]} : @args;
  260         830  
25              
26 294         664 my $self = bless \%args => $class;
27 294 100       878 $self->set_args($args{args}) if exists $args{args};
28 294 100       686 $self->set_invocant(delete $args{invocant}) if defined $args{invocant};
29 294 100       978 $self->set_nshift(delete $args{nshift}) if defined $args{nshift};
30 294 100       589 $self->set_slurpy(delete $args{slurpy}) if defined $args{slurpy};
31              
32 294         701 return $self;
33             }
34              
35 741   100 741 1 18088 sub nshift() { my $self = shift; return $self->{nshift} // 0 }
  741         3787  
36 278     278 1 6774 sub slurpy() { my $self = shift; return $self->{slurpy} }
  278         1183  
37 2255   100 2255 1 10800 sub args() { my $self = shift; return $self->{args} // [] }
  2255         11243  
38 332     332 1 9555 sub invocant() { my $self = shift; return $self->{invocant} }
  332         900  
39 1800 100   1800 1 10055 sub invocants() { my $self = shift; return $self->has_invocant ? [ $self->{invocant} ] : [] }
  1800         2280  
40 1629     1629 1 25454 sub all_args() { my $self = shift; return [ @{$self->invocants}, @{$self->args} ] }
  1629         1630  
  1629         1996  
  1629         2164  
41              
42 117     117 1 7200 sub has_args() { my $self = shift; return defined $self->{args} }
  117         246  
43 1881     1881 1 8696 sub has_invocant() { my $self = shift; return defined $self->{invocant} }
  1881         4193  
44 507     507 1 7446 sub has_slurpy() { my $self = shift; return defined $self->{slurpy} }
  507         1979  
45              
46             sub set_slurpy {
47 30     30 1 54 my ($self, $v) = @_;
48 30 100 100     189 $self->{slurpy} = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
49             ? $v
50             : $self->param_class->new($v);
51 30         59 return $self;
52             }
53              
54             sub set_args {
55 178     178 1 1528 my ($self, @args) = @_;
56 178         365 $self->{args} = $self->_normalize_args(@args);
57 175         294 return $self;
58             }
59              
60             sub set_nshift {
61 258     258 1 1302 my ($self, $v) = @_;
62              
63 258 100 100     967 unless (defined $v && ($v == 0 || $v == 1) ) {
      100        
64 2   100     12 _croak sprintf("Can't set this nshift: %s", $v//'');
65             }
66              
67 256         898 $self->{nshift} = $v;
68              
69 256 100 100     622 if ($v == 1 && !defined $self->invocant) {
70 40         90 my $default_invocant = $self->param_class->new(invocant => 1);
71 40         95 $self->set_invocant($default_invocant)
72             }
73              
74 256 100 100     821 if ($v == 0 && defined $self->invocant) {
75             delete $self->{invocant}
76 1         2 }
77              
78 256         388 return $self;
79             }
80              
81             sub set_invocant {
82 64     64 1 124 my ($self, $v) = @_;
83              
84 64 100 100     384 my $invocant = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
85             ? $v
86             : $self->param_class->new($v);
87              
88 64         220 $invocant->set_invocant(1);
89              
90 64         108 $self->{invocant} = $invocant;
91              
92 64 100       115 if ($self->nshift == 0) {
93 15         59 $self->set_nshift(1);
94             }
95              
96 64         118 return $self;
97             }
98              
99             sub _normalize_args {
100 178     178   270 my ($self, @args) = @_;
101 178         238 my $args = $args[0];
102 178 100 100     697 _croak 'args must be a single reference' unless @args == 1 && ref $args;
103              
104 175         321 my @normalized_args;
105 175 100       370 if (ref $args eq 'ARRAY') {
    100          
106 165         229 @normalized_args = @{$args};
  165         297  
107             }
108             elsif (ref $args eq 'HASH') {
109 5         8 for my $name (sort { $a cmp $b } keys %{$args}) {
  1         5  
  5         17  
110 5         10 my $v = $args->{$name};
111 5   66     17 my $f = ref $v && ref $v eq 'HASH';
112 5 100       21 push @normalized_args => {
113             name => $name,
114             named => 1,
115             ($f ? %$v : (type => $v) ),
116             }
117             }
118             }
119             else {
120 5         8 @normalized_args = ($args);
121             }
122              
123             return [
124             map {
125 175 100 100     452 Scalar::Util::blessed($_) && $_->isa('Sub::Meta::Param')
  156         846  
126             ? $_
127             : $self->param_class->new($_)
128             } @normalized_args
129             ]
130             }
131              
132             sub _all_positional_required() {
133 125     125   25089 my $self = shift;
134 125         154 return [ @{$self->invocants}, @{$self->positional_required} ];
  125         207  
  125         232  
135             }
136              
137              
138 45     45 1 17426 sub positional() { my $self = shift; return [ grep { $_->positional } @{$self->args} ] }
  45         87  
  26         48  
  45         98  
139 170 100   170 1 16864 sub positional_required() { my $self = shift; return [ grep { $_->positional && $_->required } @{$self->args} ] }
  170         227  
  88         170  
  170         270  
140 80 100   80 1 15051 sub positional_optional() { my $self = shift; return [ grep { $_->positional && $_->optional } @{$self->args} ] }
  80         106  
  36         66  
  80         132  
141              
142 88     88 1 10296 sub named() { my $self = shift; return [ grep { $_->named } @{$self->args} ] }
  88         132  
  51         94  
  88         154  
143 90 100   90 1 15134 sub named_required() { my $self = shift; return [ grep { $_->named && $_->required } @{$self->args} ] }
  90         112  
  52         91  
  90         154  
144 45 100   45 1 12987 sub named_optional() { my $self = shift; return [ grep { $_->named && $_->optional } @{$self->args} ] }
  45         78  
  26         50  
  45         97  
145              
146             sub args_min() {
147 45     45 1 10337 my $self = shift;
148 45         76 my $r = 0;
149 45         63 $r += @{$self->_all_positional_required};
  45         80  
150 45         77 $r += @{$self->named_required} * 2;
  45         85  
151 45         96 return $r
152             }
153              
154             sub args_max() {
155 45     45 1 7115 my $self = shift;
156 45 100 100     91 return 0 + 'Inf' if $self->slurpy || @{$self->named}; ## no critic (ProhibitMismatchedOperators)
  43         92  
157 35         67 my $r = 0;
158 35         51 $r += @{$self->_all_positional_required};
  35         65  
159 35         55 $r += @{$self->positional_optional};
  35         67  
160 35         84 return $r
161             }
162              
163             sub is_same_interface {
164 81     81 1 464 my ($self, $other) = @_;
165              
166 81 100 100     430 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
167              
168 79 100       180 if ($self->has_slurpy) {
169 11 100       21 return unless $self->slurpy->is_same_interface($other->slurpy)
170             }
171             else {
172 68 100       114 return if $other->has_slurpy;
173             }
174              
175 73 100       202 return unless $self->nshift == $other->nshift;
176              
177 67 100       136 return unless @{$self->all_args} == @{$other->all_args};
  67         122  
  67         132  
178              
179 54         118 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  79         131  
180 38 100       72 return unless $self->all_args->[$i]->is_same_interface($other->all_args->[$i]);
181             }
182              
183 41         114 return !!1;
184             }
185              
186             sub is_relaxed_same_interface {
187 87     87 1 168 my ($self, $other) = @_;
188              
189 87 100 100     426 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
190              
191 85 100       190 if ($self->has_slurpy) {
192 11 100       19 return unless $self->slurpy->is_same_interface($other->slurpy)
193             }
194              
195 82 100       161 return unless $self->nshift == $other->nshift;
196              
197 76 100       103 return unless @{$self->all_args} <= @{$other->all_args};
  76         117  
  76         121  
198              
199 70         142 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  108         177  
200 49 100       84 return unless $self->all_args->[$i]->is_relaxed_same_interface($other->all_args->[$i]);
201             }
202              
203 59         157 return !!1;
204             }
205              
206             sub is_same_interface_inlined {
207 20     20 1 44 my ($self, $v) = @_;
208              
209 20         27 my @src;
210              
211 20         71 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
212              
213 20 100       59 push @src => $self->has_slurpy ? $self->slurpy->is_same_interface_inlined(sprintf('%s->slurpy', $v))
214             : sprintf('!%s->has_slurpy', $v);
215              
216 20         59 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
217              
218 20         36 push @src => sprintf('%d == @{%s->all_args}', scalar @{$self->all_args}, $v);
  20         47  
219              
220 20         56 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  35         58  
221 15         33 push @src => $self->all_args->[$i]->is_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
222             }
223              
224 20         1714 return join "\n && ", @src;
225             }
226              
227             sub is_relaxed_same_interface_inlined {
228 24     24 1 58 my ($self, $v) = @_;
229              
230 24         33 my @src;
231              
232 24         68 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
233              
234 24 100       51 push @src => $self->slurpy->is_relaxed_same_interface_inlined(sprintf('%s->slurpy', $v)) if $self->has_slurpy;
235              
236 24         55 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
237              
238 24         44 push @src => sprintf('%d <= @{%s->all_args}', scalar @{$self->all_args}, $v);
  24         48  
239              
240 24         56 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  45         70  
241 21         37 push @src => $self->all_args->[$i]->is_relaxed_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
242             }
243              
244 24         1321 return join "\n && ", @src;
245             }
246              
247              
248             sub error_message {
249 25     25 1 44 my ($self, $other) = @_;
250              
251 25 100 100     150 return sprintf('other parameters must be Sub::Meta::Parameters. got: %s', $other // 'Undef')
      100        
252             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
253              
254 23 100       50 if ($self->has_slurpy) {
255 3 100       18 return sprintf('invalid slurpy. got: %s, expected: %s', $other->has_slurpy ? $other->slurpy->display : '', $self->slurpy->display)
    100          
256             unless $self->slurpy->is_same_interface($other->slurpy)
257             }
258             else {
259 20 100       34 return 'should not have slurpy' if $other->has_slurpy;
260             }
261              
262 20 100       42 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
263             unless $self->nshift == $other->nshift;
264              
265 4         6 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  4         20  
266 17 100       24 unless @{$self->all_args} == @{$other->all_args};
  17         29  
  17         27  
267              
268 13         27 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  18         29  
269 10         22 my $s = $self->all_args->[$i];
270 10         22 my $o = $other->all_args->[$i];
271 10 100       27 return sprintf('args[%d] is invalid. got: %s, expected: %s', $i, $o->display, $s->display)
272             unless $s->is_same_interface($o);
273             }
274              
275 8         45 return '';
276             }
277              
278             sub relaxed_error_message {
279 25     25 1 41 my ($self, $other) = @_;
280              
281 25 100 100     155 return sprintf('other parameters must be Sub::Meta::Parameters. got: %s', $other // 'Undef')
      100        
282             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
283              
284 23 100       48 if ($self->has_slurpy) {
285 3 100       7 return sprintf('invalid slurpy. got: %s, expected: %s', $other->has_slurpy ? $other->slurpy->display : '', $self->slurpy->display)
    100          
286             unless $self->slurpy->is_same_interface($other->slurpy)
287             }
288              
289 21 100       42 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
290             unless $self->nshift == $other->nshift;
291              
292 3         8 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  3         7  
293 18 100       24 unless @{$self->all_args} <= @{$other->all_args};
  18         28  
  18         30  
294              
295 15         29 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  20         27  
296 10         16 my $s = $self->all_args->[$i];
297 10         18 my $o = $other->all_args->[$i];
298 10 100       29 return sprintf('args[%d] is invalid. got: %s, expected: %s', $i, $o->display, $s->display)
299             unless $s->is_relaxed_same_interface($o);
300             }
301              
302 10         20 return '';
303             }
304             sub display {
305 36     36 1 48 my $self = shift;
306              
307 36         44 my $s = '';
308 36 100       55 if ($self->has_invocant) {
309 4         6 my $d = $self->invocant->display;
310 4 100       8 $s .= "$d: " if $d;
311             }
312              
313 36 100       70 if ($self->has_args) {
314 24         34 $s .= join ', ', map { $_->display } @{$self->args};
  33         64  
  24         36  
315             }
316              
317 36 100       69 if ($self->has_slurpy) {
318 3 100       7 $s .= ', ' if $s;
319 3         6 $s .= $self->slurpy->display;
320             }
321              
322 36 100 100     59 if(!$self->has_args && !$self->has_slurpy) {
323 11         17 $s .= '*';
324             }
325              
326 36         87 return $s;
327             }
328              
329             1;
330             __END__