File Coverage

blib/lib/Smart/Match.pm
Criterion Covered Total %
statement 106 228 46.4
branch 43 68 63.2
condition 7 12 58.3
subroutine 75 79 94.9
pod 29 29 100.0
total 260 416 62.5


line stmt bran cond sub pod time code
1             package Smart::Match;
2             {
3             $Smart::Match::VERSION = '0.007';
4             }
5              
6 0     1   0 use 5.010001;
  16         182  
  0         0  
7 0     1   0 use strict;
  0         0  
  5         28  
8 0     1   0 use warnings FATAL => 'all';
  0         0  
  3         31  
9 0     1   0 use experimental 'smartmatch';
  0         0  
  0         0  
10              
11 0     1   0 use Carp qw/croak/;
  0         0  
  0         0  
12 3     1   33 use List::MoreUtils qw//;
  0         0  
  0         0  
13 0     1   0 use Scalar::Util qw(blessed looks_like_number refaddr);
  2         5  
  0         0  
14              
15 0     1   0 use Smart::Match::Overload;
  0         0  
  0         0  
16              
17 0         0 use Sub::Exporter::Progressive -setup => {
18             exports => [qw/
19             match delegate
20             always never
21             any all none one
22             true false
23             number integer even odd
24             more_than less_than at_least at_most positive negative range
25             numwise stringwise
26             string string_length
27             object instance_of ref_type
28             array array_length tuple head sequence contains sorted sorted_by
29             hash hash_keys hash_values sub_hash hashwise
30             address value
31             /],
32             groups => {
33             junctive => [qw/any all none one/],
34             definite => [qw/always never/],
35             boolean => [qw/true false/],
36             numeric => [qw/number integer even odd more_than less_than at_least at_most/],
37             compare => [qw/numwise stringwise hashwise/],
38             meta => [qw/match delegate/],
39             string => [qw/string string_length/],
40             refs => [qw/object instance_of ref_type/],
41             arrays => [qw/array array_length tuple head sequence contains sorted/],
42             hashes => [qw/hash hash_keys hash_values sub_hash/],
43             direct => [qw/address value/],
44             },
45 0     1   0 };
  0         0  
46              
47             ## no critic (Subroutines::ProhibitSubroutinePrototypes,ValuesAndExpressions::ProhibitConstantPragma)
48             sub match (&) {
49 0     127 1 0 my $sub = shift;
50              
51 0         0 return Smart::Match::Overload->new($sub);
52             }
53              
54             sub _matchall (&@) {
55 0     10   0 my $sub = shift;
56 0 50       0 croak 'No arguments given to match' if not @_;
57 0 50       0 if (wantarray) {
58 0         0 return map { $sub->($_) } @_;
  0         0  
59             }
60             else {
61 0 0       0 croak 'Can\'t use multiple matchers in scalar context' if @_ > 1;
62 0         0 return $sub->($_[0]);
63             }
64             }
65              
66             sub delegate (&@) {
67 0     0 1 0 my ($sub, $match) = @_;
68 0     0   0 return match { return $_ ~~ $match for $sub->() };
  0         0  
69             }
70              
71             sub any {
72 0     7 1 0 my @possibilities = @_;
73             return match {
74 0     7   0 for my $candidate (@possibilities) {
75 0 100       0 return 1 if $_ ~~ $candidate;
76             }
77 0         0 return;
78 0         0 };
79             }
80              
81             sub all {
82 0     4 1 0 my @possibilities = @_;
83             return match {
84 0     4   0 for my $candidate (@possibilities) {
85 0 100       0 return if not $_ ~~ $candidate;
86             }
87 0         0 return 1;
88 0         0 };
89             }
90              
91             sub none {
92 0     2 1 0 my @possibilities = @_;
93             return match {
94 0     2   0 for my $candidate (@possibilities) {
95 0 100       0 return if $_ ~~ $candidate;
96             }
97 0         0 return 1;
98 0         0 };
99             }
100              
101             sub one {
102 0     2 1 0 my @possibilities = @_;
103             return match {
104 0     2   0 my $count = 0;
105 0         0 for my $candidate (@possibilities) {
106 0 100       0 $count++ if $_ ~~ $candidate;
107 0 100       0 return if $count > 1;
108             }
109 0         0 return $count == 1;
110 0         0 };
111             }
112              
113 3     1   17 use constant always => match { 1 };
  0         0  
  0         0  
  15         108  
114 0     1   0 use constant never => match { };
  0         0  
  0         0  
  0         0  
115              
116 0     1   0 use constant true => match { $_ };
  0         0  
  0         0  
  0         0  
117 2     1   9 use constant false => match { not $_ };
  0         0  
  0         0  
  0         0  
118              
119 0     1   0 use constant number => match { looks_like_number($_) };
  2         19  
  0         0  
  0         0  
120 0 50   1   0 use constant integer => match { looks_like_number($_) and int == $_ };
  204         852  
  0         0  
  0         0  
121              
122 3 50   1   14 use constant even => match { scalar integer and $_ % 2 == 0 };
  0         0  
  0         0  
  0         0  
123 2 50   1   10 use constant odd => match { scalar integer and $_ % 2 == 1 };
  0         0  
  0         0  
  0         0  
124              
125             sub more_than {
126 0     1 1 0 my $cutoff = shift;
127 0 50   3   0 return match { looks_like_number($_) and $_ > $cutoff };
  0         0  
128             }
129              
130             sub at_least {
131 0     7 1 0 my $cutoff = shift;
132 0 50   12   0 return match { looks_like_number($_) and $_ >= $cutoff };
  0         0  
133             }
134              
135             sub less_than {
136 0     1 1 0 my $cutoff = shift;
137 0 50   3   0 return match { looks_like_number($_) and $_ < $cutoff };
  0         0  
138             }
139              
140             sub at_most {
141 0     2 1 0 my $cutoff = shift;
142 0 50   2   0 return match { looks_like_number($_) and $_ <= $cutoff };
  0         0  
143             }
144              
145             sub range {
146 0     2 1 0 my ($bottom, $top) = @_;
147 0         0 return all(at_least($bottom), at_most($top));
148             }
149              
150 0     1   0 use constant positive => more_than(0);
  0         0  
  0         0  
151 0     1   0 use constant negative => less_than(0);
  0         0  
  0         0  
152              
153             sub numwise {
154 0 50   7 1 0 croak 'No number given' if not @_;
155 0 100   13   0 return _matchall { my $other = shift ; match { scalar number and $_ == $other } } @_;
  1         22345  
  1         4  
  1         90  
156             }
157              
158 0 100 33 1   0 use constant string => match { ref() ? blessed($_) && overload::OverloadedStringify($_) : defined };
  0         0  
  0         0  
  0         0  
159              
160             sub string_length {
161 1     3 1 5 my $match = shift;
162 1 50   3   2 return match { scalar string and length $_ ~~ $match };
  1         34  
163             }
164              
165             sub stringwise {
166 1 50   3 1 4 croak 'No number given' if not @_;
167 1 50   3   2 return _matchall { my $other = shift; match { scalar string and $_ eq $other } } @_;
  1         57  
  1         795  
  1         876  
168             }
169              
170 0     1   0 use constant object => match { blessed($_) };
  0         0  
  0         0  
  0         0  
171              
172             sub instance_of {
173 1     0 1 5 my $class = shift;
174 1 0   0   49 return match { blessed($_) and $_->isa($class) };
  1         2  
175             }
176              
177             sub ref_type {
178 1     2 1 40 my $type = shift;
179 44     204   242 return match { ref eq $type };
  1         880  
180             }
181              
182             sub address {
183 1     4 1 1181 my $addr = refaddr($_[0]);
184 1     4   59 return match { refaddr($_) == $addr };
  1         6  
185             }
186              
187 0     1   0 use constant array => ref_type('ARRAY');
  0         0  
  0         0  
188              
189             sub array_length {
190 1     6 1 2 my $match = shift;
191 1 100   12   125 return match { scalar array and @{$_} + 0 ~~ $match };
  1         545  
  1         4  
192             }
193              
194             sub tuple {
195 1     7 1 129 my @entries = @_;
196 1 50   7   828 return match { scalar array and $_ ~~ @entries };
  1         1130  
197             }
198              
199             sub sequence {
200 1     2 1 42 my $matcher = shift;
201 1 50   2   983 return match { scalar array and List::MoreUtils::all { $_ ~~ $matcher } @{$_} };
  1         2  
  1         5  
  1         9  
202             }
203              
204             sub head {
205 1     5 1 1 my @entries = @_;
206 1 100   5   5 return match { scalar array_length(at_least(scalar @entries)) and [ @{$_}[ 0..$#entries ] ] ~~ @entries };
  1         11  
  1         1  
207             }
208              
209             sub contains {
210 1     4 1 5 my @matchers = @_;
211             return match {
212 1     4   1 my $lsh = $_;
213 1 50       5 $_ ~~ array and List::MoreUtils::all { my $matcher = $_; List::MoreUtils::any { $_ ~~ $matcher } @{$lsh} } @matchers;
  1         5  
  1         7  
  1         5  
  1         6  
214 1         6 };
215             }
216              
217             sub sorted {
218 1     15 1 1 my $matcher = shift;
219 1 50   31   4 return match { scalar array and [ sort @{$_} ] ~~ $matcher };
  1         6  
  1         2  
220             }
221              
222             sub sorted_by {
223 1     1 1 11 my ($sorter, $matcher) = @_;
224 1 50   1   11 return match { scalar array and [ sort { $sorter->($a, $b) } @{$_} ] ~~ $matcher };
  1         2  
  1         5  
  1         5  
225             }
226              
227 0     1   0 use constant hash => ref_type('HASH');
  0         0  
  0         0  
228              
229             sub hash_keys {
230 1     10 1 2 my $matcher = shift;
231 1 100   30   2 return match { scalar hash and [ keys %{$_} ] ~~ $matcher };
  1         5  
  1         2  
232             }
233              
234             sub hash_values {
235 1     2 1 2 my $matcher = shift;
236 1 50   2   5 return match { scalar hash and [ values %{$_} ] ~~ $matcher };
  1         2  
  1         4  
237             }
238              
239             sub sub_hash {
240 1     6 1 5 my $hash = shift;
241             return match {
242 1     6   9 my $lhs = $_; # for grep { }
243              
244 1 50 66     5 $lhs ~~ hash and keys %{$lhs} >= keys %{$hash} and List::MoreUtils::all { exists $lhs->{$_} } keys %{$hash} and [ @{$lhs}{keys %{$hash}} ] ~~ [ values %{$hash} ];
  1   66     1  
  1         3  
  1         6  
  1         2  
  1         3  
  127         176  
  127         414  
245 1         2 };
246             }
247              
248             sub hashwise {
249 10     8 1 16 my $hash = shift;
250 10 100 66 8   24 return match { scalar hash and hash_keys(sorted([ sort keys %{$hash} ])) and [ @{$_}{keys %{$hash}} ] ~~ [ values %{$hash} ] };
  10         28  
  10         17  
  16         30  
  0         0  
  0         0  
251             }
252              
253             sub value {
254 0     16 1 0 my $value = shift;
255 0 50       0 return $value if blessed($value);
256 0         0 given (ref $value) {
257 7         609 when ('') {
258 7         29 return $value;
259             }
260 7         15 when ('ARRAY') {
261 13         40 return tuple(map { value($_) } @{$value});
  3         13  
  4         12  
262             }
263 4         17 when ('HASH') {
264 4         8 my %match = map { ( $_ => value($value->{$_}) ) } keys %{$value};
  10         29  
  2         10  
265 2         6 return hashwise(\%match);
266             }
267 2         11 when ([qw/CODE SCALAR REF/]) {
268 2         5 return address($value);
269             }
270 5         17 when ([qw/GLOB IO FORMAT/]) {
271 1         5 croak "Can't match \L$_";
272             }
273 2         5 default {
274 2         10 croak "Don't know what you want me to do with a $_";
275             }
276             }
277             }
278              
279             1;
280              
281             # ABSTRACT: Smart matching utilities
282              
283             __END__