diff options
author | Akshay <[email protected]> | 2021-10-19 11:28:46 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-10-19 11:28:46 +0100 |
commit | 214e3bc4b675b08ce4df76b1373f4548949e67ee (patch) | |
tree | db6584a360d69bc9759e2b8059a013ba9e8db5e1 | |
parent | 0076b3a37dcca0e11afd05dc98174f646cdb8fa9 (diff) |
fully flesh out CLI
-rw-r--r-- | Cargo.lock | 306 | ||||
-rw-r--r-- | bin/Cargo.toml | 5 | ||||
-rw-r--r-- | bin/src/config.rs | 170 | ||||
-rw-r--r-- | bin/src/err.rs | 28 | ||||
-rw-r--r-- | bin/src/main.rs | 112 | ||||
-rw-r--r-- | bin/src/traits.rs | 83 |
6 files changed, 632 insertions, 72 deletions
@@ -3,10 +3,13 @@ | |||
3 | version = 3 | 3 | version = 3 |
4 | 4 | ||
5 | [[package]] | 5 | [[package]] |
6 | name = "anyhow" | 6 | name = "aho-corasick" |
7 | version = "1.0.44" | 7 | version = "0.7.18" |
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | 8 | source = "registry+https://github.com/rust-lang/crates.io-index" |
9 | checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" | 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" |
10 | dependencies = [ | ||
11 | "memchr", | ||
12 | ] | ||
10 | 13 | ||
11 | [[package]] | 14 | [[package]] |
12 | name = "ariadne" | 15 | name = "ariadne" |
@@ -18,12 +21,38 @@ dependencies = [ | |||
18 | ] | 21 | ] |
19 | 22 | ||
20 | [[package]] | 23 | [[package]] |
24 | name = "atty" | ||
25 | version = "0.2.14" | ||
26 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
27 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" | ||
28 | dependencies = [ | ||
29 | "hermit-abi", | ||
30 | "libc", | ||
31 | "winapi", | ||
32 | ] | ||
33 | |||
34 | [[package]] | ||
21 | name = "autocfg" | 35 | name = "autocfg" |
22 | version = "1.0.1" | 36 | version = "1.0.1" |
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | 37 | source = "registry+https://github.com/rust-lang/crates.io-index" |
24 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | 38 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" |
25 | 39 | ||
26 | [[package]] | 40 | [[package]] |
41 | name = "bitflags" | ||
42 | version = "1.3.2" | ||
43 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
44 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
45 | |||
46 | [[package]] | ||
47 | name = "bstr" | ||
48 | version = "0.2.17" | ||
49 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
50 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" | ||
51 | dependencies = [ | ||
52 | "memchr", | ||
53 | ] | ||
54 | |||
55 | [[package]] | ||
27 | name = "cbitset" | 56 | name = "cbitset" |
28 | version = "0.2.0" | 57 | version = "0.2.0" |
29 | source = "registry+https://github.com/rust-lang/crates.io-index" | 58 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -33,24 +62,114 @@ dependencies = [ | |||
33 | ] | 62 | ] |
34 | 63 | ||
35 | [[package]] | 64 | [[package]] |
65 | name = "cfg-if" | ||
66 | version = "1.0.0" | ||
67 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
68 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||
69 | |||
70 | [[package]] | ||
71 | name = "clap" | ||
72 | version = "3.0.0-beta.4" | ||
73 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
74 | checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" | ||
75 | dependencies = [ | ||
76 | "atty", | ||
77 | "bitflags", | ||
78 | "clap_derive", | ||
79 | "indexmap", | ||
80 | "lazy_static", | ||
81 | "os_str_bytes", | ||
82 | "strsim", | ||
83 | "termcolor", | ||
84 | "textwrap", | ||
85 | "vec_map", | ||
86 | ] | ||
87 | |||
88 | [[package]] | ||
89 | name = "clap_derive" | ||
90 | version = "3.0.0-beta.4" | ||
91 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
92 | checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" | ||
93 | dependencies = [ | ||
94 | "heck", | ||
95 | "proc-macro-error", | ||
96 | "proc-macro2", | ||
97 | "quote", | ||
98 | "syn", | ||
99 | ] | ||
100 | |||
101 | [[package]] | ||
36 | name = "countme" | 102 | name = "countme" |
37 | version = "2.0.4" | 103 | version = "2.0.4" |
38 | source = "registry+https://github.com/rust-lang/crates.io-index" | 104 | source = "registry+https://github.com/rust-lang/crates.io-index" |
39 | checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58" | 105 | checksum = "328b822bdcba4d4e402be8d9adb6eebf269f969f8eadef977a553ff3c4fbcb58" |
40 | 106 | ||
41 | [[package]] | 107 | [[package]] |
108 | name = "fnv" | ||
109 | version = "1.0.7" | ||
110 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
111 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||
112 | |||
113 | [[package]] | ||
114 | name = "globset" | ||
115 | version = "0.4.8" | ||
116 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
117 | checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" | ||
118 | dependencies = [ | ||
119 | "aho-corasick", | ||
120 | "bstr", | ||
121 | "fnv", | ||
122 | "log", | ||
123 | "regex", | ||
124 | ] | ||
125 | |||
126 | [[package]] | ||
42 | name = "hashbrown" | 127 | name = "hashbrown" |
43 | version = "0.9.1" | 128 | version = "0.9.1" |
44 | source = "registry+https://github.com/rust-lang/crates.io-index" | 129 | source = "registry+https://github.com/rust-lang/crates.io-index" |
45 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" | 130 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" |
46 | 131 | ||
47 | [[package]] | 132 | [[package]] |
133 | name = "hashbrown" | ||
134 | version = "0.11.2" | ||
135 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
136 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" | ||
137 | |||
138 | [[package]] | ||
139 | name = "heck" | ||
140 | version = "0.3.3" | ||
141 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
142 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" | ||
143 | dependencies = [ | ||
144 | "unicode-segmentation", | ||
145 | ] | ||
146 | |||
147 | [[package]] | ||
148 | name = "hermit-abi" | ||
149 | version = "0.1.19" | ||
150 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
151 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" | ||
152 | dependencies = [ | ||
153 | "libc", | ||
154 | ] | ||
155 | |||
156 | [[package]] | ||
48 | name = "if_chain" | 157 | name = "if_chain" |
49 | version = "1.0.2" | 158 | version = "1.0.2" |
50 | source = "registry+https://github.com/rust-lang/crates.io-index" | 159 | source = "registry+https://github.com/rust-lang/crates.io-index" |
51 | checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" | 160 | checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" |
52 | 161 | ||
53 | [[package]] | 162 | [[package]] |
163 | name = "indexmap" | ||
164 | version = "1.7.0" | ||
165 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
166 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" | ||
167 | dependencies = [ | ||
168 | "autocfg", | ||
169 | "hashbrown 0.11.2", | ||
170 | ] | ||
171 | |||
172 | [[package]] | ||
54 | name = "lazy_static" | 173 | name = "lazy_static" |
55 | version = "1.4.0" | 174 | version = "1.4.0" |
56 | source = "registry+https://github.com/rust-lang/crates.io-index" | 175 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -68,6 +187,21 @@ dependencies = [ | |||
68 | ] | 187 | ] |
69 | 188 | ||
70 | [[package]] | 189 | [[package]] |
190 | name = "libc" | ||
191 | version = "0.2.103" | ||
192 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
193 | checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" | ||
194 | |||
195 | [[package]] | ||
196 | name = "log" | ||
197 | version = "0.4.14" | ||
198 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
199 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" | ||
200 | dependencies = [ | ||
201 | "cfg-if", | ||
202 | ] | ||
203 | |||
204 | [[package]] | ||
71 | name = "macros" | 205 | name = "macros" |
72 | version = "0.1.0" | 206 | version = "0.1.0" |
73 | dependencies = [ | 207 | dependencies = [ |
@@ -77,6 +211,12 @@ dependencies = [ | |||
77 | ] | 211 | ] |
78 | 212 | ||
79 | [[package]] | 213 | [[package]] |
214 | name = "memchr" | ||
215 | version = "2.4.1" | ||
216 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
217 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" | ||
218 | |||
219 | [[package]] | ||
80 | name = "memoffset" | 220 | name = "memoffset" |
81 | version = "0.6.4" | 221 | version = "0.6.4" |
82 | source = "registry+https://github.com/rust-lang/crates.io-index" | 222 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -95,6 +235,36 @@ dependencies = [ | |||
95 | ] | 235 | ] |
96 | 236 | ||
97 | [[package]] | 237 | [[package]] |
238 | name = "os_str_bytes" | ||
239 | version = "3.1.0" | ||
240 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
241 | checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" | ||
242 | |||
243 | [[package]] | ||
244 | name = "proc-macro-error" | ||
245 | version = "1.0.4" | ||
246 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
247 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | ||
248 | dependencies = [ | ||
249 | "proc-macro-error-attr", | ||
250 | "proc-macro2", | ||
251 | "quote", | ||
252 | "syn", | ||
253 | "version_check", | ||
254 | ] | ||
255 | |||
256 | [[package]] | ||
257 | name = "proc-macro-error-attr" | ||
258 | version = "1.0.4" | ||
259 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
260 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | ||
261 | dependencies = [ | ||
262 | "proc-macro2", | ||
263 | "quote", | ||
264 | "version_check", | ||
265 | ] | ||
266 | |||
267 | [[package]] | ||
98 | name = "proc-macro2" | 268 | name = "proc-macro2" |
99 | version = "1.0.29" | 269 | version = "1.0.29" |
100 | source = "registry+https://github.com/rust-lang/crates.io-index" | 270 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -113,6 +283,23 @@ dependencies = [ | |||
113 | ] | 283 | ] |
114 | 284 | ||
115 | [[package]] | 285 | [[package]] |
286 | name = "regex" | ||
287 | version = "1.5.4" | ||
288 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
289 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" | ||
290 | dependencies = [ | ||
291 | "aho-corasick", | ||
292 | "memchr", | ||
293 | "regex-syntax", | ||
294 | ] | ||
295 | |||
296 | [[package]] | ||
297 | name = "regex-syntax" | ||
298 | version = "0.6.25" | ||
299 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
300 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" | ||
301 | |||
302 | [[package]] | ||
116 | name = "rnix" | 303 | name = "rnix" |
117 | version = "0.9.0" | 304 | version = "0.9.0" |
118 | source = "registry+https://github.com/rust-lang/crates.io-index" | 305 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -130,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
130 | checksum = "a1b36e449f3702f3b0c821411db1cbdf30fb451726a9456dce5dabcd44420043" | 317 | checksum = "a1b36e449f3702f3b0c821411db1cbdf30fb451726a9456dce5dabcd44420043" |
131 | dependencies = [ | 318 | dependencies = [ |
132 | "countme", | 319 | "countme", |
133 | "hashbrown", | 320 | "hashbrown 0.9.1", |
134 | "memoffset", | 321 | "memoffset", |
135 | "rustc-hash", | 322 | "rustc-hash", |
136 | "text-size", | 323 | "text-size", |
@@ -152,13 +339,22 @@ checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0" | |||
152 | name = "statix" | 339 | name = "statix" |
153 | version = "0.1.0" | 340 | version = "0.1.0" |
154 | dependencies = [ | 341 | dependencies = [ |
155 | "anyhow", | ||
156 | "ariadne", | 342 | "ariadne", |
343 | "clap", | ||
344 | "globset", | ||
157 | "lib", | 345 | "lib", |
158 | "rnix", | 346 | "rnix", |
347 | "thiserror", | ||
348 | "vfs", | ||
159 | ] | 349 | ] |
160 | 350 | ||
161 | [[package]] | 351 | [[package]] |
352 | name = "strsim" | ||
353 | version = "0.10.0" | ||
354 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
355 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||
356 | |||
357 | [[package]] | ||
162 | name = "syn" | 358 | name = "syn" |
163 | version = "1.0.76" | 359 | version = "1.0.76" |
164 | source = "registry+https://github.com/rust-lang/crates.io-index" | 360 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -170,18 +366,118 @@ dependencies = [ | |||
170 | ] | 366 | ] |
171 | 367 | ||
172 | [[package]] | 368 | [[package]] |
369 | name = "termcolor" | ||
370 | version = "1.1.2" | ||
371 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
372 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" | ||
373 | dependencies = [ | ||
374 | "winapi-util", | ||
375 | ] | ||
376 | |||
377 | [[package]] | ||
173 | name = "text-size" | 378 | name = "text-size" |
174 | version = "1.1.0" | 379 | version = "1.1.0" |
175 | source = "registry+https://github.com/rust-lang/crates.io-index" | 380 | source = "registry+https://github.com/rust-lang/crates.io-index" |
176 | checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" | 381 | checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" |
177 | 382 | ||
178 | [[package]] | 383 | [[package]] |
384 | name = "textwrap" | ||
385 | version = "0.14.2" | ||
386 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
387 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" | ||
388 | dependencies = [ | ||
389 | "unicode-width", | ||
390 | ] | ||
391 | |||
392 | [[package]] | ||
393 | name = "thiserror" | ||
394 | version = "1.0.30" | ||
395 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
396 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" | ||
397 | dependencies = [ | ||
398 | "thiserror-impl", | ||
399 | ] | ||
400 | |||
401 | [[package]] | ||
402 | name = "thiserror-impl" | ||
403 | version = "1.0.30" | ||
404 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
405 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" | ||
406 | dependencies = [ | ||
407 | "proc-macro2", | ||
408 | "quote", | ||
409 | "syn", | ||
410 | ] | ||
411 | |||
412 | [[package]] | ||
413 | name = "unicode-segmentation" | ||
414 | version = "1.8.0" | ||
415 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
416 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" | ||
417 | |||
418 | [[package]] | ||
419 | name = "unicode-width" | ||
420 | version = "0.1.9" | ||
421 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
422 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" | ||
423 | |||
424 | [[package]] | ||
179 | name = "unicode-xid" | 425 | name = "unicode-xid" |
180 | version = "0.2.2" | 426 | version = "0.2.2" |
181 | source = "registry+https://github.com/rust-lang/crates.io-index" | 427 | source = "registry+https://github.com/rust-lang/crates.io-index" |
182 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" | 428 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" |
183 | 429 | ||
184 | [[package]] | 430 | [[package]] |
431 | name = "vec_map" | ||
432 | version = "0.8.2" | ||
433 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
434 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" | ||
435 | |||
436 | [[package]] | ||
437 | name = "version_check" | ||
438 | version = "0.9.3" | ||
439 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
440 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" | ||
441 | |||
442 | [[package]] | ||
443 | name = "vfs" | ||
444 | version = "0.1.0" | ||
445 | dependencies = [ | ||
446 | "indexmap", | ||
447 | ] | ||
448 | |||
449 | [[package]] | ||
450 | name = "winapi" | ||
451 | version = "0.3.9" | ||
452 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
453 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
454 | dependencies = [ | ||
455 | "winapi-i686-pc-windows-gnu", | ||
456 | "winapi-x86_64-pc-windows-gnu", | ||
457 | ] | ||
458 | |||
459 | [[package]] | ||
460 | name = "winapi-i686-pc-windows-gnu" | ||
461 | version = "0.4.0" | ||
462 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
463 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
464 | |||
465 | [[package]] | ||
466 | name = "winapi-util" | ||
467 | version = "0.1.5" | ||
468 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
469 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" | ||
470 | dependencies = [ | ||
471 | "winapi", | ||
472 | ] | ||
473 | |||
474 | [[package]] | ||
475 | name = "winapi-x86_64-pc-windows-gnu" | ||
476 | version = "0.4.0" | ||
477 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
478 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
479 | |||
480 | [[package]] | ||
185 | name = "yansi" | 481 | name = "yansi" |
186 | version = "0.5.0" | 482 | version = "0.5.0" |
187 | source = "registry+https://github.com/rust-lang/crates.io-index" | 483 | source = "registry+https://github.com/rust-lang/crates.io-index" |
diff --git a/bin/Cargo.toml b/bin/Cargo.toml index 9452c2b..0d6f970 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml | |||
@@ -8,5 +8,8 @@ edition = "2018" | |||
8 | [dependencies] | 8 | [dependencies] |
9 | lib = { path = "../lib" } | 9 | lib = { path = "../lib" } |
10 | ariadne = "0.1.3" | 10 | ariadne = "0.1.3" |
11 | anyhow = "1.0" | ||
12 | rnix = "0.9.0" | 11 | rnix = "0.9.0" |
12 | clap = "3.0.0-beta.4" | ||
13 | globset = "0.4.8" | ||
14 | thiserror = "1.0.30" | ||
15 | vfs = { path = "../vfs" } | ||
diff --git a/bin/src/config.rs b/bin/src/config.rs new file mode 100644 index 0000000..f2cf29d --- /dev/null +++ b/bin/src/config.rs | |||
@@ -0,0 +1,170 @@ | |||
1 | use std::{default::Default, fs, path::PathBuf, str::FromStr}; | ||
2 | |||
3 | use clap::Clap; | ||
4 | use globset::{GlobBuilder, GlobSetBuilder}; | ||
5 | use vfs::ReadOnlyVfs; | ||
6 | |||
7 | use crate::err::ConfigErr; | ||
8 | |||
9 | /// Static analysis and linting for the nix programming language | ||
10 | #[derive(Clap, Debug)] | ||
11 | #[clap(version = "0.1.0", author = "Akshay <[email protected]>")] | ||
12 | pub struct Opts { | ||
13 | /// File or directory to run statix on | ||
14 | #[clap(default_value = ".")] | ||
15 | target: String, | ||
16 | |||
17 | // /// Path to statix config | ||
18 | // #[clap(short, long, default_value = ".statix.toml")] | ||
19 | // config: String, | ||
20 | /// Regex of file patterns to not lint | ||
21 | #[clap(short, long)] | ||
22 | ignore: Vec<String>, | ||
23 | |||
24 | /// Output format. Supported values: json, errfmt | ||
25 | #[clap(short = 'o', long)] | ||
26 | format: Option<OutFormat>, | ||
27 | |||
28 | #[clap(subcommand)] | ||
29 | pub subcmd: Option<SubCommand>, | ||
30 | } | ||
31 | |||
32 | #[derive(Clap, Debug)] | ||
33 | #[clap(version = "0.1.0", author = "Akshay <[email protected]>")] | ||
34 | pub enum SubCommand { | ||
35 | /// Find and fix issues raised by statix | ||
36 | Fix(Fix), | ||
37 | } | ||
38 | |||
39 | #[derive(Clap, Debug)] | ||
40 | pub struct Fix { | ||
41 | /// Do not write to files, display a diff instead | ||
42 | #[clap(short = 'd', long = "dry-run")] | ||
43 | diff_only: bool, | ||
44 | } | ||
45 | |||
46 | #[derive(Debug, Copy, Clone)] | ||
47 | pub enum OutFormat { | ||
48 | Json, | ||
49 | Errfmt, | ||
50 | StdErr, | ||
51 | } | ||
52 | |||
53 | impl Default for OutFormat { | ||
54 | fn default() -> Self { | ||
55 | OutFormat::StdErr | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl FromStr for OutFormat { | ||
60 | type Err = &'static str; | ||
61 | |||
62 | fn from_str(value: &str) -> Result<Self, Self::Err> { | ||
63 | match value.to_ascii_lowercase().as_str() { | ||
64 | "json" => Ok(Self::Json), | ||
65 | "errfmt" => Ok(Self::Errfmt), | ||
66 | "stderr" => Ok(Self::StdErr), | ||
67 | _ => Err("unknown output format, try: json, errfmt"), | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[derive(Debug)] | ||
73 | pub struct LintConfig { | ||
74 | pub files: Vec<PathBuf>, | ||
75 | pub format: OutFormat, | ||
76 | } | ||
77 | |||
78 | impl LintConfig { | ||
79 | pub fn from_opts(opts: Opts) -> Result<Self, ConfigErr> { | ||
80 | let ignores = { | ||
81 | let mut set = GlobSetBuilder::new(); | ||
82 | for pattern in opts.ignore { | ||
83 | let glob = GlobBuilder::new(&pattern).build().map_err(|err| { | ||
84 | ConfigErr::InvalidGlob(err.glob().map(|i| i.to_owned()), err.kind().clone()) | ||
85 | })?; | ||
86 | set.add(glob); | ||
87 | } | ||
88 | set.build().map_err(|err| { | ||
89 | ConfigErr::InvalidGlob(err.glob().map(|i| i.to_owned()), err.kind().clone()) | ||
90 | }) | ||
91 | }?; | ||
92 | |||
93 | let walker = dirs::Walker::new(opts.target).map_err(ConfigErr::InvalidPath)?; | ||
94 | |||
95 | let files = walker | ||
96 | .filter(|path| matches!(path.extension(), Some(e) if e == "nix")) | ||
97 | .filter(|path| !ignores.is_match(path)) | ||
98 | .collect(); | ||
99 | Ok(Self { | ||
100 | files, | ||
101 | format: opts.format.unwrap_or_default(), | ||
102 | }) | ||
103 | } | ||
104 | |||
105 | pub fn vfs(&self) -> Result<ReadOnlyVfs, ConfigErr> { | ||
106 | let mut vfs = ReadOnlyVfs::default(); | ||
107 | for file in self.files.iter() { | ||
108 | let _id = vfs.alloc_file_id(&file); | ||
109 | let data = fs::read_to_string(&file).map_err(ConfigErr::InvalidPath)?; | ||
110 | vfs.set_file_contents(&file, data.as_bytes()); | ||
111 | } | ||
112 | Ok(vfs) | ||
113 | } | ||
114 | } | ||
115 | |||
116 | mod dirs { | ||
117 | use std::{ | ||
118 | fs, | ||
119 | io::{self, Error, ErrorKind}, | ||
120 | path::{Path, PathBuf}, | ||
121 | }; | ||
122 | |||
123 | #[derive(Default, Debug)] | ||
124 | pub struct Walker { | ||
125 | dirs: Vec<PathBuf>, | ||
126 | files: Vec<PathBuf>, | ||
127 | } | ||
128 | |||
129 | impl Walker { | ||
130 | pub fn new<P: AsRef<Path>>(target: P) -> io::Result<Self> { | ||
131 | let target = target.as_ref().to_path_buf(); | ||
132 | if !target.exists() { | ||
133 | Err(Error::new( | ||
134 | ErrorKind::NotFound, | ||
135 | format!("file not found: {}", target.display()), | ||
136 | )) | ||
137 | } else if target.is_dir() { | ||
138 | Ok(Self { | ||
139 | dirs: vec![target], | ||
140 | ..Default::default() | ||
141 | }) | ||
142 | } else { | ||
143 | Ok(Self { | ||
144 | files: vec![target], | ||
145 | ..Default::default() | ||
146 | }) | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | impl Iterator for Walker { | ||
152 | type Item = PathBuf; | ||
153 | fn next(&mut self) -> Option<Self::Item> { | ||
154 | if let Some(dir) = self.dirs.pop() { | ||
155 | if dir.is_dir() { | ||
156 | for entry in fs::read_dir(dir).ok()? { | ||
157 | let entry = entry.ok()?; | ||
158 | let path = entry.path(); | ||
159 | if path.is_dir() { | ||
160 | self.dirs.push(path); | ||
161 | } else if path.is_file() { | ||
162 | self.files.push(path); | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | self.files.pop() | ||
168 | } | ||
169 | } | ||
170 | } | ||
diff --git a/bin/src/err.rs b/bin/src/err.rs new file mode 100644 index 0000000..b3a79c2 --- /dev/null +++ b/bin/src/err.rs | |||
@@ -0,0 +1,28 @@ | |||
1 | use std::{io, path::PathBuf}; | ||
2 | |||
3 | use globset::ErrorKind; | ||
4 | use rnix::parser::ParseError; | ||
5 | use thiserror::Error; | ||
6 | |||
7 | #[derive(Error, Debug)] | ||
8 | pub enum ConfigErr { | ||
9 | #[error("error parsing glob `{0:?}`: {1}")] | ||
10 | InvalidGlob(Option<String>, ErrorKind), | ||
11 | |||
12 | #[error("path error: {0}")] | ||
13 | InvalidPath(#[from] io::Error), | ||
14 | } | ||
15 | |||
16 | #[derive(Error, Debug)] | ||
17 | pub enum LintErr { | ||
18 | #[error("[{0}] syntax error: {1}")] | ||
19 | Parse(PathBuf, ParseError), | ||
20 | } | ||
21 | |||
22 | #[derive(Error, Debug)] | ||
23 | pub enum StatixErr { | ||
24 | #[error("linter error: {0}")] | ||
25 | Lint(#[from] LintErr), | ||
26 | #[error("config error: {0}")] | ||
27 | Config(#[from] ConfigErr), | ||
28 | } | ||
diff --git a/bin/src/main.rs b/bin/src/main.rs index ab99aee..b26151d 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs | |||
@@ -1,20 +1,28 @@ | |||
1 | use std::{ | 1 | #![feature(path_try_exists)] |
2 | env, fs, | 2 | |
3 | path::{Path, PathBuf}, | 3 | mod config; |
4 | }; | 4 | mod err; |
5 | mod traits; | ||
5 | 6 | ||
6 | use anyhow::{Context, Result}; | 7 | use std::io; |
7 | use ariadne::{ | 8 | |
8 | CharSet, Color, Config as CliConfig, Label, LabelAttach, Report as CliReport, | 9 | use crate::{ |
9 | ReportKind as CliReportKind, Source, | 10 | err::{LintErr, StatixErr}, |
11 | traits::{LintResult, WriteDiagnostic}, | ||
10 | }; | 12 | }; |
11 | use lib::{Report, LINTS}; | ||
12 | use rnix::{TextRange, WalkEvent}; | ||
13 | 13 | ||
14 | fn analyze(source: &str) -> Result<Vec<Report>> { | 14 | use clap::Clap; |
15 | let parsed = rnix::parse(source).as_result()?; | 15 | use config::{LintConfig, Opts, SubCommand}; |
16 | use lib::LINTS; | ||
17 | use rnix::WalkEvent; | ||
18 | use vfs::VfsEntry; | ||
16 | 19 | ||
17 | Ok(parsed | 20 | fn analyze<'ρ>(vfs_entry: VfsEntry<'ρ>) -> Result<LintResult, LintErr> { |
21 | let source = vfs_entry.contents; | ||
22 | let parsed = rnix::parse(source) | ||
23 | .as_result() | ||
24 | .map_err(|e| LintErr::Parse(vfs_entry.file_path.to_path_buf(), e))?; | ||
25 | let reports = parsed | ||
18 | .node() | 26 | .node() |
19 | .preorder_with_tokens() | 27 | .preorder_with_tokens() |
20 | .filter_map(|event| match event { | 28 | .filter_map(|event| match event { |
@@ -27,61 +35,33 @@ fn analyze(source: &str) -> Result<Vec<Report>> { | |||
27 | _ => None, | 35 | _ => None, |
28 | }) | 36 | }) |
29 | .flatten() | 37 | .flatten() |
30 | .collect()) | 38 | .collect(); |
39 | Ok(LintResult { | ||
40 | file_id: vfs_entry.file_id, | ||
41 | reports, | ||
42 | }) | ||
31 | } | 43 | } |
32 | 44 | ||
33 | fn print_report(report: Report, file_src: &str, file_path: &Path) -> Result<()> { | 45 | fn _main() -> Result<(), StatixErr> { |
34 | let range = |at: TextRange| at.start().into()..at.end().into(); | ||
35 | let src_id = file_path.to_str().unwrap_or("<unknown>"); | ||
36 | let offset = report | ||
37 | .diagnostics | ||
38 | .iter() | ||
39 | .map(|d| d.at.start().into()) | ||
40 | .min() | ||
41 | .unwrap_or(0usize); | ||
42 | report | ||
43 | .diagnostics | ||
44 | .iter() | ||
45 | .fold( | ||
46 | CliReport::build(CliReportKind::Warning, src_id, offset) | ||
47 | .with_config( | ||
48 | CliConfig::default() | ||
49 | .with_cross_gap(true) | ||
50 | .with_multiline_arrows(false) | ||
51 | .with_label_attach(LabelAttach::Middle) | ||
52 | .with_char_set(CharSet::Unicode), | ||
53 | ) | ||
54 | .with_message(report.note) | ||
55 | .with_code(report.code), | ||
56 | |cli_report, diagnostic| { | ||
57 | cli_report.with_label( | ||
58 | Label::new((src_id, range(diagnostic.at))) | ||
59 | .with_message(&diagnostic.message) | ||
60 | .with_color(Color::Magenta), | ||
61 | ) | ||
62 | }, | ||
63 | ) | ||
64 | .finish() | ||
65 | .eprint((src_id, Source::from(file_src))) | ||
66 | .context("failed to print report to stdout") | ||
67 | } | ||
68 | |||
69 | fn _main() -> Result<()> { | ||
70 | // TODO: accept cli args, construct a CLI config with a list of files to analyze | 46 | // TODO: accept cli args, construct a CLI config with a list of files to analyze |
71 | let args = env::args(); | 47 | let opts = Opts::parse(); |
72 | for (file_src, file_path, reports) in args | 48 | match opts.subcmd { |
73 | .skip(1) | 49 | Some(SubCommand::Fix(_)) => {} |
74 | .map(|s| PathBuf::from(&s)) | 50 | None => { |
75 | .filter(|p| p.is_file()) | 51 | let lint_config = LintConfig::from_opts(opts)?; |
76 | .filter_map(|path| { | 52 | let vfs = lint_config.vfs()?; |
77 | let s = fs::read_to_string(&path).ok()?; | 53 | let (reports, errors): (Vec<_>, Vec<_>) = |
78 | analyze(&s) | 54 | vfs.iter().map(analyze).partition(Result::is_ok); |
79 | .map(|analysis_result| (s, path, analysis_result)) | 55 | let lint_results: Vec<_> = reports.into_iter().map(Result::unwrap).collect(); |
80 | .ok() | 56 | let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect(); |
81 | }) | 57 | |
82 | { | 58 | let mut stderr = io::stderr(); |
83 | for r in reports { | 59 | lint_results.into_iter().for_each(|r| { |
84 | print_report(r, &file_src, &file_path)? | 60 | stderr.write(&r, &vfs).unwrap(); |
61 | }); | ||
62 | errors.into_iter().for_each(|e| { | ||
63 | eprintln!("{}", e); | ||
64 | }); | ||
85 | } | 65 | } |
86 | } | 66 | } |
87 | Ok(()) | 67 | Ok(()) |
@@ -90,6 +70,6 @@ fn _main() -> Result<()> { | |||
90 | fn main() { | 70 | fn main() { |
91 | match _main() { | 71 | match _main() { |
92 | Err(e) => eprintln!("{}", e), | 72 | Err(e) => eprintln!("{}", e), |
93 | _ => {} | 73 | _ => (), |
94 | } | 74 | } |
95 | } | 75 | } |
diff --git a/bin/src/traits.rs b/bin/src/traits.rs new file mode 100644 index 0000000..1807ad0 --- /dev/null +++ b/bin/src/traits.rs | |||
@@ -0,0 +1,83 @@ | |||
1 | use std::{ | ||
2 | io::{self, Write}, | ||
3 | str, | ||
4 | }; | ||
5 | |||
6 | use ariadne::{ | ||
7 | CharSet, Color, Config as CliConfig, Label, LabelAttach, Report as CliReport, | ||
8 | ReportKind as CliReportKind, Source, Fmt | ||
9 | }; | ||
10 | use lib::Report; | ||
11 | use rnix::TextRange; | ||
12 | use vfs::{FileId, ReadOnlyVfs}; | ||
13 | |||
14 | #[derive(Debug)] | ||
15 | pub struct LintResult { | ||
16 | pub file_id: FileId, | ||
17 | pub reports: Vec<Report>, | ||
18 | } | ||
19 | |||
20 | pub trait WriteDiagnostic { | ||
21 | fn write(&mut self, report: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()>; | ||
22 | } | ||
23 | |||
24 | impl<T> WriteDiagnostic for T | ||
25 | where | ||
26 | T: Write, | ||
27 | { | ||
28 | fn write(&mut self, lint_result: &LintResult, vfs: &ReadOnlyVfs) -> io::Result<()> { | ||
29 | let file_id = lint_result.file_id; | ||
30 | let src = str::from_utf8(vfs.get(file_id)).unwrap(); | ||
31 | let path = vfs.file_path(file_id); | ||
32 | let range = |at: TextRange| at.start().into()..at.end().into(); | ||
33 | let src_id = path.to_str().unwrap_or("<unknown>"); | ||
34 | for report in lint_result.reports.iter() { | ||
35 | let offset = report | ||
36 | .diagnostics | ||
37 | .iter() | ||
38 | .map(|d| d.at.start().into()) | ||
39 | .min() | ||
40 | .unwrap_or(0usize); | ||
41 | report | ||
42 | .diagnostics | ||
43 | .iter() | ||
44 | .fold( | ||
45 | CliReport::build(CliReportKind::Warning, src_id, offset) | ||
46 | .with_config( | ||
47 | CliConfig::default() | ||
48 | .with_cross_gap(true) | ||
49 | .with_multiline_arrows(false) | ||
50 | .with_label_attach(LabelAttach::Middle) | ||
51 | .with_char_set(CharSet::Unicode), | ||
52 | ) | ||
53 | .with_message(report.note) | ||
54 | .with_code(report.code), | ||
55 | |cli_report, diagnostic| { | ||
56 | cli_report.with_label( | ||
57 | Label::new((src_id, range(diagnostic.at))) | ||
58 | .with_message(&colorize(&diagnostic.message)) | ||
59 | .with_color(Color::Magenta), | ||
60 | ) | ||
61 | }, | ||
62 | ) | ||
63 | .finish() | ||
64 | .write((src_id, Source::from(src)), &mut *self)?; | ||
65 | } | ||
66 | Ok(()) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // everything within backticks is colorized, backticks are removed | ||
71 | fn colorize(message: &str) -> String { | ||
72 | message.split('`') | ||
73 | .enumerate() | ||
74 | .map(|(idx, part)| { | ||
75 | if idx % 2 == 1 { | ||
76 | part.fg(Color::Cyan).to_string() | ||
77 | } else { | ||
78 | part.to_string() | ||
79 | } | ||
80 | }) | ||
81 | .collect::<Vec<_>>() | ||
82 | .join("") | ||
83 | } | ||