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 }