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     bool empty() @safe pure nothrow const @nogc {
30         return payload.length == 0;
31     }
32 
33     bool opEquals(const string s) @safe pure nothrow const @nogc {
34         return payload == s;
35     }
36 
37     bool opEquals(const AbsolutePath s) @safe pure nothrow const @nogc {
38         return payload == s.payload;
39     }
40 }
41 
42 /** The path is guaranteed to be the absolute path.
43  *
44  * The user of the type has to make an explicit judgment when using the
45  * assignment operator. Either a `Path` and then pay the cost of the path
46  * expansion or an absolute which is already assured to be _ok_.
47  * This divides the domain in two, one unchecked and one checked.
48  */
49 struct AbsolutePath {
50     import std.path : buildNormalizedPath, asAbsolutePath, asNormalizedPath;
51     import std.utf : toUTF8;
52 
53     Path payload;
54     alias payload this;
55 
56     invariant {
57         import std.path : isAbsolute;
58 
59         assert(payload.empty || payload.isAbsolute);
60     }
61 
62     this(Path p) {
63         // the second buildNormalizedPath is needed to correctly resolve "."
64         // otherwise it is resolved to /foo/bar/.
65         payload = buildNormalizedPath(expand(p)).asAbsolutePath.asNormalizedPath.toUTF8.Path;
66     }
67 
68     /// Build the normalised path from workdir.
69     this(Path p, Path workdir) {
70         // the second buildNormalizedPath is needed to correctly resolve "."
71         // otherwise it is resolved to /foo/bar/.
72         payload = buildNormalizedPath(expand(workdir), expand(p))
73             .asAbsolutePath.asNormalizedPath.toUTF8.Path;
74     }
75 
76     pure nothrow @nogc void opAssign(AbsolutePath p) {
77         payload = p.payload;
78     }
79 
80     pure nothrow const @nogc string opCast(T : string)() {
81         return payload;
82     }
83 
84     bool opEquals(const string s) @safe pure nothrow const @nogc {
85         return payload == s;
86     }
87 
88     bool opEquals(const Path s) @safe pure nothrow const @nogc {
89         return payload == s.payload;
90     }
91 
92     bool opEquals(const AbsolutePath s) @safe pure nothrow const @nogc {
93         return payload == s.payload;
94     }
95 
96     private static Path expand(Path p) @trusted {
97         import std.path : expandTilde;
98 
99         return p.expandTilde.Path;
100     }
101 }
102 
103 /** During construction checks that the file exists on the filesystem.
104  *
105  * If it doesn't exist it will throw an Exception.
106  */
107 struct Exists(T) {
108     AbsolutePath payload;
109     alias payload this;
110 
111     this(AbsolutePath p) {
112         import std.file : exists, FileException;
113 
114         if (!exists(p)) {
115             throw new FileException("File do not exist: " ~ cast(string) p);
116         }
117 
118         payload = p;
119     }
120 
121     this(Exists!T p) {
122         payload = p.payload;
123     }
124 
125     void opAssign(Exists!T p) pure nothrow @nogc {
126         payload = p;
127     }
128 }
129 
130 auto makeExists(T)(T p) {
131     return Exists!T(p);
132 }
133 
134 @("shall always be the absolute path")
135 unittest {
136     import std.algorithm : canFind;
137     import std.path;
138     import unit_threaded;
139 
140     AbsolutePath(Path("~/foo")).canFind('~').shouldEqual(false);
141     AbsolutePath(Path("foo")).isAbsolute.shouldEqual(true);
142 }
143 
144 @("shall expand . without any trailing /.")
145 unittest {
146     import std.algorithm : canFind;
147     import unit_threaded;
148 
149     AbsolutePath(Path(".")).canFind('.').shouldBeFalse;
150     AbsolutePath(Path("."), Path(".")).canFind('.').shouldBeFalse;
151 }
152 
153 @("shall be an instantiation of Exists")
154 nothrow unittest {
155     // the file is not expected to exist.
156 
157     try {
158         auto p = makeExists(AbsolutePath(Path("foo")));
159     } catch (Exception e) {
160     }
161 }