File Coverage

blib/lib/Sub/SmartMatch.pm
Criterion Covered Total %
statement 84 89 94.3
branch 21 32 65.6
condition 11 21 52.3
subroutine 17 17 100.0
pod 4 6 66.6
total 137 165 83.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Sub::SmartMatch;
4              
5 1     1   23496 use strict;
  1         3  
  1         27  
6 1     1   4 use warnings;
  1         2  
  1         24  
7              
8 1     1   17 use 5.010;
  1         7  
  1         46  
9              
10 1     1   5 use Carp qw(croak);
  1         1  
  1         68  
11 1     1   4 use Scalar::Util qw(reftype);
  1         7  
  1         184  
12              
13             our $VERSION = "0.02";
14              
15 1     1   6 use base qw(Exporter);
  1         2  
  1         160  
16              
17             our @EXPORT = our @EXPORT_OK = qw(multi multi_default def_multi exactly);
18              
19              
20              
21             BEGIN {
22             # If we have Sub::Name, great. If not, nevermoose
23              
24 1     1   2 local $@;
25              
26 1         2 eval {
27 1         418 require Sub::Name;
28 1     1   5 no warnings 'redefine';
  1         1  
  1         70  
29 0         0 *subname = \&Sub::Name::subname;
30             };
31              
32 1 50   6   808 *subname = sub { $_[1] } unless defined &subname;
  6         21  
33             }
34              
35             sub exactly ($) {
36 1     1 1 289 my $value = shift;
37              
38 1 50 33     7 if ( ref($value) and ref($value) eq 'ARRAY' ) {
39 1         13 bless \$value, __PACKAGE__ . "::Exact";
40             } else {
41 0         0 return $value;
42             }
43             }
44              
45             # guess the fully qualified name for a sub using caller()
46             sub full_name ($) {
47 32     32 0 37 my $name = shift;
48              
49 32 50 33     119 croak "A subroutine name is required"
50             unless defined($name) and length($name);
51              
52 32 100       77 return $name if $name =~ /::/;
53              
54 10         17 foreach my $level ( 0 .. 2 ) {
55 20         24 my $pkg = caller($level);
56 20 100       37 next if $pkg eq __PACKAGE__;
57 10         29 return join "::", $pkg, $name;
58             }
59             }
60              
61              
62             our ( %variants, %default );
63              
64             sub multi ($$$) {
65 11     11 1 1374 my ( $name, $case, $body ) = @_;
66              
67 11         17 $name = full_name($name);
68              
69              
70 11 50 33     57 unless ( ref($body) and reftype($body) eq 'CODE' ) {
71 0 0       0 my $body_str = defined($body)
    0          
72             ? ( ref($body) ? $body : "'$body'" )
73             : "undef";
74              
75 0         0 croak "$body_str is not a code reference";
76             }
77              
78 11         18 def_multi($name);
79              
80 11   66     38 my $exact = ref($case) && ref($case) eq __PACKAGE__ . "::Exact";
81 11 100       26 $case = $$case if $exact;
82              
83 11   100     71 my $partial_match = not($exact) && ref($case) && ref($case) eq 'ARRAY' && @$case;
84              
85 11         10 push @{ $variants{$name} }, [ $partial_match, $case, $body ];
  11         27  
86              
87 11         24 return $body;
88             }
89              
90             sub multi_default ($$) {
91 2     2 1 3 my ( $name, $body ) = @_;
92              
93 2 50 33     526 croak "$body is not a code reference"
94             unless ref($body) and reftype($body) eq 'CODE';
95              
96 2         4 $name = full_name($name);
97              
98 2         4 def_multi($name);
99              
100 2         9 $default{$name} = $body;
101             }
102              
103             sub def_multi ($;@) {
104 17     17 1 358 my ( $name, @args ) = @_;
105 17         22 $name = full_name($name);
106              
107 17 100       38 unless ( exists $variants{$name} ) {
108 6         5 my @variants;
109              
110             my $sub = sub {
111 37     37   3297 given ( \@_ ) {
112 37         51 foreach my $variant ( @variants ) {
113 55         71 my ( $partial, $case, $body ) = @$variant;
114              
115 55 100       79 if ( $partial ) {
116 37         79 given ( [ @_[0 .. $#$case] ] ) {
117 37         199 when ( $case ) { goto $body }
  18         66  
118             }
119             } else {
120 18         41 when ( $case ) { goto $body }
  13         50  
121             }
122             }
123              
124 6         8 default {
125 6 50       12 if ( my $default = $default{$name} ) {
126 6         17 goto $default;
127             } else {
128 0         0 croak "No variant found for arguments";
129             }
130             }
131             }
132 6         27 };
133              
134             {
135 1     1   16 no strict 'refs';
  1         2  
  1         274  
  6         7  
136 6         11 *$name = subname $name, $sub;
137             }
138              
139 6         15 $variants{$name} = \@variants;
140             }
141              
142 17 100       45 def_variants($name, @args) if @args;
143             }
144              
145             sub def_variants ($;) {
146 2     2 0 5 my ( $name, @variants ) = @_;
147              
148 2         4 $name = full_name($name);
149              
150 2         19 def_multi($name);
151              
152 2 50       7 croak "The variant list is not even sized"
153             unless @variants % 2 == 0;
154              
155 2         4 while ( @variants ) {
156 5         9 my ( $case, $body ) = splice(@variants, 0, 2);
157              
158 5 100 66     18 if ( not ref($case) and $case ~~ 'default' ) {
159 2         4 multi_default $name, $body;
160             } else{
161 3         6 multi $name, $case, $body;
162             }
163             }
164             }
165              
166             __PACKAGE__
167              
168             __END__