File Coverage

blib/lib/Smart/Match.pm
Criterion Covered Total %
statement 111 242 45.8
branch 45 74 60.8
condition 7 12 58.3
subroutine 79 83 95.1
pod 31 31 100.0
total 273 442 61.7


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