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   2019 use strict;
  19         34  
  19         570  
20 19     19   85 use warnings;
  19         36  
  19         541  
21 19     19   103 use parent 'Exporter';
  19         26  
  19         90  
22 19     19   951 use Text::Locus;
  19         30  
  19         628  
23 19     19   144 use Clone 'clone';
  19         97  
  19         942  
24              
25 19     19   115 use Carp;
  19         52  
  19         13118  
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 198 my $class = shift;
79 126         245 local %_ = @_;
80 126         184 my $v;
81             my $self;
82 126 50       290 if ($v = delete $_{clone}) {
83 0         0 $self = Clone::clone($v);
84             } else {
85 126         248 $self = bless { }, $class;
86             }
87 126 100       285 if (defined($v = delete $_{default})) {
88 4         15 $self->default($v);
89             }
90 126 100       324 if (defined($v = delete $_{locus})) {
91 105         281 $self->locus($v);
92             }
93              
94 126 50       4011 if (defined($v = delete $_{file})) {
95 0   0     0 $self->locus($v, delete $_{line} // 0);
96             }
97 126 100       268 if (defined($v = delete $_{order})) {
98 35         124 $self->order($v);
99             }
100 126 50       325 croak "unrecognized arguments" if keys(%_);
101 126         385 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 383 my $self = shift;
119 217 100       509 if (@_ == 1) {
    50          
    50          
120 105 50       272 croak "bad argument type"
121             unless ref($_[0]) eq 'Text::Locus';
122 105         1492 $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     731 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 170 my ($self, $val) = @_;
141 77 50       163 if (defined($val)) {
142 77         112 $self->{_order} = $val;
143             }
144 77   50     173 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 13 my ($self, $val) = @_;
157 5 50       11 if (defined($val)) {
158 5         83 $self->{_default} = $val;
159             }
160 5         10 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 9 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 38 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 5 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 211 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         62 sort { join('.',@{$a->[0]}) cmp join('.', @{$b->[0]}) } @_
  28         32  
  28         53  
  28         88  
202             }
203 19     19   146 };
  19         45  
  19         12678  
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         39 local %_ = @_;
248 10   50     52 my $sort = delete($_{sort}) || SORT_NATURAL;
249 10         22 my @ar;
250             my $i;
251            
252 10 50       48 croak "unrecognized keyword arguments: ". join(',', keys %_)
253             if keys %_;
254              
255 10         39 push @ar, [ [], $self ];
256 10         44 foreach my $elt (@ar) {
257 61 100       163 next if $elt->[1]->is_value;
258 34         45 while (my ($kw, $val) = each %{$elt->[1]->subtree}) {
  85         160  
259 51         66 push @ar, [ [@{$elt->[0]}, $kw], $val ];
  51         169  
260             }
261             }
262              
263 10 50       50 croak "sort must be a coderef"
264             unless ref($sort) eq 'CODE';
265              
266 10         28 shift @ar; # toss off first entry
267 10         32 return &{$sort}(grep { $_->[1]->is_value } @ar);
  10         37  
  51         78  
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 23 my $self = shift;
303 9         39 local %_ = @_;
304 9         15 my $delim;
305 9 50       52 unless (defined($delim = delete $_{delim})) {
306 0         0 $delim = "\n";
307             }
308 9         45 my $prloc = delete $_{locus};
309 9 50       51 carp "unrecognized parameters: " . join(', ', keys(%_)) if (keys(%_));
310            
311             return join $delim, map {
312 9         61 ($prloc ? '[' . $_->[1]->locus . ']: ' : '')
313             . join('.', map {
314 67 50       143 if (/[\.="]/) {
315 0         0 s/\"/\\"/;
316 0         0 '"'.$_.'"'
317             } else {
318 67         219 $_
319             }
320 26 100       1358 } @{$_->[0]})
  26         161  
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   1532 bool => sub { 1 },
332 0     0   0 '""' => sub { shift->as_string },
333             eq => sub {
334 5     5   172 my ($self,$other) = @_;
335 5         52 return $self->as_string eq $other
336 19     19   157 };
  19         28  
  19         183  
337            
338             =head1 SEE ALSO
339              
340             L,
341             L,
342             L,
343             L.
344            
345             =cut
346              
347             1;
348              
349