File Coverage

perllib/Arch/Log.pm
Criterion Covered Total %
statement 65 117 55.5
branch 13 46 28.2
condition 6 15 40.0
subroutine 14 17 82.3
pod 10 11 90.9
total 108 206 52.4


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 4     4   77 use 5.005;
  4         12  
  4         176  
18 4     4   20 use strict;
  4         7  
  4         169  
19              
20             package Arch::Log;
21              
22 4     4   526 use Arch::Changes qw(:type);
  4         8  
  4         662  
23 4     4   21 use Arch::Util qw(standardize_date parse_creator_email date2age);
  4         8  
  4         654  
24              
25             sub new ($$%) {
26 1     1 0 3 my $class = shift;
27 1   50     5 my $message = shift || die "Arch::Log::new: no message\n";
28 1         2 my %init = @_;
29              
30 1         5 my $self = {
31             message => $message,
32             headers => undef,
33             hide_ids => $init{hide_ids},
34             };
35              
36 1         3 return bless $self, $class;
37             }
38              
39             sub get_message ($) {
40 0     0 1 0 my $self = shift;
41 0         0 return $self->{message};
42             }
43              
44 4     4   20 use vars qw($SPECIAL_HEADERS);
  4         6  
  4         9672  
45             $SPECIAL_HEADERS = {
46             modified_directories => 1,
47             modified_files => 1,
48             new_directories => 1,
49             new_files => 1,
50             new_patches => -1,
51             removed_directories => 1,
52             removed_files => 1,
53             renamed_directories => 2,
54             renamed_files => 2,
55             };
56              
57             sub get_headers ($) {
58 18     18 1 20 my $self = shift;
59 18 100       99 return $self->{headers} if defined $self->{headers};
60              
61 1         1 my $message = $self->{message};
62 1 50       10 my ($headers_str, $body) = $message =~ /^(.*?\n)\n(.*)$/s
63             or die "Incorrect message:\n\n$message\n\n- No body delimeter\n";
64              
65 1         2 my $headers = { body => $body };
66 1         8 $headers_str =~ s{^([\w-]+):[ \t]*(.*\n(?:[ \t]+.*\n)*)}{
67 9         20 my ($header, $value) = (lc($1), $2);
68 9         11 $header =~ s/-/_/sg;
69 9 50       18 die "Duplicate header $header in message:\n\n$message\n"
70             if exists $headers->{$header};
71 9         11 chomp($value);
72              
73             # handle special headers (lists, lists of pairs, files but ids)
74 9         11 my $type = $SPECIAL_HEADERS->{$header};
75 9 100       14 if ($type) {
76 2         7 $value = [ split(/[ \n]+/, $value) ];
77 2 50 66     10 $value = [ grep { !m:(^|/).arch-ids/: } @$value ]
  0         0  
78             if $type > 0 && $self->{hide_ids};
79 2 50       6 if ($type == 2) {
80 0         0 my @pairs = ();
81 0         0 push @pairs, [ splice @$value, 0, 2 ] while @$value;
82 0         0 $value = \@pairs;
83             }
84             }
85 9         14 $headers->{$header} = $value;
86 9         31 ""
87             }meg;
88             #print "*** $_: $headers->{$_} ***\n" foreach keys %$headers;
89              
90 1         4 return $self->{headers} = $headers;
91             }
92              
93             sub header ($$;$) {
94 15     15 1 20 my $self = shift;
95 15         16 my $header = shift;
96 15 50       42 return $self->get_headers->{$header} unless @_;
97 0         0 $self->get_headers->{$header} = shift;
98             }
99              
100             sub get_changes ($) {
101 0     0 1 0 my $self = shift;
102              
103 0         0 my $changes = Arch::Changes->new;
104              
105             # make a workaround for tla bug: missing New-directories in import log;
106             # still, there is no way to figure out empty directory added on import
107 0         0 my @import_dirs = ();
108 0 0 0     0 if ($self->get_revision_kind eq 'import' && !$self->header('new_directories')) {
109 0         0 my %import_dirs = ();
110 0 0       0 foreach (@{$self->header('new_files') || []}) {
  0         0  
111 0         0 my $file = $_;
112 0         0 $import_dirs{$1} = 1 while $file =~ s!^(.+)/.+$!$1!;
113             }
114 0         0 @import_dirs = sort keys %import_dirs;
115             }
116              
117             # new dirs
118 0 0       0 foreach my $path (@{$self->header('new_directories') || []}, @import_dirs) {
  0         0  
119 0         0 $changes->add(ADD, 1, $path);
120             }
121              
122             # new files
123 0 0       0 foreach my $path (@{$self->header('new_files') || []}) {
  0         0  
124 0         0 $changes->add(ADD, 0, $path);
125             }
126              
127             # removed dirs
128 0 0       0 foreach my $path (@{$self->header('removed_directories') || []}) {
  0         0  
129 0         0 $changes->add(DELETE, 1, $path);
130             }
131              
132             # removed files
133 0 0       0 foreach my $path (@{$self->header('removed_files') || []}) {
  0         0  
134 0         0 $changes->add(DELETE, 0, $path);
135             }
136              
137             # modified dirs
138 0 0       0 foreach my $path (@{$self->header('modified_directories') || []}) {
  0         0  
139             # directories cannot be MODIFY'ed
140 0         0 $changes->add(META_MODIFY, 1, $path);
141             }
142              
143             # modified files
144 0 0       0 foreach my $path (@{$self->header('modified_files') || []}) {
  0         0  
145             # logs don't distinguish MODIFY and META_MODIFY
146 0         0 $changes->add(MODIFY, 0, $path);
147             }
148              
149             # moved dirs
150 0 0       0 foreach my $paths (@{$self->header('renamed_directories') || []}) {
  0         0  
151 0         0 $changes->add(RENAME, 1, @{$paths});
  0         0  
152             }
153              
154             # moved files
155 0 0       0 foreach my $paths (@{$self->header('renamed_files') || []}) {
  0         0  
156 0         0 $changes->add(RENAME, 0, @{$paths});
  0         0  
157             }
158              
159 0         0 return $changes;
160             }
161              
162             sub split_version ($) {
163 3     3 1 4 my $self = shift;
164              
165 3         9 my $full_revision = $self->get_revision;
166 3 50       19 die "Invalid archive/revision ($full_revision) in log:\n$self->{message}"
167             unless $full_revision =~ /^(.+)--(.+)/;
168              
169 3         12 return ($1, $2);
170             }
171              
172             sub get_version ($) {
173 1     1 1 2 my $self = shift;
174 1         3 ($self->split_version)[0];
175             }
176              
177             sub get_revision ($) {
178 4     4 1 7 my $self = shift;
179 4         8 $self->header('archive') . "/" . $self->header('revision');
180             }
181              
182             sub get_revision_kind ($) {
183 1     1 1 2 my $self = shift;
184              
185 1 50       4 return $self->header('continuation_of')? 'tag':
    50          
186             $self->header('revision') =~ /--base-0$/? 'import': 'cset';
187             }
188              
189             sub get_revision_desc ($) {
190 1     1 1 2 my $self = shift;
191              
192 1         4 my ($version, $name) = $self->split_version;
193 1   50     3 my $summary = $self->header('summary') || '(none)';
194 1   50     4 my ($creator, $email, $username) = parse_creator_email($self->header('creator') || "N.O.Body");
195 1   33     3 my $date = $self->header('standard_date') || standardize_date($self->header('date') || "no-date");
196 1         5 my $age = date2age($date);
197 1         5 my $kind = $self->get_revision_kind;
198              
199             return {
200 1         52 name => $name,
201             version => $version,
202             summary => $summary,
203             creator => $creator,
204             email => $email,
205             username => $username,
206             date => $date,
207             age => $age,
208             kind => $kind,
209             };
210             }
211              
212             sub dump ($) {
213 1     1 1 2 my $self = shift;
214 1         2 my $headers = $self->get_headers;
215 1         11 require Data::Dumper;
216 1         9 my $dumper = Data::Dumper->new([$headers]);
217 1 50       45 $dumper->Sortkeys(1) if $dumper->can('Sortkeys');
218 1         14 return $dumper->Quotekeys(0)->Indent(1)->Terse(1)->Dump;
219             }
220              
221             sub AUTOLOAD ($@) {
222 0     0     my $self = shift;
223 0           my @params = @_;
224              
225 0           my $method = $Arch::Log::AUTOLOAD;
226              
227             # remove the package name
228 0           $method =~ s/.*://;
229             # DESTROY messages should never be propagated
230 0 0         return if $method eq 'DESTROY';
231              
232 0 0         if (exists $self->get_headers->{$method}) {
233 0           $self->header($method, @_);
234             } else {
235 0           die "Arch::Log: no such header or method ($method)\n";
236             }
237             }
238              
239             1;
240              
241             __END__