File Coverage

blib/lib/Config/Maker/Type.pm
Criterion Covered Total %
statement 94 96 97.9
branch 19 30 63.3
condition n/a
subroutine 25 25 100.0
pod 1 11 9.0
total 139 162 85.8


line stmt bran cond sub pod time code
1             package Config::Maker::Type;
2              
3 9     9   60 use utf8;
  9         19  
  9         188  
4 9     9   273 use warnings;
  9         17  
  9         220  
5 9     9   50 use strict;
  9         19  
  9         269  
6              
7 9     9   49 use Carp;
  9         21  
  9         526  
8 9     9   48 use Config::Maker;
  9         20  
  9         456  
9 9     9   5580 use Config::Maker::Path;
  9         35  
  9         837  
10              
11             require Config::Maker::Option;
12             our @CARP_NOT = qw(Config::Maker::Option);
13              
14             use overload
15 9         190 'cmp' => \&Config::Maker::truecmp,
16             '<=>' => \&Config::Maker::truecmp,
17             '""' => 'name',
18 9     9   62 fallback => 1;
  9         20  
19              
20             our $root;
21             our $meta;
22             our $repository;
23             our %checks;
24              
25             # Get top-level context ('/' special type):
26             sub root {
27 94     94 0 782 $root;
28             }
29              
30             # Get special meta content ('//' special type)
31             sub meta {
32 229     229 0 1358 $meta;
33             }
34              
35             # Get repository context ('*' special type). The repository is a special type
36             # where types for reuse are stored:
37             sub repository {
38 128     128 0 497 $repository;
39             }
40              
41             # Get a type from a context. Context is just a type currently being
42             # built:
43             sub get {
44 952     952 0 2234 my ($ctx, $name) = @_;
45            
46 952 50       5440 croak "No type $name in $ctx" unless $ctx->{children}{$name};
47 952         5084 return $ctx->{children}{$name};
48             }
49              
50             # Build an Option using given arguments:
51             sub instantiate {
52 783     783 0 2508 my ($self, $args) = @_;
53              
54 783 50       3357 ref $args eq 'HASH' or confess "Not a hash reference!";
55 783         8115 Config::Maker::Option->new(-type => $self, %$args);
56             }
57              
58             # Get arguments for syntactic rule for arguments.
59             sub body {
60 689     689 0 1165 my ($self) = @_;
61 689         1499 @{$self->{format}};
  689         4797  
62             }
63              
64             # Get the name of the option type.
65             sub name {
66 15271     15271 1 30352 my ($self) = @_;
67 15271         122872 $self->{name};
68             }
69              
70             # Build a new type...
71             sub _ref(\%$;$) {
72 1494     1494   2581 my ($hash, $key, $default) = @_;
73 1494 100       4533 if(exists $hash->{$key}) {
    50          
74 804         1299 my $rv = $hash->{$key};
75 804         1436 delete $hash->{$key};
76 804         3566 return $rv;
77             } elsif(@_ == 3) {
78 690         3676 return $default;
79             } else {
80 0         0 croak "Mandatory argument $key not specified";
81             }
82             }
83              
84             sub _type {
85 640     640   901 my ($type) = @_;
86 640 50       1719 confess "_type($type)" unless $type;
87 640 100       3986 return $type if UNIVERSAL::isa($type, __PACKAGE__);
88 128         949 return __PACKAGE__->repository->get($type);
89             }
90              
91             sub _path {
92 364     364   1628 return Config::Maker::Path->make(@_);
93             }
94              
95             sub _check {
96 364     364   818 my ($check, $path) = @_;
97 364 50       1836 croak "No check $check" unless $checks{$check};
98 364         554 $check = $checks{$check};
99 364         802 $path = _path($path);
100             return sub {
101 1601     1601   6798 $check->($_[0], $path);
102             }
103 364         13582 }
104              
105             sub new {
106 249     249 0 632 my $class = shift;
107 249 100       1518 my %args = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
  24         180  
108 249         1499 my %type = (
109             name => _ref(%args, 'name'),
110             format => _ref(%args, 'format'),
111             children => {},
112             checks => [],
113             actions => _ref(%args, 'actions', []),
114             );
115 249         1502 my $self = bless \%type, $class;
116              
117             # Children...
118 249         412 $self->add(@{_ref %args, 'children', []});
  249         612  
119              
120             # Aditional checks...
121 249         464 $self->addchecks(@{_ref %args, 'checks', []});
  249         705  
122              
123 249         974 $self->addto(@{_ref %args, 'contexts', [any => '*']});
  249         1062  
124              
125 249 50       1149 croak "Unknown arguments: " . join(', ', keys %args)
126             if %args;
127 249         1601 return $self;
128             }
129              
130             sub add {
131 575     575 0 787 my $self = shift;
132 575         1048 my @children = @_;
133 575         2091 while(my ($spec, $type) = splice(@children, 0, 2)) {
134 337         700 $type = _type($type);
135 337 50       1511 next if $self->{children}->{$type} eq $type;
136 337 50       9263 croak "Different type named $type already added to $self"
137             if exists $self->{children}->{$type};
138 337         1087 $self->{children}->{$type->name} = $type;
139 337         488 push @{$self->{checks}}, _check($spec, "$type");
  337         915  
140             }
141             }
142              
143             sub addto {
144 249     249 0 368 my $self = shift;
145 249         526 my @contexts = @_;
146 249         925 while(my ($spec, $ctx) = splice(@contexts, 0, 2)) {
147 303         854 _type($ctx)->add($spec, $self);
148             }
149             }
150              
151             sub addchecks {
152 276     276 0 411 my $self = shift;
153 276         477 my @checks = @_;
154 276         1647 while(my ($spec, $path) = splice(@checks, 0, 2)) {
155 27         68 push @{$self->{checks}}, _check($spec, $path);
  27         104  
156             }
157             }
158              
159             # Initialize:
160              
161             # format not used for the magic items, but is mandatory...
162             $repository = __PACKAGE__->new(
163             name => '*',
164             format => [],
165             contexts => [], # self-reference otherwise...
166             );
167              
168             $repository->add(any => $repository);
169             # Phew, and now _type should work...
170              
171             $root = __PACKAGE__->new(
172             name => '/',
173             format => [],
174             ); # Default context (in repository only) should work here...
175              
176             # And last but not least the meta type...
177             $meta = __PACKAGE__->new(
178             name => '//',
179             format => [],
180             ); # Default context (in repository only) should work here too...
181              
182             # Checking functions:
183              
184             sub _findtimes {
185 972 50   972   4880 confess "$_[1] can't ->find" unless UNIVERSAL::can($_[1], 'find');
186 972         5282 my $r = $_[1]->find($_[0]);
187 972 50       2731 croak "Too few $_[1] in " . $_[0]->id if @$r < $_[2];
188 972 100       23509 return 1 if @_ == 3;
189 927 50       2179 croak "Too many $_[1] in " . $_[0]->id if @$r > $_[3];
190 927         15166 return 1;
191             }
192              
193             BEGIN { # Constants must be done early enough...
194             %checks = (
195 0         0 none => sub { _findtimes(@_, 0,0); },
196 850         2095 opt => sub { _findtimes(@_,0,1); },
197 77         462 one => sub { _findtimes(@_,1,1); },
198 45         190 mand => sub { _findtimes(@_,1); },
199 629         1891 any => sub { 1; },
200 9     9   15466 );
201             }
202              
203             1;
204              
205             __END__