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   169 use strict;
  19         36  
  19         587  
20 19     19   108 use warnings;
  19         48  
  19         595  
21 19     19   94 use parent 'Exporter';
  19         28  
  19         105  
22 19     19   982 use Text::Locus;
  19         45  
  19         805  
23 19     19   125 use Clone 'clone';
  19         57  
  19         1046  
24              
25 19     19   137 use Carp;
  19         65  
  19         13661  
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 222 my $class = shift;
79 126         251 local %_ = @_;
80 126         178 my $v;
81             my $self;
82 126 50       261 if ($v = delete $_{clone}) {
83 0         0 $self = Clone::clone($v);
84             } else {
85 126         349 $self = bless { }, $class;
86             }
87 126 100       285 if (defined($v = delete $_{default})) {
88 4         17 $self->default($v);
89             }
90 126 100       324 if (defined($v = delete $_{locus})) {
91 105         322 $self->locus($v);
92             }
93              
94 126 50       4193 if (defined($v = delete $_{file})) {
95 0   0     0 $self->locus($v, delete $_{line} // 0);
96             }
97 126 100       267 if (defined($v = delete $_{order})) {
98 35         98 $self->order($v);
99             }
100 126 50       337 croak "unrecognized arguments" if keys(%_);
101 126         375 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 409 my $self = shift;
119 217 100       545 if (@_ == 1) {
    50          
    50          
120 105 50       310 croak "bad argument type"
121             unless ref($_[0]) eq 'Text::Locus';
122 105         1488 $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     671 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 131 my ($self, $val) = @_;
141 77 50       149 if (defined($val)) {
142 77         123 $self->{_order} = $val;
143             }
144 77   50     219 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       18 if (defined($val)) {
158 5         93 $self->{_default} = $val;
159             }
160 5         12 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 10 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 36 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 3 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 204 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         51 sort { join('.',@{$a->[0]}) cmp join('.', @{$b->[0]}) } @_
  28         48  
  28         53  
  28         90  
202             }
203 19     19   157 };
  19         31  
  19         12549  
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 27 my $self = shift;
247 10         34 local %_ = @_;
248 10   50     48 my $sort = delete($_{sort}) || SORT_NATURAL;
249 10         27 my @ar;
250             my $i;
251            
252 10 50       54 croak "unrecognized keyword arguments: ". join(',', keys %_)
253             if keys %_;
254              
255 10         34 push @ar, [ [], $self ];
256 10         29 foreach my $elt (@ar) {
257 61 100       141 next if $elt->[1]->is_value;
258 34         44 while (my ($kw, $val) = each %{$elt->[1]->subtree}) {
  85         159  
259 51         70 push @ar, [ [@{$elt->[0]}, $kw], $val ];
  51         164  
260             }
261             }
262              
263 10 50       60 croak "sort must be a coderef"
264             unless ref($sort) eq 'CODE';
265              
266 10         22 shift @ar; # toss off first entry
267 10         33 return &{$sort}(grep { $_->[1]->is_value } @ar);
  10         50  
  51         80  
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 22 my $self = shift;
303 9         47 local %_ = @_;
304 9         26 my $delim;
305 9 50       45 unless (defined($delim = delete $_{delim})) {
306 0         0 $delim = "\n";
307             }
308 9         25 my $prloc = delete $_{locus};
309 9 50       46 carp "unrecognized parameters: " . join(', ', keys(%_)) if (keys(%_));
310            
311             return join $delim, map {
312 9         62 ($prloc ? '[' . $_->[1]->locus . ']: ' : '')
313             . join('.', map {
314 67 50       140 if (/[\.="]/) {
315 0         0 s/\"/\\"/;
316 0         0 '"'.$_.'"'
317             } else {
318 67         205 $_
319             }
320 26 100       1488 } @{$_->[0]})
  26         121  
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   1573 bool => sub { 1 },
332 0     0   0 '""' => sub { shift->as_string },
333             eq => sub {
334 5     5   176 my ($self,$other) = @_;
335 5         34 return $self->as_string eq $other
336 19     19   186 };
  19         32  
  19         258  
337            
338             =head1 SEE ALSO
339              
340             L,
341             L,
342             L,
343             L.
344            
345             =cut
346              
347             1;
348              
349