File Coverage

perllib/Arch/Name.pm
Criterion Covered Total %
statement 138 151 91.3
branch 73 94 77.6
condition 21 28 75.0
subroutine 29 31 93.5
pod 21 21 100.0
total 282 325 86.7


line stmt bran cond sub pod time code
1             # Arch Perl library, Copyright (C) 2004 Mikhael Goikhman
2             #
3             # This program is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation; either version 2 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program; if not, write to the Free Software
15             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16              
17 7     7   163 use 5.005;
  7         24  
  7         272  
18 7     7   37 use strict;
  7         12  
  7         16992  
19              
20             package Arch::Name;
21              
22             my @ELEMENTS = qw(none archive category branch version revision);
23             my $i = -1;
24             my %ELEMENT_INDEX = map { $_ => $i, substr($_, 0, 1) => ++$i } @ELEMENTS;
25             my $ERROR = undef; # yes, it is intentionally global
26              
27             my $archive_re = qr/[-\w]+(?:\.[-\w]+)*@[-\w.]*/;
28             my $category_re = qr/[a-zA-Z](?:[\w]|-[\w])*/;
29             my $branch_re = $category_re;
30             my $version_re = qr/\d+(?:\.\d+)*/;
31             my $revision_re = qr/base-0|(?:version|patch|versionfix)-\d+/;
32              
33             sub new ($;$$) {
34 101     101 1 147 my $class = shift;
35 101         106 my $param = shift;
36 101   100     1444 my $on_error = shift || 0;
37 101 100       230 $class = ref($class) if ref($class);
38              
39 101         142 my $self = [];
40 101         205 bless $self, $class;
41 101 100       179 if ($param) {
42 88         176 $self->set($param);
43             } else {
44 13 100       37 $ERROR = "$class object constructed with empty name" if $on_error >= 0;
45             }
46              
47 101 50 33     226 die "$ERROR\n" if $on_error > 1 && $ERROR;
48 101         361 return $self;
49             }
50              
51             sub set ($$) {
52 90     90 1 103 my $self = shift;
53 90         112 my $param = shift;
54              
55 90         292 @$self = ();
56 90         117 $ERROR = undef;
57              
58 90 50       236 if (!$param) {
    100          
    50          
    100          
    50          
59             # do nothing
60             } elsif (!ref($param)) {
61             # parse string
62 86 100       5010 if ($param =~ m!^($archive_re)(?:/($category_re)(?:(?:--($branch_re|))?(?:--($version_re|FIRST|LATEST)(?:--($revision_re|FIRST|LATEST))?)?)?)?$!o) {
63 40         208 @$self = ($1, $2, $3, $4, $5);
64 40         200 splice(@$self, @$self - 1) until defined $self->[-1];
65             # handle branchless names
66 40 100 100     168 $self->[$ELEMENT_INDEX{branch} - 1] ||= ""
67             if defined $self->[$ELEMENT_INDEX{version} - 1];
68             } else {
69 46         105 $ERROR = "Can't parse name ($param)";
70             }
71             } elsif (UNIVERSAL::isa($param, 'Arch::Name')) {
72 0         0 @$self = @$param;
73             } elsif (ref($param) eq 'ARRAY') {
74 3 50       15 $self->apply(@$param) if @$param;
75             } elsif (ref($param) eq 'HASH') {
76 1 50       7 $self->apply($param) if %$param;
77             } else {
78 0         0 $ERROR = "set: invalid parameter ($param), ignored";
79             }
80 90         187 return $self;
81             }
82              
83             sub clone ($;@) {
84 8     8 1 13 my $self = shift;
85 8         20 my $clone = $self->new(undef, -1);
86 8         23 @$clone = @$self; # faster, don't perform checks
87 8 100       20 $clone->apply(@_) if @_;
88 8         21 return $clone;
89             }
90              
91             sub apply ($;@) {
92 32     32 1 41 my $self = shift;
93 32         35 my $items = $_[0];
94              
95 32         34 $ERROR = undef;
96 32 100       73 if (ref($items) eq 'HASH') {
97 17         57 my %items = %$items; # modify a copy
98 17         21 my $i = 0;
99 17         28 foreach my $regexp (
100             $archive_re,
101             $category_re,
102             $branch_re,
103             $version_re,
104             $revision_re,
105             ) {
106 84         99 my $elem = $ELEMENTS[$i + 1];
107 84 100       253 next unless exists $items{$elem};
108 30         35 my $item = $items{$elem};
109 30 50 66     110 if (defined $item && $i > @$self) {
110 0         0 $ERROR = "apply: can't change $elem without $ELEMENTS[$i]";
111 0         0 last;
112             }
113 30 100 100     1638 if (!defined $item) {
    100 100        
114 11 100       32 splice(@$self, $i) if @$self > $i;
115             } elsif ($elem eq "branch" && $item eq "" || $item =~ /^$regexp$/) {
116 18         32 $self->[$i] = $item;
117             } else {
118 1         4 $ERROR = "apply: invalid $elem ($item)";
119 1         3 last;
120             }
121 29         81 delete $items{$elem};
122             } continue {
123 83         115 $i++;
124             }
125 17 50       41 splice(@$self, $i) if @$self > $i;
126 17 100 33     46 $ERROR ||= "apply: unknown elements (" . join(', ', keys %items) . "), ignored"
127             if %items;
128             } else {
129 15         24 my %hash = ();
130 15 100       36 if (ref($items) eq 'ARRAY') {
131 6         9 my $level = @$self;
132 6 50       15 $ERROR = "apply: excess of items (@$items), some but $level are ignored"
133             if @$items > $level;
134 6   50     40 $hash{$ELEMENTS[$level--] || 'none'} = $_ foreach @$items;
135 6         9 shift;
136             }
137 15         35 my @items = @_;
138 15 50       35 if (ref($items[0])) {
139 0         0 $ERROR = "apply: unsupported arguments (@items)";
140 0         0 @items = ();
141             }
142 15         21 my $level = @$self;
143 15 50       43 $ERROR = "apply: excess of items (@items) for level $level, some are ignored"
144             if @items >= @ELEMENTS - $level;
145 15   50     70 $hash{$ELEMENTS[++$level] || 'none'} = $_ foreach @items;
146 15         25 delete $hash{none};
147 15         52 $self->apply(\%hash);
148             }
149 32         139 return $self;
150             }
151              
152             sub go_up ($;$) {
153 5     5 1 7 my $self = shift;
154 5   100     17 my $level = shift || 1;
155 5         16 return $self->apply([ (undef) x $level ]);
156             }
157              
158             sub go_down ($@) {
159 4     4 1 5 my $self = shift;
160 4         10 return $self->apply(@_);
161             }
162              
163             sub parent ($;$) {
164 3     3 1 4 my $self = shift;
165 3         7 return $self->clone->go_up(@_);
166             }
167              
168             sub child ($@) {
169 2     2 1 3 my $self = shift;
170 2         4 return $self->clone->go_down(@_);
171             }
172              
173             sub to_string ($) {
174 27     27 1 35 my $self = shift;
175 27         34 my $name = "";
176 27 50       86 $name .= $self->[0] if @$self;
177 27 100       79 $name .= "/$self->[1]" if @$self > 1;
178 27 100 100     105 $name .= "--$self->[2]" if @$self > 2 && $self->[2] ne "";
179 27 100       62 $name .= "--$self->[3]" if @$self > 3;
180 27 100       58 $name .= "--$self->[4]" if @$self > 4;
181 27         121 return $name;
182             }
183              
184             sub to_nonarch_string ($) {
185 2     2 1 5 my $self = shift;
186 2         6 my $name = $self->to_string;
187 2         13 $name =~ s|^.*/||;
188 2         9 return $name;
189             }
190              
191             sub to_array ($) {
192 3     3 1 5 my $self = shift;
193 3 50       25 return wantarray? @$self: [ @$self ];
194             }
195              
196             sub to_hash ($) {
197 3     3 1 3 my $self = shift;
198 3         5 my %hash = ();
199 3         8 for (my $i = 0; $i < @$self; $i++) {
200 6         19 $hash{$ELEMENTS[$i + 1]} = $self->[$i];
201             }
202 3 100       22 return wantarray? %hash: \%hash;
203             }
204              
205             *fqn = *to_string; *fqn = *fqn;
206             *get = *to_array; *get = *get;
207             *nan = *to_nonarch_string; *nan = *nan;
208              
209             sub archive ($;$) {
210 3     3 1 6 my $self = shift;
211 3 50       20 return $self->[0] unless @_;
212 0         0 $self->apply({ archive => shift });
213             }
214              
215             sub category ($;$) {
216 2     2 1 4 my $self = shift;
217 2 50       12 return $self->[1] unless @_;
218 0         0 $self->apply({ category => shift });
219             }
220              
221             sub branch ($;$) {
222 4     4 1 5 my $self = shift;
223 4 50       23 return $self->[2] unless @_;
224 0         0 $self->apply({ branch => shift });
225             }
226              
227             sub version ($;$) {
228 1     1 1 2 my $self = shift;
229 1 50       8 return $self->[3] unless @_;
230 0         0 $self->apply({ version => shift });
231             }
232              
233             sub revision ($;$) {
234 1     1 1 3 my $self = shift;
235 1 50       13 return $self->[4] unless @_;
236 0         0 $self->apply({ revision => shift });
237             }
238              
239             sub error ($) {
240 3     3 1 5 my $self = shift;
241 3         12 return $ERROR;
242             }
243              
244             sub level ($;$) {
245 15     15 1 24 my $self = shift;
246 15         92 my $stringify = shift;
247 15 100       67 return scalar @$self unless $stringify;
248 2         8 return $ELEMENTS[@$self];
249             }
250              
251             sub cast ($$) {
252 5     5 1 8 my $self = shift;
253 5         6 my $elem = shift;
254 5 100       20 my $index1 = $elem =~ /^\d+$/? $elem: $ELEMENT_INDEX{$elem};
255 5 50       12 die "cast: invalid arg given ($elem)\n" unless defined $index1;
256 5 100       16 return undef if $index1 > @$self;
257              
258 3         7 my $clone = $self->new(undef, -1);
259 3         14 @$clone = (@$self)[0 .. $index1 - 1];
260 3         8 return $clone;
261             }
262              
263             sub is_valid ($;$$) {
264 92     92 1 120 my $this = shift;
265 92 50       178 my $self = ref($this)? $this: $this->new(shift);
266 92         102 my $elem = shift;
267 92 100       302 return @$self > 0 unless defined $elem;
268 77         179 my $at_least = $elem =~ s/\+$//;
269 77 100       261 my $index1 = $elem =~ /^\d+$/? $elem: $ELEMENT_INDEX{$elem};
270 77 50       144 die "is_valid: invalid arg given ($elem)\n" unless defined $index1;
271 77 100       157 return $index1 <= @$self if $at_least;
272 69         383 return $index1 == @$self;
273             }
274              
275             use overload
276             '""' => 'to_string',
277 2     2   7 '0+' => sub { $_[0]->level },
278 4     4   10 'bool' => sub { $_[0]->is_valid },
279 0     0   0 '=' => sub { $_[0]->clone },
280 2     2   6 '+' => sub { $_[0]->child($_[1]) },
281 0     0   0 '-' => sub { $_[0]->parent($_[1]) },
282 2     2   23 '+=' => sub { $_[0]->go_down($_[1]) },
283 2     2   7 '-=' => sub { $_[0]->go_up($_[1]) },
284 7     7   59 'fallback' => 1;
  7         13  
  7         187  
285              
286             1;
287              
288             __END__