File Coverage

blib/lib/Sub/Meta/Returns.pm
Criterion Covered Total %
statement 128 128 100.0
branch 100 100 100.0
condition 28 28 100.0
subroutine 27 27 100.0
pod 20 20 100.0
total 303 303 100.0


line stmt bran cond sub pod time code
1             package Sub::Meta::Returns;
2 23     23   714754 use 5.010;
  23         100  
3 23     23   124 use strict;
  23         50  
  23         522  
4 23     23   176 use warnings;
  23         57  
  23         1023  
5              
6             our $VERSION = "0.13";
7              
8 23     23   130 use Scalar::Util ();
  23         43  
  23         717  
9              
10             use overload
11 23         226 fallback => 1,
12             eq => \&is_same_interface
13 23     23   159 ;
  23         51  
14              
15             sub new {
16 72     72 1 111585 my ($class, @args) = @_;
17 72         132 my $v = $args[0];
18 72 100 100     408 my %args = @args == 1 ? ref $v && ref $v eq 'HASH' ? %{$v}
  53 100       217  
19             : ( scalar => $v, list => $v, void => $v )
20             : @args;
21              
22 72         258 return bless \%args => $class;
23             }
24              
25 223     223 1 369 sub scalar() :method { my $self = shift; return $self->{scalar} } ## no critic (ProhibitBuiltinHomonyms)
  223         1493  
26 115     115 1 3698 sub list() { my $self = shift; return $self->{list} }
  115         630  
27 106     106 1 3296 sub void() { my $self = shift; return $self->{void} }
  106         345  
28 17     17 1 4014 sub coerce() { my $self = shift; return $self->{coerce} }
  17         73  
29              
30 151     151 1 230 sub has_scalar() { my $self = shift; return defined $self->{scalar} }
  151         735  
31 125     125 1 193 sub has_list() { my $self = shift; return defined $self->{list} }
  125         550  
32 111     111 1 190 sub has_void() { my $self = shift; return defined $self->{void} }
  111         322  
33 15     15 1 35 sub has_coerce() { my $self = shift; return defined $self->{coerce} }
  15         57  
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 5 sub set_list { my ($self, $v) = @_; $self->{list} = $v; return $self }
  1         3  
  1         4  
37 1     1 1 4 sub set_void { my ($self, $v) = @_; $self->{void} = $v; return $self }
  1         3  
  1         5  
38 1     1 1 4 sub set_coerce { my ($self, $v) = @_; $self->{coerce} = $v; return $self }
  1         3  
  1         4  
39              
40             sub is_same_interface {
41 35     35 1 68 my ($self, $other) = @_;
42              
43 35 100 100     224 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
44              
45 31 100       87 if ($self->has_scalar) {
46 23 100       56 return unless _eq($self->scalar, $other->scalar)
47             }
48             else {
49 8 100       17 return if $other->has_scalar
50             }
51              
52 19 100       51 if ($self->has_list) {
53 9 100       25 return unless _eq($self->list, $other->list)
54             }
55             else {
56 10 100       21 return if $other->has_list
57             }
58              
59 16 100       42 if ($self->has_void) {
60 8 100       21 return unless _eq($self->void, $other->void)
61             }
62             else {
63 8 100       13 return if $other->has_void
64             }
65              
66 13         38 return !!1;
67             }
68              
69             sub is_relaxed_same_interface {
70 37     37 1 74 my ($self, $other) = @_;
71              
72 37 100 100     201 return unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
73              
74 33 100       74 if ($self->has_scalar) {
75 25 100       49 return unless _eq($self->scalar, $other->scalar)
76             }
77              
78 23 100       52 if ($self->has_list) {
79 12 100       28 return unless _eq($self->list, $other->list)
80             }
81              
82 22 100       47 if ($self->has_void) {
83 12 100       26 return unless _eq($self->void, $other->void)
84             }
85              
86 21         58 return !!1;
87             }
88              
89             sub is_same_interface_inlined {
90 7     7 1 17 my ($self, $v) = @_;
91              
92 7         13 my @src;
93              
94 7         38 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
95              
96 7 100       21 push @src => $self->has_scalar ? _eq_inlined($self->scalar, sprintf('%s->scalar', $v))
97             : sprintf('!%s->has_scalar', $v);
98              
99 7 100       22 push @src => $self->has_list ? _eq_inlined($self->list, sprintf('%s->list', $v))
100             : sprintf('!%s->has_list', $v);
101              
102 7 100       25 push @src => $self->has_void ? _eq_inlined($self->void, sprintf('%s->void', $v))
103             : sprintf('!%s->has_void', $v);
104              
105 7         972 return join "\n && ", @src;
106             }
107              
108             sub is_relaxed_same_interface_inlined {
109 7     7 1 24 my ($self, $v) = @_;
110              
111 7         11 my @src;
112              
113 7         34 push @src => sprintf("Scalar::Util::blessed(%s) && %s->isa('Sub::Meta::Returns')", $v, $v);
114              
115 7 100       18 push @src => _eq_inlined($self->scalar, sprintf('%s->scalar', $v)) if $self->has_scalar;
116              
117 7 100       21 push @src => _eq_inlined($self->list, sprintf('%s->list', $v)) if $self->has_list;
118              
119 7 100       17 push @src => _eq_inlined($self->void, sprintf('%s->void', $v)) if $self->has_void;
120              
121 7         518 return join "\n && ", @src;
122             }
123              
124             sub _eq {
125 123     123   211 my ($type, $other) = @_;
126              
127 123 100 100     312 if (ref $type && ref $type eq "ARRAY") {
128 10 100       28 return unless ref $other eq "ARRAY";
129 8 100       27 return unless @$type == @$other;
130 4         10 for (my $i = 0; $i < @$type; $i++) {
131 8 100       28 return unless $type->[$i] eq $other->[$i];
132             }
133             }
134             else {
135 113 100       303 return unless $type eq $other;
136             }
137 89         217 return 1;
138             }
139              
140             sub error_message {
141 19     19 1 40 my ($self, $other) = @_;
142              
143 19 100 100     150 return sprintf('must be Sub::Meta::Returns. got: %s', $other // '')
      100        
144             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
145              
146 16 100       43 if ($self->has_scalar) {
147 6 100       29 return sprintf('invalid scalar return. got: %s, expected: %s', $other->scalar, $self->scalar)
148             unless _eq($self->scalar, $other->scalar);
149             }
150             else {
151 10 100       18 return 'should not have scalar return' if $other->has_scalar;
152             }
153              
154 12 100       26 if ($self->has_list) {
155 4 100       9 return sprintf('invalid list return. got: %s, expected: %s', $other->list, $self->list)
156             unless _eq($self->list, $other->list);
157             }
158             else {
159 8 100       15 return 'should not have list return' if $other->has_list;
160             }
161              
162 9 100       20 if ($self->has_void) {
163 3 100       11 return sprintf('invalid void return. got: %s, expected: %s', $other->void, $self->void)
164             unless _eq($self->void, $other->void);
165             }
166             else {
167 6 100       10 return 'should not have void return' if $other->has_void;
168             }
169 6         15 return '';
170             }
171              
172             sub relaxed_error_message {
173 19     19 1 35 my ($self, $other) = @_;
174              
175 19 100 100     112 return sprintf('must be Sub::Meta::Returns. got: %s', $other // '')
      100        
176             unless Scalar::Util::blessed($other) && $other->isa('Sub::Meta::Returns');
177              
178 16 100       33 if ($self->has_scalar) {
179 6 100       12 return sprintf('invalid scalar return. got: %s, expected: %s', $other->scalar, $self->scalar)
180             unless _eq($self->scalar, $other->scalar);
181             }
182              
183 14 100       28 if ($self->has_list) {
184 5 100       10 return sprintf('invalid list return. got: %s, expected: %s', $other->list, $self->list)
185             unless _eq($self->list, $other->list);
186             }
187              
188 13 100       26 if ($self->has_void) {
189 5 100       8 return sprintf('invalid void return. got: %s, expected: %s', $other->void, $self->void)
190             unless _eq($self->void, $other->void);
191             }
192              
193 12         30 return '';
194             }
195              
196             sub _eq_inlined {
197 22     22   46 my ($type, $v) = @_;
198              
199 22         28 my @src;
200 22 100 100     72 if (ref $type && ref $type eq "ARRAY") {
201 2         7 push @src => sprintf('ref %s eq "ARRAY"', $v);
202 2         7 push @src => sprintf('%d == @{%s}', scalar @$type, $v);
203 2         9 for (my $i = 0; $i < @$type; $i++) {
204 4         16 push @src => sprintf('"%s" eq %s->[%d]', $type->[$i], $v, $i);
205             }
206             }
207             else {
208 20         85 push @src => sprintf('"%s" eq %s', $type, $v);
209             }
210              
211 22         67 return join "\n && ", @src;
212             }
213              
214             sub display {
215 3     3 1 4 my $self = shift;
216              
217 3 100 100     8 if (_eq($self->scalar, $self->list) && _eq($self->list, $self->void)) {
218 1         4 return $self->scalar . '';
219             }
220             else {
221 2 100       4 my @r = map { $self->$_ ? "$_ => @{[$self->$_]}" : () } qw(scalar list void);
  6         15  
  5         11  
222 2         5 return "(@{[join ', ', @r]})";
  2         14  
223             }
224             }
225              
226             1;
227             __END__