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   917254 use 5.010;
  34         165  
3 34     34   186 use strict;
  34         66  
  34         737  
4 34     34   177 use warnings;
  34         76  
  34         1748  
5              
6             our $VERSION = "0.14";
7              
8 34     34   207 use Carp ();
  34         70  
  34         579  
9 34     34   194 use Scalar::Util ();
  34         64  
  34         736  
10              
11 34     34   14709 use Sub::Meta::Param;
  34         92  
  34         1520  
12              
13             use overload
14 34         297 fallback => 1,
15             eq => \&is_same_interface
16 34     34   410 ;
  34         78  
17              
18 5     5   37 sub _croak { require Carp; goto &Carp::croak }
  5         572  
19              
20 132     132 1 785 sub param_class { return 'Sub::Meta::Param' }
21              
22             sub new {
23 296     296 1 235603 my ($class, @args) = @_;
24 296 100       964 my %args = @args == 1 ? %{$args[0]} : @args;
  260         1184  
25              
26 294         774 my $self = bless \%args => $class;
27 294 100       1302 $self->set_args($args{args}) if exists $args{args};
28 294 100       932 $self->set_invocant(delete $args{invocant}) if defined $args{invocant};
29 294 100       1254 $self->set_nshift(delete $args{nshift}) if defined $args{nshift};
30 294 100       826 $self->set_slurpy(delete $args{slurpy}) if defined $args{slurpy};
31              
32 294         871 return $self;
33             }
34              
35 741   100 741 1 24311 sub nshift() { my $self = shift; return $self->{nshift} // 0 }
  741         4941  
36 278     278 1 8488 sub slurpy() { my $self = shift; return $self->{slurpy} }
  278         1579  
37 2255   100 2255 1 14657 sub args() { my $self = shift; return $self->{args} // [] }
  2255         14660  
38 332     332 1 12086 sub invocant() { my $self = shift; return $self->{invocant} }
  332         1217  
39 1800 100   1800 1 12653 sub invocants() { my $self = shift; return $self->has_invocant ? [ $self->{invocant} ] : [] }
  1800         2944  
40 1629     1629 1 32308 sub all_args() { my $self = shift; return [ @{$self->invocants}, @{$self->args} ] }
  1629         2230  
  1629         2598  
  1629         2769  
41              
42 117     117 1 8722 sub has_args() { my $self = shift; return defined $self->{args} }
  117         361  
43 1881     1881 1 10742 sub has_invocant() { my $self = shift; return defined $self->{invocant} }
  1881         5456  
44 507     507 1 9228 sub has_slurpy() { my $self = shift; return defined $self->{slurpy} }
  507         2591  
45              
46             sub set_slurpy {
47 30     30 1 83 my ($self, $v) = @_;
48 30 100 100     284 $self->{slurpy} = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
49             ? $v
50             : $self->param_class->new($v);
51 30         89 return $self;
52             }
53              
54             sub set_args {
55 178     178 1 1551 my ($self, @args) = @_;
56 178         568 $self->{args} = $self->_normalize_args(@args);
57 175         414 return $self;
58             }
59              
60             sub set_nshift {
61 258     258 1 1805 my ($self, $v) = @_;
62              
63 258 100 100     1273 unless (defined $v && ($v == 0 || $v == 1) ) {
      100        
64 2   100     17 _croak sprintf("Can't set this nshift: %s", $v//'');
65             }
66              
67 256         1038 $self->{nshift} = $v;
68              
69 256 100 100     828 if ($v == 1 && !defined $self->invocant) {
70 40         146 my $default_invocant = $self->param_class->new(invocant => 1);
71 40         145 $self->set_invocant($default_invocant)
72             }
73              
74 256 100 100     1020 if ($v == 0 && defined $self->invocant) {
75             delete $self->{invocant}
76 1         3 }
77              
78 256         503 return $self;
79             }
80              
81             sub set_invocant {
82 64     64 1 187 my ($self, $v) = @_;
83              
84 64 100 100     571 my $invocant = Scalar::Util::blessed($v) && $v->isa('Sub::Meta::Param')
85             ? $v
86             : $self->param_class->new($v);
87              
88 64         295 $invocant->set_invocant(1);
89              
90 64         128 $self->{invocant} = $invocant;
91              
92 64 100       162 if ($self->nshift == 0) {
93 15         81 $self->set_nshift(1);
94             }
95              
96 64         171 return $self;
97             }
98              
99             sub _normalize_args {
100 178     178   436 my ($self, @args) = @_;
101 178         353 my $args = $args[0];
102 178 100 100     1090 _croak 'args must be a single reference' unless @args == 1 && ref $args;
103              
104 175         417 my @normalized_args;
105 175 100       539 if (ref $args eq 'ARRAY') {
    100          
106 165         322 @normalized_args = @{$args};
  165         429  
107             }
108             elsif (ref $args eq 'HASH') {
109 5         10 for my $name (sort { $a cmp $b } keys %{$args}) {
  1         6  
  5         20  
110 5         22 my $v = $args->{$name};
111 5   66     81 my $f = ref $v && ref $v eq 'HASH';
112 5 100       47 push @normalized_args => {
113             name => $name,
114             named => 1,
115             ($f ? %$v : (type => $v) ),
116             }
117             }
118             }
119             else {
120 5         13 @normalized_args = ($args);
121             }
122              
123             return [
124             map {
125 175 100 100     668 Scalar::Util::blessed($_) && $_->isa('Sub::Meta::Param')
  156         1254  
126             ? $_
127             : $self->param_class->new($_)
128             } @normalized_args
129             ]
130             }
131              
132             sub _all_positional_required() {
133 125     125   31419 my $self = shift;
134 125         201 return [ @{$self->invocants}, @{$self->positional_required} ];
  125         288  
  125         317  
135             }
136              
137              
138 45     45 1 21354 sub positional() { my $self = shift; return [ grep { $_->positional } @{$self->args} ] }
  45         90  
  26         65  
  45         107  
139 170 100   170 1 21279 sub positional_required() { my $self = shift; return [ grep { $_->positional && $_->required } @{$self->args} ] }
  170         286  
  88         269  
  170         311  
140 80 100   80 1 18641 sub positional_optional() { my $self = shift; return [ grep { $_->positional && $_->optional } @{$self->args} ] }
  80         136  
  36         91  
  80         164  
141              
142 88     88 1 12846 sub named() { my $self = shift; return [ grep { $_->named } @{$self->args} ] }
  88         234  
  51         129  
  88         194  
143 90 100   90 1 18094 sub named_required() { my $self = shift; return [ grep { $_->named && $_->required } @{$self->args} ] }
  90         161  
  52         142  
  90         188  
144 45 100   45 1 16085 sub named_optional() { my $self = shift; return [ grep { $_->named && $_->optional } @{$self->args} ] }
  45         88  
  26         76  
  45         139  
145              
146             sub args_min() {
147 45     45 1 12816 my $self = shift;
148 45         87 my $r = 0;
149 45         103 $r += @{$self->_all_positional_required};
  45         143  
150 45         96 $r += @{$self->named_required} * 2;
  45         97  
151 45         126 return $r
152             }
153              
154             sub args_max() {
155 45     45 1 8797 my $self = shift;
156 45 100 100     131 return 0 + 'Inf' if $self->slurpy || @{$self->named}; ## no critic (ProhibitMismatchedOperators)
  43         108  
157 35         77 my $r = 0;
158 35         55 $r += @{$self->_all_positional_required};
  35         77  
159 35         70 $r += @{$self->positional_optional};
  35         86  
160 35         84 return $r
161             }
162              
163             sub is_same_interface {
164 81     81 1 256 my ($self, $other) = @_;
165              
166 81 100 100     596 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
167              
168 79 100       282 if ($self->has_slurpy) {
169 11 100       36 return unless $self->slurpy->is_same_interface($other->slurpy)
170             }
171             else {
172 68 100       164 return if $other->has_slurpy;
173             }
174              
175 73 100       208 return unless $self->nshift == $other->nshift;
176              
177 67 100       132 return unless @{$self->all_args} == @{$other->all_args};
  67         204  
  67         182  
178              
179 54         155 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  79         183  
180 38 100       108 return unless $self->all_args->[$i]->is_same_interface($other->all_args->[$i]);
181             }
182              
183 41         152 return !!1;
184             }
185              
186             sub is_relaxed_same_interface {
187 87     87 1 239 my ($self, $other) = @_;
188              
189 87 100 100     697 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Parameters');
190              
191 85 100       280 if ($self->has_slurpy) {
192 11 100       33 return unless $self->slurpy->is_same_interface($other->slurpy)
193             }
194              
195 82 100       252 return unless $self->nshift == $other->nshift;
196              
197 76 100       148 return unless @{$self->all_args} <= @{$other->all_args};
  76         217  
  76         185  
198              
199 70         249 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  108         225  
200 49 100       125 return unless $self->all_args->[$i]->is_relaxed_same_interface($other->all_args->[$i]);
201             }
202              
203 59         197 return !!1;
204             }
205              
206             sub is_same_interface_inlined {
207 20     20 1 62 my ($self, $v) = @_;
208              
209 20         93 my @src;
210              
211 20         137 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
212              
213 20 100       80 push @src => $self->has_slurpy ? $self->slurpy->is_same_interface_inlined(sprintf('%s->slurpy', $v))
214             : sprintf('!%s->has_slurpy', $v);
215              
216 20         79 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
217              
218 20         53 push @src => sprintf('%d == @{%s->all_args}', scalar @{$self->all_args}, $v);
  20         79  
219              
220 20         66 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  35         80  
221 15         54 push @src => $self->all_args->[$i]->is_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
222             }
223              
224 20         2771 return join "\n && ", @src;
225             }
226              
227             sub is_relaxed_same_interface_inlined {
228 24     24 1 73 my ($self, $v) = @_;
229              
230 24         47 my @src;
231              
232 24         109 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Parameters')", $v, $v);
233              
234 24 100       71 push @src => $self->slurpy->is_relaxed_same_interface_inlined(sprintf('%s->slurpy', $v)) if $self->has_slurpy;
235              
236 24         85 push @src => sprintf('%d == %s->nshift', $self->nshift, $v);
237              
238 24         66 push @src => sprintf('%d <= @{%s->all_args}', scalar @{$self->all_args}, $v);
  24         68  
239              
240 24         88 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  45         105  
241 21         58 push @src => $self->all_args->[$i]->is_relaxed_same_interface_inlined(sprintf('%s->all_args->[%d]', $v, $i))
242             }
243              
244 24         1762 return join "\n && ", @src;
245             }
246              
247              
248             sub error_message {
249 25     25 1 60 my ($self, $other) = @_;
250              
251 25 100 100     226 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       75 if ($self->has_slurpy) {
255 3 100       69 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       47 return 'should not have slurpy' if $other->has_slurpy;
260             }
261              
262 20 100       63 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
263             unless $self->nshift == $other->nshift;
264              
265 4         12 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  4         25  
266 17 100       42 unless @{$self->all_args} == @{$other->all_args};
  17         55  
  17         40  
267              
268 13         40 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  18         36  
269 10         27 my $s = $self->all_args->[$i];
270 10         24 my $o = $other->all_args->[$i];
271 10 100       45 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         58 return '';
276             }
277              
278             sub relaxed_error_message {
279 25     25 1 74 my ($self, $other) = @_;
280              
281 25 100 100     207 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       64 if ($self->has_slurpy) {
285 3 100       10 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       71 return sprintf('nshift is not equal. got: %d, expected: %d', $other->nshift, $self->nshift)
290             unless $self->nshift == $other->nshift;
291              
292 3         12 return sprintf('invalid args length. got: %d, expected: %d', scalar @{$other->all_args}, scalar @{$self->all_args})
  3         9  
293 18 100       42 unless @{$self->all_args} <= @{$other->all_args};
  18         43  
  18         36  
294              
295 15         40 for (my $i = 0; $i < @{$self->all_args}; $i++) {
  20         39  
296 10         22 my $s = $self->all_args->[$i];
297 10         25 my $o = $other->all_args->[$i];
298 10 100       33 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         30 return '';
303             }
304             sub display {
305 36     36 1 107 my $self = shift;
306              
307 36         67 my $s = '';
308 36 100       71 if ($self->has_invocant) {
309 4         8 my $d = $self->invocant->display;
310 4 100       11 $s .= "$d: " if $d;
311             }
312              
313 36 100       108 if ($self->has_args) {
314 24         48 $s .= join ', ', map { $_->display } @{$self->args};
  33         89  
  24         53  
315             }
316              
317 36 100       88 if ($self->has_slurpy) {
318 3 100       8 $s .= ', ' if $s;
319 3         7 $s .= $self->slurpy->display;
320             }
321              
322 36 100 100     77 if(!$self->has_args && !$self->has_slurpy) {
323 11         24 $s .= '*';
324             }
325              
326 36         147 return $s;
327             }
328              
329             1;
330             __END__