1 /**
2 Date: 2015-2017, Joakim Brännström
3 License: MPL-2, Mozilla Public License 2.0
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module dextool.io;
7 
8 import std.stdio : File;
9 import logger = std.experimental.logger;
10 
11 import dextool.type : ExitStatusType;
12 
13 /// Used when writing data to files on the filesystem.
14 enum WriteStrategy {
15     overwrite,
16     skip
17 }
18 
19 ///TODO don't catch Exception, catch the specific.
20 auto tryOpenFile(string filename, string mode) @trusted {
21     import std.exception;
22     import std.typecons : Unique;
23 
24     Unique!File rval;
25 
26     try {
27         rval = Unique!File(new File(filename, mode));
28     } catch (Exception ex) {
29     }
30     if (rval.isEmpty) {
31         try {
32             logger.errorf("Unable to read/write file '%s'", filename);
33         } catch (Exception ex) {
34         }
35     }
36 
37     return rval;
38 }
39 
40 ///TODO don't catch Exception, catch the specific.
41 auto tryWriting(T)(string fname, T data, WriteStrategy strategy = WriteStrategy.overwrite) @trusted nothrow {
42     import std.exception;
43 
44     static auto action(T)(string fname, T data, WriteStrategy strategy) {
45         import std.file : exists;
46 
47         debug logger.tracef("Trying to write to (%s): %s", strategy, fname);
48 
49         if (strategy == WriteStrategy.skip && exists(fname)) {
50             logger.info("File already exist, skipping: ", fname);
51             return ExitStatusType.Ok;
52         }
53 
54         auto f = tryOpenFile(fname, "w");
55 
56         if (f.isEmpty) {
57             return ExitStatusType.Errors;
58         }
59         scope (exit)
60             f.close();
61 
62         f.rawWrite(cast(void[]) data);
63 
64         return ExitStatusType.Ok;
65     }
66 
67     auto status = ExitStatusType.Errors;
68 
69     try {
70         status = action(fname, data, strategy);
71     } catch (Exception ex) {
72     }
73 
74     try {
75         final switch (status) with (ExitStatusType) {
76         case Ok:
77             break;
78         case Errors:
79             logger.error("Failed to write file: ", fname);
80             break;
81         }
82     } catch (Exception ex) {
83     }
84 
85     return status;
86 }
87 
88 /** Try to write the data to the destination directory.
89  *
90  * If the directory do not exist try and create it.
91  */
92 ExitStatusType writeFileData(T)(ref T data) {
93     import std.path : dirName;
94 
95     static ExitStatusType tryMkdir(string path) nothrow {
96         import std.file : isDir, mkdirRecurse;
97 
98         try {
99             if (path.isDir) {
100                 return ExitStatusType.Ok;
101             }
102         } catch (Exception ex) {
103         }
104 
105         try {
106             mkdirRecurse(path);
107             return ExitStatusType.Ok;
108         } catch (Exception ex) {
109         }
110 
111         return ExitStatusType.Errors;
112     }
113 
114     foreach (p; data) {
115         if (tryMkdir(p.filename.dirName) == ExitStatusType.Errors) {
116             logger.error("Unable to create destination directory: ", p.filename.dirName);
117         }
118 
119         auto status = tryWriting(p.filename, p.data, p.strategy);
120         if (status != ExitStatusType.Ok) {
121             return ExitStatusType.Errors;
122         }
123     }
124 
125     return ExitStatusType.Ok;
126 }