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.type; 7 8 public import dextool.compilation_db : FilterClangFlag; 9 10 @safe: 11 12 enum ExitStatusType { 13 Ok, 14 Errors 15 } 16 17 struct DextoolVersion { 18 string payload; 19 alias payload this; 20 } 21 22 /// No guarantee regarding the path. May be absolute, relative, contain a '~'. 23 /// The user of this type must do all the safety checks to ensure that the 24 /// datacontained in valid. 25 struct Path { 26 string payload; 27 alias payload this; 28 29 this(string s) @safe nothrow { 30 const h = s.hashOf; 31 if (auto v = h in pathCache) { 32 payload = *v; 33 } else { 34 pathCache[h] = s; 35 payload = s; 36 } 37 } 38 39 bool empty() @safe pure nothrow const @nogc { 40 return payload.length == 0; 41 } 42 43 bool opEquals(const string s) @safe pure nothrow const @nogc { 44 return payload == s; 45 } 46 47 bool opEquals(const AbsolutePath s) @safe pure nothrow const @nogc { 48 return payload == s.payload; 49 } 50 51 size_t toHash() @safe pure nothrow const @nogc scope { 52 return payload.hashOf; 53 } 54 55 private static string fromCache(size_t h) { 56 if (pathCache.length > 1024) { 57 pathCache = null; 58 } 59 if (auto v = h in pathCache) { 60 return *v; 61 } 62 return null; 63 } 64 } 65 66 private { 67 // Reduce memory usage by reusing paths. 68 private string[size_t] pathCache; 69 } 70 71 /** The path is guaranteed to be the absolute path. 72 * 73 * The user of the type has to make an explicit judgment when using the 74 * assignment operator. Either a `Path` and then pay the cost of the path 75 * expansion or an absolute which is already assured to be _ok_. 76 * This divides the domain in two, one unchecked and one checked. 77 */ 78 struct AbsolutePath { 79 import std.path : buildNormalizedPath, asAbsolutePath, asNormalizedPath; 80 import std.utf : toUTF8; 81 82 Path payload; 83 alias payload this; 84 85 invariant { 86 import std.path : isAbsolute; 87 88 assert(payload.empty || payload.isAbsolute); 89 } 90 91 this(Path p) { 92 // the second buildNormalizedPath is needed to correctly resolve "." 93 // otherwise it is resolved to /foo/bar/. 94 payload = buildNormalizedPath(expand(p)).asAbsolutePath.asNormalizedPath.toUTF8.Path; 95 } 96 97 /// Build the normalised path from workdir. 98 this(Path p, Path workdir) { 99 // the second buildNormalizedPath is needed to correctly resolve "." 100 // otherwise it is resolved to /foo/bar/. 101 payload = buildNormalizedPath(expand(workdir), expand(p)) 102 .asAbsolutePath.asNormalizedPath.toUTF8.Path; 103 } 104 105 pure nothrow @nogc void opAssign(AbsolutePath p) { 106 payload = p.payload; 107 } 108 109 pure nothrow const @nogc string opCast(T : string)() { 110 return payload; 111 } 112 113 bool opEquals(const string s) @safe pure nothrow const @nogc { 114 return payload == s; 115 } 116 117 bool opEquals(const Path s) @safe pure nothrow const @nogc { 118 return payload == s.payload; 119 } 120 121 bool opEquals(const AbsolutePath s) @safe pure nothrow const @nogc { 122 return payload == s.payload; 123 } 124 125 private static Path expand(Path p) @trusted { 126 import std.path : expandTilde; 127 128 return p.expandTilde.Path; 129 } 130 } 131 132 /** During construction checks that the file exists on the filesystem. 133 * 134 * If it doesn't exist it will throw an Exception. 135 */ 136 struct Exists(T) { 137 AbsolutePath payload; 138 alias payload this; 139 140 this(AbsolutePath p) { 141 import std.file : exists, FileException; 142 143 if (!exists(p)) { 144 throw new FileException("File do not exist: " ~ cast(string) p); 145 } 146 147 payload = p; 148 } 149 150 this(Exists!T p) { 151 payload = p.payload; 152 } 153 154 void opAssign(Exists!T p) pure nothrow @nogc { 155 payload = p; 156 } 157 } 158 159 auto makeExists(T)(T p) { 160 return Exists!T(p); 161 } 162 163 @("shall always be the absolute path") 164 unittest { 165 import std.algorithm : canFind; 166 import std.path; 167 import unit_threaded; 168 169 AbsolutePath(Path("~/foo")).canFind('~').shouldEqual(false); 170 AbsolutePath(Path("foo")).isAbsolute.shouldEqual(true); 171 } 172 173 @("shall expand . without any trailing /.") 174 unittest { 175 import std.algorithm : canFind; 176 import unit_threaded; 177 178 AbsolutePath(Path(".")).canFind('.').shouldBeFalse; 179 AbsolutePath(Path("."), Path(".")).canFind('.').shouldBeFalse; 180 } 181 182 @("shall be an instantiation of Exists") 183 nothrow unittest { 184 // the file is not expected to exist. 185 186 try { 187 auto p = makeExists(AbsolutePath(Path("foo"))); 188 } catch (Exception e) { 189 } 190 }