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