File Coverage

blib/lib/SVN/Dumpfile/Node/Headers.pm
Criterion Covered Total %
statement 60 91 65.9
branch 24 46 52.1
condition 18 48 37.5
subroutine 10 10 100.0
pod 6 6 100.0
total 118 201 58.7


line stmt bran cond sub pod time code
1             ################################################################################
2             # Copyright (c) 2008 Martin Scharrer
3             # This is open source software under the GPL v3 or later.
4             #
5             # $Id: Headers.pm 103 2008-10-14 21:11:21Z martin $
6             ################################################################################
7             package SVN::Dumpfile::Node::Headers;
8 11     11   57 use strict;
  11         18  
  11         355  
9 11     11   56 use warnings;
  11         20  
  11         246  
10 11     11   59 use Carp;
  11         16  
  11         951  
11 11     11   10544 use Readonly;
  11         35858  
  11         14659  
12             Readonly my $NL => chr(10);
13              
14             our $VERSION = do { '$Rev: 103 $' =~ /\$Rev: (\d+) \$/; '0.13' . ".$1" };
15              
16             my @SVNHEADER = qw(
17             Revision-number
18             Node-path
19             Node-kind
20             Node-action
21             Node-copyfrom-rev
22             Node-copyfrom-path
23             Prop-delta
24             Prop-content-length
25             Text-delta
26             Text-content-length
27             Text-copy-source-md5
28             Text-content-md5
29             Content-length
30             );
31              
32             sub new {
33 43     43 1 64 my $class = shift;
34 43         134 my $self = {};
35              
36 43 100 66     654 if ( @_ == 1 && ref $_[0] eq 'HASH' ) {
  1 100 66     5  
    50 66        
    50 33        
37 3         5 $self = { %{ $_[0] } };
  3         14  
38             }
39             elsif ( @_ == 1 && ref $_[0] eq 'ARRAY' && @{ $_[0] } % 2 == 0 ) {
40 1         2 $self = { @{ $_[0] } };
  1         6  
41             }
42             elsif ( @_ % 2 == 0 ) {
43 0         0 $self = {@_};
44             }
45             elsif ( @_ == 1 && !defined $_[0] ) {
46              
47             # Ignore single undef value
48             }
49             else {
50 0         0 carp "${class}::new() awaits hashref, key/value pairs or an "
51             . "arrayref to them as arguments. Ignoring all arguments.";
52 0         0 return;
53             }
54 43         124 $self = bless $self, $class;
55 43         321 return $self;
56             }
57              
58             sub number {
59 18     18 1 17 my $self = shift;
60 18         69 scalar keys %$self;
61             }
62              
63             sub read {
64 29     29 1 37 my $self = shift;
65 29         29 my $fh = shift;
66              
67 29         33 my $line = eval { $fh->getline };
  29         5586  
68 29 100       845 return unless defined $line;
69              
70 26   33     143 while ( defined $line and $line =~ /^$/ ) {
71 0         0 $line = $fh->getline;
72             }
73              
74             # Should be 'Node-path: ' or 'Revision-number: ' now
75 26 50       119 if ( $line !~ /^(Node-path|Revision-number): / ) {
76 0         0 chomp($line);
77 0         0 croak "No node start found at input file position ", $fh->tell, ".";
78             }
79              
80             # Read headers
81 26   66     30 do {
82 128 50       3316 if ( $line =~ /^([^:]+):\s*(.*)$/ ) {
83 128         2978 $self->{$1} = $2;
84             }
85             else {
86 0         0 chomp $line;
87 0         0 croak "Error in header at position ", $fh->tell,
88             ", input line '$line'";
89             }
90             } while ( defined( $line = $fh->getline ) && $line !~ /^$/ );
91              
92 26         764 return 1;
93             }
94              
95             sub as_string {
96 44     44 1 51 my $self = shift;
97 44         54 my $str = "";
98              
99 44         115 my %not_printed = map { $_ => 0 } keys %$self;
  214         448  
100              
101             # Print in given order
102 44         106 foreach my $key (@SVNHEADER) {
103 572 100       1226 next unless exists $self->{$key};
104 214         733 $str .= "${key}: $self->{$key}$NL";
105 214         1159 delete $not_printed{$key};
106             }
107              
108             # Print rest if exists
109 44         105 foreach my $key ( keys %not_printed ) {
110 0         0 $str .= "${key}: $self->{$key}$NL";
111             }
112 44         165 return $str;
113             }
114              
115             *to_string = \&as_string;
116              
117             sub write {
118 18     18 1 21 my $self = shift;
119 18         21 my $fh = shift;
120              
121 18 0 33     19 unless ( eval { $fh->isa('IO::Handle') }
  18   33     96  
122             || ref $fh eq 'GLOB'
123             || ref \$fh eq 'GLOB' )
124             {
125 0         0 croak "Given argument is no valid file handle.";
126             }
127              
128 18         43 return $fh->print( $self->as_string . $NL );
129             }
130              
131             #################
132             ## sanitycheck - Checks if needed Headers exists and belong to each other
133             #####
134              
135             sub sanitycheck {
136 9     9 1 17 my $header = shift;
137 9         13 my $error = 0;
138              
139             # Revision entry needs also 'Prop-content-length' and 'Content-length'
140 9 100       49 if ( exists $header->{'Revision-number'} ) {
    50          
    50          
141 4 50 33     31 if ( !exists $header->{'Prop-content-length'}
142             || !exists $header->{'Content-length'} )
143             {
144 0         0 carp "Missing needed header(s) after 'Revision-number'.\n";
145 0         0 $error++;
146             }
147             }
148              
149             elsif ( !exists $header->{'Node-path'} ) {
150 0         0 carp "Missing needed header 'Node-path' or 'Revision-number'.";
151 0         0 return 10_000;
152             }
153              
154             # Nodes need 'Node-action' at minimum.
155             elsif ( !exists $header->{'Node-action'} ) {
156 0         0 carp "Missing needed header 'Node-action' after 'Node-path'.";
157 0         0 $error++;
158             }
159             else # 'Node-action' exists:
160             {
161 5         10 my $action = $header->{'Node-action'}; # buffer
162 5 50 33     28 if ( $action eq 'delete' ) {
    50          
    0          
163 0 0       0 my $num_headers_expected
164             = ( exists $header->{'Node-kind'} ) ? 3 : 2;
165              
166 0 0       0 if ( keys %$header != $num_headers_expected ) {
167 0         0 carp "Two much headers for 'Node-action: delete'.\n";
168 0         0 local $, = "\n";
169              
170 0         0 while ( my ( $key, $value ) = each %$header ) {
171 0         0 print STDERR "$key: $value\n";
172             }
173 0         0 $error++;
174             }
175             }
176             elsif ( $action eq 'add' or $action eq 'replace' ) {
177 5 50       23 if ( !exists $header->{'Node-kind'} ) {
    100          
    50          
178 0         0 carp "Missing header 'Node-kind' for 'Node-action: add'.\n";
179 0         0 $error++;
180             }
181             elsif ( $header->{'Node-kind'} eq 'file' ) {
182 2 0 33     25 unless ( # This two header both exist
      33        
      0        
      0        
      33        
183             ( exists $header->{'Text-content-length'}
184             && exists $header->{'Text-content-md5'}
185             && !( # and this two both exist or both non-exist
186             exists $header->{
187             'Node-copyfrom-rev'} ^ #\ xor+negation
188             exists $header->{
189             'Node-copyfrom-path'} #/ = equivalence
190             )
191             )
192             || ( # This two header both exist
193             exists $header->{'Node-copyfrom-rev'}
194             && exists $header->{'Node-copyfrom-path'}
195             && !( # and this two both exist or both non-exist
196             exists $header->{
197             'Text-content-length'} ^ #\ xor+negation
198             exists $header->{
199             'Text-content-md5'} #/ = equivalence
200             )
201             )
202             )
203             { # then there is something wrong
204 0         0 carp "Missing/wrong header(s) for 'Node-action: add'/"
205             . "'Node-kind: file'.";
206 0         0 $error++;
207             }
208             }
209             elsif ( $header->{'Node-kind'} eq 'dir' ) {
210 3 50 33     22 if ( exists $header->{'Text-content-length'}
211             || exists $header->{'Text-content-md5'} )
212             {
213 0         0 carp "To much header(s) for 'Node-action: add'/'Node-kind:
214             dir'.";
215 0         0 $error++;
216             }
217             }
218             else {
219 0         0 carp "Invalid value '"
220             . $header->{'Node-kind'}
221             . "' for 'Node-kind'.";
222 0         0 $error++;
223             }
224             }
225             elsif ( $action eq 'change' ) {
226              
227             }
228             else {
229              
230             }
231             } # end of else path of "if ( !exists $header->{'Node-action'} )"
232              
233             #print STDERR Data::Dumper->Dump([$header], ['%header']) if $error;
234 9         41 return $error;
235             }
236              
237             1;
238             __END__