File Coverage

lib/Config/AST/Node.pm
Criterion Covered Total %
statement 90 101 89.1
branch 25 38 65.7
condition 4 9 44.4
subroutine 20 21 95.2
pod 10 10 100.0
total 149 179 83.2


line stmt bran cond sub pod time code
1             # This file is part of Config::AST -*- perl -*-
2             # Copyright (C) 2017-2019 Sergey Poznyakoff
3             #
4             # Config::AST is free software; you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation; either version 3, or (at your option)
7             # any later version.
8             #
9             # Config::AST is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with Config::AST. If not, see .
16              
17             package Config::AST::Node;
18              
19 19     19   5609 use strict;
  19         26  
  19         544  
20 19     19   72 use warnings;
  19         26  
  19         404  
21 19     19   85 use parent 'Exporter';
  19         25  
  19         69  
22 19     19   819 use Text::Locus;
  19         26  
  19         808  
23 19     19   117 use Clone 'clone';
  19         44  
  19         1039  
24              
25 19     19   100 use Carp;
  19         41  
  19         11412  
26              
27             our %EXPORT_TAGS = ( 'sort' => [ qw(NO_SORT SORT_NATURAL SORT_PATH) ] );
28             our @EXPORT_OK = qw(NO_SORT SORT_NATURAL SORT_PATH);
29              
30             =head1 NAME
31              
32             Config::AST::Node - generic configuration syntax tree node
33              
34             =head1 SYNOPSIS
35              
36             use parent 'Config::AST::Node';
37            
38             =head1 DESCRIPTION
39              
40             This is an abstract class representing a node in the configuration parse
41             tree. A node can be either a non-leaf node, representing a I
, or
42             a leaf node, representing a I.
43              
44             =head1 METHODS
45              
46             =head2 new(ARG => VAL, ...)
47              
48             Creates new object. Recognized arguments are:
49              
50             =over 4
51              
52             =item B> I
53              
54             Clone object I, which must be an instance of B
55             or its derived class.
56              
57             =item B> I
58              
59             Sets default value.
60              
61             =item B> I
62              
63             Sets the locus - an object of class B, which see.
64              
65             =item B> I
66              
67             Sets the file name.
68              
69             =item B> I
70              
71             Sets ordinal number.
72              
73             =back
74            
75             =cut
76            
77             sub new {
78 126     126 1 178 my $class = shift;
79 126         234 local %_ = @_;
80 126         159 my $v;
81             my $self;
82 126 50       213 if ($v = delete $_{clone}) {
83 0         0 $self = Clone::clone($v);
84             } else {
85 126         211 $self = bless { }, $class;
86             }
87 126 100       244 if (defined($v = delete $_{default})) {
88 4         13 $self->default($v);
89             }
90 126 100       220 if (defined($v = delete $_{locus})) {
91 105         252 $self->locus($v);
92             }
93              
94 126 50       3246 if (defined($v = delete $_{file})) {
95 0   0     0 $self->locus($v, delete $_{line} // 0);
96             }
97 126 100       248 if (defined($v = delete $_{order})) {
98 35         91 $self->order($v);
99             }
100 126 50       276 croak "unrecognized arguments" if keys(%_);
101 126         420 return $self;
102             }
103              
104             =head2 $x = $node->locus;
105              
106             Returns a locus associated with the node.
107            
108             =head2 $node->locus($LOC)
109              
110             =head2 $node->locus($FILE, $LINE)
111              
112             Associates a locus with the node. In the second form, a new locus object
113             is created for location I<$FILE>:I<$LINE>.
114            
115             =cut
116              
117             sub locus {
118 217     217 1 313 my $self = shift;
119 217 100       487 if (@_ == 1) {
    50          
    50          
120 105 50       206 croak "bad argument type"
121             unless ref($_[0]) eq 'Text::Locus';
122 105         1202 $self->{_locus} = $_[0];
123             } elsif (@_ == 2) {
124 0         0 $self->{_locus} = new Text::Locus(@_);
125             } elsif (@_) {
126 0         0 croak "bad number of arguments";
127             }
128 217   66     557 return $self->{_locus} ||= new Text::Locus;
129             }
130              
131             =head2 $x = $node->order
132              
133             =head2 $node->order(I<$N>)
134              
135             Returns or sets and returns ordinal number for the node.
136              
137             =cut
138            
139             sub order {
140 77     77 1 115 my ($self, $val) = @_;
141 77 50       127 if (defined($val)) {
142 77         95 $self->{_order} = $val;
143             }
144 77   50     143 return $self->{_order} // 0;
145             }
146              
147             =head2 $x = $node->default
148              
149             =head2 $node->default(I<$N>)
150              
151             Returns or sets and returns default value for the node.
152              
153             =cut
154            
155             sub default {
156 5     5 1 10 my ($self, $val) = @_;
157 5 50       21 if (defined($val)) {
158 5         71 $self->{_default} = $val;
159             }
160 5         9 return $self->{_default};
161             }
162              
163             =head2 $node->is_leaf
164              
165             Returns true if node is a leaf node
166            
167             =cut
168              
169 2     2 1 8 sub is_leaf { 0 }
170              
171             =head2 $node->is_null
172              
173             Returns true if node is a null node
174              
175             =cut
176              
177 19     19 1 31 sub is_null { 0 }
178              
179             =head2 $node->is_section
180              
181             Returns true if node represents a section.
182              
183             =cut
184              
185 2     2 1 15 sub is_section { 0 }
186              
187             =head2 $node->is_value
188              
189             Returns true if node represents a value (or statement).
190              
191             =cut
192              
193 113     113 1 169 sub is_value { shift->is_leaf }
194            
195             use constant {
196 0         0 NO_SORT => sub { @_ },
197             SORT_NATURAL => sub {
198 0         0 sort { $a->[1]->order <=> $b->[1]->order } @_
  0         0  
199             },
200             SORT_PATH => sub {
201 10         38 sort { join('.',@{$a->[0]}) cmp join('.', @{$b->[0]}) } @_
  27         27  
  27         51  
  27         58  
202             }
203 19     19   138 };
  19         33  
  19         11385  
204              
205             =head2 @array = $cfg->flatten()
206              
207             =head2 @array = $cfg->flatten(sort => $sort)
208              
209             Returns a I representation of the configuration, as a
210             list of pairs B<[ $path, $value ]>, where B<$path> is a reference
211             to the variable pathname, and B<$value> is a
212             B object.
213              
214             The I<$sort> argument controls the ordering of the entries in the returned
215             B<@array>. It is either a code reference suitable to pass to the Perl B
216             function, or one of the following constants:
217              
218             =over 4
219              
220             =item NO_SORT
221              
222             Don't sort the array. Statements will be placed in an apparently random
223             order.
224              
225             =item SORT_NATURAL
226              
227             Preserve relative positions of the statements. Entries in the array will
228             be in the same order as they appeared in the configuration file. This is
229             the default.
230              
231             =item SORT_PATH
232              
233             Sort by pathname.
234              
235             =back
236              
237             These constants are not exported by default. You can either import the
238             ones you need, or use the B<:sort> keyword to import them all, e.g.:
239              
240             use Config::AST::Node qw(:sort);
241             @array = $node->flatten(sort => SORT_PATH);
242            
243             =cut
244              
245             sub flatten {
246 10     10 1 18 my $self = shift;
247 10         25 local %_ = @_;
248 10   50     46 my $sort = delete($_{sort}) || SORT_NATURAL;
249 10         23 my @ar;
250             my $i;
251            
252 10 50       25 croak "unrecognized keyword arguments: ". join(',', keys %_)
253             if keys %_;
254              
255 10         214 push @ar, [ [], $self ];
256 10         33 foreach my $elt (@ar) {
257 61 100       126 next if $elt->[1]->is_value;
258 34         41 while (my ($kw, $val) = each %{$elt->[1]->subtree}) {
  85         127  
259 51         63 push @ar, [ [@{$elt->[0]}, $kw], $val ];
  51         128  
260             }
261             }
262              
263 10 50       31 croak "sort must be a coderef"
264             unless ref($sort) eq 'CODE';
265              
266 10         14 shift @ar; # toss off first entry
267 10         38 return &{$sort}(grep { $_->[1]->is_value } @ar);
  10         29  
  51         59  
268             }
269              
270             =head2 $cfg->canonical(%args)
271              
272             Returns the canonical string representation of the configuration node.
273             For value nodes, canonical representation is:
274              
275             QVAR=VALUE
276              
277             where QVAR is fully qualified variable name, and VALUE is the corresponding
278             value.
279              
280             For sections, canonical representation is a list of canonical representations
281             of the underlying nodes, delimited by newlines (or another character - see the
282             description of the B argument, below). The list is sorted by QVAR in
283             ascending lexicographical order.
284              
285             B<%args> are zero or more of the following keywords:
286              
287             =over 4
288              
289             =item B >I
290              
291             Use I to delimit statements, instead of the newline.
292              
293             =item B 1>
294              
295             Prefix each statement with its location.
296              
297             =back
298            
299             =cut
300              
301             sub canonical {
302 9     9 1 16 my $self = shift;
303 9         36 local %_ = @_;
304 9         14 my $delim;
305 9 50       38 unless (defined($delim = delete $_{delim})) {
306 0         0 $delim = "\n";
307             }
308 9         19 my $prloc = delete $_{locus};
309 9 50       36 carp "unrecognized parameters: " . join(', ', keys(%_)) if (keys(%_));
310            
311             return join $delim, map {
312 9         55 ($prloc ? '[' . $_->[1]->locus . ']: ' : '')
313             . join('.', map {
314 67 50       118 if (/[\.="]/) {
315 0         0 s/\"/\\"/;
316 0         0 '"'.$_.'"'
317             } else {
318 67         173 $_
319             }
320 26 100       989 } @{$_->[0]})
  26         107  
321             . "="
322             . Data::Dumper->new([scalar $_->[1]->value])
323             ->Useqq(1)
324             ->Terse(1)
325             ->Indent(0)
326             ->Dump
327             } $self->flatten(sort => SORT_PATH);
328             }
329              
330             use overload
331 587     587   1340 bool => sub { 1 },
332 0     0   0 '""' => sub { shift->as_string },
333             eq => sub {
334 5     5   145 my ($self,$other) = @_;
335 5         14 return $self->as_string eq $other
336 19     19   121 };
  19         26  
  19         158  
337            
338             =head1 SEE ALSO
339              
340             L,
341             L,
342             L,
343             L.
344            
345             =cut
346              
347             1;
348              
349