File Coverage

blib/lib/SVN/Hooks/CheckStructure.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package SVN::Hooks::CheckStructure;
2             # ABSTRACT: Check the structure of a repository.
3             $SVN::Hooks::CheckStructure::VERSION = '1.34';
4 1     1   33940 use strict;
  1         2  
  1         55  
5 1     1   8 use warnings;
  1         2  
  1         45  
6              
7 1     1   7 use Carp;
  1         2  
  1         290  
8 1     1   991 use Data::Util qw(:check);
  1         1474  
  1         324  
9 1     1   697 use SVN::Hooks;
  0            
  0            
10              
11             use Exporter qw/import/;
12             my $HOOK = 'CHECK_STRUCTURE';
13             our @EXPORT = ($HOOK, 'check_structure');
14              
15              
16             my $Structure;
17              
18             sub CHECK_STRUCTURE {
19             ($Structure) = @_;
20              
21             PRE_COMMIT(\&pre_commit);
22              
23             return 1;
24             }
25              
26             sub _check_structure {
27             my ($structure, $path) = @_;
28              
29             @$path > 0 or croak "Can't happen!";
30              
31             if (is_string($structure)) {
32             if ($structure eq 'DIR') {
33             return (1) if @$path > 1;
34             return (0, "the component ($path->[0]) should be a DIR in");
35             } elsif ($structure eq 'FILE') {
36             return (0, "the component ($path->[0]) should be a FILE in") if @$path > 1;
37             return (1);
38             } elsif (is_integer($structure)) {
39             return (1) if $structure;
40             return (0, "invalid path");
41             } else {
42             return (0, "syntax error: unknown string spec ($structure), while checking");
43             }
44             } elsif (is_array_ref($structure)) {
45             return (0, "syntax error: odd number of elements in the structure spec, while checking")
46             unless scalar(@$structure) % 2 == 0;
47             return (0, "the component ($path->[0]) should be a DIR in")
48             unless @$path > 1;
49             shift @$path;
50             # Return ok if the directory doesn't have subcomponents.
51             return (1) if @$path == 1 && length($path->[0]) == 0;
52              
53             for (my $s=0; $s<$#$structure; $s+=2) {
54             my ($lhs, $rhs) = @{$structure}[$s, $s+1];
55             if (is_string($lhs)) {
56             if ($lhs eq $path->[0]) {
57             return _check_structure($rhs, $path);
58             } elsif (is_integer($lhs)) {
59             if ($lhs) {
60             return _check_structure($rhs, $path);
61             } elsif (is_string($rhs)) {
62             return (0, "$rhs, while checking");
63             } else {
64             return (0, "syntax error: the right hand side of a number must be string, while checking");
65             }
66             }
67             } elsif (is_rx($lhs)) {
68             if ($path->[0] =~ $lhs) {
69             return _check_structure($rhs, $path);
70             }
71             } else {
72             my $what = ref $lhs;
73             return (0, "syntax error: the left hand side of arrays in the structure spec must be scalars or qr/Regexes/, not $what, while checking");
74             }
75             }
76             return (0, "the component ($path->[0]) is not allowed in");
77             } else {
78             my $what = ref $structure;
79             return (0, "syntax error: invalid reference to a $what in the structure spec, while checking");
80             }
81             }
82              
83              
84             sub check_structure {
85             my ($structure, $path) = @_;
86             $path = "/$path" unless $path =~ m@^/@; # make sure it's an absolute path
87             my @path = split '/', $path, -1; # preserve trailing empty components
88             my ($code, $error) = _check_structure($structure, \@path);
89             croak "$error: $path\n" if $code == 0;
90             return 1;
91             }
92              
93             sub pre_commit {
94             my ($svnlook) = @_;
95              
96             my @errors;
97              
98             foreach my $added ($svnlook->added()) {
99             # Split the $added path in its components. We prefix $added
100             # with a slash to make it look like an absolute path for
101             # _check_structure. The '-1' is to preserve trailing empty
102             # components so that we can differentiate directory paths from
103             # file paths.
104             my @added = split '/', "/$added", -1;
105             my ($code, $error) = _check_structure($Structure, \@added);
106             push @errors, "$error: $added" if $code == 0;
107             }
108              
109             croak join("\n", "$HOOK:", @errors), "\n"
110             if @errors;
111              
112             return;
113             }
114              
115             1; # End of SVN::Hooks::CheckStructure
116              
117             __END__