File Coverage

blib/lib/Data/Enum.pm
Criterion Covered Total %
statement 69 69 100.0
branch 6 6 100.0
condition n/a
subroutine 21 21 100.0
pod 2 2 100.0
total 98 98 100.0


line stmt bran cond sub pod time code
1             package Data::Enum;
2              
3             # ABSTRACT: immutable enumeration classes
4              
5 4     4   944648 use v5.20;
  4         15  
6 4     4   23 use warnings;
  4         6  
  4         264  
7              
8 4     4   2095 use Package::Stash;
  4         42548  
  4         204  
9 4     4   31 use List::Util 1.45 qw/ any uniqstr /;
  4         66  
  4         408  
10 4     4   35 use Scalar::Util qw/ blessed refaddr /;
  4         10  
  4         334  
11              
12             # RECOMMEND PREREQ: Package::Stash::XS
13              
14 4     4   2150 use experimental qw/ lexical_subs postderef signatures /;
  4         15572  
  4         30  
15              
16             use overload
17 4         67 q{""} => \&as_string,
18             q{eq} => \&MATCH,
19 4     4   1135 q{ne} => \&_NOT_MATCH;
  4         9  
20              
21 4     4   459 use constant TRUE => 1;
  4         45  
  4         331  
22 4     4   70 use constant FALSE => 0;
  4         10  
  4         6226  
23              
24             our $VERSION = 'v0.6.0';
25              
26              
27             sub new ( $this, @args ) {
28              
29             my $opts = ref( $args[0] ) eq "HASH" ? shift @args : {};
30             my $prefix = $opts->{prefix} // "is_";
31              
32             my @values = uniqstr( sort map { "$_" } @args );
33              
34             die "has no values" unless @values;
35              
36 24     24   87 die "values must be alphanumeric" if any { /\W/ } @values;
37              
38             my $key = join chr(28), @values;
39              
40             state %Cache;
41             state $Counter = 1;
42              
43             if ( my $name = $Cache{$key} ) {
44             return $name;
45             }
46              
47             my $name = $opts->{name} || ( __PACKAGE__ . "::" . $Counter++ );
48              
49             my $base = Package::Stash->new($name);
50              
51 17     17   22 my sub _make_symbol($value) {
  17     12   61  
  17         22  
52 17         48 my $self = bless \$value, "${name}::${value}";
53 17         46 Internals::SvREADONLY( $value, 1 );
54 17         55 return $self;
55             };
56              
57 72     72   95 my sub _make_predicate($value) {
  72     54   112  
  72         91  
58 72         442 return $prefix . $value;
59             };
60              
61 44         80 $base->add_symbol(
62             '&new',
63 44     44   4056 sub( $class, $value ) {
  44         76  
  44         66  
64             state $symbols = {
65 44         76 map { $_ => _make_symbol($_) } @values
  17         43  
66             };
67 44 100       262 exists $symbols->{"$value"} or die "invalid value: '$value'";
68 40         432 return $symbols->{"$value"};
69             }
70             );
71              
72 6     6   16 $base->add_symbol( '&values', sub($) { return @values } );
  6         8  
  6         56  
73              
74 2     2   7 $base->add_symbol(
75             '&predicates',
76 2         4 sub($) {
77 2         6 return map { _make_predicate($_) } @values;
  6         39  
78             }
79             );
80              
81 2     2   7 $base->add_symbol( '&prefix', sub($) { $prefix } );
  2         4  
  2         11  
82              
83             for my $value (@values) {
84             my $predicate = _make_predicate($value);
85             $base->add_symbol( '&' . $predicate, \&FALSE );
86             my $elem = "${name}::${value}";
87             my $subtype = Package::Stash->new($elem);
88             $subtype->add_symbol( '@ISA', [ __PACKAGE__, $name ] );
89             for my $other (@values) {
90             $subtype->add_symbol( '&' . _make_predicate($other), $other eq $value ? \&TRUE : \&FALSE );
91             }
92             }
93              
94             return $Cache{$key} = $name;
95             }
96              
97 15     15   2809 sub _NOT_MATCH( $self, $arg, @ ) {
  15         32  
  15         27  
  15         24  
98 15 100       100 return blessed($arg)
99             ? refaddr($arg) != refaddr($self)
100             : $arg ne $self->$*; # as_string
101             }
102              
103              
104 21     21 1 4330 sub MATCH( $self, $arg, @ ) {
  21         44  
  21         57  
  21         28  
105 21 100       165 return blessed($arg)
106             ? refaddr($arg) == refaddr($self)
107             : $arg eq $self->$*; # as_string
108             }
109              
110              
111 17     17 1 1322 sub as_string($self, @) {
  17         33  
  17         90  
112 17         107 return $self->$*;
113             }
114              
115              
116             1;
117              
118             __END__