##// END OF EJS Templates
rhg: Fall back to Python if unsupported extensions are enabled...
Simon Sapin -
r47467:1bac7764 default
parent child Browse files
Show More
@@ -1,1084 +1,1084 b''
1 1 # This file is automatically @generated by Cargo.
2 2 # It is not intended for manual editing.
3 3 [[package]]
4 4 name = "adler"
5 5 version = "0.2.3"
6 6 source = "registry+https://github.com/rust-lang/crates.io-index"
7 7 checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
8 8
9 9 [[package]]
10 10 name = "aho-corasick"
11 11 version = "0.7.15"
12 12 source = "registry+https://github.com/rust-lang/crates.io-index"
13 13 checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
14 14 dependencies = [
15 15 "memchr",
16 16 ]
17 17
18 18 [[package]]
19 19 name = "ansi_term"
20 20 version = "0.11.0"
21 21 source = "registry+https://github.com/rust-lang/crates.io-index"
22 22 checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
23 23 dependencies = [
24 24 "winapi",
25 25 ]
26 26
27 27 [[package]]
28 28 name = "atty"
29 29 version = "0.2.14"
30 30 source = "registry+https://github.com/rust-lang/crates.io-index"
31 31 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
32 32 dependencies = [
33 33 "hermit-abi",
34 34 "libc",
35 35 "winapi",
36 36 ]
37 37
38 38 [[package]]
39 39 name = "autocfg"
40 40 version = "1.0.1"
41 41 source = "registry+https://github.com/rust-lang/crates.io-index"
42 42 checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
43 43
44 44 [[package]]
45 45 name = "bitflags"
46 46 version = "1.2.1"
47 47 source = "registry+https://github.com/rust-lang/crates.io-index"
48 48 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
49 49
50 50 [[package]]
51 51 name = "bitmaps"
52 52 version = "2.1.0"
53 53 source = "registry+https://github.com/rust-lang/crates.io-index"
54 54 checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
55 55 dependencies = [
56 56 "typenum",
57 57 ]
58 58
59 59 [[package]]
60 60 name = "byteorder"
61 61 version = "1.3.4"
62 62 source = "registry+https://github.com/rust-lang/crates.io-index"
63 63 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
64 64
65 65 [[package]]
66 66 name = "bytes-cast"
67 67 version = "0.1.0"
68 68 source = "registry+https://github.com/rust-lang/crates.io-index"
69 69 checksum = "3196ba300c7bc9282a4331e878496cb3e9603a898a8f1446601317163e16ca52"
70 70 dependencies = [
71 71 "bytes-cast-derive",
72 72 ]
73 73
74 74 [[package]]
75 75 name = "bytes-cast-derive"
76 76 version = "0.1.0"
77 77 source = "registry+https://github.com/rust-lang/crates.io-index"
78 78 checksum = "cb936af9de38476664d6b58e529aff30d482e4ce1c5e150293d00730b0d81fdb"
79 79 dependencies = [
80 80 "proc-macro2",
81 81 "quote",
82 82 "syn",
83 83 ]
84 84
85 85 [[package]]
86 86 name = "cc"
87 87 version = "1.0.66"
88 88 source = "registry+https://github.com/rust-lang/crates.io-index"
89 89 checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
90 90 dependencies = [
91 91 "jobserver",
92 92 ]
93 93
94 94 [[package]]
95 95 name = "cfg-if"
96 96 version = "0.1.10"
97 97 source = "registry+https://github.com/rust-lang/crates.io-index"
98 98 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
99 99
100 100 [[package]]
101 101 name = "cfg-if"
102 102 version = "1.0.0"
103 103 source = "registry+https://github.com/rust-lang/crates.io-index"
104 104 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
105 105
106 106 [[package]]
107 107 name = "chrono"
108 108 version = "0.4.19"
109 109 source = "registry+https://github.com/rust-lang/crates.io-index"
110 110 checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
111 111 dependencies = [
112 112 "libc",
113 113 "num-integer",
114 114 "num-traits",
115 115 "time",
116 116 "winapi",
117 117 ]
118 118
119 119 [[package]]
120 120 name = "clap"
121 121 version = "2.33.3"
122 122 source = "registry+https://github.com/rust-lang/crates.io-index"
123 123 checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
124 124 dependencies = [
125 125 "ansi_term",
126 126 "atty",
127 127 "bitflags",
128 128 "strsim",
129 129 "textwrap",
130 130 "unicode-width",
131 131 "vec_map",
132 132 ]
133 133
134 134 [[package]]
135 135 name = "const_fn"
136 136 version = "0.4.4"
137 137 source = "registry+https://github.com/rust-lang/crates.io-index"
138 138 checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
139 139
140 140 [[package]]
141 141 name = "cpython"
142 142 version = "0.4.1"
143 143 source = "registry+https://github.com/rust-lang/crates.io-index"
144 144 checksum = "bfaf3847ab963e40c4f6dd8d6be279bdf74007ae2413786a0dcbb28c52139a95"
145 145 dependencies = [
146 146 "libc",
147 147 "num-traits",
148 148 "python27-sys",
149 149 "python3-sys",
150 150 ]
151 151
152 152 [[package]]
153 153 name = "crc32fast"
154 154 version = "1.2.1"
155 155 source = "registry+https://github.com/rust-lang/crates.io-index"
156 156 checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
157 157 dependencies = [
158 158 "cfg-if 1.0.0",
159 159 ]
160 160
161 161 [[package]]
162 162 name = "crossbeam-channel"
163 163 version = "0.4.4"
164 164 source = "registry+https://github.com/rust-lang/crates.io-index"
165 165 checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
166 166 dependencies = [
167 167 "crossbeam-utils 0.7.2",
168 168 "maybe-uninit",
169 169 ]
170 170
171 171 [[package]]
172 172 name = "crossbeam-channel"
173 173 version = "0.5.0"
174 174 source = "registry+https://github.com/rust-lang/crates.io-index"
175 175 checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
176 176 dependencies = [
177 177 "cfg-if 1.0.0",
178 178 "crossbeam-utils 0.8.1",
179 179 ]
180 180
181 181 [[package]]
182 182 name = "crossbeam-deque"
183 183 version = "0.8.0"
184 184 source = "registry+https://github.com/rust-lang/crates.io-index"
185 185 checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
186 186 dependencies = [
187 187 "cfg-if 1.0.0",
188 188 "crossbeam-epoch",
189 189 "crossbeam-utils 0.8.1",
190 190 ]
191 191
192 192 [[package]]
193 193 name = "crossbeam-epoch"
194 194 version = "0.9.1"
195 195 source = "registry+https://github.com/rust-lang/crates.io-index"
196 196 checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
197 197 dependencies = [
198 198 "cfg-if 1.0.0",
199 199 "const_fn",
200 200 "crossbeam-utils 0.8.1",
201 201 "lazy_static",
202 202 "memoffset",
203 203 "scopeguard",
204 204 ]
205 205
206 206 [[package]]
207 207 name = "crossbeam-utils"
208 208 version = "0.7.2"
209 209 source = "registry+https://github.com/rust-lang/crates.io-index"
210 210 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
211 211 dependencies = [
212 212 "autocfg",
213 213 "cfg-if 0.1.10",
214 214 "lazy_static",
215 215 ]
216 216
217 217 [[package]]
218 218 name = "crossbeam-utils"
219 219 version = "0.8.1"
220 220 source = "registry+https://github.com/rust-lang/crates.io-index"
221 221 checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
222 222 dependencies = [
223 223 "autocfg",
224 224 "cfg-if 1.0.0",
225 225 "lazy_static",
226 226 ]
227 227
228 228 [[package]]
229 229 name = "ctor"
230 230 version = "0.1.16"
231 231 source = "registry+https://github.com/rust-lang/crates.io-index"
232 232 checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484"
233 233 dependencies = [
234 234 "quote",
235 235 "syn",
236 236 ]
237 237
238 238 [[package]]
239 239 name = "derive_more"
240 240 version = "0.99.11"
241 241 source = "registry+https://github.com/rust-lang/crates.io-index"
242 242 checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
243 243 dependencies = [
244 244 "proc-macro2",
245 245 "quote",
246 246 "syn",
247 247 ]
248 248
249 249 [[package]]
250 250 name = "difference"
251 251 version = "2.0.0"
252 252 source = "registry+https://github.com/rust-lang/crates.io-index"
253 253 checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
254 254
255 255 [[package]]
256 256 name = "either"
257 257 version = "1.6.1"
258 258 source = "registry+https://github.com/rust-lang/crates.io-index"
259 259 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
260 260
261 261 [[package]]
262 262 name = "env_logger"
263 263 version = "0.7.1"
264 264 source = "registry+https://github.com/rust-lang/crates.io-index"
265 265 checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
266 266 dependencies = [
267 267 "atty",
268 268 "humantime",
269 269 "log",
270 270 "regex",
271 271 "termcolor",
272 272 ]
273 273
274 274 [[package]]
275 275 name = "flate2"
276 276 version = "1.0.19"
277 277 source = "registry+https://github.com/rust-lang/crates.io-index"
278 278 checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
279 279 dependencies = [
280 280 "cfg-if 1.0.0",
281 281 "crc32fast",
282 282 "libc",
283 283 "libz-sys",
284 284 "miniz_oxide",
285 285 ]
286 286
287 287 [[package]]
288 288 name = "format-bytes"
289 version = "0.2.0"
289 version = "0.2.1"
290 290 source = "registry+https://github.com/rust-lang/crates.io-index"
291 checksum = "cc35f5e45d6b31053cea13078ffc6fa52fa8617aa54b7ac2011720d9c009e04f"
291 checksum = "8030ff4b04f0ca1c612d6fe49f2fc18caf56fb01497cb370b41cfd36d89b3b06"
292 292 dependencies = [
293 293 "format-bytes-macros",
294 294 "proc-macro-hack",
295 295 ]
296 296
297 297 [[package]]
298 298 name = "format-bytes-macros"
299 299 version = "0.3.0"
300 300 source = "registry+https://github.com/rust-lang/crates.io-index"
301 301 checksum = "b05089e341a0460449e2210c3bf7b61597860b07f0deae58da38dbed0a4c6b6d"
302 302 dependencies = [
303 303 "proc-macro-hack",
304 304 "proc-macro2",
305 305 "quote",
306 306 "syn",
307 307 ]
308 308
309 309 [[package]]
310 310 name = "fuchsia-cprng"
311 311 version = "0.1.1"
312 312 source = "registry+https://github.com/rust-lang/crates.io-index"
313 313 checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
314 314
315 315 [[package]]
316 316 name = "gcc"
317 317 version = "0.3.55"
318 318 source = "registry+https://github.com/rust-lang/crates.io-index"
319 319 checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
320 320
321 321 [[package]]
322 322 name = "getrandom"
323 323 version = "0.1.15"
324 324 source = "registry+https://github.com/rust-lang/crates.io-index"
325 325 checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
326 326 dependencies = [
327 327 "cfg-if 0.1.10",
328 328 "libc",
329 329 "wasi 0.9.0+wasi-snapshot-preview1",
330 330 ]
331 331
332 332 [[package]]
333 333 name = "glob"
334 334 version = "0.3.0"
335 335 source = "registry+https://github.com/rust-lang/crates.io-index"
336 336 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
337 337
338 338 [[package]]
339 339 name = "hermit-abi"
340 340 version = "0.1.17"
341 341 source = "registry+https://github.com/rust-lang/crates.io-index"
342 342 checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
343 343 dependencies = [
344 344 "libc",
345 345 ]
346 346
347 347 [[package]]
348 348 name = "hg-core"
349 349 version = "0.1.0"
350 350 dependencies = [
351 351 "byteorder",
352 352 "bytes-cast",
353 353 "clap",
354 354 "crossbeam-channel 0.4.4",
355 355 "derive_more",
356 356 "flate2",
357 357 "format-bytes",
358 358 "home",
359 359 "im-rc",
360 360 "lazy_static",
361 361 "log",
362 362 "memmap",
363 363 "micro-timer",
364 364 "pretty_assertions",
365 365 "rand 0.7.3",
366 366 "rand_distr",
367 367 "rand_pcg",
368 368 "rayon",
369 369 "regex",
370 370 "rust-crypto",
371 371 "same-file",
372 372 "tempfile",
373 373 "twox-hash",
374 374 "zstd",
375 375 ]
376 376
377 377 [[package]]
378 378 name = "hg-cpython"
379 379 version = "0.1.0"
380 380 dependencies = [
381 381 "cpython",
382 382 "crossbeam-channel 0.4.4",
383 383 "env_logger",
384 384 "hg-core",
385 385 "libc",
386 386 "log",
387 387 ]
388 388
389 389 [[package]]
390 390 name = "home"
391 391 version = "0.5.3"
392 392 source = "registry+https://github.com/rust-lang/crates.io-index"
393 393 checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
394 394 dependencies = [
395 395 "winapi",
396 396 ]
397 397
398 398 [[package]]
399 399 name = "humantime"
400 400 version = "1.3.0"
401 401 source = "registry+https://github.com/rust-lang/crates.io-index"
402 402 checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
403 403 dependencies = [
404 404 "quick-error",
405 405 ]
406 406
407 407 [[package]]
408 408 name = "im-rc"
409 409 version = "15.0.0"
410 410 source = "registry+https://github.com/rust-lang/crates.io-index"
411 411 checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f"
412 412 dependencies = [
413 413 "bitmaps",
414 414 "rand_core 0.5.1",
415 415 "rand_xoshiro",
416 416 "sized-chunks",
417 417 "typenum",
418 418 "version_check",
419 419 ]
420 420
421 421 [[package]]
422 422 name = "itertools"
423 423 version = "0.9.0"
424 424 source = "registry+https://github.com/rust-lang/crates.io-index"
425 425 checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
426 426 dependencies = [
427 427 "either",
428 428 ]
429 429
430 430 [[package]]
431 431 name = "jobserver"
432 432 version = "0.1.21"
433 433 source = "registry+https://github.com/rust-lang/crates.io-index"
434 434 checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
435 435 dependencies = [
436 436 "libc",
437 437 ]
438 438
439 439 [[package]]
440 440 name = "lazy_static"
441 441 version = "1.4.0"
442 442 source = "registry+https://github.com/rust-lang/crates.io-index"
443 443 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
444 444
445 445 [[package]]
446 446 name = "libc"
447 447 version = "0.2.81"
448 448 source = "registry+https://github.com/rust-lang/crates.io-index"
449 449 checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
450 450
451 451 [[package]]
452 452 name = "libz-sys"
453 453 version = "1.1.2"
454 454 source = "registry+https://github.com/rust-lang/crates.io-index"
455 455 checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
456 456 dependencies = [
457 457 "cc",
458 458 "pkg-config",
459 459 "vcpkg",
460 460 ]
461 461
462 462 [[package]]
463 463 name = "log"
464 464 version = "0.4.11"
465 465 source = "registry+https://github.com/rust-lang/crates.io-index"
466 466 checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
467 467 dependencies = [
468 468 "cfg-if 0.1.10",
469 469 ]
470 470
471 471 [[package]]
472 472 name = "maybe-uninit"
473 473 version = "2.0.0"
474 474 source = "registry+https://github.com/rust-lang/crates.io-index"
475 475 checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
476 476
477 477 [[package]]
478 478 name = "memchr"
479 479 version = "2.3.4"
480 480 source = "registry+https://github.com/rust-lang/crates.io-index"
481 481 checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
482 482
483 483 [[package]]
484 484 name = "memmap"
485 485 version = "0.7.0"
486 486 source = "registry+https://github.com/rust-lang/crates.io-index"
487 487 checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
488 488 dependencies = [
489 489 "libc",
490 490 "winapi",
491 491 ]
492 492
493 493 [[package]]
494 494 name = "memoffset"
495 495 version = "0.6.1"
496 496 source = "registry+https://github.com/rust-lang/crates.io-index"
497 497 checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
498 498 dependencies = [
499 499 "autocfg",
500 500 ]
501 501
502 502 [[package]]
503 503 name = "micro-timer"
504 504 version = "0.3.1"
505 505 source = "registry+https://github.com/rust-lang/crates.io-index"
506 506 checksum = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c"
507 507 dependencies = [
508 508 "micro-timer-macros",
509 509 "scopeguard",
510 510 ]
511 511
512 512 [[package]]
513 513 name = "micro-timer-macros"
514 514 version = "0.3.1"
515 515 source = "registry+https://github.com/rust-lang/crates.io-index"
516 516 checksum = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d"
517 517 dependencies = [
518 518 "proc-macro2",
519 519 "quote",
520 520 "scopeguard",
521 521 "syn",
522 522 ]
523 523
524 524 [[package]]
525 525 name = "miniz_oxide"
526 526 version = "0.4.3"
527 527 source = "registry+https://github.com/rust-lang/crates.io-index"
528 528 checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
529 529 dependencies = [
530 530 "adler",
531 531 "autocfg",
532 532 ]
533 533
534 534 [[package]]
535 535 name = "num-integer"
536 536 version = "0.1.44"
537 537 source = "registry+https://github.com/rust-lang/crates.io-index"
538 538 checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
539 539 dependencies = [
540 540 "autocfg",
541 541 "num-traits",
542 542 ]
543 543
544 544 [[package]]
545 545 name = "num-traits"
546 546 version = "0.2.14"
547 547 source = "registry+https://github.com/rust-lang/crates.io-index"
548 548 checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
549 549 dependencies = [
550 550 "autocfg",
551 551 ]
552 552
553 553 [[package]]
554 554 name = "num_cpus"
555 555 version = "1.13.0"
556 556 source = "registry+https://github.com/rust-lang/crates.io-index"
557 557 checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
558 558 dependencies = [
559 559 "hermit-abi",
560 560 "libc",
561 561 ]
562 562
563 563 [[package]]
564 564 name = "output_vt100"
565 565 version = "0.1.2"
566 566 source = "registry+https://github.com/rust-lang/crates.io-index"
567 567 checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
568 568 dependencies = [
569 569 "winapi",
570 570 ]
571 571
572 572 [[package]]
573 573 name = "pkg-config"
574 574 version = "0.3.19"
575 575 source = "registry+https://github.com/rust-lang/crates.io-index"
576 576 checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
577 577
578 578 [[package]]
579 579 name = "ppv-lite86"
580 580 version = "0.2.10"
581 581 source = "registry+https://github.com/rust-lang/crates.io-index"
582 582 checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
583 583
584 584 [[package]]
585 585 name = "pretty_assertions"
586 586 version = "0.6.1"
587 587 source = "registry+https://github.com/rust-lang/crates.io-index"
588 588 checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
589 589 dependencies = [
590 590 "ansi_term",
591 591 "ctor",
592 592 "difference",
593 593 "output_vt100",
594 594 ]
595 595
596 596 [[package]]
597 597 name = "proc-macro-hack"
598 598 version = "0.5.19"
599 599 source = "registry+https://github.com/rust-lang/crates.io-index"
600 600 checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
601 601
602 602 [[package]]
603 603 name = "proc-macro2"
604 604 version = "1.0.24"
605 605 source = "registry+https://github.com/rust-lang/crates.io-index"
606 606 checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
607 607 dependencies = [
608 608 "unicode-xid",
609 609 ]
610 610
611 611 [[package]]
612 612 name = "python27-sys"
613 613 version = "0.4.1"
614 614 source = "registry+https://github.com/rust-lang/crates.io-index"
615 615 checksum = "67cb041de8615111bf224dd75667af5f25c6e032118251426fed7f1b70ce4c8c"
616 616 dependencies = [
617 617 "libc",
618 618 "regex",
619 619 ]
620 620
621 621 [[package]]
622 622 name = "python3-sys"
623 623 version = "0.4.1"
624 624 source = "registry+https://github.com/rust-lang/crates.io-index"
625 625 checksum = "90af11779515a1e530af60782d273b59ac79d33b0e253c071a728563957c76d4"
626 626 dependencies = [
627 627 "libc",
628 628 "regex",
629 629 ]
630 630
631 631 [[package]]
632 632 name = "quick-error"
633 633 version = "1.2.3"
634 634 source = "registry+https://github.com/rust-lang/crates.io-index"
635 635 checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
636 636
637 637 [[package]]
638 638 name = "quote"
639 639 version = "1.0.7"
640 640 source = "registry+https://github.com/rust-lang/crates.io-index"
641 641 checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
642 642 dependencies = [
643 643 "proc-macro2",
644 644 ]
645 645
646 646 [[package]]
647 647 name = "rand"
648 648 version = "0.3.23"
649 649 source = "registry+https://github.com/rust-lang/crates.io-index"
650 650 checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
651 651 dependencies = [
652 652 "libc",
653 653 "rand 0.4.6",
654 654 ]
655 655
656 656 [[package]]
657 657 name = "rand"
658 658 version = "0.4.6"
659 659 source = "registry+https://github.com/rust-lang/crates.io-index"
660 660 checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
661 661 dependencies = [
662 662 "fuchsia-cprng",
663 663 "libc",
664 664 "rand_core 0.3.1",
665 665 "rdrand",
666 666 "winapi",
667 667 ]
668 668
669 669 [[package]]
670 670 name = "rand"
671 671 version = "0.7.3"
672 672 source = "registry+https://github.com/rust-lang/crates.io-index"
673 673 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
674 674 dependencies = [
675 675 "getrandom",
676 676 "libc",
677 677 "rand_chacha",
678 678 "rand_core 0.5.1",
679 679 "rand_hc",
680 680 ]
681 681
682 682 [[package]]
683 683 name = "rand_chacha"
684 684 version = "0.2.2"
685 685 source = "registry+https://github.com/rust-lang/crates.io-index"
686 686 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
687 687 dependencies = [
688 688 "ppv-lite86",
689 689 "rand_core 0.5.1",
690 690 ]
691 691
692 692 [[package]]
693 693 name = "rand_core"
694 694 version = "0.3.1"
695 695 source = "registry+https://github.com/rust-lang/crates.io-index"
696 696 checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
697 697 dependencies = [
698 698 "rand_core 0.4.2",
699 699 ]
700 700
701 701 [[package]]
702 702 name = "rand_core"
703 703 version = "0.4.2"
704 704 source = "registry+https://github.com/rust-lang/crates.io-index"
705 705 checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
706 706
707 707 [[package]]
708 708 name = "rand_core"
709 709 version = "0.5.1"
710 710 source = "registry+https://github.com/rust-lang/crates.io-index"
711 711 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
712 712 dependencies = [
713 713 "getrandom",
714 714 ]
715 715
716 716 [[package]]
717 717 name = "rand_distr"
718 718 version = "0.2.2"
719 719 source = "registry+https://github.com/rust-lang/crates.io-index"
720 720 checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
721 721 dependencies = [
722 722 "rand 0.7.3",
723 723 ]
724 724
725 725 [[package]]
726 726 name = "rand_hc"
727 727 version = "0.2.0"
728 728 source = "registry+https://github.com/rust-lang/crates.io-index"
729 729 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
730 730 dependencies = [
731 731 "rand_core 0.5.1",
732 732 ]
733 733
734 734 [[package]]
735 735 name = "rand_pcg"
736 736 version = "0.2.1"
737 737 source = "registry+https://github.com/rust-lang/crates.io-index"
738 738 checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
739 739 dependencies = [
740 740 "rand_core 0.5.1",
741 741 ]
742 742
743 743 [[package]]
744 744 name = "rand_xoshiro"
745 745 version = "0.4.0"
746 746 source = "registry+https://github.com/rust-lang/crates.io-index"
747 747 checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
748 748 dependencies = [
749 749 "rand_core 0.5.1",
750 750 ]
751 751
752 752 [[package]]
753 753 name = "rayon"
754 754 version = "1.5.0"
755 755 source = "registry+https://github.com/rust-lang/crates.io-index"
756 756 checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
757 757 dependencies = [
758 758 "autocfg",
759 759 "crossbeam-deque",
760 760 "either",
761 761 "rayon-core",
762 762 ]
763 763
764 764 [[package]]
765 765 name = "rayon-core"
766 766 version = "1.9.0"
767 767 source = "registry+https://github.com/rust-lang/crates.io-index"
768 768 checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
769 769 dependencies = [
770 770 "crossbeam-channel 0.5.0",
771 771 "crossbeam-deque",
772 772 "crossbeam-utils 0.8.1",
773 773 "lazy_static",
774 774 "num_cpus",
775 775 ]
776 776
777 777 [[package]]
778 778 name = "rdrand"
779 779 version = "0.4.0"
780 780 source = "registry+https://github.com/rust-lang/crates.io-index"
781 781 checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
782 782 dependencies = [
783 783 "rand_core 0.3.1",
784 784 ]
785 785
786 786 [[package]]
787 787 name = "redox_syscall"
788 788 version = "0.1.57"
789 789 source = "registry+https://github.com/rust-lang/crates.io-index"
790 790 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
791 791
792 792 [[package]]
793 793 name = "regex"
794 794 version = "1.4.2"
795 795 source = "registry+https://github.com/rust-lang/crates.io-index"
796 796 checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
797 797 dependencies = [
798 798 "aho-corasick",
799 799 "memchr",
800 800 "regex-syntax",
801 801 "thread_local",
802 802 ]
803 803
804 804 [[package]]
805 805 name = "regex-syntax"
806 806 version = "0.6.21"
807 807 source = "registry+https://github.com/rust-lang/crates.io-index"
808 808 checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
809 809
810 810 [[package]]
811 811 name = "remove_dir_all"
812 812 version = "0.5.3"
813 813 source = "registry+https://github.com/rust-lang/crates.io-index"
814 814 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
815 815 dependencies = [
816 816 "winapi",
817 817 ]
818 818
819 819 [[package]]
820 820 name = "rhg"
821 821 version = "0.1.0"
822 822 dependencies = [
823 823 "chrono",
824 824 "clap",
825 825 "derive_more",
826 826 "env_logger",
827 827 "format-bytes",
828 828 "hg-core",
829 829 "lazy_static",
830 830 "log",
831 831 "micro-timer",
832 832 "regex",
833 833 "users",
834 834 ]
835 835
836 836 [[package]]
837 837 name = "rust-crypto"
838 838 version = "0.2.36"
839 839 source = "registry+https://github.com/rust-lang/crates.io-index"
840 840 checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
841 841 dependencies = [
842 842 "gcc",
843 843 "libc",
844 844 "rand 0.3.23",
845 845 "rustc-serialize",
846 846 "time",
847 847 ]
848 848
849 849 [[package]]
850 850 name = "rustc-serialize"
851 851 version = "0.3.24"
852 852 source = "registry+https://github.com/rust-lang/crates.io-index"
853 853 checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
854 854
855 855 [[package]]
856 856 name = "same-file"
857 857 version = "1.0.6"
858 858 source = "registry+https://github.com/rust-lang/crates.io-index"
859 859 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
860 860 dependencies = [
861 861 "winapi-util",
862 862 ]
863 863
864 864 [[package]]
865 865 name = "scopeguard"
866 866 version = "1.1.0"
867 867 source = "registry+https://github.com/rust-lang/crates.io-index"
868 868 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
869 869
870 870 [[package]]
871 871 name = "sized-chunks"
872 872 version = "0.6.2"
873 873 source = "registry+https://github.com/rust-lang/crates.io-index"
874 874 checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f"
875 875 dependencies = [
876 876 "bitmaps",
877 877 "typenum",
878 878 ]
879 879
880 880 [[package]]
881 881 name = "static_assertions"
882 882 version = "1.1.0"
883 883 source = "registry+https://github.com/rust-lang/crates.io-index"
884 884 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
885 885
886 886 [[package]]
887 887 name = "strsim"
888 888 version = "0.8.0"
889 889 source = "registry+https://github.com/rust-lang/crates.io-index"
890 890 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
891 891
892 892 [[package]]
893 893 name = "syn"
894 894 version = "1.0.54"
895 895 source = "registry+https://github.com/rust-lang/crates.io-index"
896 896 checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
897 897 dependencies = [
898 898 "proc-macro2",
899 899 "quote",
900 900 "unicode-xid",
901 901 ]
902 902
903 903 [[package]]
904 904 name = "tempfile"
905 905 version = "3.1.0"
906 906 source = "registry+https://github.com/rust-lang/crates.io-index"
907 907 checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
908 908 dependencies = [
909 909 "cfg-if 0.1.10",
910 910 "libc",
911 911 "rand 0.7.3",
912 912 "redox_syscall",
913 913 "remove_dir_all",
914 914 "winapi",
915 915 ]
916 916
917 917 [[package]]
918 918 name = "termcolor"
919 919 version = "1.1.2"
920 920 source = "registry+https://github.com/rust-lang/crates.io-index"
921 921 checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
922 922 dependencies = [
923 923 "winapi-util",
924 924 ]
925 925
926 926 [[package]]
927 927 name = "textwrap"
928 928 version = "0.11.0"
929 929 source = "registry+https://github.com/rust-lang/crates.io-index"
930 930 checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
931 931 dependencies = [
932 932 "unicode-width",
933 933 ]
934 934
935 935 [[package]]
936 936 name = "thread_local"
937 937 version = "1.0.1"
938 938 source = "registry+https://github.com/rust-lang/crates.io-index"
939 939 checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
940 940 dependencies = [
941 941 "lazy_static",
942 942 ]
943 943
944 944 [[package]]
945 945 name = "time"
946 946 version = "0.1.44"
947 947 source = "registry+https://github.com/rust-lang/crates.io-index"
948 948 checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
949 949 dependencies = [
950 950 "libc",
951 951 "wasi 0.10.0+wasi-snapshot-preview1",
952 952 "winapi",
953 953 ]
954 954
955 955 [[package]]
956 956 name = "twox-hash"
957 957 version = "1.6.0"
958 958 source = "registry+https://github.com/rust-lang/crates.io-index"
959 959 checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59"
960 960 dependencies = [
961 961 "cfg-if 0.1.10",
962 962 "rand 0.7.3",
963 963 "static_assertions",
964 964 ]
965 965
966 966 [[package]]
967 967 name = "typenum"
968 968 version = "1.12.0"
969 969 source = "registry+https://github.com/rust-lang/crates.io-index"
970 970 checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
971 971
972 972 [[package]]
973 973 name = "unicode-width"
974 974 version = "0.1.8"
975 975 source = "registry+https://github.com/rust-lang/crates.io-index"
976 976 checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
977 977
978 978 [[package]]
979 979 name = "unicode-xid"
980 980 version = "0.2.1"
981 981 source = "registry+https://github.com/rust-lang/crates.io-index"
982 982 checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
983 983
984 984 [[package]]
985 985 name = "users"
986 986 version = "0.11.0"
987 987 source = "registry+https://github.com/rust-lang/crates.io-index"
988 988 checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
989 989 dependencies = [
990 990 "libc",
991 991 "log",
992 992 ]
993 993
994 994 [[package]]
995 995 name = "vcpkg"
996 996 version = "0.2.11"
997 997 source = "registry+https://github.com/rust-lang/crates.io-index"
998 998 checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
999 999
1000 1000 [[package]]
1001 1001 name = "vec_map"
1002 1002 version = "0.8.2"
1003 1003 source = "registry+https://github.com/rust-lang/crates.io-index"
1004 1004 checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
1005 1005
1006 1006 [[package]]
1007 1007 name = "version_check"
1008 1008 version = "0.9.2"
1009 1009 source = "registry+https://github.com/rust-lang/crates.io-index"
1010 1010 checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
1011 1011
1012 1012 [[package]]
1013 1013 name = "wasi"
1014 1014 version = "0.9.0+wasi-snapshot-preview1"
1015 1015 source = "registry+https://github.com/rust-lang/crates.io-index"
1016 1016 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
1017 1017
1018 1018 [[package]]
1019 1019 name = "wasi"
1020 1020 version = "0.10.0+wasi-snapshot-preview1"
1021 1021 source = "registry+https://github.com/rust-lang/crates.io-index"
1022 1022 checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
1023 1023
1024 1024 [[package]]
1025 1025 name = "winapi"
1026 1026 version = "0.3.9"
1027 1027 source = "registry+https://github.com/rust-lang/crates.io-index"
1028 1028 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1029 1029 dependencies = [
1030 1030 "winapi-i686-pc-windows-gnu",
1031 1031 "winapi-x86_64-pc-windows-gnu",
1032 1032 ]
1033 1033
1034 1034 [[package]]
1035 1035 name = "winapi-i686-pc-windows-gnu"
1036 1036 version = "0.4.0"
1037 1037 source = "registry+https://github.com/rust-lang/crates.io-index"
1038 1038 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1039 1039
1040 1040 [[package]]
1041 1041 name = "winapi-util"
1042 1042 version = "0.1.5"
1043 1043 source = "registry+https://github.com/rust-lang/crates.io-index"
1044 1044 checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
1045 1045 dependencies = [
1046 1046 "winapi",
1047 1047 ]
1048 1048
1049 1049 [[package]]
1050 1050 name = "winapi-x86_64-pc-windows-gnu"
1051 1051 version = "0.4.0"
1052 1052 source = "registry+https://github.com/rust-lang/crates.io-index"
1053 1053 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1054 1054
1055 1055 [[package]]
1056 1056 name = "zstd"
1057 1057 version = "0.5.3+zstd.1.4.5"
1058 1058 source = "registry+https://github.com/rust-lang/crates.io-index"
1059 1059 checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
1060 1060 dependencies = [
1061 1061 "zstd-safe",
1062 1062 ]
1063 1063
1064 1064 [[package]]
1065 1065 name = "zstd-safe"
1066 1066 version = "2.0.5+zstd.1.4.5"
1067 1067 source = "registry+https://github.com/rust-lang/crates.io-index"
1068 1068 checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
1069 1069 dependencies = [
1070 1070 "libc",
1071 1071 "zstd-sys",
1072 1072 ]
1073 1073
1074 1074 [[package]]
1075 1075 name = "zstd-sys"
1076 1076 version = "1.4.17+zstd.1.4.5"
1077 1077 source = "registry+https://github.com/rust-lang/crates.io-index"
1078 1078 checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
1079 1079 dependencies = [
1080 1080 "cc",
1081 1081 "glob",
1082 1082 "itertools",
1083 1083 "libc",
1084 1084 ]
@@ -1,428 +1,437 b''
1 1 // config.rs
2 2 //
3 3 // Copyright 2020
4 4 // Valentin Gatien-Baron,
5 5 // Raphaël Gomès <rgomes@octobus.net>
6 6 //
7 7 // This software may be used and distributed according to the terms of the
8 8 // GNU General Public License version 2 or any later version.
9 9
10 10 use super::layer;
11 11 use super::values;
12 12 use crate::config::layer::{
13 13 ConfigError, ConfigLayer, ConfigOrigin, ConfigValue,
14 14 };
15 15 use crate::utils::files::get_bytes_from_os_str;
16 16 use format_bytes::{write_bytes, DisplayBytes};
17 use std::collections::HashSet;
17 18 use std::env;
18 19 use std::path::{Path, PathBuf};
19 20 use std::str;
20 21
21 22 use crate::errors::{HgResultExt, IoResultExt};
22 23
23 24 /// Holds the config values for the current repository
24 25 /// TODO update this docstring once we support more sources
25 26 pub struct Config {
26 27 layers: Vec<layer::ConfigLayer>,
27 28 }
28 29
29 30 impl DisplayBytes for Config {
30 31 fn display_bytes(
31 32 &self,
32 33 out: &mut dyn std::io::Write,
33 34 ) -> std::io::Result<()> {
34 35 for (index, layer) in self.layers.iter().rev().enumerate() {
35 36 write_bytes!(
36 37 out,
37 38 b"==== Layer {} (trusted: {}) ====\n{}",
38 39 index,
39 40 if layer.trusted {
40 41 &b"yes"[..]
41 42 } else {
42 43 &b"no"[..]
43 44 },
44 45 layer
45 46 )?;
46 47 }
47 48 Ok(())
48 49 }
49 50 }
50 51
51 52 pub enum ConfigSource {
52 53 /// Absolute path to a config file
53 54 AbsPath(PathBuf),
54 55 /// Already parsed (from the CLI, env, Python resources, etc.)
55 56 Parsed(layer::ConfigLayer),
56 57 }
57 58
58 59 #[derive(Debug)]
59 60 pub struct ConfigValueParseError {
60 61 pub origin: ConfigOrigin,
61 62 pub line: Option<usize>,
62 63 pub section: Vec<u8>,
63 64 pub item: Vec<u8>,
64 65 pub value: Vec<u8>,
65 66 pub expected_type: &'static str,
66 67 }
67 68
68 69 impl Config {
69 70 /// Load system and user configuration from various files.
70 71 ///
71 72 /// This is also affected by some environment variables.
72 73 pub fn load(
73 74 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
74 75 ) -> Result<Self, ConfigError> {
75 76 let mut config = Self { layers: Vec::new() };
76 77 let opt_rc_path = env::var_os("HGRCPATH");
77 78 // HGRCPATH replaces system config
78 79 if opt_rc_path.is_none() {
79 80 config.add_system_config()?
80 81 }
81 82
82 83 config.add_for_environment_variable("EDITOR", b"ui", b"editor");
83 84 config.add_for_environment_variable("VISUAL", b"ui", b"editor");
84 85 config.add_for_environment_variable("PAGER", b"pager", b"pager");
85 86
86 87 // These are set by `run-tests.py --rhg` to enable fallback for the
87 88 // entire test suite. Alternatives would be setting configuration
88 89 // through `$HGRCPATH` but some tests override that, or changing the
89 90 // `hg` shell alias to include `--config` but that disrupts tests that
90 91 // print command lines and check expected output.
91 92 config.add_for_environment_variable(
92 93 "RHG_ON_UNSUPPORTED",
93 94 b"rhg",
94 95 b"on-unsupported",
95 96 );
96 97 config.add_for_environment_variable(
97 98 "RHG_FALLBACK_EXECUTABLE",
98 99 b"rhg",
99 100 b"fallback-executable",
100 101 );
101 102
102 103 // HGRCPATH replaces user config
103 104 if opt_rc_path.is_none() {
104 105 config.add_user_config()?
105 106 }
106 107 if let Some(rc_path) = &opt_rc_path {
107 108 for path in env::split_paths(rc_path) {
108 109 if !path.as_os_str().is_empty() {
109 110 if path.is_dir() {
110 111 config.add_trusted_dir(&path)?
111 112 } else {
112 113 config.add_trusted_file(&path)?
113 114 }
114 115 }
115 116 }
116 117 }
117 118 if let Some(layer) = ConfigLayer::parse_cli_args(cli_config_args)? {
118 119 config.layers.push(layer)
119 120 }
120 121 Ok(config)
121 122 }
122 123
123 124 fn add_trusted_dir(&mut self, path: &Path) -> Result<(), ConfigError> {
124 125 if let Some(entries) = std::fs::read_dir(path)
125 126 .when_reading_file(path)
126 127 .io_not_found_as_none()?
127 128 {
128 129 let mut file_paths = entries
129 130 .map(|result| {
130 131 result.when_reading_file(path).map(|entry| entry.path())
131 132 })
132 133 .collect::<Result<Vec<_>, _>>()?;
133 134 file_paths.sort();
134 135 for file_path in &file_paths {
135 136 if file_path.extension() == Some(std::ffi::OsStr::new("rc")) {
136 137 self.add_trusted_file(&file_path)?
137 138 }
138 139 }
139 140 }
140 141 Ok(())
141 142 }
142 143
143 144 fn add_trusted_file(&mut self, path: &Path) -> Result<(), ConfigError> {
144 145 if let Some(data) = std::fs::read(path)
145 146 .when_reading_file(path)
146 147 .io_not_found_as_none()?
147 148 {
148 149 self.layers.extend(ConfigLayer::parse(path, &data)?)
149 150 }
150 151 Ok(())
151 152 }
152 153
153 154 fn add_for_environment_variable(
154 155 &mut self,
155 156 var: &str,
156 157 section: &[u8],
157 158 key: &[u8],
158 159 ) {
159 160 if let Some(value) = env::var_os(var) {
160 161 let origin = layer::ConfigOrigin::Environment(var.into());
161 162 let mut layer = ConfigLayer::new(origin);
162 163 layer.add(
163 164 section.to_owned(),
164 165 key.to_owned(),
165 166 get_bytes_from_os_str(value),
166 167 None,
167 168 );
168 169 self.layers.push(layer)
169 170 }
170 171 }
171 172
172 173 #[cfg(unix)] // TODO: other platforms
173 174 fn add_system_config(&mut self) -> Result<(), ConfigError> {
174 175 let mut add_for_prefix = |prefix: &Path| -> Result<(), ConfigError> {
175 176 let etc = prefix.join("etc").join("mercurial");
176 177 self.add_trusted_file(&etc.join("hgrc"))?;
177 178 self.add_trusted_dir(&etc.join("hgrc.d"))
178 179 };
179 180 let root = Path::new("/");
180 181 // TODO: use `std::env::args_os().next().unwrap()` a.k.a. argv[0]
181 182 // instead? TODO: can this be a relative path?
182 183 let hg = crate::utils::current_exe()?;
183 184 // TODO: this order (per-installation then per-system) matches
184 185 // `systemrcpath()` in `mercurial/scmposix.py`, but
185 186 // `mercurial/helptext/config.txt` suggests it should be reversed
186 187 if let Some(installation_prefix) = hg.parent().and_then(Path::parent) {
187 188 if installation_prefix != root {
188 189 add_for_prefix(&installation_prefix)?
189 190 }
190 191 }
191 192 add_for_prefix(root)?;
192 193 Ok(())
193 194 }
194 195
195 196 #[cfg(unix)] // TODO: other plateforms
196 197 fn add_user_config(&mut self) -> Result<(), ConfigError> {
197 198 let opt_home = home::home_dir();
198 199 if let Some(home) = &opt_home {
199 200 self.add_trusted_file(&home.join(".hgrc"))?
200 201 }
201 202 let darwin = cfg!(any(target_os = "macos", target_os = "ios"));
202 203 if !darwin {
203 204 if let Some(config_home) = env::var_os("XDG_CONFIG_HOME")
204 205 .map(PathBuf::from)
205 206 .or_else(|| opt_home.map(|home| home.join(".config")))
206 207 {
207 208 self.add_trusted_file(&config_home.join("hg").join("hgrc"))?
208 209 }
209 210 }
210 211 Ok(())
211 212 }
212 213
213 214 /// Loads in order, which means that the precedence is the same
214 215 /// as the order of `sources`.
215 216 pub fn load_from_explicit_sources(
216 217 sources: Vec<ConfigSource>,
217 218 ) -> Result<Self, ConfigError> {
218 219 let mut layers = vec![];
219 220
220 221 for source in sources.into_iter() {
221 222 match source {
222 223 ConfigSource::Parsed(c) => layers.push(c),
223 224 ConfigSource::AbsPath(c) => {
224 225 // TODO check if it should be trusted
225 226 // mercurial/ui.py:427
226 227 let data = match std::fs::read(&c) {
227 228 Err(_) => continue, // same as the python code
228 229 Ok(data) => data,
229 230 };
230 231 layers.extend(ConfigLayer::parse(&c, &data)?)
231 232 }
232 233 }
233 234 }
234 235
235 236 Ok(Config { layers })
236 237 }
237 238
238 239 /// Loads the per-repository config into a new `Config` which is combined
239 240 /// with `self`.
240 241 pub(crate) fn combine_with_repo(
241 242 &self,
242 243 repo_config_files: &[PathBuf],
243 244 ) -> Result<Self, ConfigError> {
244 245 let (cli_layers, other_layers) = self
245 246 .layers
246 247 .iter()
247 248 .cloned()
248 249 .partition(ConfigLayer::is_from_command_line);
249 250
250 251 let mut repo_config = Self {
251 252 layers: other_layers,
252 253 };
253 254 for path in repo_config_files {
254 255 // TODO: check if this file should be trusted:
255 256 // `mercurial/ui.py:427`
256 257 repo_config.add_trusted_file(path)?;
257 258 }
258 259 repo_config.layers.extend(cli_layers);
259 260 Ok(repo_config)
260 261 }
261 262
262 263 fn get_parse<'config, T: 'config>(
263 264 &'config self,
264 265 section: &[u8],
265 266 item: &[u8],
266 267 expected_type: &'static str,
267 268 parse: impl Fn(&'config [u8]) -> Option<T>,
268 269 ) -> Result<Option<T>, ConfigValueParseError> {
269 270 match self.get_inner(&section, &item) {
270 271 Some((layer, v)) => match parse(&v.bytes) {
271 272 Some(b) => Ok(Some(b)),
272 273 None => Err(ConfigValueParseError {
273 274 origin: layer.origin.to_owned(),
274 275 line: v.line,
275 276 value: v.bytes.to_owned(),
276 277 section: section.to_owned(),
277 278 item: item.to_owned(),
278 279 expected_type,
279 280 }),
280 281 },
281 282 None => Ok(None),
282 283 }
283 284 }
284 285
285 286 /// Returns an `Err` if the first value found is not a valid UTF-8 string.
286 287 /// Otherwise, returns an `Ok(value)` if found, or `None`.
287 288 pub fn get_str(
288 289 &self,
289 290 section: &[u8],
290 291 item: &[u8],
291 292 ) -> Result<Option<&str>, ConfigValueParseError> {
292 293 self.get_parse(section, item, "ASCII or UTF-8 string", |value| {
293 294 str::from_utf8(value).ok()
294 295 })
295 296 }
296 297
297 298 /// Returns an `Err` if the first value found is not a valid unsigned
298 299 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`.
299 300 pub fn get_u32(
300 301 &self,
301 302 section: &[u8],
302 303 item: &[u8],
303 304 ) -> Result<Option<u32>, ConfigValueParseError> {
304 305 self.get_parse(section, item, "valid integer", |value| {
305 306 str::from_utf8(value).ok()?.parse().ok()
306 307 })
307 308 }
308 309
309 310 /// Returns an `Err` if the first value found is not a valid file size
310 311 /// value such as `30` (default unit is bytes), `7 MB`, or `42.5 kb`.
311 312 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`.
312 313 pub fn get_byte_size(
313 314 &self,
314 315 section: &[u8],
315 316 item: &[u8],
316 317 ) -> Result<Option<u64>, ConfigValueParseError> {
317 318 self.get_parse(section, item, "byte quantity", values::parse_byte_size)
318 319 }
319 320
320 321 /// Returns an `Err` if the first value found is not a valid boolean.
321 322 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
322 323 /// found, or `None`.
323 324 pub fn get_option(
324 325 &self,
325 326 section: &[u8],
326 327 item: &[u8],
327 328 ) -> Result<Option<bool>, ConfigValueParseError> {
328 329 self.get_parse(section, item, "boolean", values::parse_bool)
329 330 }
330 331
331 332 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
332 333 /// if the value is not found, an `Err` if it's not a valid boolean.
333 334 pub fn get_bool(
334 335 &self,
335 336 section: &[u8],
336 337 item: &[u8],
337 338 ) -> Result<bool, ConfigValueParseError> {
338 339 Ok(self.get_option(section, item)?.unwrap_or(false))
339 340 }
340 341
341 342 /// Returns the raw value bytes of the first one found, or `None`.
342 343 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> {
343 344 self.get_inner(section, item)
344 345 .map(|(_, value)| value.bytes.as_ref())
345 346 }
346 347
347 348 /// Returns the layer and the value of the first one found, or `None`.
348 349 fn get_inner(
349 350 &self,
350 351 section: &[u8],
351 352 item: &[u8],
352 353 ) -> Option<(&ConfigLayer, &ConfigValue)> {
353 354 for layer in self.layers.iter().rev() {
354 355 if !layer.trusted {
355 356 continue;
356 357 }
357 358 if let Some(v) = layer.get(&section, &item) {
358 359 return Some((&layer, v));
359 360 }
360 361 }
361 362 None
362 363 }
363 364
365 /// Return all keys defined for the given section
366 pub fn get_section_keys(&self, section: &[u8]) -> HashSet<&[u8]> {
367 self.layers
368 .iter()
369 .flat_map(|layer| layer.iter_keys(section))
370 .collect()
371 }
372
364 373 /// Get raw values bytes from all layers (even untrusted ones) in order
365 374 /// of precedence.
366 375 #[cfg(test)]
367 376 fn get_all(&self, section: &[u8], item: &[u8]) -> Vec<&[u8]> {
368 377 let mut res = vec![];
369 378 for layer in self.layers.iter().rev() {
370 379 if let Some(v) = layer.get(&section, &item) {
371 380 res.push(v.bytes.as_ref());
372 381 }
373 382 }
374 383 res
375 384 }
376 385 }
377 386
378 387 #[cfg(test)]
379 388 mod tests {
380 389 use super::*;
381 390 use pretty_assertions::assert_eq;
382 391 use std::fs::File;
383 392 use std::io::Write;
384 393
385 394 #[test]
386 395 fn test_include_layer_ordering() {
387 396 let tmpdir = tempfile::tempdir().unwrap();
388 397 let tmpdir_path = tmpdir.path();
389 398 let mut included_file =
390 399 File::create(&tmpdir_path.join("included.rc")).unwrap();
391 400
392 401 included_file.write_all(b"[section]\nitem=value1").unwrap();
393 402 let base_config_path = tmpdir_path.join("base.rc");
394 403 let mut config_file = File::create(&base_config_path).unwrap();
395 404 let data =
396 405 b"[section]\nitem=value0\n%include included.rc\nitem=value2\n\
397 406 [section2]\ncount = 4\nsize = 1.5 KB\nnot-count = 1.5\nnot-size = 1 ub";
398 407 config_file.write_all(data).unwrap();
399 408
400 409 let sources = vec![ConfigSource::AbsPath(base_config_path)];
401 410 let config = Config::load_from_explicit_sources(sources)
402 411 .expect("expected valid config");
403 412
404 413 let (_, value) = config.get_inner(b"section", b"item").unwrap();
405 414 assert_eq!(
406 415 value,
407 416 &ConfigValue {
408 417 bytes: b"value2".to_vec(),
409 418 line: Some(4)
410 419 }
411 420 );
412 421
413 422 let value = config.get(b"section", b"item").unwrap();
414 423 assert_eq!(value, b"value2",);
415 424 assert_eq!(
416 425 config.get_all(b"section", b"item"),
417 426 [b"value2", b"value1", b"value0"]
418 427 );
419 428
420 429 assert_eq!(config.get_u32(b"section2", b"count").unwrap(), Some(4));
421 430 assert_eq!(
422 431 config.get_byte_size(b"section2", b"size").unwrap(),
423 432 Some(1024 + 512)
424 433 );
425 434 assert!(config.get_u32(b"section2", b"not-count").is_err());
426 435 assert!(config.get_byte_size(b"section2", b"not-size").is_err());
427 436 }
428 437 }
@@ -1,297 +1,305 b''
1 1 // layer.rs
2 2 //
3 3 // Copyright 2020
4 4 // Valentin Gatien-Baron,
5 5 // Raphaël Gomès <rgomes@octobus.net>
6 6 //
7 7 // This software may be used and distributed according to the terms of the
8 8 // GNU General Public License version 2 or any later version.
9 9
10 10 use crate::errors::{HgError, IoResultExt};
11 11 use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
12 12 use format_bytes::{format_bytes, write_bytes, DisplayBytes};
13 13 use lazy_static::lazy_static;
14 14 use regex::bytes::Regex;
15 15 use std::collections::HashMap;
16 16 use std::path::{Path, PathBuf};
17 17
18 18 lazy_static! {
19 19 static ref SECTION_RE: Regex = make_regex(r"^\[([^\[]+)\]");
20 20 static ref ITEM_RE: Regex = make_regex(r"^([^=\s][^=]*?)\s*=\s*((.*\S)?)");
21 21 /// Continuation whitespace
22 22 static ref CONT_RE: Regex = make_regex(r"^\s+(\S|\S.*\S)\s*$");
23 23 static ref EMPTY_RE: Regex = make_regex(r"^(;|#|\s*$)");
24 24 static ref COMMENT_RE: Regex = make_regex(r"^(;|#)");
25 25 /// A directive that allows for removing previous entries
26 26 static ref UNSET_RE: Regex = make_regex(r"^%unset\s+(\S+)");
27 27 /// A directive that allows for including other config files
28 28 static ref INCLUDE_RE: Regex = make_regex(r"^%include\s+(\S|\S.*\S)\s*$");
29 29 }
30 30
31 31 /// All config values separated by layers of precedence.
32 32 /// Each config source may be split in multiple layers if `%include` directives
33 33 /// are used.
34 34 /// TODO detail the general precedence
35 35 #[derive(Clone)]
36 36 pub struct ConfigLayer {
37 37 /// Mapping of the sections to their items
38 38 sections: HashMap<Vec<u8>, ConfigItem>,
39 39 /// All sections (and their items/values) in a layer share the same origin
40 40 pub origin: ConfigOrigin,
41 41 /// Whether this layer comes from a trusted user or group
42 42 pub trusted: bool,
43 43 }
44 44
45 45 impl ConfigLayer {
46 46 pub fn new(origin: ConfigOrigin) -> Self {
47 47 ConfigLayer {
48 48 sections: HashMap::new(),
49 49 trusted: true, // TODO check
50 50 origin,
51 51 }
52 52 }
53 53
54 54 /// Parse `--config` CLI arguments and return a layer if there’s any
55 55 pub(crate) fn parse_cli_args(
56 56 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
57 57 ) -> Result<Option<Self>, ConfigError> {
58 58 fn parse_one(arg: &[u8]) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)> {
59 59 use crate::utils::SliceExt;
60 60
61 61 let (section_and_item, value) = arg.split_2(b'=')?;
62 62 let (section, item) = section_and_item.trim().split_2(b'.')?;
63 63 Some((
64 64 section.to_owned(),
65 65 item.to_owned(),
66 66 value.trim().to_owned(),
67 67 ))
68 68 }
69 69
70 70 let mut layer = Self::new(ConfigOrigin::CommandLine);
71 71 for arg in cli_config_args {
72 72 let arg = arg.as_ref();
73 73 if let Some((section, item, value)) = parse_one(arg) {
74 74 layer.add(section, item, value, None);
75 75 } else {
76 76 Err(HgError::abort(format!(
77 77 "malformed --config option: '{}' \
78 78 (use --config section.name=value)",
79 79 String::from_utf8_lossy(arg),
80 80 )))?
81 81 }
82 82 }
83 83 if layer.sections.is_empty() {
84 84 Ok(None)
85 85 } else {
86 86 Ok(Some(layer))
87 87 }
88 88 }
89 89
90 90 /// Returns whether this layer comes from `--config` CLI arguments
91 91 pub(crate) fn is_from_command_line(&self) -> bool {
92 92 if let ConfigOrigin::CommandLine = self.origin {
93 93 true
94 94 } else {
95 95 false
96 96 }
97 97 }
98 98
99 99 /// Add an entry to the config, overwriting the old one if already present.
100 100 pub fn add(
101 101 &mut self,
102 102 section: Vec<u8>,
103 103 item: Vec<u8>,
104 104 value: Vec<u8>,
105 105 line: Option<usize>,
106 106 ) {
107 107 self.sections
108 108 .entry(section)
109 109 .or_insert_with(|| HashMap::new())
110 110 .insert(item, ConfigValue { bytes: value, line });
111 111 }
112 112
113 113 /// Returns the config value in `<section>.<item>` if it exists
114 114 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&ConfigValue> {
115 115 Some(self.sections.get(section)?.get(item)?)
116 116 }
117 117
118 /// Returns the keys defined in the given section
119 pub fn iter_keys(&self, section: &[u8]) -> impl Iterator<Item = &[u8]> {
120 self.sections
121 .get(section)
122 .into_iter()
123 .flat_map(|section| section.keys().map(|vec| &**vec))
124 }
125
118 126 pub fn is_empty(&self) -> bool {
119 127 self.sections.is_empty()
120 128 }
121 129
122 130 /// Returns a `Vec` of layers in order of precedence (so, in read order),
123 131 /// recursively parsing the `%include` directives if any.
124 132 pub fn parse(src: &Path, data: &[u8]) -> Result<Vec<Self>, ConfigError> {
125 133 let mut layers = vec![];
126 134
127 135 // Discard byte order mark if any
128 136 let data = if data.starts_with(b"\xef\xbb\xbf") {
129 137 &data[3..]
130 138 } else {
131 139 data
132 140 };
133 141
134 142 // TODO check if it's trusted
135 143 let mut current_layer = Self::new(ConfigOrigin::File(src.to_owned()));
136 144
137 145 let mut lines_iter =
138 146 data.split(|b| *b == b'\n').enumerate().peekable();
139 147 let mut section = b"".to_vec();
140 148
141 149 while let Some((index, bytes)) = lines_iter.next() {
142 150 if let Some(m) = INCLUDE_RE.captures(&bytes) {
143 151 let filename_bytes = &m[1];
144 152 // `Path::parent` only fails for the root directory,
145 153 // which `src` can’t be since we’ve managed to open it as a
146 154 // file.
147 155 let dir = src
148 156 .parent()
149 157 .expect("Path::parent fail on a file we’ve read");
150 158 // `Path::join` with an absolute argument correctly ignores the
151 159 // base path
152 160 let filename = dir.join(&get_path_from_bytes(&filename_bytes));
153 161 let data =
154 162 std::fs::read(&filename).when_reading_file(&filename)?;
155 163 layers.push(current_layer);
156 164 layers.extend(Self::parse(&filename, &data)?);
157 165 current_layer = Self::new(ConfigOrigin::File(src.to_owned()));
158 166 } else if let Some(_) = EMPTY_RE.captures(&bytes) {
159 167 } else if let Some(m) = SECTION_RE.captures(&bytes) {
160 168 section = m[1].to_vec();
161 169 } else if let Some(m) = ITEM_RE.captures(&bytes) {
162 170 let item = m[1].to_vec();
163 171 let mut value = m[2].to_vec();
164 172 loop {
165 173 match lines_iter.peek() {
166 174 None => break,
167 175 Some((_, v)) => {
168 176 if let Some(_) = COMMENT_RE.captures(&v) {
169 177 } else if let Some(_) = CONT_RE.captures(&v) {
170 178 value.extend(b"\n");
171 179 value.extend(&m[1]);
172 180 } else {
173 181 break;
174 182 }
175 183 }
176 184 };
177 185 lines_iter.next();
178 186 }
179 187 current_layer.add(
180 188 section.clone(),
181 189 item,
182 190 value,
183 191 Some(index + 1),
184 192 );
185 193 } else if let Some(m) = UNSET_RE.captures(&bytes) {
186 194 if let Some(map) = current_layer.sections.get_mut(&section) {
187 195 map.remove(&m[1]);
188 196 }
189 197 } else {
190 198 let message = if bytes.starts_with(b" ") {
191 199 format_bytes!(b"unexpected leading whitespace: {}", bytes)
192 200 } else {
193 201 bytes.to_owned()
194 202 };
195 203 return Err(ConfigParseError {
196 204 origin: ConfigOrigin::File(src.to_owned()),
197 205 line: Some(index + 1),
198 206 message,
199 207 }
200 208 .into());
201 209 }
202 210 }
203 211 if !current_layer.is_empty() {
204 212 layers.push(current_layer);
205 213 }
206 214 Ok(layers)
207 215 }
208 216 }
209 217
210 218 impl DisplayBytes for ConfigLayer {
211 219 fn display_bytes(
212 220 &self,
213 221 out: &mut dyn std::io::Write,
214 222 ) -> std::io::Result<()> {
215 223 let mut sections: Vec<_> = self.sections.iter().collect();
216 224 sections.sort_by(|e0, e1| e0.0.cmp(e1.0));
217 225
218 226 for (section, items) in sections.into_iter() {
219 227 let mut items: Vec<_> = items.into_iter().collect();
220 228 items.sort_by(|e0, e1| e0.0.cmp(e1.0));
221 229
222 230 for (item, config_entry) in items {
223 231 write_bytes!(
224 232 out,
225 233 b"{}.{}={} # {}\n",
226 234 section,
227 235 item,
228 236 &config_entry.bytes,
229 237 &self.origin,
230 238 )?
231 239 }
232 240 }
233 241 Ok(())
234 242 }
235 243 }
236 244
237 245 /// Mapping of section item to value.
238 246 /// In the following:
239 247 /// ```text
240 248 /// [ui]
241 249 /// paginate=no
242 250 /// ```
243 251 /// "paginate" is the section item and "no" the value.
244 252 pub type ConfigItem = HashMap<Vec<u8>, ConfigValue>;
245 253
246 254 #[derive(Clone, Debug, PartialEq)]
247 255 pub struct ConfigValue {
248 256 /// The raw bytes of the value (be it from the CLI, env or from a file)
249 257 pub bytes: Vec<u8>,
250 258 /// Only present if the value comes from a file, 1-indexed.
251 259 pub line: Option<usize>,
252 260 }
253 261
254 262 #[derive(Clone, Debug)]
255 263 pub enum ConfigOrigin {
256 264 /// From a configuration file
257 265 File(PathBuf),
258 266 /// From a `--config` CLI argument
259 267 CommandLine,
260 268 /// From environment variables like `$PAGER` or `$EDITOR`
261 269 Environment(Vec<u8>),
262 270 /* TODO cli
263 271 * TODO defaults (configitems.py)
264 272 * TODO extensions
265 273 * TODO Python resources?
266 274 * Others? */
267 275 }
268 276
269 277 impl DisplayBytes for ConfigOrigin {
270 278 fn display_bytes(
271 279 &self,
272 280 out: &mut dyn std::io::Write,
273 281 ) -> std::io::Result<()> {
274 282 match self {
275 283 ConfigOrigin::File(p) => out.write_all(&get_bytes_from_path(p)),
276 284 ConfigOrigin::CommandLine => out.write_all(b"--config"),
277 285 ConfigOrigin::Environment(e) => write_bytes!(out, b"${}", e),
278 286 }
279 287 }
280 288 }
281 289
282 290 #[derive(Debug)]
283 291 pub struct ConfigParseError {
284 292 pub origin: ConfigOrigin,
285 293 pub line: Option<usize>,
286 294 pub message: Vec<u8>,
287 295 }
288 296
289 297 #[derive(Debug, derive_more::From)]
290 298 pub enum ConfigError {
291 299 Parse(ConfigParseError),
292 300 Other(HgError),
293 301 }
294 302
295 303 fn make_regex(pattern: &'static str) -> Regex {
296 304 Regex::new(pattern).expect("expected a valid regex")
297 305 }
@@ -1,21 +1,21 b''
1 1 [package]
2 2 name = "rhg"
3 3 version = "0.1.0"
4 4 authors = [
5 5 "Antoine Cezar <antoine.cezar@octobus.net>",
6 6 "Raphaël Gomès <raphael.gomes@octobus.net>",
7 7 ]
8 8 edition = "2018"
9 9
10 10 [dependencies]
11 11 hg-core = { path = "../hg-core"}
12 12 chrono = "0.4.19"
13 13 clap = "2.33.1"
14 14 derive_more = "0.99"
15 15 lazy_static = "1.4.0"
16 16 log = "0.4.11"
17 17 micro-timer = "0.3.1"
18 18 regex = "1.3.9"
19 19 env_logger = "0.7.1"
20 format-bytes = "0.2.0"
20 format-bytes = "0.2.1"
21 21 users = "0.11.0"
@@ -1,354 +1,378 b''
1 1 extern crate log;
2 2 use crate::ui::Ui;
3 3 use clap::App;
4 4 use clap::AppSettings;
5 5 use clap::Arg;
6 6 use clap::ArgMatches;
7 use format_bytes::format_bytes;
7 use format_bytes::{format_bytes, join};
8 8 use hg::config::Config;
9 9 use hg::repo::{Repo, RepoError};
10 10 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
11 11 use hg::utils::SliceExt;
12 12 use std::ffi::OsString;
13 13 use std::path::PathBuf;
14 14 use std::process::Command;
15 15
16 16 mod blackbox;
17 17 mod error;
18 18 mod exitcode;
19 19 mod ui;
20 20 use error::CommandError;
21 21
22 22 fn main_with_result(
23 23 process_start_time: &blackbox::ProcessStartTime,
24 24 ui: &ui::Ui,
25 25 repo: Result<&Repo, &NoRepoInCwdError>,
26 26 config: &Config,
27 27 ) -> Result<(), CommandError> {
28 check_extensions(config)?;
29
28 30 let app = App::new("rhg")
29 31 .global_setting(AppSettings::AllowInvalidUtf8)
30 32 .setting(AppSettings::SubcommandRequired)
31 33 .setting(AppSettings::VersionlessSubcommands)
32 34 .arg(
33 35 Arg::with_name("repository")
34 36 .help("repository root directory")
35 37 .short("-R")
36 38 .long("--repository")
37 39 .value_name("REPO")
38 40 .takes_value(true)
39 41 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
40 42 .global(true),
41 43 )
42 44 .arg(
43 45 Arg::with_name("config")
44 46 .help("set/override config option (use 'section.name=value')")
45 47 .long("--config")
46 48 .value_name("CONFIG")
47 49 .takes_value(true)
48 50 .global(true)
49 51 // Ok: `--config section.key1=val --config section.key2=val2`
50 52 .multiple(true)
51 53 // Not ok: `--config section.key1=val section.key2=val2`
52 54 .number_of_values(1),
53 55 )
54 56 .version("0.0.1");
55 57 let app = add_subcommand_args(app);
56 58
57 59 let matches = app.clone().get_matches_safe()?;
58 60
59 61 let (subcommand_name, subcommand_matches) = matches.subcommand();
60 62 let run = subcommand_run_fn(subcommand_name)
61 63 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
62 64 let subcommand_args = subcommand_matches
63 65 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
64 66
65 67 let invocation = CliInvocation {
66 68 ui,
67 69 subcommand_args,
68 70 config,
69 71 repo,
70 72 };
71 73 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
72 74 blackbox.log_command_start();
73 75 let result = run(&invocation);
74 76 blackbox.log_command_end(exit_code(&result));
75 77 result
76 78 }
77 79
78 80 fn main() {
79 81 // Run this first, before we find out if the blackbox extension is even
80 82 // enabled, in order to include everything in-between in the duration
81 83 // measurements. Reading config files can be slow if they’re on NFS.
82 84 let process_start_time = blackbox::ProcessStartTime::now();
83 85
84 86 env_logger::init();
85 87 let ui = ui::Ui::new();
86 88
87 89 let early_args = EarlyArgs::parse(std::env::args_os());
88 90 let non_repo_config =
89 91 Config::load(early_args.config).unwrap_or_else(|error| {
90 92 // Normally this is decided based on config, but we don’t have that
91 93 // available. As of this writing config loading never returns an
92 94 // "unsupported" error but that is not enforced by the type system.
93 95 let on_unsupported = OnUnsupported::Abort;
94 96
95 97 exit(&ui, on_unsupported, Err(error.into()))
96 98 });
97 99
98 100 if let Some(repo_path_bytes) = &early_args.repo {
99 101 lazy_static::lazy_static! {
100 102 static ref SCHEME_RE: regex::bytes::Regex =
101 103 // Same as `_matchscheme` in `mercurial/util.py`
102 104 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
103 105 }
104 106 if SCHEME_RE.is_match(&repo_path_bytes) {
105 107 exit(
106 108 &ui,
107 109 OnUnsupported::from_config(&non_repo_config),
108 110 Err(CommandError::UnsupportedFeature {
109 111 message: format_bytes!(
110 112 b"URL-like --repository {}",
111 113 repo_path_bytes
112 114 ),
113 115 }),
114 116 )
115 117 }
116 118 }
117 119 let repo_path = early_args.repo.as_deref().map(get_path_from_bytes);
118 120 let repo_result = match Repo::find(&non_repo_config, repo_path) {
119 121 Ok(repo) => Ok(repo),
120 122 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
121 123 // Not finding a repo is not fatal yet, if `-R` was not given
122 124 Err(NoRepoInCwdError { cwd: at })
123 125 }
124 126 Err(error) => exit(
125 127 &ui,
126 128 OnUnsupported::from_config(&non_repo_config),
127 129 Err(error.into()),
128 130 ),
129 131 };
130 132
131 133 let config = if let Ok(repo) = &repo_result {
132 134 repo.config()
133 135 } else {
134 136 &non_repo_config
135 137 };
136 138
137 139 let result = main_with_result(
138 140 &process_start_time,
139 141 &ui,
140 142 repo_result.as_ref(),
141 143 config,
142 144 );
143 145 exit(&ui, OnUnsupported::from_config(config), result)
144 146 }
145 147
146 148 fn exit_code(result: &Result<(), CommandError>) -> i32 {
147 149 match result {
148 150 Ok(()) => exitcode::OK,
149 151 Err(CommandError::Abort { .. }) => exitcode::ABORT,
150 152
151 153 // Exit with a specific code and no error message to let a potential
152 154 // wrapper script fallback to Python-based Mercurial.
153 155 Err(CommandError::UnsupportedFeature { .. }) => {
154 156 exitcode::UNIMPLEMENTED
155 157 }
156 158 }
157 159 }
158 160
159 161 fn exit(
160 162 ui: &Ui,
161 163 mut on_unsupported: OnUnsupported,
162 164 result: Result<(), CommandError>,
163 165 ) -> ! {
164 166 if let (
165 167 OnUnsupported::Fallback { executable },
166 168 Err(CommandError::UnsupportedFeature { .. }),
167 169 ) = (&on_unsupported, &result)
168 170 {
169 171 let mut args = std::env::args_os();
170 172 let executable_path = get_path_from_bytes(&executable);
171 173 let this_executable = args.next().expect("exepcted argv[0] to exist");
172 174 if executable_path == &PathBuf::from(this_executable) {
173 175 // Avoid spawning infinitely many processes until resource
174 176 // exhaustion.
175 177 let _ = ui.write_stderr(&format_bytes!(
176 178 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
177 179 points to `rhg` itself.\n",
178 180 executable
179 181 ));
180 182 on_unsupported = OnUnsupported::Abort
181 183 } else {
182 184 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
183 185 let result = Command::new(executable_path).args(args).status();
184 186 match result {
185 187 Ok(status) => std::process::exit(
186 188 status.code().unwrap_or(exitcode::ABORT),
187 189 ),
188 190 Err(error) => {
189 191 let _ = ui.write_stderr(&format_bytes!(
190 192 b"tried to fall back to a '{}' sub-process but got error {}\n",
191 193 executable, format_bytes::Utf8(error)
192 194 ));
193 195 on_unsupported = OnUnsupported::Abort
194 196 }
195 197 }
196 198 }
197 199 }
198 200 match &result {
199 201 Ok(_) => {}
200 202 Err(CommandError::Abort { message }) => {
201 203 if !message.is_empty() {
202 204 // Ignore errors when writing to stderr, we’re already exiting
203 205 // with failure code so there’s not much more we can do.
204 206 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
205 207 }
206 208 }
207 209 Err(CommandError::UnsupportedFeature { message }) => {
208 210 match on_unsupported {
209 211 OnUnsupported::Abort => {
210 212 let _ = ui.write_stderr(&format_bytes!(
211 213 b"unsupported feature: {}\n",
212 214 message
213 215 ));
214 216 }
215 217 OnUnsupported::AbortSilent => {}
216 218 OnUnsupported::Fallback { .. } => unreachable!(),
217 219 }
218 220 }
219 221 }
220 222 std::process::exit(exit_code(&result))
221 223 }
222 224
223 225 macro_rules! subcommands {
224 226 ($( $command: ident )+) => {
225 227 mod commands {
226 228 $(
227 229 pub mod $command;
228 230 )+
229 231 }
230 232
231 233 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
232 234 app
233 235 $(
234 236 .subcommand(commands::$command::args())
235 237 )+
236 238 }
237 239
238 240 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
239 241
240 242 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
241 243 match name {
242 244 $(
243 245 stringify!($command) => Some(commands::$command::run),
244 246 )+
245 247 _ => None,
246 248 }
247 249 }
248 250 };
249 251 }
250 252
251 253 subcommands! {
252 254 cat
253 255 debugdata
254 256 debugrequirements
255 257 files
256 258 root
257 259 config
258 260 }
259 261 pub struct CliInvocation<'a> {
260 262 ui: &'a Ui,
261 263 subcommand_args: &'a ArgMatches<'a>,
262 264 config: &'a Config,
263 265 /// References inside `Result` is a bit peculiar but allow
264 266 /// `invocation.repo?` to work out with `&CliInvocation` since this
265 267 /// `Result` type is `Copy`.
266 268 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
267 269 }
268 270
269 271 struct NoRepoInCwdError {
270 272 cwd: PathBuf,
271 273 }
272 274
273 275 /// CLI arguments to be parsed "early" in order to be able to read
274 276 /// configuration before using Clap. Ideally we would also use Clap for this,
275 277 /// see <https://github.com/clap-rs/clap/discussions/2366>.
276 278 ///
277 279 /// These arguments are still declared when we do use Clap later, so that Clap
278 280 /// does not return an error for their presence.
279 281 struct EarlyArgs {
280 282 /// Values of all `--config` arguments. (Possibly none)
281 283 config: Vec<Vec<u8>>,
282 284 /// Value of the `-R` or `--repository` argument, if any.
283 285 repo: Option<Vec<u8>>,
284 286 }
285 287
286 288 impl EarlyArgs {
287 289 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
288 290 let mut args = args.into_iter().map(get_bytes_from_os_str);
289 291 let mut config = Vec::new();
290 292 let mut repo = None;
291 293 // Use `while let` instead of `for` so that we can also call
292 294 // `args.next()` inside the loop.
293 295 while let Some(arg) = args.next() {
294 296 if arg == b"--config" {
295 297 if let Some(value) = args.next() {
296 298 config.push(value)
297 299 }
298 300 } else if let Some(value) = arg.drop_prefix(b"--config=") {
299 301 config.push(value.to_owned())
300 302 }
301 303
302 304 if arg == b"--repository" || arg == b"-R" {
303 305 if let Some(value) = args.next() {
304 306 repo = Some(value)
305 307 }
306 308 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
307 309 repo = Some(value.to_owned())
308 310 } else if let Some(value) = arg.drop_prefix(b"-R") {
309 311 repo = Some(value.to_owned())
310 312 }
311 313 }
312 314 Self { config, repo }
313 315 }
314 316 }
315 317
316 318 /// What to do when encountering some unsupported feature.
317 319 ///
318 320 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
319 321 enum OnUnsupported {
320 322 /// Print an error message describing what feature is not supported,
321 323 /// and exit with code 252.
322 324 Abort,
323 325 /// Silently exit with code 252.
324 326 AbortSilent,
325 327 /// Try running a Python implementation
326 328 Fallback { executable: Vec<u8> },
327 329 }
328 330
329 331 impl OnUnsupported {
330 332 const DEFAULT: Self = OnUnsupported::Abort;
331 333 const DEFAULT_FALLBACK_EXECUTABLE: &'static [u8] = b"hg";
332 334
333 335 fn from_config(config: &Config) -> Self {
334 336 match config
335 337 .get(b"rhg", b"on-unsupported")
336 338 .map(|value| value.to_ascii_lowercase())
337 339 .as_deref()
338 340 {
339 341 Some(b"abort") => OnUnsupported::Abort,
340 342 Some(b"abort-silent") => OnUnsupported::AbortSilent,
341 343 Some(b"fallback") => OnUnsupported::Fallback {
342 344 executable: config
343 345 .get(b"rhg", b"fallback-executable")
344 346 .unwrap_or(Self::DEFAULT_FALLBACK_EXECUTABLE)
345 347 .to_owned(),
346 348 },
347 349 None => Self::DEFAULT,
348 350 Some(_) => {
349 351 // TODO: warn about unknown config value
350 352 Self::DEFAULT
351 353 }
352 354 }
353 355 }
354 356 }
357
358 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
359
360 fn check_extensions(config: &Config) -> Result<(), CommandError> {
361 let enabled = config.get_section_keys(b"extensions");
362
363 let mut unsupported = enabled;
364 for supported in SUPPORTED_EXTENSIONS {
365 unsupported.remove(supported);
366 }
367
368 if unsupported.is_empty() {
369 Ok(())
370 } else {
371 Err(CommandError::UnsupportedFeature {
372 message: format_bytes!(
373 b"extensions: {}",
374 join(unsupported, b", ")
375 ),
376 })
377 }
378 }
General Comments 0
You need to be logged in to leave comments. Login now