File Coverage

blib/lib/Sub/Meta/Returns.pm
Criterion Covered Total %
statement 138 138 100.0
branch 104 104 100.0
condition 49 49 100.0
subroutine 29 29 100.0
pod 20 20 100.0
total 340 340 100.0


line stmt bran cond sub pod time code
1             package Sub::Meta::Returns;
2 32     32   544020 use 5.010;
  32         110  
3 32     32   152 use strict;
  32         52  
  32         579  
4 32     32   129 use warnings;
  32         52  
  32         1113  
5              
6             our $VERSION = "0.15";
7              
8 32     32   170 use Scalar::Util ();
  32         57  
  32         973  
9              
10             use overload
11 32         243 fallback => 1,
12             eq => \&is_same_interface
13 32     32   187 ;
  32         52  
14              
15             sub new {
16 248     248 1 86680 my ($class, @args) = @_;
17 248         363 my $v = $args[0];
18 248 100 100     1297 my %args = @args == 1 ? ref $v && ref $v eq 'HASH' ? %{$v}
  58 100       202  
19             : ( scalar => $v, list => $v, void => $v )
20             : @args;
21              
22 248         883 return bless \%args => $class;
23             }
24              
25 377     377 1 14981 sub scalar() :method { my $self = shift; return $self->{scalar} } ## no critic (ProhibitBuiltinHomonyms)
  377         2139  
26 198     198 1 6098 sub list() { my $self = shift; return $self->{list} }
  198         940  
27 189     189 1 5526 sub void() { my $self = shift; return $self->{void} }
  189         709  
28 40     40 1 5895 sub coerce() { my $self = shift; return $self->{coerce} }
  40         85  
29              
30 340     340 1 5943 sub has_scalar() { my $self = shift; return defined $self->{scalar} }
  340         1391  
31 261     261 1 6085 sub has_list() { my $self = shift; return defined $self->{list} }
  261         910  
32 243     243 1 5978 sub has_void() { my $self = shift; return defined $self->{void} }
  243         592  
33 38     38 1 5713 sub has_coerce() { my $self = shift; return defined $self->{coerce} }
  38         97  
34              
35 1     1 1 4 sub set_scalar { my ($self, $v) = @_; $self->{scalar} = $v; return $self }
  1         3  
  1         4  
36 1     1 1 4 sub set_list { my ($self, $v) = @_; $self->{list} = $v; return $self }
  1         2  
  1         4  
37 1     1 1 4 sub set_void { my ($self, $v) = @_; $self->{void} = $v; return $self }
  1         3  
  1         4  
38 1     1 1 4 sub set_coerce { my ($self, $v) = @_; $self->{coerce} = $v; return $self }
  1         2  
  1         4  
39              
40             sub is_same_interface {
41 53     53 1 114 my ($self, $other) = @_;
42              
43 53 100 100     337 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
44              
45 51 100       150 if ($self->has_scalar) {
46 25 100       62 return unless _eq($self->scalar, $other->scalar)
47             }
48             else {
49 26 100       53 return if $other->has_scalar
50             }
51              
52 34 100       93 if ($self->has_list) {
53 9 100       31 return unless _eq($self->list, $other->list)
54             }
55             else {
56 25 100       49 return if $other->has_list
57             }
58              
59 31 100       72 if ($self->has_void) {
60 8 100       22 return unless _eq($self->void, $other->void)
61             }
62             else {
63 23 100       44 return if $other->has_void
64             }
65              
66 28         89 return !!1;
67             }
68              
69             sub is_relaxed_same_interface {
70 60     60 1 114 my ($self, $other) = @_;
71              
72 60 100 100     318 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
73              
74 58 100       143 if ($self->has_scalar) {
75 27 100       55 return unless _eq($self->scalar, $other->scalar)
76             }
77              
78 46 100       101 if ($self->has_list) {
79 12 100       27 return unless _eq($self->list, $other->list)
80             }
81              
82 45 100       92 if ($self->has_void) {
83 12 100       26 return unless _eq($self->void, $other->void)
84             }
85              
86 44         113 return !!1;
87             }
88              
89             sub is_same_interface_inlined {
90 16     16 1 38 my ($self, $v) = @_;
91              
92 16         25 my @src;
93              
94 16         66 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
95              
96 16 100       46 push @src => $self->has_scalar ? _eq_inlined($self->scalar, sprintf('%s->scalar', $v))
97             : sprintf('!%s->has_scalar', $v);
98              
99 16 100       58 push @src => $self->has_list ? _eq_inlined($self->list, sprintf('%s->list', $v))
100             : sprintf('!%s->has_list', $v);
101              
102 16 100       50 push @src => $self->has_void ? _eq_inlined($self->void, sprintf('%s->void', $v))
103             : sprintf('!%s->has_void', $v);
104              
105 16         952 return join "\n && ", @src;
106             }
107              
108             sub is_relaxed_same_interface_inlined {
109 20     20 1 46 my ($self, $v) = @_;
110              
111 20         30 my @src;
112              
113 20         60 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
114              
115 20 100       39 push @src => _eq_inlined($self->scalar, sprintf('%s->scalar', $v)) if $self->has_scalar;
116              
117 20 100       50 push @src => _eq_inlined($self->list, sprintf('%s->list', $v)) if $self->has_list;
118              
119 20 100       45 push @src => _eq_inlined($self->void, sprintf('%s->void', $v)) if $self->has_void;
120              
121 20         485 return join "\n && ", @src;
122             }
123              
124             sub _eq {
125 160     160   255 my ($type, $other) = @_;
126              
127 160 100 100     355 if (ref $type && ref $type eq "ARRAY") {
128 11 100       29 return unless ref $other eq "ARRAY";
129 8 100       23 return unless @$type == @$other;
130 4         11 for (my $i = 0; $i < @$type; $i++) {
131 8 100       23 return unless $type->[$i] eq $other->[$i];
132             }
133             }
134             else {
135 149 100 100     483 return unless defined $other && $type eq $other;
136             }
137 115         501 return 1;
138             }
139              
140             sub _eq_inlined {
141 40     40   71 my ($type, $v) = @_;
142              
143 40         44 my @src;
144 40 100 100     109 if (ref $type && ref $type eq "ARRAY") {
145 2         5 push @src => sprintf('ref %s eq "ARRAY"', $v);
146 2         7 push @src => sprintf('%d == @{%s}', scalar @$type, $v);
147 2         7 for (my $i = 0; $i < @$type; $i++) {
148 4         14 push @src => sprintf('"%s" eq %s->[%d]', $type->[$i], $v, $i);
149             }
150             }
151             else {
152 38         123 push @src => sprintf('defined %s && "%s" eq %s', $v, $type, $v);
153             }
154              
155 40         189 return join "\n && ", @src;
156             }
157              
158             sub error_message {
159 22     22 1 38 my ($self, $other) = @_;
160              
161 22 100 100     142 return sprintf('other returns must be Sub::Meta::Returns. got: %s', $other // 'Undef')
      100        
162             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
163              
164 20 100       44 if ($self->has_scalar) {
165 7 100 100     16 return sprintf('invalid scalar return. got: %s, expected: %s', $other->scalar // '', $self->scalar)
166             unless _eq($self->scalar, $other->scalar);
167             }
168             else {
169 13 100       25 return 'should not have scalar return' if $other->has_scalar;
170             }
171              
172 14 100       26 if ($self->has_list) {
173 5 100 100     11 return sprintf('invalid list return. got: %s, expected: %s', $other->list // '', $self->list)
174             unless _eq($self->list, $other->list);
175             }
176             else {
177 9 100       16 return 'should not have list return' if $other->has_list;
178             }
179              
180 10 100       20 if ($self->has_void) {
181 4 100 100     11 return sprintf('invalid void return. got: %s, expected: %s', $other->void // '', $self->void)
182             unless _eq($self->void, $other->void);
183             }
184             else {
185 6 100       13 return 'should not have void return' if $other->has_void;
186             }
187 6         13 return '';
188             }
189              
190             sub relaxed_error_message {
191 21     21 1 37 my ($self, $other) = @_;
192              
193 21 100 100     96 return sprintf('other returns must be Sub::Meta::Returns. got: %s', $other // 'Undef')
      100        
194             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
195              
196 19 100       89 if ($self->has_scalar) {
197 7 100 100     15 return sprintf('invalid scalar return. got: %s, expected: %s', $other->scalar // '', $self->scalar)
198             unless _eq($self->scalar, $other->scalar);
199             }
200              
201 16 100       26 if ($self->has_list) {
202 6 100 100     11 return sprintf('invalid list return. got: %s, expected: %s', $other->list // '', $self->list)
203             unless _eq($self->list, $other->list);
204             }
205              
206 14 100       27 if ($self->has_void) {
207 6 100 100     11 return sprintf('invalid void return. got: %s, expected: %s', $other->void // '', $self->void)
208             unless _eq($self->void, $other->void);
209             }
210              
211 12         25 return '';
212             }
213              
214             sub _all_eq {
215 19     19   33 my $self = shift;
216 19   100     42 return $self->has_scalar
217             && _eq($self->scalar, $self->list)
218             && _eq($self->scalar, $self->void);
219             }
220              
221             sub _display {
222 22     22   28 my $type = shift;
223              
224 22 100 100     60 if (ref $type && ref $type eq "ARRAY") {
225 1         3 return sprintf('[%s]', join ",", map { $_ . '' } @$type);
  2         10  
226             }
227             else {
228 21         103 return $type . '';
229             }
230             }
231              
232             sub display {
233 36     36 1 44 my $self = shift;
234              
235 36 100 100     67 if (!$self->has_scalar && !$self->has_list && !$self->has_void) {
    100          
236 17         41 return '*';
237             }
238             elsif (_all_eq($self)) {
239 14         25 return _display($self->scalar);
240             }
241             else {
242 5         7 my @r;
243 5         9 for my $key (qw(scalar list void)) {
244 15         30 my $has = "has_$key";
245 15 100       24 push @r => "$key => @{[_display($self->$key)]}" if $self->$has;
  8         14  
246             }
247 5         8 return "(@{[join ', ', @r]})";
  5         22  
248             }
249             }
250              
251             1;
252             __END__