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 }