File Coverage

blib/lib/File/Digest.pm
Criterion Covered Total %
statement 54 62 87.1
branch 15 18 83.3
condition 2 6 33.3
subroutine 8 8 100.0
pod 2 2 100.0
total 81 96 84.3


line stmt bran cond sub pod time code
1             package File::Digest;
2              
3             our $DATE = '2019-09-12'; # DATE
4             our $VERSION = '0.009'; # VERSION
5              
6 1     1   70865 use 5.010001;
  1         13  
7 1     1   5 use strict;
  1         3  
  1         34  
8 1     1   6 use warnings;
  1         2  
  1         32  
9 1     1   1671 use Log::ger;
  1         51  
  1         5  
10              
11 1     1   253 use Exporter qw(import);
  1         2  
  1         47  
12             our @EXPORT_OK = qw(digest_files);
13              
14 1     1   458 use Perinci::Object;
  1         440  
  1         809  
15              
16             our %SPEC;
17              
18             $SPEC{':package'} = {
19             v => 1.1,
20             summary => 'Calculate file checksum/digest (using various algorithms)',
21             };
22              
23             my %arg_file = (
24             file => {
25             summary => 'Filename ("-" means stdin)',
26             schema => ['filename*'],
27             req => 1,
28             pos => 0,
29             cmdline_aliases => {f=>{}},
30             },
31             );
32              
33             my %arg_files = (
34             files => {
35             'x.name.is_plural' => 1,
36             'x.name.singular' => 'file',
37             summary => 'Array of filenames (filename "-" means stdin)',
38             schema => ['array*', of=>'filename*'],
39             req => 1,
40             pos => 0,
41             greedy => 1,
42             cmdline_aliases => {f=>{}},
43             },
44             );
45              
46             my %arg_algorithm = (
47             algorithm => {
48             schema => ['str*', in=>[qw/crc32 md5 sha1 sha224 sha256 sha384 sha512 sha512224 sha512256 Digest/]],
49             default => 'md5',
50             cmdline_aliases => {a=>{}},
51             },
52             digest_args => {
53             schema => ['array*', of=>'str*', 'x.perl.coerce_rules'=>['str_comma_sep']],
54             cmdline_aliases => {A=>{}},
55             },
56             );
57              
58             $SPEC{digest_file} = {
59             v => 1.1,
60             summary => 'Calculate file checksum/digest (using various algorithms)',
61             description => <<'_',
62              
63             Return 400 status when algorithm is unknown/unsupported.
64              
65             _
66             args => {
67             %arg_file,
68             %arg_algorithm,
69             },
70             };
71             sub digest_file {
72 28     28 1 89 my %args = @_;
73              
74 28         54 my $file = $args{file};
75 28   50     65 my $algo = $args{algorithm} // 'md5';
76              
77 28         34 my $fh;
78 28 50       63 if ($file eq '-') {
79 0         0 $fh = \*STDIN;
80             } else {
81 28 100       405 unless (-f $file) {
82 9         38 log_warn("Can't open %s: no such file", $file);
83 9         61 return [404, "No such file '$file'"];
84             }
85 19 50       626 open $fh, "<", $file or do {
86 0         0 log_warn("Can't open %s: %s", $file, $!);
87 0         0 return [500, "Can't open '$file': $!"];
88             };
89             }
90              
91 19 100       130 if ($algo eq 'md5') {
    100          
    100          
    50          
92 2         13 require Digest::MD5;
93 2         16 my $ctx = Digest::MD5->new;
94 2         30 $ctx->addfile($fh);
95 2         38 return [200, "OK", $ctx->hexdigest];
96             } elsif ($algo =~ /\Asha(512224|512256|224|256|384|512|1)\z/) {
97 14         659 require Digest::SHA;
98 14         3715 my $ctx = Digest::SHA->new($1);
99 14         248 $ctx->addfile($fh);
100 14         760 return [200, "OK", $ctx->hexdigest];
101             } elsif ($algo eq 'crc32') {
102 2         488 require Digest::CRC;
103 2         2514 my $ctx = Digest::CRC->new(type=>'crc32');
104 2         162 $ctx->addfile($fh);
105 2         136 return [200, "OK", $ctx->hexdigest];
106             } elsif ($algo eq 'Digest') {
107 0         0 require Digest;
108 0   0     0 my $ctx = Digest->new(@{ $args{digest_args} // [] });
  0         0  
109 0         0 $ctx->addfile($fh);
110 0         0 return [200, "OK", $ctx->hexdigest];
111             } else {
112 1         16 return [400, "Invalid/unsupported algorithm '$algo'"];
113             }
114             }
115              
116             $SPEC{digest_files} = {
117             v => 1.1,
118             summary => 'Calculate file checksum/digest (using various algorithms)',
119             description => <<'_',
120              
121             Dies when algorithm is unsupported/unknown.
122              
123             _
124             args => {
125             %arg_files,
126             %arg_algorithm,
127             },
128             };
129             sub digest_files {
130 10     10 1 33095 my %args = @_;
131              
132 10         22 my $files = $args{files};
133 10   50     26 my $algo = $args{algorithm} // 'md5';
134              
135 10         29 my $envres = envresmulti();
136 10         2327 my @res;
137              
138 10         21 for my $file (@$files) {
139 28         62 my $itemres = digest_file(file => $file, algorithm=>$algo);
140 28 100       198 die $itemres->[1] if $itemres->[0] == 400;
141 27         130 $envres->add_result($itemres->[0], $itemres->[1], {item_id=>$file});
142 27 100       1388 push @res, {file=>$file, digest=>$itemres->[2]} if $itemres->[0] == 200;
143             }
144              
145 9         29 $envres = $envres->as_struct;
146 9         70 $envres->[2] = \@res;
147 9         19 $envres->[3]{'table.fields'} = [qw/file digest/];
148 9         34 $envres;
149             }
150              
151             1;
152             # ABSTRACT: Calculate file checksum/digest (using various algorithms)
153              
154             __END__