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   672091 use 5.010;
  32         131  
3 32     32   187 use strict;
  32         70  
  32         661  
4 32     32   155 use warnings;
  32         62  
  32         1303  
5              
6             our $VERSION = "0.14";
7              
8 32     32   181 use Scalar::Util ();
  32         84  
  32         1141  
9              
10             use overload
11 32         299 fallback => 1,
12             eq => \&is_same_interface
13 32     32   178 ;
  32         63  
14              
15             sub new {
16 248     248 1 93932 my ($class, @args) = @_;
17 248         426 my $v = $args[0];
18 248 100 100     1545 my %args = @args == 1 ? ref $v && ref $v eq 'HASH' ? %{$v}
  58 100       244  
19             : ( scalar => $v, list => $v, void => $v )
20             : @args;
21              
22 248         1012 return bless \%args => $class;
23             }
24              
25 377     377 1 18303 sub scalar() :method { my $self = shift; return $self->{scalar} } ## no critic (ProhibitBuiltinHomonyms)
  377         2665  
26 198     198 1 7383 sub list() { my $self = shift; return $self->{list} }
  198         1171  
27 189     189 1 6965 sub void() { my $self = shift; return $self->{void} }
  189         876  
28 40     40 1 7148 sub coerce() { my $self = shift; return $self->{coerce} }
  40         117  
29              
30 340     340 1 7495 sub has_scalar() { my $self = shift; return defined $self->{scalar} }
  340         1777  
31 261     261 1 7446 sub has_list() { my $self = shift; return defined $self->{list} }
  261         1144  
32 243     243 1 7316 sub has_void() { my $self = shift; return defined $self->{void} }
  243         814  
33 38     38 1 7333 sub has_coerce() { my $self = shift; return defined $self->{coerce} }
  38         128  
34              
35 1     1 1 5 sub set_scalar { my ($self, $v) = @_; $self->{scalar} = $v; return $self }
  1         3  
  1         5  
36 1     1 1 4 sub set_list { my ($self, $v) = @_; $self->{list} = $v; return $self }
  1         3  
  1         5  
37 1     1 1 4 sub set_void { my ($self, $v) = @_; $self->{void} = $v; return $self }
  1         4  
  1         5  
38 1     1 1 4 sub set_coerce { my ($self, $v) = @_; $self->{coerce} = $v; return $self }
  1         5  
  1         5  
39              
40             sub is_same_interface {
41 53     53 1 128 my ($self, $other) = @_;
42              
43 53 100 100     379 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
44              
45 51 100       155 if ($self->has_scalar) {
46 25 100       75 return unless _eq($self->scalar, $other->scalar)
47             }
48             else {
49 26 100       65 return if $other->has_scalar
50             }
51              
52 34 100       105 if ($self->has_list) {
53 9 100       44 return unless _eq($self->list, $other->list)
54             }
55             else {
56 25 100       54 return if $other->has_list
57             }
58              
59 31 100       101 if ($self->has_void) {
60 8 100       26 return unless _eq($self->void, $other->void)
61             }
62             else {
63 23 100       74 return if $other->has_void
64             }
65              
66 28         99 return !!1;
67             }
68              
69             sub is_relaxed_same_interface {
70 60     60 1 145 my ($self, $other) = @_;
71              
72 60 100 100     379 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
73              
74 58 100       161 if ($self->has_scalar) {
75 27 100       66 return unless _eq($self->scalar, $other->scalar)
76             }
77              
78 46 100       127 if ($self->has_list) {
79 12 100       39 return unless _eq($self->list, $other->list)
80             }
81              
82 45 100       116 if ($self->has_void) {
83 12 100       35 return unless _eq($self->void, $other->void)
84             }
85              
86 44         128 return !!1;
87             }
88              
89             sub is_same_interface_inlined {
90 16     16 1 51 my ($self, $v) = @_;
91              
92 16         62 my @src;
93              
94 16         82 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
95              
96 16 100       64 push @src => $self->has_scalar ? _eq_inlined($self->scalar, sprintf('%s->scalar', $v))
97             : sprintf('!%s->has_scalar', $v);
98              
99 16 100       62 push @src => $self->has_list ? _eq_inlined($self->list, sprintf('%s->list', $v))
100             : sprintf('!%s->has_list', $v);
101              
102 16 100       59 push @src => $self->has_void ? _eq_inlined($self->void, sprintf('%s->void', $v))
103             : sprintf('!%s->has_void', $v);
104              
105 16         1098 return join "\n && ", @src;
106             }
107              
108             sub is_relaxed_same_interface_inlined {
109 20     20 1 64 my ($self, $v) = @_;
110              
111 20         34 my @src;
112              
113 20         83 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
114              
115 20 100       65 push @src => _eq_inlined($self->scalar, sprintf('%s->scalar', $v)) if $self->has_scalar;
116              
117 20 100       65 push @src => _eq_inlined($self->list, sprintf('%s->list', $v)) if $self->has_list;
118              
119 20 100       59 push @src => _eq_inlined($self->void, sprintf('%s->void', $v)) if $self->has_void;
120              
121 20         616 return join "\n && ", @src;
122             }
123              
124             sub _eq {
125 160     160   306 my ($type, $other) = @_;
126              
127 160 100 100     444 if (ref $type && ref $type eq "ARRAY") {
128 11 100       35 return unless ref $other eq "ARRAY";
129 8 100       30 return unless @$type == @$other;
130 4         11 for (my $i = 0; $i < @$type; $i++) {
131 8 100       26 return unless $type->[$i] eq $other->[$i];
132             }
133             }
134             else {
135 149 100 100     649 return unless defined $other && $type eq $other;
136             }
137 115         633 return 1;
138             }
139              
140             sub _eq_inlined {
141 40     40   85 my ($type, $v) = @_;
142              
143 40         59 my @src;
144 40 100 100     153 if (ref $type && ref $type eq "ARRAY") {
145 2         8 push @src => sprintf('ref %s eq "ARRAY"', $v);
146 2         7 push @src => sprintf('%d == @{%s}', scalar @$type, $v);
147 2         9 for (my $i = 0; $i < @$type; $i++) {
148 4         18 push @src => sprintf('"%s" eq %s->[%d]', $type->[$i], $v, $i);
149             }
150             }
151             else {
152 38         149 push @src => sprintf('defined %s && "%s" eq %s', $v, $type, $v);
153             }
154              
155 40         248 return join "\n && ", @src;
156             }
157              
158             sub error_message {
159 22     22 1 41 my ($self, $other) = @_;
160              
161 22 100 100     170 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       54 if ($self->has_scalar) {
165 7 100 100     20 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       27 return 'should not have scalar return' if $other->has_scalar;
170             }
171              
172 14 100       34 if ($self->has_list) {
173 5 100 100     14 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       15 return 'should not have list return' if $other->has_list;
178             }
179              
180 10 100       26 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       11 return 'should not have void return' if $other->has_void;
186             }
187 6         14 return '';
188             }
189              
190             sub relaxed_error_message {
191 21     21 1 39 my ($self, $other) = @_;
192              
193 21 100 100     129 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       121 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       31 if ($self->has_list) {
202 6 100 100     15 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       31 if ($self->has_void) {
207 6 100 100     13 return sprintf('invalid void return. got: %s, expected: %s', $other->void // '', $self->void)
208             unless _eq($self->void, $other->void);
209             }
210              
211 12         27 return '';
212             }
213              
214             sub _all_eq {
215 19     19   46 my $self = shift;
216 19   100     50 return $self->has_scalar
217             && _eq($self->scalar, $self->list)
218             && _eq($self->scalar, $self->void);
219             }
220              
221             sub _display {
222 22     22   33 my $type = shift;
223              
224 22 100 100     77 if (ref $type && ref $type eq "ARRAY") {
225 1         4 return sprintf('[%s]', join ",", map { $_ . '' } @$type);
  2         13  
226             }
227             else {
228 21         168 return $type . '';
229             }
230             }
231              
232             sub display {
233 36     36 1 68 my $self = shift;
234              
235 36 100 100     91 if (!$self->has_scalar && !$self->has_list && !$self->has_void) {
    100          
236 17         54 return '*';
237             }
238             elsif (_all_eq($self)) {
239 14         41 return _display($self->scalar);
240             }
241             else {
242 5         8 my @r;
243 5         11 for my $key (qw(scalar list void)) {
244 15         30 my $has = "has_$key";
245 15 100       31 push @r => "$key => @{[_display($self->$key)]}" if $self->$has;
  8         19  
246             }
247 5         12 return "(@{[join ', ', @r]})";
  5         32  
248             }
249             }
250              
251             1;
252             __END__