File Coverage

blib/lib/Const/Exporter.pm
Criterion Covered Total %
statement 122 125 97.6
branch 31 36 86.1
condition 17 24 70.8
subroutine 15 16 93.7
pod n/a
total 185 201 92.0


line stmt bran cond sub pod time code
1             package Const::Exporter;
2              
3             # ABSTRACT: Declare constants for export.
4              
5 9     9   2519307 use v5.14;
  9         34  
6 9     9   54 use warnings;
  9         16  
  9         648  
7              
8             our $VERSION = 'v1.3.0';
9              
10 9     9   59 use Carp;
  9         21  
  9         722  
11 9     9   4546 use Const::Fast;
  9         29289  
  9         70  
12 9     9   849 use Exporter 5.57 ();
  9         152  
  9         298  
13 9     9   47 use List::Util 1.56 qw/ pairs mesh /;
  9         138  
  9         686  
14 9     9   4013 use Package::Stash;
  9         87538  
  9         449  
15 9     9   4737 use Ref::Util qw/ is_blessed_ref is_arrayref is_coderef is_hashref is_ref /;
  9         22930  
  9         14145  
16              
17             # RECOMMEND PREREQ: List::SomeUtils::XS
18             # RECOMMEND PREREQ: Package::Stash::XS
19             # RECOMMEND PREREQ: Ref::Util::XS
20             # RECOMMEND PREREQ: Storable 3.05
21              
22             sub import {
23 15     15   257059 my $pkg = shift;
24              
25 15         92 strict->import;
26 15         430 warnings->import;
27              
28 15         69 my $caller = caller;
29 15         506 my $stash = Package::Stash->new($caller);
30              
31             # Create @EXPORT, @EXPORT_OK, %EXPORT_TAGS and import if they
32             # don't yet exist.
33              
34 15         180 my $export = $stash->get_or_add_symbol('@EXPORT');
35              
36 15         79 my $export_ok = $stash->get_or_add_symbol('@EXPORT_OK');
37              
38 15         67 my $export_tags = $stash->get_or_add_symbol('%EXPORT_TAGS');
39              
40 15 100       143 $stash->add_symbol( '&import', \&Exporter::import )
41             unless ( $stash->has_symbol('&import') );
42              
43 15         73 $stash->add_symbol( '&const', \&Const::Fast::const );
44 15         50 _export_symbol( $stash, 'const' );
45              
46 15         118 foreach my $set ( pairs @_ ) {
47              
48 12         104 my $tag = $set->key;
49 12 100       56 croak "'${tag}' is reserved" if $tag eq 'all';
50              
51 11         82 my $defs = $set->value;
52              
53 11 50       30 croak "An array reference required for tag '${tag}'"
54             unless is_arrayref($defs);
55              
56 11         19 while ( my $item = shift @{$defs} ) {
  41         133  
57              
58 30         44 for ($item) {
59              
60             # Array reference means a list of enumerated symbols
61              
62 30 100       58 if ( is_arrayref($_) ) {
63              
64 8         19 my @enums = @{$item};
  8         24  
65 8         12 my $start = shift @{$defs};
  8         12  
66              
67 8 100       21 my @values = is_arrayref($start) ? @{$start} : ($start);
  6         12  
68              
69 8   50     21 my $last = $values[0] // 0;
70 8     8   24 my $fn = sub { $_[0] + 1 };
  8         18  
71              
72 8 100       24 if ( is_coderef $values[1] ) {
73 1         3 $fn = $values[1];
74 1         2 $values[1] = undef;
75             }
76              
77 8         80 foreach my $pair ( pairs mesh \@enums, \@values ) {
78 27   66     84 my $value = $pair->value // $fn->($last);
79 27         84 $last = $value;
80 27   100     74 my $symbol = $pair->key // next;
81              
82 26         58 _add_symbol( $stash, $symbol, $value );
83 26         263 _export_symbol( $stash, $symbol, $tag );
84              
85             }
86              
87 8         87 next;
88             }
89              
90             # A scalar is a name of a symbol
91              
92 22 50       44 if ( !is_ref($_) ) {
93              
94 22         26 my $symbol = $item;
95 22         35 my $sigil = _get_sigil($symbol);
96 22 100       54 my $norm =
97             ( $sigil eq '&' ) ? ( $sigil . $symbol ) : $symbol;
98              
99             # If the symbol is already defined, that we add it
100             # to the exports for that tag and assume no value
101             # is given for it.
102              
103 22 100       83 if ( $stash->has_symbol($norm) ) {
104              
105 4         13 my $ref = $stash->get_symbol($norm);
106              
107             # In case symbol is defined as `our`
108             # beforehand, ensure it is readonly.
109              
110 4         9 Const::Fast::_make_readonly( $ref => 1 );
111              
112 4         54 _export_symbol( $stash, $symbol, $tag );
113              
114 4         7 next;
115              
116             }
117              
118 18         24 my $value = shift @{$defs};
  18         31  
119              
120 18         38 _add_symbol( $stash, $symbol, $value );
121 18         212 _export_symbol( $stash, $symbol, $tag );
122              
123 18         72 next;
124             }
125              
126 0         0 croak "$_ is not supported";
127              
128             }
129              
130             }
131              
132             }
133              
134             # Now ensure @EXPORT, @EXPORT_OK and %EXPORT_TAGS contain unique
135             # symbols. This may not matter to Exporter, but we want to ensure
136             # the values are 'clean'. It also simplifies testing.
137              
138 14 100       103 push @{$export}, @{ $export_tags->{default} } if $export_tags->{default};
  8         14  
  8         29  
139 14         48 _uniq($export);
140              
141 14         40 _uniq($export_ok);
142              
143 14   50     56 $export_tags->{all} //= [];
144 14         25 push @{ $export_tags->{all} }, @{$export_ok};
  14         27  
  14         37  
145              
146 14         24 _uniq( $export_tags->{$_} ) for keys %{$export_tags};
  14         46  
147              
148             }
149              
150             # Add a symbol to the stash
151              
152             sub _check_sigil_against_value {
153 18     18   30 my ($sigil, $value) = @_;
154              
155 18 100 66     52 return 1 if $sigil eq '@' && is_arrayref($value);
156 16 100 66     42 return 1 if $sigil eq '%' && is_hashref($value);
157 15 50 33     30 return 1 if $sigil eq '&' && is_coderef($value);
158 15 50       41 return 1 if $sigil eq '$';
159              
160 0         0 return 0;
161             }
162              
163             sub _add_symbol {
164 44     44   88 my ( $stash, $symbol, $value ) = @_;
165              
166 44         83 my $sigil = _get_sigil($symbol);
167 44 100       87 if ( $sigil ne '&' ) {
168              
169 19 100       37 if ( is_blessed_ref $value) {
170              
171 1         5 $stash->add_symbol( $symbol, \$value );
172 1         4 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
173              
174             }
175             else {
176              
177 18 50       41 croak "Invalid type for $symbol"
178             unless _check_sigil_against_value($sigil, $value);
179              
180 18         89 $stash->add_symbol( $symbol, $value );
181 18         68 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
182             }
183              
184             }
185             else {
186              
187 25 100       92 const my $copy => is_coderef($value) ? $value->() : $value;
188 25     0   1003 $stash->add_symbol( '&' . $symbol, sub() { $copy } );
  0         0  
189              
190             }
191             }
192              
193             # Add a symbol to @EXPORT_OK and %EXPORT_TAGS
194              
195             sub _export_symbol {
196 63     63   121 my ( $stash, $symbol, $tag ) = @_;
197              
198 63         183 my $export_ok = $stash->get_symbol('@EXPORT_OK');
199 63         176 my $export_tags = $stash->get_symbol('%EXPORT_TAGS');
200              
201 63   100     178 $tag //= 'all';
202              
203 63   100     216 $export_tags->{$tag} //= [];
204              
205 63         91 push @{ $export_tags->{$tag} }, $symbol;
  63         145  
206 63         90 push @{$export_ok}, $symbol;
  63         134  
207             }
208              
209             # Function to get the sigil from a symbol. If no sigil, it assumes
210             # that it is a function reference.
211              
212             sub _get_sigil {
213 66     66   97 my ($symbol) = @_;
214 66         151 my ($sigil) = $symbol =~ /^(\W)/;
215 66   100     268 return $sigil // '&';
216             }
217              
218             # Function to take a list reference and prune duplicate elements from
219             # it.
220              
221             sub _uniq {
222 56     56   82 my ($listref) = @_;
223 56         70 my %seen;
224 56         73 while ( my $item = shift @{$listref} ) {
  658         934  
225 602         774 $seen{$item} = 1;
226             }
227 56         75 push @{$listref}, keys %seen;
  56         12213  
228             }
229              
230             1;
231              
232             __END__