File Coverage

blib/lib/File/Util/DirList.pm
Criterion Covered Total %
statement 47 52 90.3
branch 9 16 56.2
condition 3 5 60.0
subroutine 7 7 100.0
pod 1 1 100.0
total 67 81 82.7


line stmt bran cond sub pod time code
1             package File::Util::DirList;
2              
3 2     2   495816 use strict;
  2         5  
  2         120  
4 2     2   9 use warnings;
  2         4  
  2         104  
5 2     2   3263 use Log::ger;
  2         96  
  2         8  
6              
7 2     2   424 use Exporter qw(import);
  2         4  
  2         53  
8 2     2   1002 use Perinci::Object;
  2         788  
  2         1481  
9              
10             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
11             our $DATE = '2023-11-20'; # DATE
12             our $DIST = 'File-Util-DirList'; # DIST
13             our $VERSION = '0.002'; # VERSION
14              
15             our @EXPORT_OK = qw(
16             mv_files_to_dirs
17             );
18             # cp_files_to_dirs
19             # ln_files_to_dirs
20              
21             our %SPEC;
22              
23             our %argspecs_common = (
24             files_then_dirs => {
25             'x.name.is_plural' => 1,
26             'x.name.singular' => 'file_or_dir',
27             summary => 'One or more existing file (or directory) names then the same number of existing directories',
28             schema => ['array*', of=>'pathname::exists*', min_len=>2],
29             req => 1,
30             pos => 0,
31             slurpy => 1,
32             },
33             files_per_dir => {
34             summary => 'Number of files to move to each diectory',
35             schema => 'posint*',
36             default => 1,
37             },
38             reverse => {
39             summary => 'Reverse processing (so first file will go to the last dir, second file to second-last dir, and so on)',
40             schema => 'true*',
41             cmdline_aliases => {r=>{}},
42             default => 1,
43             },
44             );
45              
46             sub _cp_or_mv_or_ln_files_to_dirs {
47 3     3   12 my $action = shift;
48 3         17 my %args = @_;
49              
50 3         11 my ($files_then_dirs, $files_per_dir, $num_dirs, $dir_pos, @files, @dirs);
51             CHECK_ARGUMENTS: {
52 3 50       6 $files_then_dirs = $args{files_then_dirs} or return [400, "Please specify files_then_dirs"];
  3         21  
53 3 50 33     29 (ref $files_then_dirs eq 'ARRAY') && (@$files_then_dirs >= 2)
54             or return [400, "files_then_dirs must be array, minimum 2 elements"];
55 3   100     15 $files_per_dir = $args{files_per_dir} || 1;
56 3 50       18 (@$files_then_dirs % ($files_per_dir+1) == 0)
57             or return [400, "files_then_dirs' elements must be multiples of ".($files_per_dir+1)];
58              
59 3         11 $num_dirs = @$files_then_dirs / ($files_per_dir + 1);
60 3         9 $dir_pos = $num_dirs * $files_per_dir;
61 3         25 log_trace "num_dirs=<$num_dirs>, files_per_dir=<$files_per_dir>";
62 3         19 for my $i (0.. $dir_pos -1 ) {
63 16         58 push @files, $files_then_dirs->[$i];
64             }
65 3         7 for my $i ($dir_pos .. $#{$files_then_dirs}) {
  3         14  
66 8 50       98 -d $files_then_dirs->[$i] or return [400, "files_then_dirs[$i] ($files_then_dirs->[$i]) not a directory"];
67 8         25 push @dirs, $files_then_dirs->[$i];
68             }
69             }
70              
71 3         17 my $envres = envresmulti();
72              
73 3         6951 require File::Copy::Recursive;
74              
75             FILE:
76 3         10791 for my $i (0 .. $num_dirs-1) {
77 8         478 my $dir = $dirs[$i];
78 8         43 for my $j (0 .. $files_per_dir-1) {
79 16 100       723 my $ifile = ($args{reverse} ? ($num_dirs-1-$i) : $i) * $files_per_dir + $j;
80 16         64 my $file = $files[$ifile];
81              
82 16 50       47 if ($action eq 'mv') {
83 16 50       46 if ($args{-dry_run}) {
84 0         0 log_info "DRY-RUN: [#%d/%d] Moving %s to dir %s ...", $ifile+1, scalar(@$files_then_dirs), $file, $dir;
85 0         0 $envres->add_result(200, "OK (dry-run)", {item_id=>$file});
86             } else {
87 16         90 log_info "[#%d/%d] Moving %s to dir %s ...", $ifile+1, scalar(@$files_then_dirs), $file, $dir;
88 16         84 my $ok = File::Copy::Recursive::rmove($file, $dir);
89 16 50       29652 if ($ok) {
90 16         161 $envres->add_result(200, "OK", {item_id=>$file});
91             } else {
92 0         0 log_error "Can't move %s to dir %s: %s", $file, $dir, $!;
93 0         0 $envres->add_result(500, "Error: $!", {item_id=>$file});
94             }
95             }
96             } else {
97 0         0 return [501, "Action unknown or not yet implemented"];
98             }
99             } # for j
100             } # for i
101              
102 3         332 $envres->as_struct;
103             }
104              
105             $SPEC{mv_files_to_dirs} = {
106             v => 1.1,
107             summary => 'Move files to directories, one file to each directory',
108             args => {
109             %argspecs_common,
110             },
111             features => {
112             dry_run => 1,
113             },
114             examples => [
115             {
116             summary => 'Move f1 to d1, f2 to d2, f3 to d3',
117             argv => [qw/f1 f2 f3 d1 d2 d3/],
118             test => 0,
119             'x.doc.show_result' => 0,
120             },
121             ],
122             };
123             sub mv_files_to_dirs {
124 3     3 1 478860 _cp_or_mv_or_ln_files_to_dirs('mv', @_);
125             }
126              
127             1;
128             # ABSTRACT: File utilities involving a list of directories
129              
130             __END__