1 /// Utility functions to work with paths
2 module thepath.utils;
3 
4 private import std.exception: enforce;
5 private static import std.path;
6 
7 private import thepath.exception: PathException;
8 private import thepath.path: Path;
9 
10 
11 /** Create temporary directory
12   * Note, that caller is responsible to remove created directory.
13   * The temp directory will be created inside specified path.
14   * 
15   * Params:
16   *     path = path to already existing directory to create
17   *         temp directory inside. Default: std.file.tempDir
18   *     prefix = prefix to be used in name of temp directory. Default: "tmp"
19   * Returns: string, representing path to created temporary directory
20   * Throws: PathException in case of error
21   **/
22 string createTempDirectory(in string prefix="tmp") {
23     import std.file : tempDir;
24     return createTempDirectory(tempDir, prefix);
25 }
26 
27 /// ditto
28 string createTempDirectory(in string path, in string prefix) {
29     version(Posix) {
30         import std.string : fromStringz;
31         import std.conv: to;
32         import core.sys.posix.stdlib : mkdtemp;
33 
34         // Prepare template for mkdtemp function.
35         // It have to be mutable array of chars ended with zero to be compatibale
36         // with mkdtemp function.
37         scope char[] tempname_str = std.path.buildNormalizedPath(
38             std.path.expandTilde(path),
39             prefix ~ "-XXXXXX").dup ~ "\0";
40 
41         // mkdtemp will modify tempname_str directly. and res is pointer to
42         // tempname_str in case of success.
43         char* res = mkdtemp(tempname_str.ptr);
44         enforce!PathException(
45             res !is null, "Cannot create temporary directory");
46 
47         // Converting to string will duplicate result.
48         // But may be it have sense to do it in more obvious way
49         // for example: return tempname_str[0..$-1].idup;
50         return to!string(res.fromStringz);
51     } else {
52         import std.ascii: letters;
53         import std.random: uniform;
54         import std.file;
55 
56         // Generate new random temp path to test using provided path and prefix
57         // as template.
58         string generate_temp_dir() {
59             string suffix = "-";
60             for(ubyte i; i<6; i++) suffix ~= letters[uniform(0, $)];
61             return std.path.buildNormalizedPath(
62                 std.path.expandTilde(path), prefix ~ suffix);
63         }
64 
65         // TODO: Improve security of this approach by creating directory
66         //       and catching exception if directory already exists.
67         string temp_dir = generate_temp_dir();
68         while (std.file.exists(temp_dir)) {
69             temp_dir = generate_temp_dir();
70         }
71         std.file.mkdir(temp_dir);
72         return temp_dir;
73     }
74 }
75 
76 
77 /** Create temporary directory
78   * Note, that caller is responsible to remove created directory.
79   * The temp directory will be created inside specified path.
80   *
81   * Params:
82   *     path = path to already existing directory to create
83   *         temp directory inside. Default: std.file.tempDir
84   *     prefix = prefix to be used in name of temp directory. Default: "tmp"
85   * Returns: Path to created temp directory
86   * Throws: PathException in case of error
87   **/
88 Path createTempPath(in string prefix="tmp") {
89     return Path(createTempDirectory(prefix));
90 }
91 
92 /// ditto
93 Path createTempPath(in string path, in string prefix) {
94     return Path(createTempDirectory(path, prefix));
95 }
96 
97 /// ditto
98 Path createTempPath(in Path path, in string prefix) {
99     return createTempPath(path.toString, prefix);
100 }
101 
102