File Coverage

blib/lib/File/Temp/MoreUtils.pm
Criterion Covered Total %
statement 42 44 95.4
branch 17 20 85.0
condition 4 5 80.0
subroutine 7 7 100.0
pod 1 1 100.0
total 71 77 92.2


line stmt bran cond sub pod time code
1             package File::Temp::MoreUtils;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2021-07-12'; # DATE
5             our $DIST = 'File-Temp-MoreUtils'; # DIST
6             our $VERSION = '0.001'; # VERSION
7              
8 1     1   109357 use 5.010001; # for // & state var
  1         12  
9 1     1   6 use strict;
  1         2  
  1         24  
10 1     1   5 use warnings;
  1         1  
  1         37  
11              
12 1     1   5 use Fcntl ':DEFAULT';
  1         2  
  1         423  
13 1     1   9 use File::Temp ();
  1         2  
  1         18  
14              
15 1     1   5 use Exporter 'import';
  1         2  
  1         589  
16             our @EXPORT_OK = qw(tempfile_named);
17              
18             our %SPEC;
19              
20             $SPEC{tempfile_named} = {
21             v => 1.1,
22             summary => 'Try to create a temporary file with certain name '.
23             '(but used .tmp1, .tmp2, ... suffix if already exists) ',
24             description => <<'_',
25              
26             Unlike 's `tempfile()` which creates a temporary file with a
27             unique random name, this routine tries to create a temporary file with a
28             specific name, but adds a counter suffix when the specified name already exists.
29             This is often desirable in the case when we want the temporary file to have
30             a name similarity with another file.
31              
32             And like 's `tempfile()`, will return:
33              
34             ($fh, $filename)
35              
36             _
37             result_naked => 1,
38             args => {
39             name => {
40             schema => 'filename*',
41             pos => 0,
42             req => 1,
43             },
44             dir => {
45             summary => 'If specified, will create the temporary file here',
46             description => <<'_',
47              
48             If specified and set to `undef`, will create new temporary directory using
49             's `tempdir` (with CLEANUP option set to true unless DEBUG
50             environment variable is set to true) and use this temporary directory for the
51             directory, including for subsequent invocation for the same process whenever
52             `dir` is set to `undef` again.
53              
54             _
55             schema => 'dirname',
56             },
57             suffix_start => {
58             schema => ['str*', min_len=>1],
59             default => 1,
60             description => <<'_',
61              
62             Will use Perl's post-increment operator (`++`) to increment the suffix, so this
63             works with number as well as letter combinations, e.g. `aa` will be incremented
64             to `ab`, `ac` and so on.
65              
66             _
67             },
68             },
69             examples => [
70             {
71             args => {name=>'source.pdf'},
72             summary => 'Attempt to create source.pdf, then if already exists source.1.pdf, and so on',
73             test => 0,
74             'x.doc.show_result' => 0,
75             },
76             {
77             args => {name=>'source', dir=>undef},
78             summary => 'Attempt to create source.pdf in a temporary directory, then if already exists source.1, and so on',
79             test => 0,
80             'x.doc.show_result' => 0,
81             },
82             {
83             args => {name=>'source.pdf', suffix_start=>'tmp1'},
84             summary => 'Attempt to create source.pdf, then if already exists source.tmp1.pdf, then source.tmp2.pdf, and so on',
85             test => 0,
86             'x.doc.show_result' => 0,
87             },
88             ],
89             };
90             sub tempfile_named {
91 13     13 1 5018 my %args = @_;
92              
93 13 100       61 die "tempfile_named(): Please specify name" unless defined $args{name};
94              
95 12         16 state $tempdir;
96 12         21 my $dir;
97 12 100       34 if (exists $args{dir}) {
98 4 100       11 if (defined $args{dir}) {
99 2         5 $dir = $args{dir};
100             } else {
101 2   66     16 $tempdir //= File::Temp::tempdir(CLEANUP => !$ENV{DEBUG});
102 2         633 $dir = $tempdir;
103             }
104 4 50       69 die "tempfile_named(): dir '$dir' is not a directory" unless -d $dir;
105             }
106              
107 12   100     56 my $suffix_start = $args{suffix_start} // 1;
108 12         26 my $suffix;
109             my $fh;
110 12 100       196 my $name0 = defined $dir ? "$dir/" . File::Basename::basename($args{name}) : $args{name};
111 12         20 my $counter = 1;
112 12         23 while (1) {
113 26         48 my $name = $name0;
114 26 100       55 if (defined $suffix) {
115 14 100       69 $name =~ s/(.+\.)(?=.)/"$1$suffix."/e
  3         15  
116             or $name .= ".$suffix";
117 14         28 $suffix++;
118             } else {
119 12         19 $suffix = $suffix_start;
120             }
121 26 100       1096 if (sysopen $fh, $name, O_CREAT | O_CREAT | O_EXCL) {
122 12         178 return ($fh, $name);
123             }
124 14 50       131 unless ($! =~ /File exists/) {
125 0         0 die "tempfile_named(): Can't create temporary file '$name': $!";
126             }
127 14 50       42 if ($counter++ > 10_000) {
128 0           die "tempfile_named(): Can't create temporary file after many retries: $!";
129             }
130             }
131             }
132              
133             1;
134             # ABSTRACT: Provide more routines related to creating temporary files/dirs
135              
136             __END__