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 import dextool.utility : asAbsNormPath;
11 
12 @safe:
13 
14 enum ExitStatusType {
15     Ok,
16     Errors
17 }
18 
19 /** Prefix used for prepending generated code with a unique string to avoid
20  * name collisions.
21  * See specific functions for how it is used.
22  */
23 struct StubPrefix {
24     string payload;
25     alias payload this;
26 }
27 
28 /// Prefix used for prepending generated files.
29 struct FilePrefix {
30     string payload;
31     alias payload this;
32 }
33 
34 struct MainFileName {
35     string payload;
36     alias payload this;
37 }
38 
39 struct MainName {
40     string payload;
41     alias payload this;
42 }
43 
44 struct MainNs {
45     string payload;
46     alias payload this;
47 }
48 
49 struct MainInterface {
50     string payload;
51     alias payload this;
52 }
53 
54 struct DextoolVersion {
55     string payload;
56     alias payload this;
57 }
58 
59 struct CustomHeader {
60     string payload;
61     alias payload this;
62 }
63 
64 /// The raw arguments from the command line.
65 struct RawCliArguments {
66     string[] payload;
67     alias payload this;
68 }
69 
70 /// Used when writing data to files on the filesystem.
71 enum WriteStrategy {
72     overwrite,
73     skip
74 }
75 
76 //TODO remove FileNames
77 struct FileNames {
78     string[] payload;
79     alias payload this;
80 }
81 
82 struct InFiles {
83     string[] payload;
84     alias payload this;
85 }
86 
87 /// No guarantee regarding the path. May be absolute, relative, contain a '~'.
88 /// The user of this type must do all the safety checks to ensure that the
89 /// datacontained in valid.
90 struct Path {
91     string payload;
92     alias payload this;
93 }
94 
95 /// ditto
96 struct DirName {
97     Path payload;
98     alias payload this;
99 
100     pure nothrow @nogc this(string p) {
101         payload = Path(p);
102     }
103 }
104 
105 /// ditto
106 struct FileName {
107 pure @nogc nothrow:
108 
109     Path payload;
110     alias payload this;
111 
112     this(Path p) {
113         payload = p;
114     }
115 
116     pure nothrow @nogc this(string p) {
117         payload = Path(p);
118     }
119 }
120 
121 /** The path is guaranteed to be the absolute path.
122  *
123  * The user of the type has to make an explicit judgment when using the
124  * assignment operator. Either a `FileName` and then pay the cost of the path
125  * expansion or an absolute which is already assured to be _ok_.
126  * This divides the domain in two, one unchecked and one checked.
127  */
128 struct AbsolutePath {
129     import std.path : expandTilde, buildNormalizedPath;
130 
131     Path payload;
132     alias payload this;
133 
134     invariant {
135         import std.path : isAbsolute;
136 
137         assert(payload.length == 0 || payload.isAbsolute);
138     }
139 
140     immutable this(AbsolutePath p) {
141         this.payload = p.payload;
142     }
143 
144     this(Path p) {
145         auto p_expand = () @trusted { return p.expandTilde; }();
146         // the second buildNormalizedPath is needed to correctly resolve "."
147         // otherwise it is resolved to /foo/bar/.
148         payload = buildNormalizedPath(p_expand).asAbsNormPath.Path;
149     }
150 
151     /// Build the normalised path from workdir.
152     this(Path p, DirName workdir) {
153         auto p_expand = () @trusted { return p.expandTilde; }();
154         auto workdir_expand = () @trusted { return workdir.expandTilde; }();
155         // the second buildNormalizedPath is needed to correctly resolve "."
156         // otherwise it is resolved to /foo/bar/.
157         payload = buildNormalizedPath(workdir_expand, p_expand).asAbsNormPath.Path;
158     }
159 
160     void opAssign(FileName p) {
161         payload = typeof(this)(p).payload;
162     }
163 
164     pure nothrow @nogc void opAssign(AbsolutePath p) {
165         payload = p.payload;
166     }
167 
168     pure nothrow const @nogc FileName opCast(T : FileName)() {
169         return FileName(payload);
170     }
171 
172     pure nothrow const @nogc string opCast(T : string)() {
173         return payload;
174     }
175 }
176 
177 struct AbsoluteFileName {
178     AbsolutePath payload;
179     alias payload this;
180 
181     pure nothrow @nogc this(AbsolutePath p) {
182         payload = p;
183     }
184 }
185 
186 struct AbsoluteDirectory {
187     AbsolutePath payload;
188     alias payload this;
189 
190     pure nothrow @nogc this(AbsolutePath p) {
191         payload = p;
192     }
193 }
194 
195 struct ShellCommand {
196     AbsolutePath program;
197     string[] arguments;
198 
199     this(string cmd) {
200         import std.uni : isWhite;
201         import std.array : split;
202 
203         string[] argv = cmd.split!isWhite;
204         program = Path(argv[0]).AbsolutePath;
205         arguments = argv[1 .. $];
206     }
207 }
208 
209 /** During construction checks that the file exists on the filesystem.
210  *
211  * If it doesn't exist it will throw an Exception.
212  */
213 struct Exists(T) {
214     AbsolutePath payload;
215     alias payload this;
216 
217     this(AbsolutePath p) {
218         import std.file : exists, FileException;
219 
220         if (!exists(p)) {
221             throw new FileException("File do not exist: " ~ cast(string) p);
222         }
223 
224         payload = p;
225     }
226 
227     this(Exists!T p) {
228         payload = p.payload;
229     }
230 
231     void opAssign(Exists!T p) pure nothrow @nogc {
232         payload = p;
233     }
234 }
235 
236 auto makeExists(T)(T p) {
237     return Exists!T(p);
238 }
239 
240 @("shall always be the absolute path")
241 unittest {
242     import std.algorithm : canFind;
243     import std.path;
244     import unit_threaded;
245 
246     AbsolutePath(FileName("~/foo")).canFind('~').shouldEqual(false);
247     AbsolutePath(FileName("foo")).isAbsolute.shouldEqual(true);
248 }
249 
250 @("shall expand . without any trailing /.")
251 unittest {
252     import std.algorithm : canFind;
253     import unit_threaded;
254 
255     AbsolutePath(FileName(".")).canFind('.').shouldBeFalse;
256     AbsolutePath(FileName("."), DirName(".")).canFind('.').shouldBeFalse;
257 }
258 
259 @("shall be an instantiation of Exists")
260 nothrow unittest {
261     // the file is not expected to exist.
262 
263     try {
264         auto p = makeExists(AbsolutePath(FileName("foo")));
265     } catch (Exception e) {
266     }
267 }