##// END OF EJS Templates
rust-config: add support for default config items...
Raphaël Gomès -
r51656:f8412da8 default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (669 lines changed) Show them Hide them
@@ -0,0 +1,669 b''
1 //! Code for parsing default Mercurial config items.
2 use itertools::Itertools;
3 use serde::Deserialize;
4
5 use crate::{errors::HgError, exit_codes, FastHashMap};
6
7 /// Corresponds to the structure of `mercurial/configitems.toml`.
8 #[derive(Debug, Deserialize)]
9 pub struct ConfigItems {
10 items: Vec<DefaultConfigItem>,
11 templates: FastHashMap<String, Vec<TemplateItem>>,
12 #[serde(rename = "template-applications")]
13 template_applications: Vec<TemplateApplication>,
14 }
15
16 /// Corresponds to a config item declaration in `mercurial/configitems.toml`.
17 #[derive(Clone, Debug, PartialEq, Deserialize)]
18 #[serde(try_from = "RawDefaultConfigItem")]
19 pub struct DefaultConfigItem {
20 /// Section of the config the item is in (e.g. `[merge-tools]`)
21 section: String,
22 /// Name of the item (e.g. `meld.gui`)
23 name: String,
24 /// Default value (can be dynamic, see [`DefaultConfigItemType`])
25 default: Option<DefaultConfigItemType>,
26 /// If the config option is generic (e.g. `merge-tools.*`), defines
27 /// the priority of this item relative to other generic items.
28 /// If we're looking for <pattern>, then all generic items within the same
29 /// section will be sorted by order of priority, and the first regex match
30 /// against `name` is returned.
31 #[serde(default)]
32 priority: Option<isize>,
33 /// Aliases, if any. Each alias is a tuple of `(section, name)` for each
34 /// option that is aliased to this one.
35 #[serde(default)]
36 alias: Vec<(String, String)>,
37 /// Whether the config item is marked as experimental
38 #[serde(default)]
39 experimental: bool,
40 /// The (possibly empty) docstring for the item
41 #[serde(default)]
42 documentation: String,
43 }
44
45 /// Corresponds to the raw (i.e. on disk) structure of config items. Used as
46 /// an intermediate step in deserialization.
47 #[derive(Clone, Debug, Deserialize)]
48 struct RawDefaultConfigItem {
49 section: String,
50 name: String,
51 default: Option<toml::Value>,
52 #[serde(rename = "default-type")]
53 default_type: Option<String>,
54 #[serde(default)]
55 priority: isize,
56 #[serde(default)]
57 generic: bool,
58 #[serde(default)]
59 alias: Vec<(String, String)>,
60 #[serde(default)]
61 experimental: bool,
62 #[serde(default)]
63 documentation: String,
64 }
65
66 impl TryFrom<RawDefaultConfigItem> for DefaultConfigItem {
67 type Error = HgError;
68
69 fn try_from(value: RawDefaultConfigItem) -> Result<Self, Self::Error> {
70 Ok(Self {
71 section: value.section,
72 name: value.name,
73 default: raw_default_to_concrete(
74 value.default_type,
75 value.default,
76 )?,
77 priority: if value.generic {
78 Some(value.priority)
79 } else {
80 None
81 },
82 alias: value.alias,
83 experimental: value.experimental,
84 documentation: value.documentation,
85 })
86 }
87 }
88
89 impl DefaultConfigItem {
90 fn is_generic(&self) -> bool {
91 self.priority.is_some()
92 }
93 }
94
95 impl<'a> TryFrom<&'a DefaultConfigItem> for Option<&'a str> {
96 type Error = HgError;
97
98 fn try_from(
99 value: &'a DefaultConfigItem,
100 ) -> Result<Option<&'a str>, Self::Error> {
101 match &value.default {
102 Some(default) => {
103 let err = HgError::abort(
104 format!(
105 "programming error: wrong query on config item '{}.{}'",
106 value.section,
107 value.name
108 ),
109 exit_codes::ABORT,
110 Some(format!(
111 "asked for '&str', type of default is '{}'",
112 default.type_str()
113 )),
114 );
115 match default {
116 DefaultConfigItemType::Primitive(toml::Value::String(
117 s,
118 )) => Ok(Some(s)),
119 _ => Err(err),
120 }
121 }
122 None => Ok(None),
123 }
124 }
125 }
126
127 impl TryFrom<&DefaultConfigItem> for Option<bool> {
128 type Error = HgError;
129
130 fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> {
131 match &value.default {
132 Some(default) => {
133 let err = HgError::abort(
134 format!(
135 "programming error: wrong query on config item '{}.{}'",
136 value.section,
137 value.name
138 ),
139 exit_codes::ABORT,
140 Some(format!(
141 "asked for 'bool', type of default is '{}'",
142 default.type_str()
143 )),
144 );
145 match default {
146 DefaultConfigItemType::Primitive(
147 toml::Value::Boolean(b),
148 ) => Ok(Some(*b)),
149 _ => Err(err),
150 }
151 }
152 None => Ok(Some(false)),
153 }
154 }
155 }
156
157 impl TryFrom<&DefaultConfigItem> for Option<u32> {
158 type Error = HgError;
159
160 fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> {
161 match &value.default {
162 Some(default) => {
163 let err = HgError::abort(
164 format!(
165 "programming error: wrong query on config item '{}.{}'",
166 value.section,
167 value.name
168 ),
169 exit_codes::ABORT,
170 Some(format!(
171 "asked for 'u32', type of default is '{}'",
172 default.type_str()
173 )),
174 );
175 match default {
176 DefaultConfigItemType::Primitive(
177 toml::Value::Integer(b),
178 ) => {
179 Ok(Some((*b).try_into().expect("TOML integer to u32")))
180 }
181 _ => Err(err),
182 }
183 }
184 None => Ok(None),
185 }
186 }
187 }
188
189 impl TryFrom<&DefaultConfigItem> for Option<u64> {
190 type Error = HgError;
191
192 fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> {
193 match &value.default {
194 Some(default) => {
195 let err = HgError::abort(
196 format!(
197 "programming error: wrong query on config item '{}.{}'",
198 value.section,
199 value.name
200 ),
201 exit_codes::ABORT,
202 Some(format!(
203 "asked for 'u64', type of default is '{}'",
204 default.type_str()
205 )),
206 );
207 match default {
208 DefaultConfigItemType::Primitive(
209 toml::Value::Integer(b),
210 ) => {
211 Ok(Some((*b).try_into().expect("TOML integer to u64")))
212 }
213 _ => Err(err),
214 }
215 }
216 None => Ok(None),
217 }
218 }
219 }
220
221 /// Allows abstracting over more complex default values than just primitives.
222 /// The former `configitems.py` contained some dynamic code that is encoded
223 /// in this enum.
224 #[derive(Debug, PartialEq, Clone, Deserialize)]
225 pub enum DefaultConfigItemType {
226 /// Some primitive type (string, integer, boolean)
227 Primitive(toml::Value),
228 /// A dynamic value that will be given by the code at runtime
229 Dynamic,
230 /// An lazily-returned array (possibly only relevant in the Python impl)
231 /// Example: `lambda: [b"zstd", b"zlib"]`
232 Lambda(Vec<String>),
233 /// For now, a special case for `web.encoding` that points to the
234 /// `encoding.encoding` module in the Python impl so that local encoding
235 /// is correctly resolved at runtime
236 LazyModule(String),
237 ListType,
238 }
239
240 impl DefaultConfigItemType {
241 pub fn type_str(&self) -> &str {
242 match self {
243 DefaultConfigItemType::Primitive(primitive) => {
244 primitive.type_str()
245 }
246 DefaultConfigItemType::Dynamic => "dynamic",
247 DefaultConfigItemType::Lambda(_) => "lambda",
248 DefaultConfigItemType::LazyModule(_) => "lazy_module",
249 DefaultConfigItemType::ListType => "list_type",
250 }
251 }
252 }
253
254 /// Most of the fields are shared with [`DefaultConfigItem`].
255 #[derive(Debug, Clone, Deserialize)]
256 #[serde(try_from = "RawTemplateItem")]
257 struct TemplateItem {
258 suffix: String,
259 default: Option<DefaultConfigItemType>,
260 priority: Option<isize>,
261 #[serde(default)]
262 alias: Vec<(String, String)>,
263 #[serde(default)]
264 experimental: bool,
265 #[serde(default)]
266 documentation: String,
267 }
268
269 /// Corresponds to the raw (i.e. on disk) representation of a template item.
270 /// Used as an intermediate step in deserialization.
271 #[derive(Clone, Debug, Deserialize)]
272 struct RawTemplateItem {
273 suffix: String,
274 default: Option<toml::Value>,
275 #[serde(rename = "default-type")]
276 default_type: Option<String>,
277 #[serde(default)]
278 priority: isize,
279 #[serde(default)]
280 generic: bool,
281 #[serde(default)]
282 alias: Vec<(String, String)>,
283 #[serde(default)]
284 experimental: bool,
285 #[serde(default)]
286 documentation: String,
287 }
288
289 impl TemplateItem {
290 fn into_default_item(
291 self,
292 application: TemplateApplication,
293 ) -> DefaultConfigItem {
294 DefaultConfigItem {
295 section: application.section,
296 name: application
297 .prefix
298 .map(|prefix| format!("{}.{}", prefix, self.suffix))
299 .unwrap_or(self.suffix),
300 default: self.default,
301 priority: self.priority,
302 alias: self.alias,
303 experimental: self.experimental,
304 documentation: self.documentation,
305 }
306 }
307 }
308
309 impl TryFrom<RawTemplateItem> for TemplateItem {
310 type Error = HgError;
311
312 fn try_from(value: RawTemplateItem) -> Result<Self, Self::Error> {
313 Ok(Self {
314 suffix: value.suffix,
315 default: raw_default_to_concrete(
316 value.default_type,
317 value.default,
318 )?,
319 priority: if value.generic {
320 Some(value.priority)
321 } else {
322 None
323 },
324 alias: value.alias,
325 experimental: value.experimental,
326 documentation: value.documentation,
327 })
328 }
329 }
330
331 /// Transforms the on-disk string-based representation of complex default types
332 /// to the concrete [`DefaultconfigItemType`].
333 fn raw_default_to_concrete(
334 default_type: Option<String>,
335 default: Option<toml::Value>,
336 ) -> Result<Option<DefaultConfigItemType>, HgError> {
337 Ok(match default_type.as_deref() {
338 None => default.as_ref().map(|default| {
339 DefaultConfigItemType::Primitive(default.to_owned())
340 }),
341 Some("dynamic") => Some(DefaultConfigItemType::Dynamic),
342 Some("list_type") => Some(DefaultConfigItemType::ListType),
343 Some("lambda") => match &default {
344 Some(default) => Some(DefaultConfigItemType::Lambda(
345 default.to_owned().try_into().map_err(|e| {
346 HgError::abort(
347 e.to_string(),
348 exit_codes::ABORT,
349 Some("Check 'mercurial/configitems.toml'".into()),
350 )
351 })?,
352 )),
353 None => {
354 return Err(HgError::abort(
355 "lambda defined with no return value".to_string(),
356 exit_codes::ABORT,
357 Some("Check 'mercurial/configitems.toml'".into()),
358 ))
359 }
360 },
361 Some("lazy_module") => match &default {
362 Some(default) => {
363 Some(DefaultConfigItemType::LazyModule(match default {
364 toml::Value::String(module) => module.to_owned(),
365 _ => {
366 return Err(HgError::abort(
367 "lazy_module module name should be a string"
368 .to_string(),
369 exit_codes::ABORT,
370 Some("Check 'mercurial/configitems.toml'".into()),
371 ))
372 }
373 }))
374 }
375 None => {
376 return Err(HgError::abort(
377 "lazy_module should have a default value".to_string(),
378 exit_codes::ABORT,
379 Some("Check 'mercurial/configitems.toml'".into()),
380 ))
381 }
382 },
383 Some(invalid) => {
384 return Err(HgError::abort(
385 format!("invalid default_type '{}'", invalid),
386 exit_codes::ABORT,
387 Some("Check 'mercurial/configitems.toml'".into()),
388 ))
389 }
390 })
391 }
392
393 #[derive(Debug, Clone, Deserialize)]
394 struct TemplateApplication {
395 template: String,
396 section: String,
397 #[serde(default)]
398 prefix: Option<String>,
399 }
400
401 /// Represents the (dynamic) set of default core Mercurial config items from
402 /// `mercurial/configitems.toml`.
403 #[derive(Clone, Debug, Default)]
404 pub struct DefaultConfig {
405 /// Mapping of section -> (mapping of name -> item)
406 items: FastHashMap<String, FastHashMap<String, DefaultConfigItem>>,
407 }
408
409 impl DefaultConfig {
410 pub fn empty() -> DefaultConfig {
411 Self {
412 items: Default::default(),
413 }
414 }
415
416 /// Returns `Self`, given the contents of `mercurial/configitems.toml`
417 #[logging_timer::time("trace")]
418 pub fn from_contents(contents: &str) -> Result<Self, HgError> {
419 let mut from_file: ConfigItems =
420 toml::from_str(contents).map_err(|e| {
421 HgError::abort(
422 e.to_string(),
423 exit_codes::ABORT,
424 Some("Check 'mercurial/configitems.toml'".into()),
425 )
426 })?;
427
428 let mut flat_items = from_file.items;
429
430 for application in from_file.template_applications.drain(..) {
431 match from_file.templates.get(&application.template) {
432 None => return Err(
433 HgError::abort(
434 format!(
435 "template application refers to undefined template '{}'",
436 application.template
437 ),
438 exit_codes::ABORT,
439 Some("Check 'mercurial/configitems.toml'".into())
440 )
441 ),
442 Some(template_items) => {
443 for template_item in template_items {
444 flat_items.push(
445 template_item
446 .clone()
447 .into_default_item(application.clone()),
448 )
449 }
450 }
451 };
452 }
453
454 let items = flat_items.into_iter().fold(
455 FastHashMap::default(),
456 |mut acc, item| {
457 acc.entry(item.section.to_owned())
458 .or_insert_with(|| {
459 let mut section = FastHashMap::default();
460 section.insert(item.name.to_owned(), item.to_owned());
461 section
462 })
463 .insert(item.name.to_owned(), item);
464 acc
465 },
466 );
467
468 Ok(Self { items })
469 }
470
471 /// Return the default config item that matches `section` and `item`.
472 pub fn get(
473 &self,
474 section: &[u8],
475 item: &[u8],
476 ) -> Option<&DefaultConfigItem> {
477 // Core items must be valid UTF-8
478 let section = String::from_utf8_lossy(section);
479 let section_map = self.items.get(section.as_ref())?;
480 let item_name_lossy = String::from_utf8_lossy(item);
481 match section_map.get(item_name_lossy.as_ref()) {
482 Some(item) => Some(item),
483 None => {
484 for generic_item in section_map
485 .values()
486 .filter(|item| item.is_generic())
487 .sorted_by_key(|item| match item.priority {
488 Some(priority) => (priority, &item.name),
489 _ => unreachable!(),
490 })
491 {
492 if regex::bytes::Regex::new(&generic_item.name)
493 .expect("invalid regex in configitems")
494 .is_match(item)
495 {
496 return Some(generic_item);
497 }
498 }
499 None
500 }
501 }
502 }
503 }
504
505 #[cfg(test)]
506 mod tests {
507 use crate::config::config_items::{
508 DefaultConfigItem, DefaultConfigItemType,
509 };
510
511 use super::DefaultConfig;
512
513 #[test]
514 fn test_config_read() {
515 let contents = r#"
516 [[items]]
517 section = "alias"
518 name = "abcd.*"
519 default = 3
520 generic = true
521 priority = -1
522
523 [[items]]
524 section = "alias"
525 name = ".*"
526 default-type = "dynamic"
527 generic = true
528
529 [[items]]
530 section = "cmdserver"
531 name = "track-log"
532 default-type = "lambda"
533 default = [ "chgserver", "cmdserver", "repocache",]
534
535 [[items]]
536 section = "chgserver"
537 name = "idletimeout"
538 default = 3600
539
540 [[items]]
541 section = "cmdserver"
542 name = "message-encodings"
543 default-type = "list_type"
544
545 [[items]]
546 section = "web"
547 name = "encoding"
548 default-type = "lazy_module"
549 default = "encoding.encoding"
550
551 [[items]]
552 section = "command-templates"
553 name = "graphnode"
554 alias = [["ui", "graphnodetemplate"]]
555 documentation = """This is a docstring.
556 This is another line \
557 but this is not."""
558
559 [[items]]
560 section = "censor"
561 name = "policy"
562 default = "abort"
563 experimental = true
564
565 [[template-applications]]
566 template = "diff-options"
567 section = "commands"
568 prefix = "revert.interactive"
569
570 [[template-applications]]
571 template = "diff-options"
572 section = "diff"
573
574 [templates]
575 [[templates.diff-options]]
576 suffix = "nodates"
577 default = false
578
579 [[templates.diff-options]]
580 suffix = "showfunc"
581 default = false
582
583 [[templates.diff-options]]
584 suffix = "unified"
585 "#;
586 let res = DefaultConfig::from_contents(contents);
587 let config = match res {
588 Ok(config) => config,
589 Err(e) => panic!("{}", e),
590 };
591 let expected = DefaultConfigItem {
592 section: "censor".into(),
593 name: "policy".into(),
594 default: Some(DefaultConfigItemType::Primitive("abort".into())),
595 priority: None,
596 alias: vec![],
597 experimental: true,
598 documentation: "".into(),
599 };
600 assert_eq!(config.get(b"censor", b"policy"), Some(&expected));
601
602 // Test generic priority. The `.*` pattern is wider than `abcd.*`, but
603 // `abcd.*` has priority, so it should match first.
604 let expected = DefaultConfigItem {
605 section: "alias".into(),
606 name: "abcd.*".into(),
607 default: Some(DefaultConfigItemType::Primitive(3.into())),
608 priority: Some(-1),
609 alias: vec![],
610 experimental: false,
611 documentation: "".into(),
612 };
613 assert_eq!(config.get(b"alias", b"abcdsomething"), Some(&expected));
614
615 //... but if it doesn't, we should fallback to `.*`
616 let expected = DefaultConfigItem {
617 section: "alias".into(),
618 name: ".*".into(),
619 default: Some(DefaultConfigItemType::Dynamic),
620 priority: Some(0),
621 alias: vec![],
622 experimental: false,
623 documentation: "".into(),
624 };
625 assert_eq!(config.get(b"alias", b"something"), Some(&expected));
626
627 let expected = DefaultConfigItem {
628 section: "chgserver".into(),
629 name: "idletimeout".into(),
630 default: Some(DefaultConfigItemType::Primitive(3600.into())),
631 priority: None,
632 alias: vec![],
633 experimental: false,
634 documentation: "".into(),
635 };
636 assert_eq!(config.get(b"chgserver", b"idletimeout"), Some(&expected));
637
638 let expected = DefaultConfigItem {
639 section: "cmdserver".into(),
640 name: "track-log".into(),
641 default: Some(DefaultConfigItemType::Lambda(vec![
642 "chgserver".into(),
643 "cmdserver".into(),
644 "repocache".into(),
645 ])),
646 priority: None,
647 alias: vec![],
648 experimental: false,
649 documentation: "".into(),
650 };
651 assert_eq!(config.get(b"cmdserver", b"track-log"), Some(&expected));
652
653 let expected = DefaultConfigItem {
654 section: "command-templates".into(),
655 name: "graphnode".into(),
656 default: None,
657 priority: None,
658 alias: vec![("ui".into(), "graphnodetemplate".into())],
659 experimental: false,
660 documentation:
661 "This is a docstring.\nThis is another line but this is not."
662 .into(),
663 };
664 assert_eq!(
665 config.get(b"command-templates", b"graphnode"),
666 Some(&expected)
667 );
668 }
669 }
@@ -1,1435 +1,1525 b''
1 # This file is automatically @generated by Cargo.
1 # This file is automatically @generated by Cargo.
2 # It is not intended for manual editing.
2 # It is not intended for manual editing.
3 version = 3
3 version = 3
4
4
5 [[package]]
5 [[package]]
6 name = "adler"
6 name = "adler"
7 version = "1.0.2"
7 version = "1.0.2"
8 source = "registry+https://github.com/rust-lang/crates.io-index"
8 source = "registry+https://github.com/rust-lang/crates.io-index"
9 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
9 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
10
10
11 [[package]]
11 [[package]]
12 name = "ahash"
12 name = "ahash"
13 version = "0.8.2"
13 version = "0.8.2"
14 source = "registry+https://github.com/rust-lang/crates.io-index"
14 source = "registry+https://github.com/rust-lang/crates.io-index"
15 checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
15 checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
16 dependencies = [
16 dependencies = [
17 "cfg-if",
17 "cfg-if",
18 "once_cell",
18 "once_cell",
19 "version_check",
19 "version_check",
20 ]
20 ]
21
21
22 [[package]]
22 [[package]]
23 name = "aho-corasick"
23 name = "aho-corasick"
24 version = "0.7.19"
24 version = "0.7.19"
25 source = "registry+https://github.com/rust-lang/crates.io-index"
25 source = "registry+https://github.com/rust-lang/crates.io-index"
26 checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
26 checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
27 dependencies = [
27 dependencies = [
28 "memchr",
28 "memchr",
29 ]
29 ]
30
30
31 [[package]]
31 [[package]]
32 name = "android_system_properties"
32 name = "android_system_properties"
33 version = "0.1.5"
33 version = "0.1.5"
34 source = "registry+https://github.com/rust-lang/crates.io-index"
34 source = "registry+https://github.com/rust-lang/crates.io-index"
35 checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
35 checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
36 dependencies = [
36 dependencies = [
37 "libc",
37 "libc",
38 ]
38 ]
39
39
40 [[package]]
40 [[package]]
41 name = "atty"
41 name = "atty"
42 version = "0.2.14"
42 version = "0.2.14"
43 source = "registry+https://github.com/rust-lang/crates.io-index"
43 source = "registry+https://github.com/rust-lang/crates.io-index"
44 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
44 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
45 dependencies = [
45 dependencies = [
46 "hermit-abi",
46 "hermit-abi",
47 "libc",
47 "libc",
48 "winapi",
48 "winapi",
49 ]
49 ]
50
50
51 [[package]]
51 [[package]]
52 name = "autocfg"
52 name = "autocfg"
53 version = "1.1.0"
53 version = "1.1.0"
54 source = "registry+https://github.com/rust-lang/crates.io-index"
54 source = "registry+https://github.com/rust-lang/crates.io-index"
55 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
55 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
56
56
57 [[package]]
57 [[package]]
58 name = "bitflags"
58 name = "bitflags"
59 version = "1.3.2"
59 version = "1.3.2"
60 source = "registry+https://github.com/rust-lang/crates.io-index"
60 source = "registry+https://github.com/rust-lang/crates.io-index"
61 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
61 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
62
62
63 [[package]]
63 [[package]]
64 name = "bitmaps"
64 name = "bitmaps"
65 version = "2.1.0"
65 version = "2.1.0"
66 source = "registry+https://github.com/rust-lang/crates.io-index"
66 source = "registry+https://github.com/rust-lang/crates.io-index"
67 checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
67 checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
68 dependencies = [
68 dependencies = [
69 "typenum",
69 "typenum",
70 ]
70 ]
71
71
72 [[package]]
72 [[package]]
73 name = "block-buffer"
73 name = "block-buffer"
74 version = "0.9.0"
74 version = "0.9.0"
75 source = "registry+https://github.com/rust-lang/crates.io-index"
75 source = "registry+https://github.com/rust-lang/crates.io-index"
76 checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
76 checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
77 dependencies = [
77 dependencies = [
78 "generic-array",
78 "generic-array",
79 ]
79 ]
80
80
81 [[package]]
81 [[package]]
82 name = "block-buffer"
82 name = "block-buffer"
83 version = "0.10.3"
83 version = "0.10.3"
84 source = "registry+https://github.com/rust-lang/crates.io-index"
84 source = "registry+https://github.com/rust-lang/crates.io-index"
85 checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
85 checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
86 dependencies = [
86 dependencies = [
87 "generic-array",
87 "generic-array",
88 ]
88 ]
89
89
90 [[package]]
90 [[package]]
91 name = "bumpalo"
91 name = "bumpalo"
92 version = "3.11.1"
92 version = "3.11.1"
93 source = "registry+https://github.com/rust-lang/crates.io-index"
93 source = "registry+https://github.com/rust-lang/crates.io-index"
94 checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
94 checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
95
95
96 [[package]]
96 [[package]]
97 name = "byteorder"
97 name = "byteorder"
98 version = "1.4.3"
98 version = "1.4.3"
99 source = "registry+https://github.com/rust-lang/crates.io-index"
99 source = "registry+https://github.com/rust-lang/crates.io-index"
100 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
100 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
101
101
102 [[package]]
102 [[package]]
103 name = "bytes-cast"
103 name = "bytes-cast"
104 version = "0.3.0"
104 version = "0.3.0"
105 source = "registry+https://github.com/rust-lang/crates.io-index"
105 source = "registry+https://github.com/rust-lang/crates.io-index"
106 checksum = "a20de93b91d7703ca0e39e12930e310acec5ff4d715f4166e0ab026babb352e8"
106 checksum = "a20de93b91d7703ca0e39e12930e310acec5ff4d715f4166e0ab026babb352e8"
107 dependencies = [
107 dependencies = [
108 "bytes-cast-derive",
108 "bytes-cast-derive",
109 ]
109 ]
110
110
111 [[package]]
111 [[package]]
112 name = "bytes-cast-derive"
112 name = "bytes-cast-derive"
113 version = "0.2.0"
113 version = "0.2.0"
114 source = "registry+https://github.com/rust-lang/crates.io-index"
114 source = "registry+https://github.com/rust-lang/crates.io-index"
115 checksum = "7470a6fcce58cde3d62cce758bf71007978b75247e6becd9255c9b884bcb4f71"
115 checksum = "7470a6fcce58cde3d62cce758bf71007978b75247e6becd9255c9b884bcb4f71"
116 dependencies = [
116 dependencies = [
117 "proc-macro2",
117 "proc-macro2",
118 "quote",
118 "quote",
119 "syn",
119 "syn",
120 ]
120 ]
121
121
122 [[package]]
122 [[package]]
123 name = "cc"
123 name = "cc"
124 version = "1.0.76"
124 version = "1.0.76"
125 source = "registry+https://github.com/rust-lang/crates.io-index"
125 source = "registry+https://github.com/rust-lang/crates.io-index"
126 checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
126 checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
127 dependencies = [
127 dependencies = [
128 "jobserver",
128 "jobserver",
129 ]
129 ]
130
130
131 [[package]]
131 [[package]]
132 name = "cfg-if"
132 name = "cfg-if"
133 version = "1.0.0"
133 version = "1.0.0"
134 source = "registry+https://github.com/rust-lang/crates.io-index"
134 source = "registry+https://github.com/rust-lang/crates.io-index"
135 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
135 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
136
136
137 [[package]]
137 [[package]]
138 name = "chrono"
138 name = "chrono"
139 version = "0.4.23"
139 version = "0.4.23"
140 source = "registry+https://github.com/rust-lang/crates.io-index"
140 source = "registry+https://github.com/rust-lang/crates.io-index"
141 checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
141 checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
142 dependencies = [
142 dependencies = [
143 "iana-time-zone",
143 "iana-time-zone",
144 "js-sys",
144 "js-sys",
145 "num-integer",
145 "num-integer",
146 "num-traits",
146 "num-traits",
147 "time",
147 "time",
148 "wasm-bindgen",
148 "wasm-bindgen",
149 "winapi",
149 "winapi",
150 ]
150 ]
151
151
152 [[package]]
152 [[package]]
153 name = "clap"
153 name = "clap"
154 version = "4.0.24"
154 version = "4.0.24"
155 source = "registry+https://github.com/rust-lang/crates.io-index"
155 source = "registry+https://github.com/rust-lang/crates.io-index"
156 checksum = "60494cedb60cb47462c0ff7be53de32c0e42a6fc2c772184554fa12bd9489c03"
156 checksum = "60494cedb60cb47462c0ff7be53de32c0e42a6fc2c772184554fa12bd9489c03"
157 dependencies = [
157 dependencies = [
158 "atty",
158 "atty",
159 "bitflags",
159 "bitflags",
160 "clap_derive",
160 "clap_derive",
161 "clap_lex",
161 "clap_lex",
162 "once_cell",
162 "once_cell",
163 "strsim",
163 "strsim",
164 "termcolor",
164 "termcolor",
165 ]
165 ]
166
166
167 [[package]]
167 [[package]]
168 name = "clap_derive"
168 name = "clap_derive"
169 version = "4.0.21"
169 version = "4.0.21"
170 source = "registry+https://github.com/rust-lang/crates.io-index"
170 source = "registry+https://github.com/rust-lang/crates.io-index"
171 checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
171 checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
172 dependencies = [
172 dependencies = [
173 "heck",
173 "heck",
174 "proc-macro-error",
174 "proc-macro-error",
175 "proc-macro2",
175 "proc-macro2",
176 "quote",
176 "quote",
177 "syn",
177 "syn",
178 ]
178 ]
179
179
180 [[package]]
180 [[package]]
181 name = "clap_lex"
181 name = "clap_lex"
182 version = "0.3.0"
182 version = "0.3.0"
183 source = "registry+https://github.com/rust-lang/crates.io-index"
183 source = "registry+https://github.com/rust-lang/crates.io-index"
184 checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
184 checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
185 dependencies = [
185 dependencies = [
186 "os_str_bytes",
186 "os_str_bytes",
187 ]
187 ]
188
188
189 [[package]]
189 [[package]]
190 name = "codespan-reporting"
190 name = "codespan-reporting"
191 version = "0.11.1"
191 version = "0.11.1"
192 source = "registry+https://github.com/rust-lang/crates.io-index"
192 source = "registry+https://github.com/rust-lang/crates.io-index"
193 checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
193 checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
194 dependencies = [
194 dependencies = [
195 "termcolor",
195 "termcolor",
196 "unicode-width",
196 "unicode-width",
197 ]
197 ]
198
198
199 [[package]]
199 [[package]]
200 name = "convert_case"
200 name = "convert_case"
201 version = "0.4.0"
201 version = "0.4.0"
202 source = "registry+https://github.com/rust-lang/crates.io-index"
202 source = "registry+https://github.com/rust-lang/crates.io-index"
203 checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
203 checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
204
204
205 [[package]]
205 [[package]]
206 name = "core-foundation-sys"
206 name = "core-foundation-sys"
207 version = "0.8.3"
207 version = "0.8.3"
208 source = "registry+https://github.com/rust-lang/crates.io-index"
208 source = "registry+https://github.com/rust-lang/crates.io-index"
209 checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
209 checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
210
210
211 [[package]]
211 [[package]]
212 name = "cpufeatures"
212 name = "cpufeatures"
213 version = "0.2.5"
213 version = "0.2.5"
214 source = "registry+https://github.com/rust-lang/crates.io-index"
214 source = "registry+https://github.com/rust-lang/crates.io-index"
215 checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
215 checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
216 dependencies = [
216 dependencies = [
217 "libc",
217 "libc",
218 ]
218 ]
219
219
220 [[package]]
220 [[package]]
221 name = "cpython"
221 name = "cpython"
222 version = "0.7.1"
222 version = "0.7.1"
223 source = "registry+https://github.com/rust-lang/crates.io-index"
223 source = "registry+https://github.com/rust-lang/crates.io-index"
224 checksum = "3052106c29da7390237bc2310c1928335733b286287754ea85e6093d2495280e"
224 checksum = "3052106c29da7390237bc2310c1928335733b286287754ea85e6093d2495280e"
225 dependencies = [
225 dependencies = [
226 "libc",
226 "libc",
227 "num-traits",
227 "num-traits",
228 "paste",
228 "paste",
229 "python3-sys",
229 "python3-sys",
230 ]
230 ]
231
231
232 [[package]]
232 [[package]]
233 name = "crc32fast"
233 name = "crc32fast"
234 version = "1.3.2"
234 version = "1.3.2"
235 source = "registry+https://github.com/rust-lang/crates.io-index"
235 source = "registry+https://github.com/rust-lang/crates.io-index"
236 checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
236 checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
237 dependencies = [
237 dependencies = [
238 "cfg-if",
238 "cfg-if",
239 ]
239 ]
240
240
241 [[package]]
241 [[package]]
242 name = "crossbeam-channel"
242 name = "crossbeam-channel"
243 version = "0.5.6"
243 version = "0.5.6"
244 source = "registry+https://github.com/rust-lang/crates.io-index"
244 source = "registry+https://github.com/rust-lang/crates.io-index"
245 checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
245 checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
246 dependencies = [
246 dependencies = [
247 "cfg-if",
247 "cfg-if",
248 "crossbeam-utils",
248 "crossbeam-utils",
249 ]
249 ]
250
250
251 [[package]]
251 [[package]]
252 name = "crossbeam-deque"
252 name = "crossbeam-deque"
253 version = "0.8.2"
253 version = "0.8.2"
254 source = "registry+https://github.com/rust-lang/crates.io-index"
254 source = "registry+https://github.com/rust-lang/crates.io-index"
255 checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
255 checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
256 dependencies = [
256 dependencies = [
257 "cfg-if",
257 "cfg-if",
258 "crossbeam-epoch",
258 "crossbeam-epoch",
259 "crossbeam-utils",
259 "crossbeam-utils",
260 ]
260 ]
261
261
262 [[package]]
262 [[package]]
263 name = "crossbeam-epoch"
263 name = "crossbeam-epoch"
264 version = "0.9.11"
264 version = "0.9.11"
265 source = "registry+https://github.com/rust-lang/crates.io-index"
265 source = "registry+https://github.com/rust-lang/crates.io-index"
266 checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
266 checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
267 dependencies = [
267 dependencies = [
268 "autocfg",
268 "autocfg",
269 "cfg-if",
269 "cfg-if",
270 "crossbeam-utils",
270 "crossbeam-utils",
271 "memoffset",
271 "memoffset",
272 "scopeguard",
272 "scopeguard",
273 ]
273 ]
274
274
275 [[package]]
275 [[package]]
276 name = "crossbeam-utils"
276 name = "crossbeam-utils"
277 version = "0.8.12"
277 version = "0.8.12"
278 source = "registry+https://github.com/rust-lang/crates.io-index"
278 source = "registry+https://github.com/rust-lang/crates.io-index"
279 checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
279 checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
280 dependencies = [
280 dependencies = [
281 "cfg-if",
281 "cfg-if",
282 ]
282 ]
283
283
284 [[package]]
284 [[package]]
285 name = "crypto-common"
285 name = "crypto-common"
286 version = "0.1.6"
286 version = "0.1.6"
287 source = "registry+https://github.com/rust-lang/crates.io-index"
287 source = "registry+https://github.com/rust-lang/crates.io-index"
288 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
288 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
289 dependencies = [
289 dependencies = [
290 "generic-array",
290 "generic-array",
291 "typenum",
291 "typenum",
292 ]
292 ]
293
293
294 [[package]]
294 [[package]]
295 name = "ctor"
295 name = "ctor"
296 version = "0.1.26"
296 version = "0.1.26"
297 source = "registry+https://github.com/rust-lang/crates.io-index"
297 source = "registry+https://github.com/rust-lang/crates.io-index"
298 checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
298 checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
299 dependencies = [
299 dependencies = [
300 "quote",
300 "quote",
301 "syn",
301 "syn",
302 ]
302 ]
303
303
304 [[package]]
304 [[package]]
305 name = "cxx"
305 name = "cxx"
306 version = "1.0.81"
306 version = "1.0.81"
307 source = "registry+https://github.com/rust-lang/crates.io-index"
307 source = "registry+https://github.com/rust-lang/crates.io-index"
308 checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
308 checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
309 dependencies = [
309 dependencies = [
310 "cc",
310 "cc",
311 "cxxbridge-flags",
311 "cxxbridge-flags",
312 "cxxbridge-macro",
312 "cxxbridge-macro",
313 "link-cplusplus",
313 "link-cplusplus",
314 ]
314 ]
315
315
316 [[package]]
316 [[package]]
317 name = "cxx-build"
317 name = "cxx-build"
318 version = "1.0.81"
318 version = "1.0.81"
319 source = "registry+https://github.com/rust-lang/crates.io-index"
319 source = "registry+https://github.com/rust-lang/crates.io-index"
320 checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
320 checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
321 dependencies = [
321 dependencies = [
322 "cc",
322 "cc",
323 "codespan-reporting",
323 "codespan-reporting",
324 "once_cell",
324 "once_cell",
325 "proc-macro2",
325 "proc-macro2",
326 "quote",
326 "quote",
327 "scratch",
327 "scratch",
328 "syn",
328 "syn",
329 ]
329 ]
330
330
331 [[package]]
331 [[package]]
332 name = "cxxbridge-flags"
332 name = "cxxbridge-flags"
333 version = "1.0.81"
333 version = "1.0.81"
334 source = "registry+https://github.com/rust-lang/crates.io-index"
334 source = "registry+https://github.com/rust-lang/crates.io-index"
335 checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
335 checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
336
336
337 [[package]]
337 [[package]]
338 name = "cxxbridge-macro"
338 name = "cxxbridge-macro"
339 version = "1.0.81"
339 version = "1.0.81"
340 source = "registry+https://github.com/rust-lang/crates.io-index"
340 source = "registry+https://github.com/rust-lang/crates.io-index"
341 checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
341 checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
342 dependencies = [
342 dependencies = [
343 "proc-macro2",
343 "proc-macro2",
344 "quote",
344 "quote",
345 "syn",
345 "syn",
346 ]
346 ]
347
347
348 [[package]]
348 [[package]]
349 name = "derive_more"
349 name = "derive_more"
350 version = "0.99.17"
350 version = "0.99.17"
351 source = "registry+https://github.com/rust-lang/crates.io-index"
351 source = "registry+https://github.com/rust-lang/crates.io-index"
352 checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
352 checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
353 dependencies = [
353 dependencies = [
354 "convert_case",
354 "convert_case",
355 "proc-macro2",
355 "proc-macro2",
356 "quote",
356 "quote",
357 "rustc_version",
357 "rustc_version",
358 "syn",
358 "syn",
359 ]
359 ]
360
360
361 [[package]]
361 [[package]]
362 name = "diff"
362 name = "diff"
363 version = "0.1.13"
363 version = "0.1.13"
364 source = "registry+https://github.com/rust-lang/crates.io-index"
364 source = "registry+https://github.com/rust-lang/crates.io-index"
365 checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
365 checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
366
366
367 [[package]]
367 [[package]]
368 name = "digest"
368 name = "digest"
369 version = "0.9.0"
369 version = "0.9.0"
370 source = "registry+https://github.com/rust-lang/crates.io-index"
370 source = "registry+https://github.com/rust-lang/crates.io-index"
371 checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
371 checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
372 dependencies = [
372 dependencies = [
373 "generic-array",
373 "generic-array",
374 ]
374 ]
375
375
376 [[package]]
376 [[package]]
377 name = "digest"
377 name = "digest"
378 version = "0.10.5"
378 version = "0.10.5"
379 source = "registry+https://github.com/rust-lang/crates.io-index"
379 source = "registry+https://github.com/rust-lang/crates.io-index"
380 checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
380 checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
381 dependencies = [
381 dependencies = [
382 "block-buffer 0.10.3",
382 "block-buffer 0.10.3",
383 "crypto-common",
383 "crypto-common",
384 ]
384 ]
385
385
386 [[package]]
386 [[package]]
387 name = "either"
387 name = "either"
388 version = "1.8.0"
388 version = "1.8.0"
389 source = "registry+https://github.com/rust-lang/crates.io-index"
389 source = "registry+https://github.com/rust-lang/crates.io-index"
390 checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
390 checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
391
391
392 [[package]]
392 [[package]]
393 name = "env_logger"
393 name = "env_logger"
394 version = "0.9.3"
394 version = "0.9.3"
395 source = "registry+https://github.com/rust-lang/crates.io-index"
395 source = "registry+https://github.com/rust-lang/crates.io-index"
396 checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
396 checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
397 dependencies = [
397 dependencies = [
398 "atty",
398 "atty",
399 "humantime",
399 "humantime",
400 "log",
400 "log",
401 "regex",
401 "regex",
402 "termcolor",
402 "termcolor",
403 ]
403 ]
404
404
405 [[package]]
405 [[package]]
406 name = "fastrand"
406 name = "fastrand"
407 version = "1.8.0"
407 version = "1.8.0"
408 source = "registry+https://github.com/rust-lang/crates.io-index"
408 source = "registry+https://github.com/rust-lang/crates.io-index"
409 checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
409 checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
410 dependencies = [
410 dependencies = [
411 "instant",
411 "instant",
412 ]
412 ]
413
413
414 [[package]]
414 [[package]]
415 name = "flate2"
415 name = "flate2"
416 version = "1.0.24"
416 version = "1.0.24"
417 source = "registry+https://github.com/rust-lang/crates.io-index"
417 source = "registry+https://github.com/rust-lang/crates.io-index"
418 checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
418 checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
419 dependencies = [
419 dependencies = [
420 "crc32fast",
420 "crc32fast",
421 "libz-sys",
421 "libz-sys",
422 "miniz_oxide",
422 "miniz_oxide",
423 ]
423 ]
424
424
425 [[package]]
425 [[package]]
426 name = "format-bytes"
426 name = "format-bytes"
427 version = "0.3.0"
427 version = "0.3.0"
428 source = "registry+https://github.com/rust-lang/crates.io-index"
428 source = "registry+https://github.com/rust-lang/crates.io-index"
429 checksum = "48942366ef93975da38e175ac9e10068c6fc08ca9e85930d4f098f4d5b14c2fd"
429 checksum = "48942366ef93975da38e175ac9e10068c6fc08ca9e85930d4f098f4d5b14c2fd"
430 dependencies = [
430 dependencies = [
431 "format-bytes-macros",
431 "format-bytes-macros",
432 ]
432 ]
433
433
434 [[package]]
434 [[package]]
435 name = "format-bytes-macros"
435 name = "format-bytes-macros"
436 version = "0.4.0"
436 version = "0.4.0"
437 source = "registry+https://github.com/rust-lang/crates.io-index"
437 source = "registry+https://github.com/rust-lang/crates.io-index"
438 checksum = "203aadebefcc73d12038296c228eabf830f99cba991b0032adf20e9fa6ce7e4f"
438 checksum = "203aadebefcc73d12038296c228eabf830f99cba991b0032adf20e9fa6ce7e4f"
439 dependencies = [
439 dependencies = [
440 "proc-macro2",
440 "proc-macro2",
441 "quote",
441 "quote",
442 "syn",
442 "syn",
443 ]
443 ]
444
444
445 [[package]]
445 [[package]]
446 name = "generic-array"
446 name = "generic-array"
447 version = "0.14.6"
447 version = "0.14.6"
448 source = "registry+https://github.com/rust-lang/crates.io-index"
448 source = "registry+https://github.com/rust-lang/crates.io-index"
449 checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
449 checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
450 dependencies = [
450 dependencies = [
451 "typenum",
451 "typenum",
452 "version_check",
452 "version_check",
453 ]
453 ]
454
454
455 [[package]]
455 [[package]]
456 name = "getrandom"
456 name = "getrandom"
457 version = "0.1.16"
457 version = "0.1.16"
458 source = "registry+https://github.com/rust-lang/crates.io-index"
458 source = "registry+https://github.com/rust-lang/crates.io-index"
459 checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
459 checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
460 dependencies = [
460 dependencies = [
461 "cfg-if",
461 "cfg-if",
462 "libc",
462 "libc",
463 "wasi 0.9.0+wasi-snapshot-preview1",
463 "wasi 0.9.0+wasi-snapshot-preview1",
464 ]
464 ]
465
465
466 [[package]]
466 [[package]]
467 name = "getrandom"
467 name = "getrandom"
468 version = "0.2.8"
468 version = "0.2.8"
469 source = "registry+https://github.com/rust-lang/crates.io-index"
469 source = "registry+https://github.com/rust-lang/crates.io-index"
470 checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
470 checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
471 dependencies = [
471 dependencies = [
472 "cfg-if",
472 "cfg-if",
473 "libc",
473 "libc",
474 "wasi 0.11.0+wasi-snapshot-preview1",
474 "wasi 0.11.0+wasi-snapshot-preview1",
475 ]
475 ]
476
476
477 [[package]]
477 [[package]]
478 name = "hashbrown"
478 name = "hashbrown"
479 version = "0.12.3"
480 source = "registry+https://github.com/rust-lang/crates.io-index"
481 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
482
483 [[package]]
484 name = "hashbrown"
479 version = "0.13.1"
485 version = "0.13.1"
480 source = "registry+https://github.com/rust-lang/crates.io-index"
486 source = "registry+https://github.com/rust-lang/crates.io-index"
481 checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
487 checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
482 dependencies = [
488 dependencies = [
483 "ahash",
489 "ahash",
484 "rayon",
490 "rayon",
485 ]
491 ]
486
492
487 [[package]]
493 [[package]]
488 name = "heck"
494 name = "heck"
489 version = "0.4.0"
495 version = "0.4.0"
490 source = "registry+https://github.com/rust-lang/crates.io-index"
496 source = "registry+https://github.com/rust-lang/crates.io-index"
491 checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
497 checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
492
498
493 [[package]]
499 [[package]]
494 name = "hermit-abi"
500 name = "hermit-abi"
495 version = "0.1.19"
501 version = "0.1.19"
496 source = "registry+https://github.com/rust-lang/crates.io-index"
502 source = "registry+https://github.com/rust-lang/crates.io-index"
497 checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
503 checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
498 dependencies = [
504 dependencies = [
499 "libc",
505 "libc",
500 ]
506 ]
501
507
502 [[package]]
508 [[package]]
503 name = "hex"
509 name = "hex"
504 version = "0.4.3"
510 version = "0.4.3"
505 source = "registry+https://github.com/rust-lang/crates.io-index"
511 source = "registry+https://github.com/rust-lang/crates.io-index"
506 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
512 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
507
513
508 [[package]]
514 [[package]]
509 name = "hg-core"
515 name = "hg-core"
510 version = "0.1.0"
516 version = "0.1.0"
511 dependencies = [
517 dependencies = [
512 "bitflags",
518 "bitflags",
513 "byteorder",
519 "byteorder",
514 "bytes-cast",
520 "bytes-cast",
515 "clap",
521 "clap",
516 "crossbeam-channel",
522 "crossbeam-channel",
517 "derive_more",
523 "derive_more",
518 "flate2",
524 "flate2",
519 "format-bytes",
525 "format-bytes",
520 "hashbrown",
526 "hashbrown 0.13.1",
521 "home",
527 "home",
522 "im-rc",
528 "im-rc",
523 "itertools",
529 "itertools",
524 "lazy_static",
530 "lazy_static",
525 "libc",
531 "libc",
526 "log",
532 "log",
527 "logging_timer",
533 "logging_timer",
528 "memmap2",
534 "memmap2",
529 "once_cell",
535 "once_cell",
530 "pretty_assertions",
536 "pretty_assertions",
531 "rand 0.8.5",
537 "rand 0.8.5",
532 "rand_distr",
538 "rand_distr",
533 "rand_pcg",
539 "rand_pcg",
534 "rayon",
540 "rayon",
535 "regex",
541 "regex",
536 "same-file",
542 "same-file",
537 "self_cell",
543 "self_cell",
544 "serde",
538 "sha-1 0.10.0",
545 "sha-1 0.10.0",
539 "tempfile",
546 "tempfile",
540 "thread_local",
547 "thread_local",
548 "toml",
541 "twox-hash",
549 "twox-hash",
542 "zstd",
550 "zstd",
543 ]
551 ]
544
552
545 [[package]]
553 [[package]]
546 name = "hg-cpython"
554 name = "hg-cpython"
547 version = "0.1.0"
555 version = "0.1.0"
548 dependencies = [
556 dependencies = [
549 "cpython",
557 "cpython",
550 "crossbeam-channel",
558 "crossbeam-channel",
551 "env_logger",
559 "env_logger",
552 "hg-core",
560 "hg-core",
553 "libc",
561 "libc",
554 "log",
562 "log",
555 "stable_deref_trait",
563 "stable_deref_trait",
556 "vcsgraph",
564 "vcsgraph",
557 ]
565 ]
558
566
559 [[package]]
567 [[package]]
560 name = "home"
568 name = "home"
561 version = "0.5.4"
569 version = "0.5.4"
562 source = "registry+https://github.com/rust-lang/crates.io-index"
570 source = "registry+https://github.com/rust-lang/crates.io-index"
563 checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
571 checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
564 dependencies = [
572 dependencies = [
565 "winapi",
573 "winapi",
566 ]
574 ]
567
575
568 [[package]]
576 [[package]]
569 name = "humantime"
577 name = "humantime"
570 version = "2.1.0"
578 version = "2.1.0"
571 source = "registry+https://github.com/rust-lang/crates.io-index"
579 source = "registry+https://github.com/rust-lang/crates.io-index"
572 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
580 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
573
581
574 [[package]]
582 [[package]]
575 name = "iana-time-zone"
583 name = "iana-time-zone"
576 version = "0.1.53"
584 version = "0.1.53"
577 source = "registry+https://github.com/rust-lang/crates.io-index"
585 source = "registry+https://github.com/rust-lang/crates.io-index"
578 checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
586 checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
579 dependencies = [
587 dependencies = [
580 "android_system_properties",
588 "android_system_properties",
581 "core-foundation-sys",
589 "core-foundation-sys",
582 "iana-time-zone-haiku",
590 "iana-time-zone-haiku",
583 "js-sys",
591 "js-sys",
584 "wasm-bindgen",
592 "wasm-bindgen",
585 "winapi",
593 "winapi",
586 ]
594 ]
587
595
588 [[package]]
596 [[package]]
589 name = "iana-time-zone-haiku"
597 name = "iana-time-zone-haiku"
590 version = "0.1.1"
598 version = "0.1.1"
591 source = "registry+https://github.com/rust-lang/crates.io-index"
599 source = "registry+https://github.com/rust-lang/crates.io-index"
592 checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
600 checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
593 dependencies = [
601 dependencies = [
594 "cxx",
602 "cxx",
595 "cxx-build",
603 "cxx-build",
596 ]
604 ]
597
605
598 [[package]]
606 [[package]]
599 name = "im-rc"
607 name = "im-rc"
600 version = "15.1.0"
608 version = "15.1.0"
601 source = "registry+https://github.com/rust-lang/crates.io-index"
609 source = "registry+https://github.com/rust-lang/crates.io-index"
602 checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
610 checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
603 dependencies = [
611 dependencies = [
604 "bitmaps",
612 "bitmaps",
605 "rand_core 0.6.4",
613 "rand_core 0.6.4",
606 "rand_xoshiro",
614 "rand_xoshiro",
607 "sized-chunks",
615 "sized-chunks",
608 "typenum",
616 "typenum",
609 "version_check",
617 "version_check",
610 ]
618 ]
611
619
612 [[package]]
620 [[package]]
621 name = "indexmap"
622 version = "1.9.2"
623 source = "registry+https://github.com/rust-lang/crates.io-index"
624 checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
625 dependencies = [
626 "autocfg",
627 "hashbrown 0.12.3",
628 ]
629
630 [[package]]
613 name = "instant"
631 name = "instant"
614 version = "0.1.12"
632 version = "0.1.12"
615 source = "registry+https://github.com/rust-lang/crates.io-index"
633 source = "registry+https://github.com/rust-lang/crates.io-index"
616 checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
634 checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
617 dependencies = [
635 dependencies = [
618 "cfg-if",
636 "cfg-if",
619 ]
637 ]
620
638
621 [[package]]
639 [[package]]
622 name = "itertools"
640 name = "itertools"
623 version = "0.10.5"
641 version = "0.10.5"
624 source = "registry+https://github.com/rust-lang/crates.io-index"
642 source = "registry+https://github.com/rust-lang/crates.io-index"
625 checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
643 checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
626 dependencies = [
644 dependencies = [
627 "either",
645 "either",
628 ]
646 ]
629
647
630 [[package]]
648 [[package]]
631 name = "jobserver"
649 name = "jobserver"
632 version = "0.1.25"
650 version = "0.1.25"
633 source = "registry+https://github.com/rust-lang/crates.io-index"
651 source = "registry+https://github.com/rust-lang/crates.io-index"
634 checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
652 checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
635 dependencies = [
653 dependencies = [
636 "libc",
654 "libc",
637 ]
655 ]
638
656
639 [[package]]
657 [[package]]
640 name = "js-sys"
658 name = "js-sys"
641 version = "0.3.60"
659 version = "0.3.60"
642 source = "registry+https://github.com/rust-lang/crates.io-index"
660 source = "registry+https://github.com/rust-lang/crates.io-index"
643 checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
661 checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
644 dependencies = [
662 dependencies = [
645 "wasm-bindgen",
663 "wasm-bindgen",
646 ]
664 ]
647
665
648 [[package]]
666 [[package]]
649 name = "lazy_static"
667 name = "lazy_static"
650 version = "1.4.0"
668 version = "1.4.0"
651 source = "registry+https://github.com/rust-lang/crates.io-index"
669 source = "registry+https://github.com/rust-lang/crates.io-index"
652 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
670 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
653
671
654 [[package]]
672 [[package]]
655 name = "libc"
673 name = "libc"
656 version = "0.2.137"
674 version = "0.2.137"
657 source = "registry+https://github.com/rust-lang/crates.io-index"
675 source = "registry+https://github.com/rust-lang/crates.io-index"
658 checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
676 checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
659
677
660 [[package]]
678 [[package]]
661 name = "libm"
679 name = "libm"
662 version = "0.2.6"
680 version = "0.2.6"
663 source = "registry+https://github.com/rust-lang/crates.io-index"
681 source = "registry+https://github.com/rust-lang/crates.io-index"
664 checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
682 checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
665
683
666 [[package]]
684 [[package]]
667 name = "libz-sys"
685 name = "libz-sys"
668 version = "1.1.8"
686 version = "1.1.8"
669 source = "registry+https://github.com/rust-lang/crates.io-index"
687 source = "registry+https://github.com/rust-lang/crates.io-index"
670 checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
688 checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
671 dependencies = [
689 dependencies = [
672 "cc",
690 "cc",
673 "pkg-config",
691 "pkg-config",
674 "vcpkg",
692 "vcpkg",
675 ]
693 ]
676
694
677 [[package]]
695 [[package]]
678 name = "link-cplusplus"
696 name = "link-cplusplus"
679 version = "1.0.7"
697 version = "1.0.7"
680 source = "registry+https://github.com/rust-lang/crates.io-index"
698 source = "registry+https://github.com/rust-lang/crates.io-index"
681 checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
699 checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
682 dependencies = [
700 dependencies = [
683 "cc",
701 "cc",
684 ]
702 ]
685
703
686 [[package]]
704 [[package]]
687 name = "log"
705 name = "log"
688 version = "0.4.17"
706 version = "0.4.17"
689 source = "registry+https://github.com/rust-lang/crates.io-index"
707 source = "registry+https://github.com/rust-lang/crates.io-index"
690 checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
708 checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
691 dependencies = [
709 dependencies = [
692 "cfg-if",
710 "cfg-if",
693 ]
711 ]
694
712
695 [[package]]
713 [[package]]
696 name = "logging_timer"
714 name = "logging_timer"
697 version = "1.1.0"
715 version = "1.1.0"
698 source = "registry+https://github.com/rust-lang/crates.io-index"
716 source = "registry+https://github.com/rust-lang/crates.io-index"
699 checksum = "64e96f261d684b7089aa576bb74e823241dccd994b27d30fabf1dcb3af284fe9"
717 checksum = "64e96f261d684b7089aa576bb74e823241dccd994b27d30fabf1dcb3af284fe9"
700 dependencies = [
718 dependencies = [
701 "log",
719 "log",
702 "logging_timer_proc_macros",
720 "logging_timer_proc_macros",
703 ]
721 ]
704
722
705 [[package]]
723 [[package]]
706 name = "logging_timer_proc_macros"
724 name = "logging_timer_proc_macros"
707 version = "1.1.0"
725 version = "1.1.0"
708 source = "registry+https://github.com/rust-lang/crates.io-index"
726 source = "registry+https://github.com/rust-lang/crates.io-index"
709 checksum = "10a9062912d7952c5588cc474795e0b9ee008e7e6781127945b85413d4b99d81"
727 checksum = "10a9062912d7952c5588cc474795e0b9ee008e7e6781127945b85413d4b99d81"
710 dependencies = [
728 dependencies = [
711 "log",
729 "log",
712 "proc-macro2",
730 "proc-macro2",
713 "quote",
731 "quote",
714 "syn",
732 "syn",
715 ]
733 ]
716
734
717 [[package]]
735 [[package]]
718 name = "memchr"
736 name = "memchr"
719 version = "2.5.0"
737 version = "2.5.0"
720 source = "registry+https://github.com/rust-lang/crates.io-index"
738 source = "registry+https://github.com/rust-lang/crates.io-index"
721 checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
739 checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
722
740
723 [[package]]
741 [[package]]
724 name = "memmap2"
742 name = "memmap2"
725 version = "0.5.8"
743 version = "0.5.8"
726 source = "registry+https://github.com/rust-lang/crates.io-index"
744 source = "registry+https://github.com/rust-lang/crates.io-index"
727 checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
745 checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc"
728 dependencies = [
746 dependencies = [
729 "libc",
747 "libc",
730 "stable_deref_trait",
748 "stable_deref_trait",
731 ]
749 ]
732
750
733 [[package]]
751 [[package]]
734 name = "memoffset"
752 name = "memoffset"
735 version = "0.6.5"
753 version = "0.6.5"
736 source = "registry+https://github.com/rust-lang/crates.io-index"
754 source = "registry+https://github.com/rust-lang/crates.io-index"
737 checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
755 checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
738 dependencies = [
756 dependencies = [
739 "autocfg",
757 "autocfg",
740 ]
758 ]
741
759
742 [[package]]
760 [[package]]
743 name = "miniz_oxide"
761 name = "miniz_oxide"
744 version = "0.5.4"
762 version = "0.5.4"
745 source = "registry+https://github.com/rust-lang/crates.io-index"
763 source = "registry+https://github.com/rust-lang/crates.io-index"
746 checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
764 checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
747 dependencies = [
765 dependencies = [
748 "adler",
766 "adler",
749 ]
767 ]
750
768
751 [[package]]
769 [[package]]
770 name = "nom8"
771 version = "0.2.0"
772 source = "registry+https://github.com/rust-lang/crates.io-index"
773 checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
774 dependencies = [
775 "memchr",
776 ]
777
778 [[package]]
752 name = "num-integer"
779 name = "num-integer"
753 version = "0.1.45"
780 version = "0.1.45"
754 source = "registry+https://github.com/rust-lang/crates.io-index"
781 source = "registry+https://github.com/rust-lang/crates.io-index"
755 checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
782 checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
756 dependencies = [
783 dependencies = [
757 "autocfg",
784 "autocfg",
758 "num-traits",
785 "num-traits",
759 ]
786 ]
760
787
761 [[package]]
788 [[package]]
762 name = "num-traits"
789 name = "num-traits"
763 version = "0.2.15"
790 version = "0.2.15"
764 source = "registry+https://github.com/rust-lang/crates.io-index"
791 source = "registry+https://github.com/rust-lang/crates.io-index"
765 checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
792 checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
766 dependencies = [
793 dependencies = [
767 "autocfg",
794 "autocfg",
768 "libm",
795 "libm",
769 ]
796 ]
770
797
771 [[package]]
798 [[package]]
772 name = "num_cpus"
799 name = "num_cpus"
773 version = "1.14.0"
800 version = "1.14.0"
774 source = "registry+https://github.com/rust-lang/crates.io-index"
801 source = "registry+https://github.com/rust-lang/crates.io-index"
775 checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
802 checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
776 dependencies = [
803 dependencies = [
777 "hermit-abi",
804 "hermit-abi",
778 "libc",
805 "libc",
779 ]
806 ]
780
807
781 [[package]]
808 [[package]]
782 name = "once_cell"
809 name = "once_cell"
783 version = "1.16.0"
810 version = "1.16.0"
784 source = "registry+https://github.com/rust-lang/crates.io-index"
811 source = "registry+https://github.com/rust-lang/crates.io-index"
785 checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
812 checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
786
813
787 [[package]]
814 [[package]]
788 name = "opaque-debug"
815 name = "opaque-debug"
789 version = "0.3.0"
816 version = "0.3.0"
790 source = "registry+https://github.com/rust-lang/crates.io-index"
817 source = "registry+https://github.com/rust-lang/crates.io-index"
791 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
818 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
792
819
793 [[package]]
820 [[package]]
794 name = "os_str_bytes"
821 name = "os_str_bytes"
795 version = "6.4.0"
822 version = "6.4.0"
796 source = "registry+https://github.com/rust-lang/crates.io-index"
823 source = "registry+https://github.com/rust-lang/crates.io-index"
797 checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
824 checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
798
825
799 [[package]]
826 [[package]]
800 name = "output_vt100"
827 name = "output_vt100"
801 version = "0.1.3"
828 version = "0.1.3"
802 source = "registry+https://github.com/rust-lang/crates.io-index"
829 source = "registry+https://github.com/rust-lang/crates.io-index"
803 checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
830 checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
804 dependencies = [
831 dependencies = [
805 "winapi",
832 "winapi",
806 ]
833 ]
807
834
808 [[package]]
835 [[package]]
809 name = "paste"
836 name = "paste"
810 version = "1.0.9"
837 version = "1.0.9"
811 source = "registry+https://github.com/rust-lang/crates.io-index"
838 source = "registry+https://github.com/rust-lang/crates.io-index"
812 checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
839 checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
813
840
814 [[package]]
841 [[package]]
815 name = "pkg-config"
842 name = "pkg-config"
816 version = "0.3.26"
843 version = "0.3.26"
817 source = "registry+https://github.com/rust-lang/crates.io-index"
844 source = "registry+https://github.com/rust-lang/crates.io-index"
818 checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
845 checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
819
846
820 [[package]]
847 [[package]]
821 name = "ppv-lite86"
848 name = "ppv-lite86"
822 version = "0.2.17"
849 version = "0.2.17"
823 source = "registry+https://github.com/rust-lang/crates.io-index"
850 source = "registry+https://github.com/rust-lang/crates.io-index"
824 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
851 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
825
852
826 [[package]]
853 [[package]]
827 name = "pretty_assertions"
854 name = "pretty_assertions"
828 version = "1.3.0"
855 version = "1.3.0"
829 source = "registry+https://github.com/rust-lang/crates.io-index"
856 source = "registry+https://github.com/rust-lang/crates.io-index"
830 checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
857 checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
831 dependencies = [
858 dependencies = [
832 "ctor",
859 "ctor",
833 "diff",
860 "diff",
834 "output_vt100",
861 "output_vt100",
835 "yansi",
862 "yansi",
836 ]
863 ]
837
864
838 [[package]]
865 [[package]]
839 name = "proc-macro-error"
866 name = "proc-macro-error"
840 version = "1.0.4"
867 version = "1.0.4"
841 source = "registry+https://github.com/rust-lang/crates.io-index"
868 source = "registry+https://github.com/rust-lang/crates.io-index"
842 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
869 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
843 dependencies = [
870 dependencies = [
844 "proc-macro-error-attr",
871 "proc-macro-error-attr",
845 "proc-macro2",
872 "proc-macro2",
846 "quote",
873 "quote",
847 "syn",
874 "syn",
848 "version_check",
875 "version_check",
849 ]
876 ]
850
877
851 [[package]]
878 [[package]]
852 name = "proc-macro-error-attr"
879 name = "proc-macro-error-attr"
853 version = "1.0.4"
880 version = "1.0.4"
854 source = "registry+https://github.com/rust-lang/crates.io-index"
881 source = "registry+https://github.com/rust-lang/crates.io-index"
855 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
882 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
856 dependencies = [
883 dependencies = [
857 "proc-macro2",
884 "proc-macro2",
858 "quote",
885 "quote",
859 "version_check",
886 "version_check",
860 ]
887 ]
861
888
862 [[package]]
889 [[package]]
863 name = "proc-macro2"
890 name = "proc-macro2"
864 version = "1.0.47"
891 version = "1.0.47"
865 source = "registry+https://github.com/rust-lang/crates.io-index"
892 source = "registry+https://github.com/rust-lang/crates.io-index"
866 checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
893 checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
867 dependencies = [
894 dependencies = [
868 "unicode-ident",
895 "unicode-ident",
869 ]
896 ]
870
897
871 [[package]]
898 [[package]]
872 name = "python3-sys"
899 name = "python3-sys"
873 version = "0.7.1"
900 version = "0.7.1"
874 source = "registry+https://github.com/rust-lang/crates.io-index"
901 source = "registry+https://github.com/rust-lang/crates.io-index"
875 checksum = "49f8b50d72fb3015735aa403eebf19bbd72c093bfeeae24ee798be5f2f1aab52"
902 checksum = "49f8b50d72fb3015735aa403eebf19bbd72c093bfeeae24ee798be5f2f1aab52"
876 dependencies = [
903 dependencies = [
877 "libc",
904 "libc",
878 "regex",
905 "regex",
879 ]
906 ]
880
907
881 [[package]]
908 [[package]]
882 name = "quote"
909 name = "quote"
883 version = "1.0.21"
910 version = "1.0.21"
884 source = "registry+https://github.com/rust-lang/crates.io-index"
911 source = "registry+https://github.com/rust-lang/crates.io-index"
885 checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
912 checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
886 dependencies = [
913 dependencies = [
887 "proc-macro2",
914 "proc-macro2",
888 ]
915 ]
889
916
890 [[package]]
917 [[package]]
891 name = "rand"
918 name = "rand"
892 version = "0.7.3"
919 version = "0.7.3"
893 source = "registry+https://github.com/rust-lang/crates.io-index"
920 source = "registry+https://github.com/rust-lang/crates.io-index"
894 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
921 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
895 dependencies = [
922 dependencies = [
896 "getrandom 0.1.16",
923 "getrandom 0.1.16",
897 "libc",
924 "libc",
898 "rand_chacha 0.2.2",
925 "rand_chacha 0.2.2",
899 "rand_core 0.5.1",
926 "rand_core 0.5.1",
900 "rand_hc",
927 "rand_hc",
901 ]
928 ]
902
929
903 [[package]]
930 [[package]]
904 name = "rand"
931 name = "rand"
905 version = "0.8.5"
932 version = "0.8.5"
906 source = "registry+https://github.com/rust-lang/crates.io-index"
933 source = "registry+https://github.com/rust-lang/crates.io-index"
907 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
934 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
908 dependencies = [
935 dependencies = [
909 "libc",
936 "libc",
910 "rand_chacha 0.3.1",
937 "rand_chacha 0.3.1",
911 "rand_core 0.6.4",
938 "rand_core 0.6.4",
912 ]
939 ]
913
940
914 [[package]]
941 [[package]]
915 name = "rand_chacha"
942 name = "rand_chacha"
916 version = "0.2.2"
943 version = "0.2.2"
917 source = "registry+https://github.com/rust-lang/crates.io-index"
944 source = "registry+https://github.com/rust-lang/crates.io-index"
918 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
945 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
919 dependencies = [
946 dependencies = [
920 "ppv-lite86",
947 "ppv-lite86",
921 "rand_core 0.5.1",
948 "rand_core 0.5.1",
922 ]
949 ]
923
950
924 [[package]]
951 [[package]]
925 name = "rand_chacha"
952 name = "rand_chacha"
926 version = "0.3.1"
953 version = "0.3.1"
927 source = "registry+https://github.com/rust-lang/crates.io-index"
954 source = "registry+https://github.com/rust-lang/crates.io-index"
928 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
955 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
929 dependencies = [
956 dependencies = [
930 "ppv-lite86",
957 "ppv-lite86",
931 "rand_core 0.6.4",
958 "rand_core 0.6.4",
932 ]
959 ]
933
960
934 [[package]]
961 [[package]]
935 name = "rand_core"
962 name = "rand_core"
936 version = "0.5.1"
963 version = "0.5.1"
937 source = "registry+https://github.com/rust-lang/crates.io-index"
964 source = "registry+https://github.com/rust-lang/crates.io-index"
938 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
965 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
939 dependencies = [
966 dependencies = [
940 "getrandom 0.1.16",
967 "getrandom 0.1.16",
941 ]
968 ]
942
969
943 [[package]]
970 [[package]]
944 name = "rand_core"
971 name = "rand_core"
945 version = "0.6.4"
972 version = "0.6.4"
946 source = "registry+https://github.com/rust-lang/crates.io-index"
973 source = "registry+https://github.com/rust-lang/crates.io-index"
947 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
974 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
948 dependencies = [
975 dependencies = [
949 "getrandom 0.2.8",
976 "getrandom 0.2.8",
950 ]
977 ]
951
978
952 [[package]]
979 [[package]]
953 name = "rand_distr"
980 name = "rand_distr"
954 version = "0.4.3"
981 version = "0.4.3"
955 source = "registry+https://github.com/rust-lang/crates.io-index"
982 source = "registry+https://github.com/rust-lang/crates.io-index"
956 checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
983 checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
957 dependencies = [
984 dependencies = [
958 "num-traits",
985 "num-traits",
959 "rand 0.8.5",
986 "rand 0.8.5",
960 ]
987 ]
961
988
962 [[package]]
989 [[package]]
963 name = "rand_hc"
990 name = "rand_hc"
964 version = "0.2.0"
991 version = "0.2.0"
965 source = "registry+https://github.com/rust-lang/crates.io-index"
992 source = "registry+https://github.com/rust-lang/crates.io-index"
966 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
993 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
967 dependencies = [
994 dependencies = [
968 "rand_core 0.5.1",
995 "rand_core 0.5.1",
969 ]
996 ]
970
997
971 [[package]]
998 [[package]]
972 name = "rand_pcg"
999 name = "rand_pcg"
973 version = "0.3.1"
1000 version = "0.3.1"
974 source = "registry+https://github.com/rust-lang/crates.io-index"
1001 source = "registry+https://github.com/rust-lang/crates.io-index"
975 checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e"
1002 checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e"
976 dependencies = [
1003 dependencies = [
977 "rand_core 0.6.4",
1004 "rand_core 0.6.4",
978 ]
1005 ]
979
1006
980 [[package]]
1007 [[package]]
981 name = "rand_xoshiro"
1008 name = "rand_xoshiro"
982 version = "0.6.0"
1009 version = "0.6.0"
983 source = "registry+https://github.com/rust-lang/crates.io-index"
1010 source = "registry+https://github.com/rust-lang/crates.io-index"
984 checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
1011 checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
985 dependencies = [
1012 dependencies = [
986 "rand_core 0.6.4",
1013 "rand_core 0.6.4",
987 ]
1014 ]
988
1015
989 [[package]]
1016 [[package]]
990 name = "rayon"
1017 name = "rayon"
991 version = "1.7.0"
1018 version = "1.7.0"
992 source = "registry+https://github.com/rust-lang/crates.io-index"
1019 source = "registry+https://github.com/rust-lang/crates.io-index"
993 checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
1020 checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
994 dependencies = [
1021 dependencies = [
995 "either",
1022 "either",
996 "rayon-core",
1023 "rayon-core",
997 ]
1024 ]
998
1025
999 [[package]]
1026 [[package]]
1000 name = "rayon-core"
1027 name = "rayon-core"
1001 version = "1.11.0"
1028 version = "1.11.0"
1002 source = "registry+https://github.com/rust-lang/crates.io-index"
1029 source = "registry+https://github.com/rust-lang/crates.io-index"
1003 checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
1030 checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
1004 dependencies = [
1031 dependencies = [
1005 "crossbeam-channel",
1032 "crossbeam-channel",
1006 "crossbeam-deque",
1033 "crossbeam-deque",
1007 "crossbeam-utils",
1034 "crossbeam-utils",
1008 "num_cpus",
1035 "num_cpus",
1009 ]
1036 ]
1010
1037
1011 [[package]]
1038 [[package]]
1012 name = "redox_syscall"
1039 name = "redox_syscall"
1013 version = "0.2.16"
1040 version = "0.2.16"
1014 source = "registry+https://github.com/rust-lang/crates.io-index"
1041 source = "registry+https://github.com/rust-lang/crates.io-index"
1015 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
1042 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
1016 dependencies = [
1043 dependencies = [
1017 "bitflags",
1044 "bitflags",
1018 ]
1045 ]
1019
1046
1020 [[package]]
1047 [[package]]
1021 name = "regex"
1048 name = "regex"
1022 version = "1.7.0"
1049 version = "1.7.0"
1023 source = "registry+https://github.com/rust-lang/crates.io-index"
1050 source = "registry+https://github.com/rust-lang/crates.io-index"
1024 checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
1051 checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
1025 dependencies = [
1052 dependencies = [
1026 "aho-corasick",
1053 "aho-corasick",
1027 "memchr",
1054 "memchr",
1028 "regex-syntax",
1055 "regex-syntax",
1029 ]
1056 ]
1030
1057
1031 [[package]]
1058 [[package]]
1032 name = "regex-syntax"
1059 name = "regex-syntax"
1033 version = "0.6.28"
1060 version = "0.6.28"
1034 source = "registry+https://github.com/rust-lang/crates.io-index"
1061 source = "registry+https://github.com/rust-lang/crates.io-index"
1035 checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
1062 checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
1036
1063
1037 [[package]]
1064 [[package]]
1038 name = "remove_dir_all"
1065 name = "remove_dir_all"
1039 version = "0.5.3"
1066 version = "0.5.3"
1040 source = "registry+https://github.com/rust-lang/crates.io-index"
1067 source = "registry+https://github.com/rust-lang/crates.io-index"
1041 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
1068 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
1042 dependencies = [
1069 dependencies = [
1043 "winapi",
1070 "winapi",
1044 ]
1071 ]
1045
1072
1046 [[package]]
1073 [[package]]
1047 name = "rhg"
1074 name = "rhg"
1048 version = "0.1.0"
1075 version = "0.1.0"
1049 dependencies = [
1076 dependencies = [
1050 "atty",
1077 "atty",
1051 "chrono",
1078 "chrono",
1052 "clap",
1079 "clap",
1053 "derive_more",
1080 "derive_more",
1054 "env_logger",
1081 "env_logger",
1055 "format-bytes",
1082 "format-bytes",
1056 "hg-core",
1083 "hg-core",
1057 "home",
1084 "home",
1058 "lazy_static",
1085 "lazy_static",
1059 "log",
1086 "log",
1060 "logging_timer",
1087 "logging_timer",
1061 "rayon",
1088 "rayon",
1062 "regex",
1089 "regex",
1063 "which",
1090 "which",
1064 "whoami",
1091 "whoami",
1065 ]
1092 ]
1066
1093
1067 [[package]]
1094 [[package]]
1068 name = "rustc_version"
1095 name = "rustc_version"
1069 version = "0.4.0"
1096 version = "0.4.0"
1070 source = "registry+https://github.com/rust-lang/crates.io-index"
1097 source = "registry+https://github.com/rust-lang/crates.io-index"
1071 checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
1098 checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
1072 dependencies = [
1099 dependencies = [
1073 "semver",
1100 "semver",
1074 ]
1101 ]
1075
1102
1076 [[package]]
1103 [[package]]
1077 name = "same-file"
1104 name = "same-file"
1078 version = "1.0.6"
1105 version = "1.0.6"
1079 source = "registry+https://github.com/rust-lang/crates.io-index"
1106 source = "registry+https://github.com/rust-lang/crates.io-index"
1080 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
1107 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
1081 dependencies = [
1108 dependencies = [
1082 "winapi-util",
1109 "winapi-util",
1083 ]
1110 ]
1084
1111
1085 [[package]]
1112 [[package]]
1086 name = "scopeguard"
1113 name = "scopeguard"
1087 version = "1.1.0"
1114 version = "1.1.0"
1088 source = "registry+https://github.com/rust-lang/crates.io-index"
1115 source = "registry+https://github.com/rust-lang/crates.io-index"
1089 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1116 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1090
1117
1091 [[package]]
1118 [[package]]
1092 name = "scratch"
1119 name = "scratch"
1093 version = "1.0.2"
1120 version = "1.0.2"
1094 source = "registry+https://github.com/rust-lang/crates.io-index"
1121 source = "registry+https://github.com/rust-lang/crates.io-index"
1095 checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
1122 checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
1096
1123
1097 [[package]]
1124 [[package]]
1098 name = "self_cell"
1125 name = "self_cell"
1099 version = "1.0.0"
1126 version = "1.0.0"
1100 source = "registry+https://github.com/rust-lang/crates.io-index"
1127 source = "registry+https://github.com/rust-lang/crates.io-index"
1101 checksum = "4a3926e239738d36060909ffe6f511502f92149a45a1fade7fe031cb2d33e88b"
1128 checksum = "4a3926e239738d36060909ffe6f511502f92149a45a1fade7fe031cb2d33e88b"
1102
1129
1103 [[package]]
1130 [[package]]
1104 name = "semver"
1131 name = "semver"
1105 version = "1.0.14"
1132 version = "1.0.14"
1106 source = "registry+https://github.com/rust-lang/crates.io-index"
1133 source = "registry+https://github.com/rust-lang/crates.io-index"
1107 checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
1134 checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
1108
1135
1109 [[package]]
1136 [[package]]
1137 name = "serde"
1138 version = "1.0.152"
1139 source = "registry+https://github.com/rust-lang/crates.io-index"
1140 checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
1141 dependencies = [
1142 "serde_derive",
1143 ]
1144
1145 [[package]]
1146 name = "serde_derive"
1147 version = "1.0.152"
1148 source = "registry+https://github.com/rust-lang/crates.io-index"
1149 checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
1150 dependencies = [
1151 "proc-macro2",
1152 "quote",
1153 "syn",
1154 ]
1155
1156 [[package]]
1157 name = "serde_spanned"
1158 version = "0.6.1"
1159 source = "registry+https://github.com/rust-lang/crates.io-index"
1160 checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
1161 dependencies = [
1162 "serde",
1163 ]
1164
1165 [[package]]
1110 name = "sha-1"
1166 name = "sha-1"
1111 version = "0.9.8"
1167 version = "0.9.8"
1112 source = "registry+https://github.com/rust-lang/crates.io-index"
1168 source = "registry+https://github.com/rust-lang/crates.io-index"
1113 checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
1169 checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
1114 dependencies = [
1170 dependencies = [
1115 "block-buffer 0.9.0",
1171 "block-buffer 0.9.0",
1116 "cfg-if",
1172 "cfg-if",
1117 "cpufeatures",
1173 "cpufeatures",
1118 "digest 0.9.0",
1174 "digest 0.9.0",
1119 "opaque-debug",
1175 "opaque-debug",
1120 ]
1176 ]
1121
1177
1122 [[package]]
1178 [[package]]
1123 name = "sha-1"
1179 name = "sha-1"
1124 version = "0.10.0"
1180 version = "0.10.0"
1125 source = "registry+https://github.com/rust-lang/crates.io-index"
1181 source = "registry+https://github.com/rust-lang/crates.io-index"
1126 checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
1182 checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
1127 dependencies = [
1183 dependencies = [
1128 "cfg-if",
1184 "cfg-if",
1129 "cpufeatures",
1185 "cpufeatures",
1130 "digest 0.10.5",
1186 "digest 0.10.5",
1131 ]
1187 ]
1132
1188
1133 [[package]]
1189 [[package]]
1134 name = "sized-chunks"
1190 name = "sized-chunks"
1135 version = "0.6.5"
1191 version = "0.6.5"
1136 source = "registry+https://github.com/rust-lang/crates.io-index"
1192 source = "registry+https://github.com/rust-lang/crates.io-index"
1137 checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
1193 checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
1138 dependencies = [
1194 dependencies = [
1139 "bitmaps",
1195 "bitmaps",
1140 "typenum",
1196 "typenum",
1141 ]
1197 ]
1142
1198
1143 [[package]]
1199 [[package]]
1144 name = "stable_deref_trait"
1200 name = "stable_deref_trait"
1145 version = "1.2.0"
1201 version = "1.2.0"
1146 source = "registry+https://github.com/rust-lang/crates.io-index"
1202 source = "registry+https://github.com/rust-lang/crates.io-index"
1147 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1203 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1148
1204
1149 [[package]]
1205 [[package]]
1150 name = "static_assertions"
1206 name = "static_assertions"
1151 version = "1.1.0"
1207 version = "1.1.0"
1152 source = "registry+https://github.com/rust-lang/crates.io-index"
1208 source = "registry+https://github.com/rust-lang/crates.io-index"
1153 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1209 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1154
1210
1155 [[package]]
1211 [[package]]
1156 name = "strsim"
1212 name = "strsim"
1157 version = "0.10.0"
1213 version = "0.10.0"
1158 source = "registry+https://github.com/rust-lang/crates.io-index"
1214 source = "registry+https://github.com/rust-lang/crates.io-index"
1159 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
1215 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
1160
1216
1161 [[package]]
1217 [[package]]
1162 name = "syn"
1218 name = "syn"
1163 version = "1.0.103"
1219 version = "1.0.109"
1164 source = "registry+https://github.com/rust-lang/crates.io-index"
1220 source = "registry+https://github.com/rust-lang/crates.io-index"
1165 checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
1221 checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1166 dependencies = [
1222 dependencies = [
1167 "proc-macro2",
1223 "proc-macro2",
1168 "quote",
1224 "quote",
1169 "unicode-ident",
1225 "unicode-ident",
1170 ]
1226 ]
1171
1227
1172 [[package]]
1228 [[package]]
1173 name = "tempfile"
1229 name = "tempfile"
1174 version = "3.3.0"
1230 version = "3.3.0"
1175 source = "registry+https://github.com/rust-lang/crates.io-index"
1231 source = "registry+https://github.com/rust-lang/crates.io-index"
1176 checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
1232 checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
1177 dependencies = [
1233 dependencies = [
1178 "cfg-if",
1234 "cfg-if",
1179 "fastrand",
1235 "fastrand",
1180 "libc",
1236 "libc",
1181 "redox_syscall",
1237 "redox_syscall",
1182 "remove_dir_all",
1238 "remove_dir_all",
1183 "winapi",
1239 "winapi",
1184 ]
1240 ]
1185
1241
1186 [[package]]
1242 [[package]]
1187 name = "termcolor"
1243 name = "termcolor"
1188 version = "1.1.3"
1244 version = "1.1.3"
1189 source = "registry+https://github.com/rust-lang/crates.io-index"
1245 source = "registry+https://github.com/rust-lang/crates.io-index"
1190 checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
1246 checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
1191 dependencies = [
1247 dependencies = [
1192 "winapi-util",
1248 "winapi-util",
1193 ]
1249 ]
1194
1250
1195 [[package]]
1251 [[package]]
1196 name = "thread_local"
1252 name = "thread_local"
1197 version = "1.1.4"
1253 version = "1.1.4"
1198 source = "registry+https://github.com/rust-lang/crates.io-index"
1254 source = "registry+https://github.com/rust-lang/crates.io-index"
1199 checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
1255 checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
1200 dependencies = [
1256 dependencies = [
1201 "once_cell",
1257 "once_cell",
1202 ]
1258 ]
1203
1259
1204 [[package]]
1260 [[package]]
1205 name = "time"
1261 name = "time"
1206 version = "0.1.44"
1262 version = "0.1.44"
1207 source = "registry+https://github.com/rust-lang/crates.io-index"
1263 source = "registry+https://github.com/rust-lang/crates.io-index"
1208 checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
1264 checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
1209 dependencies = [
1265 dependencies = [
1210 "libc",
1266 "libc",
1211 "wasi 0.10.0+wasi-snapshot-preview1",
1267 "wasi 0.10.0+wasi-snapshot-preview1",
1212 "winapi",
1268 "winapi",
1213 ]
1269 ]
1214
1270
1215 [[package]]
1271 [[package]]
1272 name = "toml"
1273 version = "0.6.0"
1274 source = "registry+https://github.com/rust-lang/crates.io-index"
1275 checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
1276 dependencies = [
1277 "serde",
1278 "serde_spanned",
1279 "toml_datetime",
1280 "toml_edit",
1281 ]
1282
1283 [[package]]
1284 name = "toml_datetime"
1285 version = "0.5.1"
1286 source = "registry+https://github.com/rust-lang/crates.io-index"
1287 checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
1288 dependencies = [
1289 "serde",
1290 ]
1291
1292 [[package]]
1293 name = "toml_edit"
1294 version = "0.18.1"
1295 source = "registry+https://github.com/rust-lang/crates.io-index"
1296 checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
1297 dependencies = [
1298 "indexmap",
1299 "nom8",
1300 "serde",
1301 "serde_spanned",
1302 "toml_datetime",
1303 ]
1304
1305 [[package]]
1216 name = "twox-hash"
1306 name = "twox-hash"
1217 version = "1.6.3"
1307 version = "1.6.3"
1218 source = "registry+https://github.com/rust-lang/crates.io-index"
1308 source = "registry+https://github.com/rust-lang/crates.io-index"
1219 checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
1309 checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
1220 dependencies = [
1310 dependencies = [
1221 "cfg-if",
1311 "cfg-if",
1222 "rand 0.8.5",
1312 "rand 0.8.5",
1223 "static_assertions",
1313 "static_assertions",
1224 ]
1314 ]
1225
1315
1226 [[package]]
1316 [[package]]
1227 name = "typenum"
1317 name = "typenum"
1228 version = "1.15.0"
1318 version = "1.15.0"
1229 source = "registry+https://github.com/rust-lang/crates.io-index"
1319 source = "registry+https://github.com/rust-lang/crates.io-index"
1230 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
1320 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
1231
1321
1232 [[package]]
1322 [[package]]
1233 name = "unicode-ident"
1323 name = "unicode-ident"
1234 version = "1.0.5"
1324 version = "1.0.5"
1235 source = "registry+https://github.com/rust-lang/crates.io-index"
1325 source = "registry+https://github.com/rust-lang/crates.io-index"
1236 checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
1326 checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
1237
1327
1238 [[package]]
1328 [[package]]
1239 name = "unicode-width"
1329 name = "unicode-width"
1240 version = "0.1.10"
1330 version = "0.1.10"
1241 source = "registry+https://github.com/rust-lang/crates.io-index"
1331 source = "registry+https://github.com/rust-lang/crates.io-index"
1242 checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
1332 checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
1243
1333
1244 [[package]]
1334 [[package]]
1245 name = "vcpkg"
1335 name = "vcpkg"
1246 version = "0.2.15"
1336 version = "0.2.15"
1247 source = "registry+https://github.com/rust-lang/crates.io-index"
1337 source = "registry+https://github.com/rust-lang/crates.io-index"
1248 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
1338 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
1249
1339
1250 [[package]]
1340 [[package]]
1251 name = "vcsgraph"
1341 name = "vcsgraph"
1252 version = "0.2.0"
1342 version = "0.2.0"
1253 source = "registry+https://github.com/rust-lang/crates.io-index"
1343 source = "registry+https://github.com/rust-lang/crates.io-index"
1254 checksum = "4cb68c231e2575f7503a7c19213875f9d4ec2e84e963a56ce3de4b6bee351ef7"
1344 checksum = "4cb68c231e2575f7503a7c19213875f9d4ec2e84e963a56ce3de4b6bee351ef7"
1255 dependencies = [
1345 dependencies = [
1256 "hex",
1346 "hex",
1257 "rand 0.7.3",
1347 "rand 0.7.3",
1258 "sha-1 0.9.8",
1348 "sha-1 0.9.8",
1259 ]
1349 ]
1260
1350
1261 [[package]]
1351 [[package]]
1262 name = "version_check"
1352 name = "version_check"
1263 version = "0.9.4"
1353 version = "0.9.4"
1264 source = "registry+https://github.com/rust-lang/crates.io-index"
1354 source = "registry+https://github.com/rust-lang/crates.io-index"
1265 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1355 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1266
1356
1267 [[package]]
1357 [[package]]
1268 name = "wasi"
1358 name = "wasi"
1269 version = "0.9.0+wasi-snapshot-preview1"
1359 version = "0.9.0+wasi-snapshot-preview1"
1270 source = "registry+https://github.com/rust-lang/crates.io-index"
1360 source = "registry+https://github.com/rust-lang/crates.io-index"
1271 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
1361 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
1272
1362
1273 [[package]]
1363 [[package]]
1274 name = "wasi"
1364 name = "wasi"
1275 version = "0.10.0+wasi-snapshot-preview1"
1365 version = "0.10.0+wasi-snapshot-preview1"
1276 source = "registry+https://github.com/rust-lang/crates.io-index"
1366 source = "registry+https://github.com/rust-lang/crates.io-index"
1277 checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
1367 checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
1278
1368
1279 [[package]]
1369 [[package]]
1280 name = "wasi"
1370 name = "wasi"
1281 version = "0.11.0+wasi-snapshot-preview1"
1371 version = "0.11.0+wasi-snapshot-preview1"
1282 source = "registry+https://github.com/rust-lang/crates.io-index"
1372 source = "registry+https://github.com/rust-lang/crates.io-index"
1283 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1373 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1284
1374
1285 [[package]]
1375 [[package]]
1286 name = "wasm-bindgen"
1376 name = "wasm-bindgen"
1287 version = "0.2.83"
1377 version = "0.2.83"
1288 source = "registry+https://github.com/rust-lang/crates.io-index"
1378 source = "registry+https://github.com/rust-lang/crates.io-index"
1289 checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
1379 checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
1290 dependencies = [
1380 dependencies = [
1291 "cfg-if",
1381 "cfg-if",
1292 "wasm-bindgen-macro",
1382 "wasm-bindgen-macro",
1293 ]
1383 ]
1294
1384
1295 [[package]]
1385 [[package]]
1296 name = "wasm-bindgen-backend"
1386 name = "wasm-bindgen-backend"
1297 version = "0.2.83"
1387 version = "0.2.83"
1298 source = "registry+https://github.com/rust-lang/crates.io-index"
1388 source = "registry+https://github.com/rust-lang/crates.io-index"
1299 checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
1389 checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
1300 dependencies = [
1390 dependencies = [
1301 "bumpalo",
1391 "bumpalo",
1302 "log",
1392 "log",
1303 "once_cell",
1393 "once_cell",
1304 "proc-macro2",
1394 "proc-macro2",
1305 "quote",
1395 "quote",
1306 "syn",
1396 "syn",
1307 "wasm-bindgen-shared",
1397 "wasm-bindgen-shared",
1308 ]
1398 ]
1309
1399
1310 [[package]]
1400 [[package]]
1311 name = "wasm-bindgen-macro"
1401 name = "wasm-bindgen-macro"
1312 version = "0.2.83"
1402 version = "0.2.83"
1313 source = "registry+https://github.com/rust-lang/crates.io-index"
1403 source = "registry+https://github.com/rust-lang/crates.io-index"
1314 checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
1404 checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
1315 dependencies = [
1405 dependencies = [
1316 "quote",
1406 "quote",
1317 "wasm-bindgen-macro-support",
1407 "wasm-bindgen-macro-support",
1318 ]
1408 ]
1319
1409
1320 [[package]]
1410 [[package]]
1321 name = "wasm-bindgen-macro-support"
1411 name = "wasm-bindgen-macro-support"
1322 version = "0.2.83"
1412 version = "0.2.83"
1323 source = "registry+https://github.com/rust-lang/crates.io-index"
1413 source = "registry+https://github.com/rust-lang/crates.io-index"
1324 checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
1414 checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
1325 dependencies = [
1415 dependencies = [
1326 "proc-macro2",
1416 "proc-macro2",
1327 "quote",
1417 "quote",
1328 "syn",
1418 "syn",
1329 "wasm-bindgen-backend",
1419 "wasm-bindgen-backend",
1330 "wasm-bindgen-shared",
1420 "wasm-bindgen-shared",
1331 ]
1421 ]
1332
1422
1333 [[package]]
1423 [[package]]
1334 name = "wasm-bindgen-shared"
1424 name = "wasm-bindgen-shared"
1335 version = "0.2.83"
1425 version = "0.2.83"
1336 source = "registry+https://github.com/rust-lang/crates.io-index"
1426 source = "registry+https://github.com/rust-lang/crates.io-index"
1337 checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
1427 checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
1338
1428
1339 [[package]]
1429 [[package]]
1340 name = "web-sys"
1430 name = "web-sys"
1341 version = "0.3.60"
1431 version = "0.3.60"
1342 source = "registry+https://github.com/rust-lang/crates.io-index"
1432 source = "registry+https://github.com/rust-lang/crates.io-index"
1343 checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
1433 checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
1344 dependencies = [
1434 dependencies = [
1345 "js-sys",
1435 "js-sys",
1346 "wasm-bindgen",
1436 "wasm-bindgen",
1347 ]
1437 ]
1348
1438
1349 [[package]]
1439 [[package]]
1350 name = "which"
1440 name = "which"
1351 version = "4.3.0"
1441 version = "4.3.0"
1352 source = "registry+https://github.com/rust-lang/crates.io-index"
1442 source = "registry+https://github.com/rust-lang/crates.io-index"
1353 checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
1443 checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
1354 dependencies = [
1444 dependencies = [
1355 "either",
1445 "either",
1356 "libc",
1446 "libc",
1357 "once_cell",
1447 "once_cell",
1358 ]
1448 ]
1359
1449
1360 [[package]]
1450 [[package]]
1361 name = "whoami"
1451 name = "whoami"
1362 version = "1.4.0"
1452 version = "1.4.0"
1363 source = "registry+https://github.com/rust-lang/crates.io-index"
1453 source = "registry+https://github.com/rust-lang/crates.io-index"
1364 checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68"
1454 checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68"
1365 dependencies = [
1455 dependencies = [
1366 "wasm-bindgen",
1456 "wasm-bindgen",
1367 "web-sys",
1457 "web-sys",
1368 ]
1458 ]
1369
1459
1370 [[package]]
1460 [[package]]
1371 name = "winapi"
1461 name = "winapi"
1372 version = "0.3.9"
1462 version = "0.3.9"
1373 source = "registry+https://github.com/rust-lang/crates.io-index"
1463 source = "registry+https://github.com/rust-lang/crates.io-index"
1374 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1464 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1375 dependencies = [
1465 dependencies = [
1376 "winapi-i686-pc-windows-gnu",
1466 "winapi-i686-pc-windows-gnu",
1377 "winapi-x86_64-pc-windows-gnu",
1467 "winapi-x86_64-pc-windows-gnu",
1378 ]
1468 ]
1379
1469
1380 [[package]]
1470 [[package]]
1381 name = "winapi-i686-pc-windows-gnu"
1471 name = "winapi-i686-pc-windows-gnu"
1382 version = "0.4.0"
1472 version = "0.4.0"
1383 source = "registry+https://github.com/rust-lang/crates.io-index"
1473 source = "registry+https://github.com/rust-lang/crates.io-index"
1384 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1474 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1385
1475
1386 [[package]]
1476 [[package]]
1387 name = "winapi-util"
1477 name = "winapi-util"
1388 version = "0.1.5"
1478 version = "0.1.5"
1389 source = "registry+https://github.com/rust-lang/crates.io-index"
1479 source = "registry+https://github.com/rust-lang/crates.io-index"
1390 checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
1480 checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
1391 dependencies = [
1481 dependencies = [
1392 "winapi",
1482 "winapi",
1393 ]
1483 ]
1394
1484
1395 [[package]]
1485 [[package]]
1396 name = "winapi-x86_64-pc-windows-gnu"
1486 name = "winapi-x86_64-pc-windows-gnu"
1397 version = "0.4.0"
1487 version = "0.4.0"
1398 source = "registry+https://github.com/rust-lang/crates.io-index"
1488 source = "registry+https://github.com/rust-lang/crates.io-index"
1399 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1489 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1400
1490
1401 [[package]]
1491 [[package]]
1402 name = "yansi"
1492 name = "yansi"
1403 version = "0.5.1"
1493 version = "0.5.1"
1404 source = "registry+https://github.com/rust-lang/crates.io-index"
1494 source = "registry+https://github.com/rust-lang/crates.io-index"
1405 checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
1495 checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
1406
1496
1407 [[package]]
1497 [[package]]
1408 name = "zstd"
1498 name = "zstd"
1409 version = "0.12.3+zstd.1.5.2"
1499 version = "0.12.3+zstd.1.5.2"
1410 source = "registry+https://github.com/rust-lang/crates.io-index"
1500 source = "registry+https://github.com/rust-lang/crates.io-index"
1411 checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806"
1501 checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806"
1412 dependencies = [
1502 dependencies = [
1413 "zstd-safe",
1503 "zstd-safe",
1414 ]
1504 ]
1415
1505
1416 [[package]]
1506 [[package]]
1417 name = "zstd-safe"
1507 name = "zstd-safe"
1418 version = "6.0.4+zstd.1.5.4"
1508 version = "6.0.4+zstd.1.5.4"
1419 source = "registry+https://github.com/rust-lang/crates.io-index"
1509 source = "registry+https://github.com/rust-lang/crates.io-index"
1420 checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543"
1510 checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543"
1421 dependencies = [
1511 dependencies = [
1422 "libc",
1512 "libc",
1423 "zstd-sys",
1513 "zstd-sys",
1424 ]
1514 ]
1425
1515
1426 [[package]]
1516 [[package]]
1427 name = "zstd-sys"
1517 name = "zstd-sys"
1428 version = "2.0.7+zstd.1.5.4"
1518 version = "2.0.7+zstd.1.5.4"
1429 source = "registry+https://github.com/rust-lang/crates.io-index"
1519 source = "registry+https://github.com/rust-lang/crates.io-index"
1430 checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5"
1520 checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5"
1431 dependencies = [
1521 dependencies = [
1432 "cc",
1522 "cc",
1433 "libc",
1523 "libc",
1434 "pkg-config",
1524 "pkg-config",
1435 ]
1525 ]
@@ -1,50 +1,52 b''
1 [package]
1 [package]
2 name = "hg-core"
2 name = "hg-core"
3 version = "0.1.0"
3 version = "0.1.0"
4 authors = ["Georges Racinet <gracinet@anybox.fr>"]
4 authors = ["Georges Racinet <gracinet@anybox.fr>"]
5 description = "Mercurial pure Rust core library, with no assumption on Python bindings (FFI)"
5 description = "Mercurial pure Rust core library, with no assumption on Python bindings (FFI)"
6 edition = "2021"
6 edition = "2021"
7
7
8 [lib]
8 [lib]
9 name = "hg"
9 name = "hg"
10
10
11 [dependencies]
11 [dependencies]
12 bitflags = "1.3.2"
12 bitflags = "1.3.2"
13 bytes-cast = "0.3.0"
13 bytes-cast = "0.3.0"
14 byteorder = "1.4.3"
14 byteorder = "1.4.3"
15 derive_more = "0.99.17"
15 derive_more = "0.99.17"
16 hashbrown = { version = "0.13.1", features = ["rayon"] }
16 hashbrown = { version = "0.13.1", features = ["rayon"] }
17 home = "0.5.4"
17 home = "0.5.4"
18 im-rc = "15.1.0"
18 im-rc = "15.1.0"
19 itertools = "0.10.5"
19 itertools = "0.10.5"
20 lazy_static = "1.4.0"
20 lazy_static = "1.4.0"
21 libc = "0.2.137"
21 libc = "0.2.137"
22 logging_timer = "1.1.0"
22 logging_timer = "1.1.0"
23 rand = "0.8.5"
23 rand = "0.8.5"
24 rand_pcg = "0.3.1"
24 rand_pcg = "0.3.1"
25 rand_distr = "0.4.3"
25 rand_distr = "0.4.3"
26 rayon = "1.7.0"
26 rayon = "1.7.0"
27 regex = "1.7.0"
27 regex = "1.7.0"
28 self_cell = "1.0"
28 self_cell = "1.0"
29 serde = { version = "1.0", features = ["derive"] }
29 sha-1 = "0.10.0"
30 sha-1 = "0.10.0"
30 twox-hash = "1.6.3"
31 twox-hash = "1.6.3"
31 same-file = "1.0.6"
32 same-file = "1.0.6"
32 tempfile = "3.3.0"
33 tempfile = "3.3.0"
34 toml = "0.6"
33 thread_local = "1.1.4"
35 thread_local = "1.1.4"
34 crossbeam-channel = "0.5.6"
36 crossbeam-channel = "0.5.6"
35 log = "0.4.17"
37 log = "0.4.17"
36 memmap2 = { version = "0.5.8", features = ["stable_deref_trait"] }
38 memmap2 = { version = "0.5.8", features = ["stable_deref_trait"] }
37 zstd = "0.12"
39 zstd = "0.12"
38 format-bytes = "0.3.0"
40 format-bytes = "0.3.0"
39 once_cell = "1.16.0"
41 once_cell = "1.16.0"
40
42
41 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
43 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
42 # we have a clearer view of which backend is the fastest.
44 # we have a clearer view of which backend is the fastest.
43 [dependencies.flate2]
45 [dependencies.flate2]
44 version = "1.0.24"
46 version = "1.0.24"
45 features = ["zlib"]
47 features = ["zlib"]
46 default-features = false
48 default-features = false
47
49
48 [dev-dependencies]
50 [dev-dependencies]
49 clap = { version = "4.0.24", features = ["derive"] }
51 clap = { version = "~4.0", features = ["derive"] }
50 pretty_assertions = "1.1.0"
52 pretty_assertions = "1.1.0"
@@ -1,345 +1,349 b''
1 // layer.rs
1 // layer.rs
2 //
2 //
3 // Copyright 2020
3 // Copyright 2020
4 // Valentin Gatien-Baron,
4 // Valentin Gatien-Baron,
5 // Raphaël Gomès <rgomes@octobus.net>
5 // Raphaël Gomès <rgomes@octobus.net>
6 //
6 //
7 // This software may be used and distributed according to the terms of the
7 // This software may be used and distributed according to the terms of the
8 // GNU General Public License version 2 or any later version.
8 // GNU General Public License version 2 or any later version.
9
9
10 use crate::errors::HgError;
10 use crate::errors::HgError;
11 use crate::exit_codes::CONFIG_PARSE_ERROR_ABORT;
11 use crate::exit_codes::CONFIG_PARSE_ERROR_ABORT;
12 use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
12 use crate::utils::files::{get_bytes_from_path, get_path_from_bytes};
13 use format_bytes::{format_bytes, write_bytes, DisplayBytes};
13 use format_bytes::{format_bytes, write_bytes, DisplayBytes};
14 use lazy_static::lazy_static;
14 use lazy_static::lazy_static;
15 use regex::bytes::Regex;
15 use regex::bytes::Regex;
16 use std::collections::HashMap;
16 use std::collections::HashMap;
17 use std::path::{Path, PathBuf};
17 use std::path::{Path, PathBuf};
18
18
19 lazy_static! {
19 lazy_static! {
20 static ref SECTION_RE: Regex = make_regex(r"^\[([^\[]+)\]");
20 static ref SECTION_RE: Regex = make_regex(r"^\[([^\[]+)\]");
21 static ref ITEM_RE: Regex = make_regex(r"^([^=\s][^=]*?)\s*=\s*((.*\S)?)");
21 static ref ITEM_RE: Regex = make_regex(r"^([^=\s][^=]*?)\s*=\s*((.*\S)?)");
22 /// Continuation whitespace
22 /// Continuation whitespace
23 static ref CONT_RE: Regex = make_regex(r"^\s+(\S|\S.*\S)\s*$");
23 static ref CONT_RE: Regex = make_regex(r"^\s+(\S|\S.*\S)\s*$");
24 static ref EMPTY_RE: Regex = make_regex(r"^(;|#|\s*$)");
24 static ref EMPTY_RE: Regex = make_regex(r"^(;|#|\s*$)");
25 static ref COMMENT_RE: Regex = make_regex(r"^(;|#)");
25 static ref COMMENT_RE: Regex = make_regex(r"^(;|#)");
26 /// A directive that allows for removing previous entries
26 /// A directive that allows for removing previous entries
27 static ref UNSET_RE: Regex = make_regex(r"^%unset\s+(\S+)");
27 static ref UNSET_RE: Regex = make_regex(r"^%unset\s+(\S+)");
28 /// A directive that allows for including other config files
28 /// A directive that allows for including other config files
29 static ref INCLUDE_RE: Regex = make_regex(r"^%include\s+(\S|\S.*\S)\s*$");
29 static ref INCLUDE_RE: Regex = make_regex(r"^%include\s+(\S|\S.*\S)\s*$");
30 }
30 }
31
31
32 /// All config values separated by layers of precedence.
32 /// All config values separated by layers of precedence.
33 /// Each config source may be split in multiple layers if `%include` directives
33 /// Each config source may be split in multiple layers if `%include` directives
34 /// are used.
34 /// are used.
35 /// TODO detail the general precedence
35 /// TODO detail the general precedence
36 #[derive(Clone)]
36 #[derive(Clone)]
37 pub struct ConfigLayer {
37 pub struct ConfigLayer {
38 /// Mapping of the sections to their items
38 /// Mapping of the sections to their items
39 sections: HashMap<Vec<u8>, ConfigItem>,
39 sections: HashMap<Vec<u8>, ConfigItem>,
40 /// All sections (and their items/values) in a layer share the same origin
40 /// All sections (and their items/values) in a layer share the same origin
41 pub origin: ConfigOrigin,
41 pub origin: ConfigOrigin,
42 /// Whether this layer comes from a trusted user or group
42 /// Whether this layer comes from a trusted user or group
43 pub trusted: bool,
43 pub trusted: bool,
44 }
44 }
45
45
46 impl ConfigLayer {
46 impl ConfigLayer {
47 pub fn new(origin: ConfigOrigin) -> Self {
47 pub fn new(origin: ConfigOrigin) -> Self {
48 ConfigLayer {
48 ConfigLayer {
49 sections: HashMap::new(),
49 sections: HashMap::new(),
50 trusted: true, // TODO check
50 trusted: true, // TODO check
51 origin,
51 origin,
52 }
52 }
53 }
53 }
54
54
55 /// Parse `--config` CLI arguments and return a layer if there’s any
55 /// Parse `--config` CLI arguments and return a layer if there’s any
56 pub(crate) fn parse_cli_args(
56 pub(crate) fn parse_cli_args(
57 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
57 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
58 ) -> Result<Option<Self>, ConfigError> {
58 ) -> Result<Option<Self>, ConfigError> {
59 fn parse_one(arg: &[u8]) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)> {
59 fn parse_one(arg: &[u8]) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)> {
60 use crate::utils::SliceExt;
60 use crate::utils::SliceExt;
61
61
62 let (section_and_item, value) = arg.split_2(b'=')?;
62 let (section_and_item, value) = arg.split_2(b'=')?;
63 let (section, item) = section_and_item.trim().split_2(b'.')?;
63 let (section, item) = section_and_item.trim().split_2(b'.')?;
64 Some((
64 Some((
65 section.to_owned(),
65 section.to_owned(),
66 item.to_owned(),
66 item.to_owned(),
67 value.trim().to_owned(),
67 value.trim().to_owned(),
68 ))
68 ))
69 }
69 }
70
70
71 let mut layer = Self::new(ConfigOrigin::CommandLine);
71 let mut layer = Self::new(ConfigOrigin::CommandLine);
72 for arg in cli_config_args {
72 for arg in cli_config_args {
73 let arg = arg.as_ref();
73 let arg = arg.as_ref();
74 if let Some((section, item, value)) = parse_one(arg) {
74 if let Some((section, item, value)) = parse_one(arg) {
75 layer.add(section, item, value, None);
75 layer.add(section, item, value, None);
76 } else {
76 } else {
77 Err(HgError::abort(
77 Err(HgError::abort(
78 format!(
78 format!(
79 "abort: malformed --config option: '{}' \
79 "abort: malformed --config option: '{}' \
80 (use --config section.name=value)",
80 (use --config section.name=value)",
81 String::from_utf8_lossy(arg),
81 String::from_utf8_lossy(arg),
82 ),
82 ),
83 CONFIG_PARSE_ERROR_ABORT,
83 CONFIG_PARSE_ERROR_ABORT,
84 None,
84 None,
85 ))?
85 ))?
86 }
86 }
87 }
87 }
88 if layer.sections.is_empty() {
88 if layer.sections.is_empty() {
89 Ok(None)
89 Ok(None)
90 } else {
90 } else {
91 Ok(Some(layer))
91 Ok(Some(layer))
92 }
92 }
93 }
93 }
94
94
95 /// Returns whether this layer comes from `--config` CLI arguments
95 /// Returns whether this layer comes from `--config` CLI arguments
96 pub(crate) fn is_from_command_line(&self) -> bool {
96 pub(crate) fn is_from_command_line(&self) -> bool {
97 matches!(self.origin, ConfigOrigin::CommandLine)
97 matches!(self.origin, ConfigOrigin::CommandLine)
98 }
98 }
99
99
100 /// Add an entry to the config, overwriting the old one if already present.
100 /// Add an entry to the config, overwriting the old one if already present.
101 pub fn add(
101 pub fn add(
102 &mut self,
102 &mut self,
103 section: Vec<u8>,
103 section: Vec<u8>,
104 item: Vec<u8>,
104 item: Vec<u8>,
105 value: Vec<u8>,
105 value: Vec<u8>,
106 line: Option<usize>,
106 line: Option<usize>,
107 ) {
107 ) {
108 self.sections
108 self.sections
109 .entry(section)
109 .entry(section)
110 .or_insert_with(HashMap::new)
110 .or_insert_with(HashMap::new)
111 .insert(item, ConfigValue { bytes: value, line });
111 .insert(item, ConfigValue { bytes: value, line });
112 }
112 }
113
113
114 /// Returns the config value in `<section>.<item>` if it exists
114 /// Returns the config value in `<section>.<item>` if it exists
115 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&ConfigValue> {
115 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&ConfigValue> {
116 self.sections.get(section)?.get(item)
116 self.sections.get(section)?.get(item)
117 }
117 }
118
118
119 /// Returns the keys defined in the given section
119 /// Returns the keys defined in the given section
120 pub fn iter_keys(&self, section: &[u8]) -> impl Iterator<Item = &[u8]> {
120 pub fn iter_keys(&self, section: &[u8]) -> impl Iterator<Item = &[u8]> {
121 self.sections
121 self.sections
122 .get(section)
122 .get(section)
123 .into_iter()
123 .into_iter()
124 .flat_map(|section| section.keys().map(|vec| &**vec))
124 .flat_map(|section| section.keys().map(|vec| &**vec))
125 }
125 }
126
126
127 /// Returns the (key, value) pairs defined in the given section
127 /// Returns the (key, value) pairs defined in the given section
128 pub fn iter_section<'layer>(
128 pub fn iter_section<'layer>(
129 &'layer self,
129 &'layer self,
130 section: &[u8],
130 section: &[u8],
131 ) -> impl Iterator<Item = (&'layer [u8], &'layer [u8])> {
131 ) -> impl Iterator<Item = (&'layer [u8], &'layer [u8])> {
132 self.sections
132 self.sections
133 .get(section)
133 .get(section)
134 .into_iter()
134 .into_iter()
135 .flat_map(|section| section.iter().map(|(k, v)| (&**k, &*v.bytes)))
135 .flat_map(|section| section.iter().map(|(k, v)| (&**k, &*v.bytes)))
136 }
136 }
137
137
138 /// Returns whether any key is defined in the given section
138 /// Returns whether any key is defined in the given section
139 pub fn has_non_empty_section(&self, section: &[u8]) -> bool {
139 pub fn has_non_empty_section(&self, section: &[u8]) -> bool {
140 self.sections
140 self.sections
141 .get(section)
141 .get(section)
142 .map_or(false, |section| !section.is_empty())
142 .map_or(false, |section| !section.is_empty())
143 }
143 }
144
144
145 pub fn is_empty(&self) -> bool {
145 pub fn is_empty(&self) -> bool {
146 self.sections.is_empty()
146 self.sections.is_empty()
147 }
147 }
148
148
149 /// Returns a `Vec` of layers in order of precedence (so, in read order),
149 /// Returns a `Vec` of layers in order of precedence (so, in read order),
150 /// recursively parsing the `%include` directives if any.
150 /// recursively parsing the `%include` directives if any.
151 pub fn parse(src: &Path, data: &[u8]) -> Result<Vec<Self>, ConfigError> {
151 pub fn parse(src: &Path, data: &[u8]) -> Result<Vec<Self>, ConfigError> {
152 let mut layers = vec![];
152 let mut layers = vec![];
153
153
154 // Discard byte order mark if any
154 // Discard byte order mark if any
155 let data = if data.starts_with(b"\xef\xbb\xbf") {
155 let data = if data.starts_with(b"\xef\xbb\xbf") {
156 &data[3..]
156 &data[3..]
157 } else {
157 } else {
158 data
158 data
159 };
159 };
160
160
161 // TODO check if it's trusted
161 // TODO check if it's trusted
162 let mut current_layer = Self::new(ConfigOrigin::File(src.to_owned()));
162 let mut current_layer = Self::new(ConfigOrigin::File(src.to_owned()));
163
163
164 let mut lines_iter =
164 let mut lines_iter =
165 data.split(|b| *b == b'\n').enumerate().peekable();
165 data.split(|b| *b == b'\n').enumerate().peekable();
166 let mut section = b"".to_vec();
166 let mut section = b"".to_vec();
167
167
168 while let Some((index, bytes)) = lines_iter.next() {
168 while let Some((index, bytes)) = lines_iter.next() {
169 let line = Some(index + 1);
169 let line = Some(index + 1);
170 if let Some(m) = INCLUDE_RE.captures(bytes) {
170 if let Some(m) = INCLUDE_RE.captures(bytes) {
171 let filename_bytes = &m[1];
171 let filename_bytes = &m[1];
172 let filename_bytes = crate::utils::expand_vars(filename_bytes);
172 let filename_bytes = crate::utils::expand_vars(filename_bytes);
173 // `Path::parent` only fails for the root directory,
173 // `Path::parent` only fails for the root directory,
174 // which `src` can’t be since we’ve managed to open it as a
174 // which `src` can’t be since we’ve managed to open it as a
175 // file.
175 // file.
176 let dir = src
176 let dir = src
177 .parent()
177 .parent()
178 .expect("Path::parent fail on a file we’ve read");
178 .expect("Path::parent fail on a file we’ve read");
179 // `Path::join` with an absolute argument correctly ignores the
179 // `Path::join` with an absolute argument correctly ignores the
180 // base path
180 // base path
181 let filename = dir.join(&get_path_from_bytes(&filename_bytes));
181 let filename = dir.join(&get_path_from_bytes(&filename_bytes));
182 match std::fs::read(&filename) {
182 match std::fs::read(&filename) {
183 Ok(data) => {
183 Ok(data) => {
184 layers.push(current_layer);
184 layers.push(current_layer);
185 layers.extend(Self::parse(&filename, &data)?);
185 layers.extend(Self::parse(&filename, &data)?);
186 current_layer =
186 current_layer =
187 Self::new(ConfigOrigin::File(src.to_owned()));
187 Self::new(ConfigOrigin::File(src.to_owned()));
188 }
188 }
189 Err(error) => {
189 Err(error) => {
190 if error.kind() != std::io::ErrorKind::NotFound {
190 if error.kind() != std::io::ErrorKind::NotFound {
191 return Err(ConfigParseError {
191 return Err(ConfigParseError {
192 origin: ConfigOrigin::File(src.to_owned()),
192 origin: ConfigOrigin::File(src.to_owned()),
193 line,
193 line,
194 message: format_bytes!(
194 message: format_bytes!(
195 b"cannot include {} ({})",
195 b"cannot include {} ({})",
196 filename_bytes,
196 filename_bytes,
197 format_bytes::Utf8(error)
197 format_bytes::Utf8(error)
198 ),
198 ),
199 }
199 }
200 .into());
200 .into());
201 }
201 }
202 }
202 }
203 }
203 }
204 } else if EMPTY_RE.captures(bytes).is_some() {
204 } else if EMPTY_RE.captures(bytes).is_some() {
205 } else if let Some(m) = SECTION_RE.captures(bytes) {
205 } else if let Some(m) = SECTION_RE.captures(bytes) {
206 section = m[1].to_vec();
206 section = m[1].to_vec();
207 } else if let Some(m) = ITEM_RE.captures(bytes) {
207 } else if let Some(m) = ITEM_RE.captures(bytes) {
208 let item = m[1].to_vec();
208 let item = m[1].to_vec();
209 let mut value = m[2].to_vec();
209 let mut value = m[2].to_vec();
210 loop {
210 loop {
211 match lines_iter.peek() {
211 match lines_iter.peek() {
212 None => break,
212 None => break,
213 Some((_, v)) => {
213 Some((_, v)) => {
214 if COMMENT_RE.captures(v).is_some() {
214 if COMMENT_RE.captures(v).is_some() {
215 } else if CONT_RE.captures(v).is_some() {
215 } else if CONT_RE.captures(v).is_some() {
216 value.extend(b"\n");
216 value.extend(b"\n");
217 value.extend(&m[1]);
217 value.extend(&m[1]);
218 } else {
218 } else {
219 break;
219 break;
220 }
220 }
221 }
221 }
222 };
222 };
223 lines_iter.next();
223 lines_iter.next();
224 }
224 }
225 current_layer.add(section.clone(), item, value, line);
225 current_layer.add(section.clone(), item, value, line);
226 } else if let Some(m) = UNSET_RE.captures(bytes) {
226 } else if let Some(m) = UNSET_RE.captures(bytes) {
227 if let Some(map) = current_layer.sections.get_mut(&section) {
227 if let Some(map) = current_layer.sections.get_mut(&section) {
228 map.remove(&m[1]);
228 map.remove(&m[1]);
229 }
229 }
230 } else {
230 } else {
231 let message = if bytes.starts_with(b" ") {
231 let message = if bytes.starts_with(b" ") {
232 format_bytes!(b"unexpected leading whitespace: {}", bytes)
232 format_bytes!(b"unexpected leading whitespace: {}", bytes)
233 } else {
233 } else {
234 bytes.to_owned()
234 bytes.to_owned()
235 };
235 };
236 return Err(ConfigParseError {
236 return Err(ConfigParseError {
237 origin: ConfigOrigin::File(src.to_owned()),
237 origin: ConfigOrigin::File(src.to_owned()),
238 line,
238 line,
239 message,
239 message,
240 }
240 }
241 .into());
241 .into());
242 }
242 }
243 }
243 }
244 if !current_layer.is_empty() {
244 if !current_layer.is_empty() {
245 layers.push(current_layer);
245 layers.push(current_layer);
246 }
246 }
247 Ok(layers)
247 Ok(layers)
248 }
248 }
249 }
249 }
250
250
251 impl DisplayBytes for ConfigLayer {
251 impl DisplayBytes for ConfigLayer {
252 fn display_bytes(
252 fn display_bytes(
253 &self,
253 &self,
254 out: &mut dyn std::io::Write,
254 out: &mut dyn std::io::Write,
255 ) -> std::io::Result<()> {
255 ) -> std::io::Result<()> {
256 let mut sections: Vec<_> = self.sections.iter().collect();
256 let mut sections: Vec<_> = self.sections.iter().collect();
257 sections.sort_by(|e0, e1| e0.0.cmp(e1.0));
257 sections.sort_by(|e0, e1| e0.0.cmp(e1.0));
258
258
259 for (section, items) in sections.into_iter() {
259 for (section, items) in sections.into_iter() {
260 let mut items: Vec<_> = items.iter().collect();
260 let mut items: Vec<_> = items.iter().collect();
261 items.sort_by(|e0, e1| e0.0.cmp(e1.0));
261 items.sort_by(|e0, e1| e0.0.cmp(e1.0));
262
262
263 for (item, config_entry) in items {
263 for (item, config_entry) in items {
264 write_bytes!(
264 write_bytes!(
265 out,
265 out,
266 b"{}.{}={} # {}\n",
266 b"{}.{}={} # {}\n",
267 section,
267 section,
268 item,
268 item,
269 &config_entry.bytes,
269 &config_entry.bytes,
270 &self.origin,
270 &self.origin,
271 )?
271 )?
272 }
272 }
273 }
273 }
274 Ok(())
274 Ok(())
275 }
275 }
276 }
276 }
277
277
278 /// Mapping of section item to value.
278 /// Mapping of section item to value.
279 /// In the following:
279 /// In the following:
280 /// ```text
280 /// ```text
281 /// [ui]
281 /// [ui]
282 /// paginate=no
282 /// paginate=no
283 /// ```
283 /// ```
284 /// "paginate" is the section item and "no" the value.
284 /// "paginate" is the section item and "no" the value.
285 pub type ConfigItem = HashMap<Vec<u8>, ConfigValue>;
285 pub type ConfigItem = HashMap<Vec<u8>, ConfigValue>;
286
286
287 #[derive(Clone, Debug, PartialEq)]
287 #[derive(Clone, Debug, PartialEq)]
288 pub struct ConfigValue {
288 pub struct ConfigValue {
289 /// The raw bytes of the value (be it from the CLI, env or from a file)
289 /// The raw bytes of the value (be it from the CLI, env or from a file)
290 pub bytes: Vec<u8>,
290 pub bytes: Vec<u8>,
291 /// Only present if the value comes from a file, 1-indexed.
291 /// Only present if the value comes from a file, 1-indexed.
292 pub line: Option<usize>,
292 pub line: Option<usize>,
293 }
293 }
294
294
295 #[derive(Clone, Debug, PartialEq, Eq)]
295 #[derive(Clone, Debug, PartialEq, Eq)]
296 pub enum ConfigOrigin {
296 pub enum ConfigOrigin {
297 /// From a configuration file
297 /// From a configuration file
298 File(PathBuf),
298 File(PathBuf),
299 /// From [ui.tweakdefaults]
299 /// From [ui.tweakdefaults]
300 Tweakdefaults,
300 Tweakdefaults,
301 /// From a `--config` CLI argument
301 /// From a `--config` CLI argument
302 CommandLine,
302 CommandLine,
303 /// From a `--color` CLI argument
303 /// From a `--color` CLI argument
304 CommandLineColor,
304 CommandLineColor,
305 /// From environment variables like `$PAGER` or `$EDITOR`
305 /// From environment variables like `$PAGER` or `$EDITOR`
306 Environment(Vec<u8>),
306 Environment(Vec<u8>),
307 /* TODO defaults (configitems.py)
307 /// From configitems.toml
308 * TODO extensions
308 Defaults,
309 /* TODO extensions
309 * TODO Python resources?
310 * TODO Python resources?
310 * Others? */
311 * Others? */
311 }
312 }
312
313
313 impl DisplayBytes for ConfigOrigin {
314 impl DisplayBytes for ConfigOrigin {
314 fn display_bytes(
315 fn display_bytes(
315 &self,
316 &self,
316 out: &mut dyn std::io::Write,
317 out: &mut dyn std::io::Write,
317 ) -> std::io::Result<()> {
318 ) -> std::io::Result<()> {
318 match self {
319 match self {
319 ConfigOrigin::File(p) => out.write_all(&get_bytes_from_path(p)),
320 ConfigOrigin::File(p) => out.write_all(&get_bytes_from_path(p)),
320 ConfigOrigin::CommandLine => out.write_all(b"--config"),
321 ConfigOrigin::CommandLine => out.write_all(b"--config"),
321 ConfigOrigin::CommandLineColor => out.write_all(b"--color"),
322 ConfigOrigin::CommandLineColor => out.write_all(b"--color"),
322 ConfigOrigin::Environment(e) => write_bytes!(out, b"${}", e),
323 ConfigOrigin::Environment(e) => write_bytes!(out, b"${}", e),
323 ConfigOrigin::Tweakdefaults => {
324 ConfigOrigin::Tweakdefaults => {
324 write_bytes!(out, b"ui.tweakdefaults")
325 write_bytes!(out, b"ui.tweakdefaults")
325 }
326 }
327 ConfigOrigin::Defaults => {
328 write_bytes!(out, b"configitems.toml")
329 }
326 }
330 }
327 }
331 }
328 }
332 }
329
333
330 #[derive(Debug)]
334 #[derive(Debug)]
331 pub struct ConfigParseError {
335 pub struct ConfigParseError {
332 pub origin: ConfigOrigin,
336 pub origin: ConfigOrigin,
333 pub line: Option<usize>,
337 pub line: Option<usize>,
334 pub message: Vec<u8>,
338 pub message: Vec<u8>,
335 }
339 }
336
340
337 #[derive(Debug, derive_more::From)]
341 #[derive(Debug, derive_more::From)]
338 pub enum ConfigError {
342 pub enum ConfigError {
339 Parse(ConfigParseError),
343 Parse(ConfigParseError),
340 Other(HgError),
344 Other(HgError),
341 }
345 }
342
346
343 fn make_regex(pattern: &'static str) -> Regex {
347 fn make_regex(pattern: &'static str) -> Regex {
344 Regex::new(pattern).expect("expected a valid regex")
348 Regex::new(pattern).expect("expected a valid regex")
345 }
349 }
@@ -1,636 +1,674 b''
1 // config.rs
1 // config.rs
2 //
2 //
3 // Copyright 2020
3 // Copyright 2020
4 // Valentin Gatien-Baron,
4 // Valentin Gatien-Baron,
5 // Raphaël Gomès <rgomes@octobus.net>
5 // Raphaël Gomès <rgomes@octobus.net>
6 //
6 //
7 // This software may be used and distributed according to the terms of the
7 // This software may be used and distributed according to the terms of the
8 // GNU General Public License version 2 or any later version.
8 // GNU General Public License version 2 or any later version.
9
9
10 //! Mercurial config parsing and interfaces.
10 //! Mercurial config parsing and interfaces.
11
11
12 pub mod config_items;
12 mod layer;
13 mod layer;
13 mod plain_info;
14 mod plain_info;
14 mod values;
15 mod values;
15 pub use layer::{ConfigError, ConfigOrigin, ConfigParseError};
16 pub use layer::{ConfigError, ConfigOrigin, ConfigParseError};
17 use lazy_static::lazy_static;
16 pub use plain_info::PlainInfo;
18 pub use plain_info::PlainInfo;
17
19
20 use self::config_items::DefaultConfig;
21 use self::config_items::DefaultConfigItem;
18 use self::layer::ConfigLayer;
22 use self::layer::ConfigLayer;
19 use self::layer::ConfigValue;
23 use self::layer::ConfigValue;
24 use crate::errors::HgError;
20 use crate::errors::{HgResultExt, IoResultExt};
25 use crate::errors::{HgResultExt, IoResultExt};
21 use crate::utils::files::get_bytes_from_os_str;
26 use crate::utils::files::get_bytes_from_os_str;
22 use format_bytes::{write_bytes, DisplayBytes};
27 use format_bytes::{write_bytes, DisplayBytes};
23 use std::collections::HashSet;
28 use std::collections::HashSet;
24 use std::env;
29 use std::env;
25 use std::fmt;
30 use std::fmt;
26 use std::path::{Path, PathBuf};
31 use std::path::{Path, PathBuf};
27 use std::str;
32 use std::str;
28
33
34 lazy_static! {
35 static ref DEFAULT_CONFIG: Result<DefaultConfig, HgError> = {
36 DefaultConfig::from_contents(include_str!(
37 "../../../../mercurial/configitems.toml"
38 ))
39 };
40 }
41
29 /// Holds the config values for the current repository
42 /// Holds the config values for the current repository
30 /// TODO update this docstring once we support more sources
43 /// TODO update this docstring once we support more sources
31 #[derive(Clone)]
44 #[derive(Clone)]
32 pub struct Config {
45 pub struct Config {
33 layers: Vec<layer::ConfigLayer>,
46 layers: Vec<layer::ConfigLayer>,
34 plain: PlainInfo,
47 plain: PlainInfo,
35 }
48 }
36
49
37 impl DisplayBytes for Config {
50 impl DisplayBytes for Config {
38 fn display_bytes(
51 fn display_bytes(
39 &self,
52 &self,
40 out: &mut dyn std::io::Write,
53 out: &mut dyn std::io::Write,
41 ) -> std::io::Result<()> {
54 ) -> std::io::Result<()> {
42 for (index, layer) in self.layers.iter().rev().enumerate() {
55 for (index, layer) in self.layers.iter().rev().enumerate() {
43 write_bytes!(
56 write_bytes!(
44 out,
57 out,
45 b"==== Layer {} (trusted: {}) ====\n{}",
58 b"==== Layer {} (trusted: {}) ====\n{}",
46 index,
59 index,
47 if layer.trusted {
60 if layer.trusted {
48 &b"yes"[..]
61 &b"yes"[..]
49 } else {
62 } else {
50 &b"no"[..]
63 &b"no"[..]
51 },
64 },
52 layer
65 layer
53 )?;
66 )?;
54 }
67 }
55 Ok(())
68 Ok(())
56 }
69 }
57 }
70 }
58
71
59 pub enum ConfigSource {
72 pub enum ConfigSource {
60 /// Absolute path to a config file
73 /// Absolute path to a config file
61 AbsPath(PathBuf),
74 AbsPath(PathBuf),
62 /// Already parsed (from the CLI, env, Python resources, etc.)
75 /// Already parsed (from the CLI, env, Python resources, etc.)
63 Parsed(layer::ConfigLayer),
76 Parsed(layer::ConfigLayer),
64 }
77 }
65
78
66 #[derive(Debug)]
79 #[derive(Debug)]
67 pub struct ConfigValueParseErrorDetails {
80 pub struct ConfigValueParseErrorDetails {
68 pub origin: ConfigOrigin,
81 pub origin: ConfigOrigin,
69 pub line: Option<usize>,
82 pub line: Option<usize>,
70 pub section: Vec<u8>,
83 pub section: Vec<u8>,
71 pub item: Vec<u8>,
84 pub item: Vec<u8>,
72 pub value: Vec<u8>,
85 pub value: Vec<u8>,
73 pub expected_type: &'static str,
86 pub expected_type: &'static str,
74 }
87 }
75
88
76 // boxed to avoid very large Result types
89 // boxed to avoid very large Result types
77 pub type ConfigValueParseError = Box<ConfigValueParseErrorDetails>;
90 pub type ConfigValueParseError = Box<ConfigValueParseErrorDetails>;
78
91
79 impl fmt::Display for ConfigValueParseError {
92 impl fmt::Display for ConfigValueParseError {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 // TODO: add origin and line number information, here and in
94 // TODO: add origin and line number information, here and in
82 // corresponding python code
95 // corresponding python code
83 write!(
96 write!(
84 f,
97 f,
85 "config error: {}.{} is not a {} ('{}')",
98 "config error: {}.{} is not a {} ('{}')",
86 String::from_utf8_lossy(&self.section),
99 String::from_utf8_lossy(&self.section),
87 String::from_utf8_lossy(&self.item),
100 String::from_utf8_lossy(&self.item),
88 self.expected_type,
101 self.expected_type,
89 String::from_utf8_lossy(&self.value)
102 String::from_utf8_lossy(&self.value)
90 )
103 )
91 }
104 }
92 }
105 }
93
106
94 /// Returns true if the config item is disabled by PLAIN or PLAINEXCEPT
107 /// Returns true if the config item is disabled by PLAIN or PLAINEXCEPT
95 fn should_ignore(plain: &PlainInfo, section: &[u8], item: &[u8]) -> bool {
108 fn should_ignore(plain: &PlainInfo, section: &[u8], item: &[u8]) -> bool {
96 // duplication with [_applyconfig] in [ui.py],
109 // duplication with [_applyconfig] in [ui.py],
97 if !plain.is_plain() {
110 if !plain.is_plain() {
98 return false;
111 return false;
99 }
112 }
100 if section == b"alias" {
113 if section == b"alias" {
101 return plain.plainalias();
114 return plain.plainalias();
102 }
115 }
103 if section == b"revsetalias" {
116 if section == b"revsetalias" {
104 return plain.plainrevsetalias();
117 return plain.plainrevsetalias();
105 }
118 }
106 if section == b"templatealias" {
119 if section == b"templatealias" {
107 return plain.plaintemplatealias();
120 return plain.plaintemplatealias();
108 }
121 }
109 if section == b"ui" {
122 if section == b"ui" {
110 let to_delete: &[&[u8]] = &[
123 let to_delete: &[&[u8]] = &[
111 b"debug",
124 b"debug",
112 b"fallbackencoding",
125 b"fallbackencoding",
113 b"quiet",
126 b"quiet",
114 b"slash",
127 b"slash",
115 b"logtemplate",
128 b"logtemplate",
116 b"message-output",
129 b"message-output",
117 b"statuscopies",
130 b"statuscopies",
118 b"style",
131 b"style",
119 b"traceback",
132 b"traceback",
120 b"verbose",
133 b"verbose",
121 ];
134 ];
122 return to_delete.contains(&item);
135 return to_delete.contains(&item);
123 }
136 }
124 let sections_to_delete: &[&[u8]] =
137 let sections_to_delete: &[&[u8]] =
125 &[b"defaults", b"commands", b"command-templates"];
138 &[b"defaults", b"commands", b"command-templates"];
126 sections_to_delete.contains(&section)
139 sections_to_delete.contains(&section)
127 }
140 }
128
141
129 impl Config {
142 impl Config {
130 /// The configuration to use when printing configuration-loading errors
143 /// The configuration to use when printing configuration-loading errors
131 pub fn empty() -> Self {
144 pub fn empty() -> Self {
132 Self {
145 Self {
133 layers: Vec::new(),
146 layers: Vec::new(),
134 plain: PlainInfo::empty(),
147 plain: PlainInfo::empty(),
135 }
148 }
136 }
149 }
137
150
138 /// Load system and user configuration from various files.
151 /// Load system and user configuration from various files.
139 ///
152 ///
140 /// This is also affected by some environment variables.
153 /// This is also affected by some environment variables.
141 pub fn load_non_repo() -> Result<Self, ConfigError> {
154 pub fn load_non_repo() -> Result<Self, ConfigError> {
142 let mut config = Self::empty();
155 let mut config = Self::empty();
143 let opt_rc_path = env::var_os("HGRCPATH");
156 let opt_rc_path = env::var_os("HGRCPATH");
144 // HGRCPATH replaces system config
157 // HGRCPATH replaces system config
145 if opt_rc_path.is_none() {
158 if opt_rc_path.is_none() {
146 config.add_system_config()?
159 config.add_system_config()?
147 }
160 }
148
161
149 config.add_for_environment_variable("EDITOR", b"ui", b"editor");
162 config.add_for_environment_variable("EDITOR", b"ui", b"editor");
150 config.add_for_environment_variable("VISUAL", b"ui", b"editor");
163 config.add_for_environment_variable("VISUAL", b"ui", b"editor");
151 config.add_for_environment_variable("PAGER", b"pager", b"pager");
164 config.add_for_environment_variable("PAGER", b"pager", b"pager");
152
165
153 // These are set by `run-tests.py --rhg` to enable fallback for the
166 // These are set by `run-tests.py --rhg` to enable fallback for the
154 // entire test suite. Alternatives would be setting configuration
167 // entire test suite. Alternatives would be setting configuration
155 // through `$HGRCPATH` but some tests override that, or changing the
168 // through `$HGRCPATH` but some tests override that, or changing the
156 // `hg` shell alias to include `--config` but that disrupts tests that
169 // `hg` shell alias to include `--config` but that disrupts tests that
157 // print command lines and check expected output.
170 // print command lines and check expected output.
158 config.add_for_environment_variable(
171 config.add_for_environment_variable(
159 "RHG_ON_UNSUPPORTED",
172 "RHG_ON_UNSUPPORTED",
160 b"rhg",
173 b"rhg",
161 b"on-unsupported",
174 b"on-unsupported",
162 );
175 );
163 config.add_for_environment_variable(
176 config.add_for_environment_variable(
164 "RHG_FALLBACK_EXECUTABLE",
177 "RHG_FALLBACK_EXECUTABLE",
165 b"rhg",
178 b"rhg",
166 b"fallback-executable",
179 b"fallback-executable",
167 );
180 );
168
181
169 // HGRCPATH replaces user config
182 // HGRCPATH replaces user config
170 if opt_rc_path.is_none() {
183 if opt_rc_path.is_none() {
171 config.add_user_config()?
184 config.add_user_config()?
172 }
185 }
173 if let Some(rc_path) = &opt_rc_path {
186 if let Some(rc_path) = &opt_rc_path {
174 for path in env::split_paths(rc_path) {
187 for path in env::split_paths(rc_path) {
175 if !path.as_os_str().is_empty() {
188 if !path.as_os_str().is_empty() {
176 if path.is_dir() {
189 if path.is_dir() {
177 config.add_trusted_dir(&path)?
190 config.add_trusted_dir(&path)?
178 } else {
191 } else {
179 config.add_trusted_file(&path)?
192 config.add_trusted_file(&path)?
180 }
193 }
181 }
194 }
182 }
195 }
183 }
196 }
184 Ok(config)
197 Ok(config)
185 }
198 }
186
199
187 pub fn load_cli_args(
200 pub fn load_cli_args(
188 &mut self,
201 &mut self,
189 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
202 cli_config_args: impl IntoIterator<Item = impl AsRef<[u8]>>,
190 color_arg: Option<Vec<u8>>,
203 color_arg: Option<Vec<u8>>,
191 ) -> Result<(), ConfigError> {
204 ) -> Result<(), ConfigError> {
192 if let Some(layer) = ConfigLayer::parse_cli_args(cli_config_args)? {
205 if let Some(layer) = ConfigLayer::parse_cli_args(cli_config_args)? {
193 self.layers.push(layer)
206 self.layers.push(layer)
194 }
207 }
195 if let Some(arg) = color_arg {
208 if let Some(arg) = color_arg {
196 let mut layer = ConfigLayer::new(ConfigOrigin::CommandLineColor);
209 let mut layer = ConfigLayer::new(ConfigOrigin::CommandLineColor);
197 layer.add(b"ui"[..].into(), b"color"[..].into(), arg, None);
210 layer.add(b"ui"[..].into(), b"color"[..].into(), arg, None);
198 self.layers.push(layer)
211 self.layers.push(layer)
199 }
212 }
200 Ok(())
213 Ok(())
201 }
214 }
202
215
203 fn add_trusted_dir(&mut self, path: &Path) -> Result<(), ConfigError> {
216 fn add_trusted_dir(&mut self, path: &Path) -> Result<(), ConfigError> {
204 if let Some(entries) = std::fs::read_dir(path)
217 if let Some(entries) = std::fs::read_dir(path)
205 .when_reading_file(path)
218 .when_reading_file(path)
206 .io_not_found_as_none()?
219 .io_not_found_as_none()?
207 {
220 {
208 let mut file_paths = entries
221 let mut file_paths = entries
209 .map(|result| {
222 .map(|result| {
210 result.when_reading_file(path).map(|entry| entry.path())
223 result.when_reading_file(path).map(|entry| entry.path())
211 })
224 })
212 .collect::<Result<Vec<_>, _>>()?;
225 .collect::<Result<Vec<_>, _>>()?;
213 file_paths.sort();
226 file_paths.sort();
214 for file_path in &file_paths {
227 for file_path in &file_paths {
215 if file_path.extension() == Some(std::ffi::OsStr::new("rc")) {
228 if file_path.extension() == Some(std::ffi::OsStr::new("rc")) {
216 self.add_trusted_file(file_path)?
229 self.add_trusted_file(file_path)?
217 }
230 }
218 }
231 }
219 }
232 }
220 Ok(())
233 Ok(())
221 }
234 }
222
235
223 fn add_trusted_file(&mut self, path: &Path) -> Result<(), ConfigError> {
236 fn add_trusted_file(&mut self, path: &Path) -> Result<(), ConfigError> {
224 if let Some(data) = std::fs::read(path)
237 if let Some(data) = std::fs::read(path)
225 .when_reading_file(path)
238 .when_reading_file(path)
226 .io_not_found_as_none()?
239 .io_not_found_as_none()?
227 {
240 {
228 self.layers.extend(ConfigLayer::parse(path, &data)?)
241 self.layers.extend(ConfigLayer::parse(path, &data)?)
229 }
242 }
230 Ok(())
243 Ok(())
231 }
244 }
232
245
233 fn add_for_environment_variable(
246 fn add_for_environment_variable(
234 &mut self,
247 &mut self,
235 var: &str,
248 var: &str,
236 section: &[u8],
249 section: &[u8],
237 key: &[u8],
250 key: &[u8],
238 ) {
251 ) {
239 if let Some(value) = env::var_os(var) {
252 if let Some(value) = env::var_os(var) {
240 let origin = layer::ConfigOrigin::Environment(var.into());
253 let origin = layer::ConfigOrigin::Environment(var.into());
241 let mut layer = ConfigLayer::new(origin);
254 let mut layer = ConfigLayer::new(origin);
242 layer.add(
255 layer.add(
243 section.to_owned(),
256 section.to_owned(),
244 key.to_owned(),
257 key.to_owned(),
245 get_bytes_from_os_str(value),
258 get_bytes_from_os_str(value),
246 None,
259 None,
247 );
260 );
248 self.layers.push(layer)
261 self.layers.push(layer)
249 }
262 }
250 }
263 }
251
264
252 #[cfg(unix)] // TODO: other platforms
265 #[cfg(unix)] // TODO: other platforms
253 fn add_system_config(&mut self) -> Result<(), ConfigError> {
266 fn add_system_config(&mut self) -> Result<(), ConfigError> {
254 let mut add_for_prefix = |prefix: &Path| -> Result<(), ConfigError> {
267 let mut add_for_prefix = |prefix: &Path| -> Result<(), ConfigError> {
255 let etc = prefix.join("etc").join("mercurial");
268 let etc = prefix.join("etc").join("mercurial");
256 self.add_trusted_file(&etc.join("hgrc"))?;
269 self.add_trusted_file(&etc.join("hgrc"))?;
257 self.add_trusted_dir(&etc.join("hgrc.d"))
270 self.add_trusted_dir(&etc.join("hgrc.d"))
258 };
271 };
259 let root = Path::new("/");
272 let root = Path::new("/");
260 // TODO: use `std::env::args_os().next().unwrap()` a.k.a. argv[0]
273 // TODO: use `std::env::args_os().next().unwrap()` a.k.a. argv[0]
261 // instead? TODO: can this be a relative path?
274 // instead? TODO: can this be a relative path?
262 let hg = crate::utils::current_exe()?;
275 let hg = crate::utils::current_exe()?;
263 // TODO: this order (per-installation then per-system) matches
276 // TODO: this order (per-installation then per-system) matches
264 // `systemrcpath()` in `mercurial/scmposix.py`, but
277 // `systemrcpath()` in `mercurial/scmposix.py`, but
265 // `mercurial/helptext/config.txt` suggests it should be reversed
278 // `mercurial/helptext/config.txt` suggests it should be reversed
266 if let Some(installation_prefix) = hg.parent().and_then(Path::parent) {
279 if let Some(installation_prefix) = hg.parent().and_then(Path::parent) {
267 if installation_prefix != root {
280 if installation_prefix != root {
268 add_for_prefix(installation_prefix)?
281 add_for_prefix(installation_prefix)?
269 }
282 }
270 }
283 }
271 add_for_prefix(root)?;
284 add_for_prefix(root)?;
272 Ok(())
285 Ok(())
273 }
286 }
274
287
275 #[cfg(unix)] // TODO: other plateforms
288 #[cfg(unix)] // TODO: other plateforms
276 fn add_user_config(&mut self) -> Result<(), ConfigError> {
289 fn add_user_config(&mut self) -> Result<(), ConfigError> {
277 let opt_home = home::home_dir();
290 let opt_home = home::home_dir();
278 if let Some(home) = &opt_home {
291 if let Some(home) = &opt_home {
279 self.add_trusted_file(&home.join(".hgrc"))?
292 self.add_trusted_file(&home.join(".hgrc"))?
280 }
293 }
281 let darwin = cfg!(any(target_os = "macos", target_os = "ios"));
294 let darwin = cfg!(any(target_os = "macos", target_os = "ios"));
282 if !darwin {
295 if !darwin {
283 if let Some(config_home) = env::var_os("XDG_CONFIG_HOME")
296 if let Some(config_home) = env::var_os("XDG_CONFIG_HOME")
284 .map(PathBuf::from)
297 .map(PathBuf::from)
285 .or_else(|| opt_home.map(|home| home.join(".config")))
298 .or_else(|| opt_home.map(|home| home.join(".config")))
286 {
299 {
287 self.add_trusted_file(&config_home.join("hg").join("hgrc"))?
300 self.add_trusted_file(&config_home.join("hg").join("hgrc"))?
288 }
301 }
289 }
302 }
290 Ok(())
303 Ok(())
291 }
304 }
292
305
293 /// Loads in order, which means that the precedence is the same
306 /// Loads in order, which means that the precedence is the same
294 /// as the order of `sources`.
307 /// as the order of `sources`.
295 pub fn load_from_explicit_sources(
308 pub fn load_from_explicit_sources(
296 sources: Vec<ConfigSource>,
309 sources: Vec<ConfigSource>,
297 ) -> Result<Self, ConfigError> {
310 ) -> Result<Self, ConfigError> {
298 let mut layers = vec![];
311 let mut layers = vec![];
299
312
300 for source in sources.into_iter() {
313 for source in sources.into_iter() {
301 match source {
314 match source {
302 ConfigSource::Parsed(c) => layers.push(c),
315 ConfigSource::Parsed(c) => layers.push(c),
303 ConfigSource::AbsPath(c) => {
316 ConfigSource::AbsPath(c) => {
304 // TODO check if it should be trusted
317 // TODO check if it should be trusted
305 // mercurial/ui.py:427
318 // mercurial/ui.py:427
306 let data = match std::fs::read(&c) {
319 let data = match std::fs::read(&c) {
307 Err(_) => continue, // same as the python code
320 Err(_) => continue, // same as the python code
308 Ok(data) => data,
321 Ok(data) => data,
309 };
322 };
310 layers.extend(ConfigLayer::parse(&c, &data)?)
323 layers.extend(ConfigLayer::parse(&c, &data)?)
311 }
324 }
312 }
325 }
313 }
326 }
314
327
315 Ok(Config {
328 Ok(Config {
316 layers,
329 layers,
317 plain: PlainInfo::empty(),
330 plain: PlainInfo::empty(),
318 })
331 })
319 }
332 }
320
333
321 /// Loads the per-repository config into a new `Config` which is combined
334 /// Loads the per-repository config into a new `Config` which is combined
322 /// with `self`.
335 /// with `self`.
323 pub(crate) fn combine_with_repo(
336 pub(crate) fn combine_with_repo(
324 &self,
337 &self,
325 repo_config_files: &[PathBuf],
338 repo_config_files: &[PathBuf],
326 ) -> Result<Self, ConfigError> {
339 ) -> Result<Self, ConfigError> {
327 let (cli_layers, other_layers) = self
340 let (cli_layers, other_layers) = self
328 .layers
341 .layers
329 .iter()
342 .iter()
330 .cloned()
343 .cloned()
331 .partition(ConfigLayer::is_from_command_line);
344 .partition(ConfigLayer::is_from_command_line);
332
345
333 let mut repo_config = Self {
346 let mut repo_config = Self {
334 layers: other_layers,
347 layers: other_layers,
335 plain: PlainInfo::empty(),
348 plain: PlainInfo::empty(),
336 };
349 };
337 for path in repo_config_files {
350 for path in repo_config_files {
338 // TODO: check if this file should be trusted:
351 // TODO: check if this file should be trusted:
339 // `mercurial/ui.py:427`
352 // `mercurial/ui.py:427`
340 repo_config.add_trusted_file(path)?;
353 repo_config.add_trusted_file(path)?;
341 }
354 }
342 repo_config.layers.extend(cli_layers);
355 repo_config.layers.extend(cli_layers);
343 Ok(repo_config)
356 Ok(repo_config)
344 }
357 }
345
358
346 pub fn apply_plain(&mut self, plain: PlainInfo) {
359 pub fn apply_plain(&mut self, plain: PlainInfo) {
347 self.plain = plain;
360 self.plain = plain;
348 }
361 }
349
362
363 /// Returns the default value for the given config item, if any.
364 pub fn get_default(
365 &self,
366 section: &[u8],
367 item: &[u8],
368 ) -> Result<Option<&DefaultConfigItem>, HgError> {
369 let default_config = DEFAULT_CONFIG.as_ref().map_err(|e| {
370 HgError::abort(
371 e.to_string(),
372 crate::exit_codes::ABORT,
373 Some("`mercurial/configitems.toml` is not valid".into()),
374 )
375 })?;
376 Ok(default_config.get(section, item))
377 }
378
350 fn get_parse<'config, T: 'config>(
379 fn get_parse<'config, T: 'config>(
351 &'config self,
380 &'config self,
352 section: &[u8],
381 section: &[u8],
353 item: &[u8],
382 item: &[u8],
354 expected_type: &'static str,
383 expected_type: &'static str,
355 parse: impl Fn(&'config [u8]) -> Option<T>,
384 parse: impl Fn(&'config [u8]) -> Option<T>,
356 ) -> Result<Option<T>, ConfigValueParseError> {
385 ) -> Result<Option<T>, HgError>
386 where
387 Option<T>: TryFrom<&'config DefaultConfigItem, Error = HgError>,
388 {
357 match self.get_inner(section, item) {
389 match self.get_inner(section, item) {
358 Some((layer, v)) => match parse(&v.bytes) {
390 Some((layer, v)) => match parse(&v.bytes) {
359 Some(b) => Ok(Some(b)),
391 Some(b) => Ok(Some(b)),
360 None => Err(Box::new(ConfigValueParseErrorDetails {
392 None => Err(Box::new(ConfigValueParseErrorDetails {
361 origin: layer.origin.to_owned(),
393 origin: layer.origin.to_owned(),
362 line: v.line,
394 line: v.line,
363 value: v.bytes.to_owned(),
395 value: v.bytes.to_owned(),
364 section: section.to_owned(),
396 section: section.to_owned(),
365 item: item.to_owned(),
397 item: item.to_owned(),
366 expected_type,
398 expected_type,
367 })),
399 })
400 .into()),
368 },
401 },
369 None => Ok(None),
402 None => match self.get_default(section, item)? {
403 Some(default) => Ok(default.try_into()?),
404 None => {
405 Ok(None)
406 }
407 },
370 }
408 }
371 }
409 }
372
410
373 /// Returns an `Err` if the first value found is not a valid UTF-8 string.
411 /// Returns an `Err` if the first value found is not a valid UTF-8 string.
374 /// Otherwise, returns an `Ok(value)` if found, or `None`.
412 /// Otherwise, returns an `Ok(value)` if found, or `None`.
375 pub fn get_str(
413 pub fn get_str(
376 &self,
414 &self,
377 section: &[u8],
415 section: &[u8],
378 item: &[u8],
416 item: &[u8],
379 ) -> Result<Option<&str>, ConfigValueParseError> {
417 ) -> Result<Option<&str>, HgError> {
380 self.get_parse(section, item, "ASCII or UTF-8 string", |value| {
418 self.get_parse(section, item, "ASCII or UTF-8 string", |value| {
381 str::from_utf8(value).ok()
419 str::from_utf8(value).ok()
382 })
420 })
383 }
421 }
384
422
385 /// Returns an `Err` if the first value found is not a valid unsigned
423 /// Returns an `Err` if the first value found is not a valid unsigned
386 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`.
424 /// integer. Otherwise, returns an `Ok(value)` if found, or `None`.
387 pub fn get_u32(
425 pub fn get_u32(
388 &self,
426 &self,
389 section: &[u8],
427 section: &[u8],
390 item: &[u8],
428 item: &[u8],
391 ) -> Result<Option<u32>, ConfigValueParseError> {
429 ) -> Result<Option<u32>, HgError> {
392 self.get_parse(section, item, "valid integer", |value| {
430 self.get_parse(section, item, "valid integer", |value| {
393 str::from_utf8(value).ok()?.parse().ok()
431 str::from_utf8(value).ok()?.parse().ok()
394 })
432 })
395 }
433 }
396
434
397 /// Returns an `Err` if the first value found is not a valid file size
435 /// Returns an `Err` if the first value found is not a valid file size
398 /// value such as `30` (default unit is bytes), `7 MB`, or `42.5 kb`.
436 /// value such as `30` (default unit is bytes), `7 MB`, or `42.5 kb`.
399 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`.
437 /// Otherwise, returns an `Ok(value_in_bytes)` if found, or `None`.
400 pub fn get_byte_size(
438 pub fn get_byte_size(
401 &self,
439 &self,
402 section: &[u8],
440 section: &[u8],
403 item: &[u8],
441 item: &[u8],
404 ) -> Result<Option<u64>, ConfigValueParseError> {
442 ) -> Result<Option<u64>, HgError> {
405 self.get_parse(section, item, "byte quantity", values::parse_byte_size)
443 self.get_parse(section, item, "byte quantity", values::parse_byte_size)
406 }
444 }
407
445
408 /// Returns an `Err` if the first value found is not a valid boolean.
446 /// Returns an `Err` if the first value found is not a valid boolean.
409 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
447 /// Otherwise, returns an `Ok(option)`, where `option` is the boolean if
410 /// found, or `None`.
448 /// found, or `None`.
411 pub fn get_option(
449 pub fn get_option(
412 &self,
450 &self,
413 section: &[u8],
451 section: &[u8],
414 item: &[u8],
452 item: &[u8],
415 ) -> Result<Option<bool>, ConfigValueParseError> {
453 ) -> Result<Option<bool>, HgError> {
416 self.get_parse(section, item, "boolean", values::parse_bool)
454 self.get_parse(section, item, "boolean", values::parse_bool)
417 }
455 }
418
456
419 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
457 /// Returns the corresponding boolean in the config. Returns `Ok(false)`
420 /// if the value is not found, an `Err` if it's not a valid boolean.
458 /// if the value is not found, an `Err` if it's not a valid boolean.
421 pub fn get_bool(
459 pub fn get_bool(
422 &self,
460 &self,
423 section: &[u8],
461 section: &[u8],
424 item: &[u8],
462 item: &[u8],
425 ) -> Result<bool, ConfigValueParseError> {
463 ) -> Result<bool, HgError> {
426 Ok(self.get_option(section, item)?.unwrap_or(false))
464 Ok(self.get_option(section, item)?.unwrap_or(false))
427 }
465 }
428
466
429 /// Returns `true` if the extension is enabled, `false` otherwise
467 /// Returns `true` if the extension is enabled, `false` otherwise
430 pub fn is_extension_enabled(&self, extension: &[u8]) -> bool {
468 pub fn is_extension_enabled(&self, extension: &[u8]) -> bool {
431 let value = self.get(b"extensions", extension);
469 let value = self.get(b"extensions", extension);
432 match value {
470 match value {
433 Some(c) => !c.starts_with(b"!"),
471 Some(c) => !c.starts_with(b"!"),
434 None => false,
472 None => false,
435 }
473 }
436 }
474 }
437
475
438 /// If there is an `item` value in `section`, parse and return a list of
476 /// If there is an `item` value in `section`, parse and return a list of
439 /// byte strings.
477 /// byte strings.
440 pub fn get_list(
478 pub fn get_list(
441 &self,
479 &self,
442 section: &[u8],
480 section: &[u8],
443 item: &[u8],
481 item: &[u8],
444 ) -> Option<Vec<Vec<u8>>> {
482 ) -> Option<Vec<Vec<u8>>> {
445 self.get(section, item).map(values::parse_list)
483 self.get(section, item).map(values::parse_list)
446 }
484 }
447
485
448 /// Returns the raw value bytes of the first one found, or `None`.
486 /// Returns the raw value bytes of the first one found, or `None`.
449 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> {
487 pub fn get(&self, section: &[u8], item: &[u8]) -> Option<&[u8]> {
450 self.get_inner(section, item)
488 self.get_inner(section, item)
451 .map(|(_, value)| value.bytes.as_ref())
489 .map(|(_, value)| value.bytes.as_ref())
452 }
490 }
453
491
454 /// Returns the raw value bytes of the first one found, or `None`.
492 /// Returns the raw value bytes of the first one found, or `None`.
455 pub fn get_with_origin(
493 pub fn get_with_origin(
456 &self,
494 &self,
457 section: &[u8],
495 section: &[u8],
458 item: &[u8],
496 item: &[u8],
459 ) -> Option<(&[u8], &ConfigOrigin)> {
497 ) -> Option<(&[u8], &ConfigOrigin)> {
460 self.get_inner(section, item)
498 self.get_inner(section, item)
461 .map(|(layer, value)| (value.bytes.as_ref(), &layer.origin))
499 .map(|(layer, value)| (value.bytes.as_ref(), &layer.origin))
462 }
500 }
463
501
464 /// Returns the layer and the value of the first one found, or `None`.
502 /// Returns the layer and the value of the first one found, or `None`.
465 fn get_inner(
503 fn get_inner(
466 &self,
504 &self,
467 section: &[u8],
505 section: &[u8],
468 item: &[u8],
506 item: &[u8],
469 ) -> Option<(&ConfigLayer, &ConfigValue)> {
507 ) -> Option<(&ConfigLayer, &ConfigValue)> {
470 // Filter out the config items that are hidden by [PLAIN].
508 // Filter out the config items that are hidden by [PLAIN].
471 // This differs from python hg where we delete them from the config.
509 // This differs from python hg where we delete them from the config.
472 let should_ignore = should_ignore(&self.plain, section, item);
510 let should_ignore = should_ignore(&self.plain, section, item);
473 for layer in self.layers.iter().rev() {
511 for layer in self.layers.iter().rev() {
474 if !layer.trusted {
512 if !layer.trusted {
475 continue;
513 continue;
476 }
514 }
477 //The [PLAIN] config should not affect the defaults.
515 //The [PLAIN] config should not affect the defaults.
478 //
516 //
479 // However, PLAIN should also affect the "tweaked" defaults (unless
517 // However, PLAIN should also affect the "tweaked" defaults (unless
480 // "tweakdefault" is part of "HGPLAINEXCEPT").
518 // "tweakdefault" is part of "HGPLAINEXCEPT").
481 //
519 //
482 // In practice the tweak-default layer is only added when it is
520 // In practice the tweak-default layer is only added when it is
483 // relevant, so we can safely always take it into
521 // relevant, so we can safely always take it into
484 // account here.
522 // account here.
485 if should_ignore && !(layer.origin == ConfigOrigin::Tweakdefaults)
523 if should_ignore && !(layer.origin == ConfigOrigin::Tweakdefaults)
486 {
524 {
487 continue;
525 continue;
488 }
526 }
489 if let Some(v) = layer.get(section, item) {
527 if let Some(v) = layer.get(section, item) {
490 return Some((layer, v));
528 return Some((layer, v));
491 }
529 }
492 }
530 }
493 None
531 None
494 }
532 }
495
533
496 /// Return all keys defined for the given section
534 /// Return all keys defined for the given section
497 pub fn get_section_keys(&self, section: &[u8]) -> HashSet<&[u8]> {
535 pub fn get_section_keys(&self, section: &[u8]) -> HashSet<&[u8]> {
498 self.layers
536 self.layers
499 .iter()
537 .iter()
500 .flat_map(|layer| layer.iter_keys(section))
538 .flat_map(|layer| layer.iter_keys(section))
501 .collect()
539 .collect()
502 }
540 }
503
541
504 /// Returns whether any key is defined in the given section
542 /// Returns whether any key is defined in the given section
505 pub fn has_non_empty_section(&self, section: &[u8]) -> bool {
543 pub fn has_non_empty_section(&self, section: &[u8]) -> bool {
506 self.layers
544 self.layers
507 .iter()
545 .iter()
508 .any(|layer| layer.has_non_empty_section(section))
546 .any(|layer| layer.has_non_empty_section(section))
509 }
547 }
510
548
511 /// Yields (key, value) pairs for everything in the given section
549 /// Yields (key, value) pairs for everything in the given section
512 pub fn iter_section<'a>(
550 pub fn iter_section<'a>(
513 &'a self,
551 &'a self,
514 section: &'a [u8],
552 section: &'a [u8],
515 ) -> impl Iterator<Item = (&[u8], &[u8])> + 'a {
553 ) -> impl Iterator<Item = (&[u8], &[u8])> + 'a {
516 // Deduplicate keys redefined in multiple layers
554 // Deduplicate keys redefined in multiple layers
517 let mut keys_already_seen = HashSet::new();
555 let mut keys_already_seen = HashSet::new();
518 let mut key_is_new =
556 let mut key_is_new =
519 move |&(key, _value): &(&'a [u8], &'a [u8])| -> bool {
557 move |&(key, _value): &(&'a [u8], &'a [u8])| -> bool {
520 keys_already_seen.insert(key)
558 keys_already_seen.insert(key)
521 };
559 };
522 // This is similar to `flat_map` + `filter_map`, except with a single
560 // This is similar to `flat_map` + `filter_map`, except with a single
523 // closure that owns `key_is_new` (and therefore the
561 // closure that owns `key_is_new` (and therefore the
524 // `keys_already_seen` set):
562 // `keys_already_seen` set):
525 let mut layer_iters = self
563 let mut layer_iters = self
526 .layers
564 .layers
527 .iter()
565 .iter()
528 .rev()
566 .rev()
529 .map(move |layer| layer.iter_section(section))
567 .map(move |layer| layer.iter_section(section))
530 .peekable();
568 .peekable();
531 std::iter::from_fn(move || loop {
569 std::iter::from_fn(move || loop {
532 if let Some(pair) = layer_iters.peek_mut()?.find(&mut key_is_new) {
570 if let Some(pair) = layer_iters.peek_mut()?.find(&mut key_is_new) {
533 return Some(pair);
571 return Some(pair);
534 } else {
572 } else {
535 layer_iters.next();
573 layer_iters.next();
536 }
574 }
537 })
575 })
538 }
576 }
539
577
540 /// Get raw values bytes from all layers (even untrusted ones) in order
578 /// Get raw values bytes from all layers (even untrusted ones) in order
541 /// of precedence.
579 /// of precedence.
542 #[cfg(test)]
580 #[cfg(test)]
543 fn get_all(&self, section: &[u8], item: &[u8]) -> Vec<&[u8]> {
581 fn get_all(&self, section: &[u8], item: &[u8]) -> Vec<&[u8]> {
544 let mut res = vec![];
582 let mut res = vec![];
545 for layer in self.layers.iter().rev() {
583 for layer in self.layers.iter().rev() {
546 if let Some(v) = layer.get(section, item) {
584 if let Some(v) = layer.get(section, item) {
547 res.push(v.bytes.as_ref());
585 res.push(v.bytes.as_ref());
548 }
586 }
549 }
587 }
550 res
588 res
551 }
589 }
552
590
553 // a config layer that's introduced by ui.tweakdefaults
591 // a config layer that's introduced by ui.tweakdefaults
554 fn tweakdefaults_layer() -> ConfigLayer {
592 fn tweakdefaults_layer() -> ConfigLayer {
555 let mut layer = ConfigLayer::new(ConfigOrigin::Tweakdefaults);
593 let mut layer = ConfigLayer::new(ConfigOrigin::Tweakdefaults);
556
594
557 let mut add = |section: &[u8], item: &[u8], value: &[u8]| {
595 let mut add = |section: &[u8], item: &[u8], value: &[u8]| {
558 layer.add(
596 layer.add(
559 section[..].into(),
597 section[..].into(),
560 item[..].into(),
598 item[..].into(),
561 value[..].into(),
599 value[..].into(),
562 None,
600 None,
563 );
601 );
564 };
602 };
565 // duplication of [tweakrc] from [ui.py]
603 // duplication of [tweakrc] from [ui.py]
566 add(b"ui", b"rollback", b"False");
604 add(b"ui", b"rollback", b"False");
567 add(b"ui", b"statuscopies", b"yes");
605 add(b"ui", b"statuscopies", b"yes");
568 add(b"ui", b"interface", b"curses");
606 add(b"ui", b"interface", b"curses");
569 add(b"ui", b"relative-paths", b"yes");
607 add(b"ui", b"relative-paths", b"yes");
570 add(b"commands", b"grep.all-files", b"True");
608 add(b"commands", b"grep.all-files", b"True");
571 add(b"commands", b"update.check", b"noconflict");
609 add(b"commands", b"update.check", b"noconflict");
572 add(b"commands", b"status.verbose", b"True");
610 add(b"commands", b"status.verbose", b"True");
573 add(b"commands", b"resolve.explicit-re-merge", b"True");
611 add(b"commands", b"resolve.explicit-re-merge", b"True");
574 add(b"git", b"git", b"1");
612 add(b"git", b"git", b"1");
575 add(b"git", b"showfunc", b"1");
613 add(b"git", b"showfunc", b"1");
576 add(b"git", b"word-diff", b"1");
614 add(b"git", b"word-diff", b"1");
577 layer
615 layer
578 }
616 }
579
617
580 // introduce the tweaked defaults as implied by ui.tweakdefaults
618 // introduce the tweaked defaults as implied by ui.tweakdefaults
581 pub fn tweakdefaults(&mut self) {
619 pub fn tweakdefaults(&mut self) {
582 self.layers.insert(0, Config::tweakdefaults_layer());
620 self.layers.insert(0, Config::tweakdefaults_layer());
583 }
621 }
584 }
622 }
585
623
586 #[cfg(test)]
624 #[cfg(test)]
587 mod tests {
625 mod tests {
588 use super::*;
626 use super::*;
589 use pretty_assertions::assert_eq;
627 use pretty_assertions::assert_eq;
590 use std::fs::File;
628 use std::fs::File;
591 use std::io::Write;
629 use std::io::Write;
592
630
593 #[test]
631 #[test]
594 fn test_include_layer_ordering() {
632 fn test_include_layer_ordering() {
595 let tmpdir = tempfile::tempdir().unwrap();
633 let tmpdir = tempfile::tempdir().unwrap();
596 let tmpdir_path = tmpdir.path();
634 let tmpdir_path = tmpdir.path();
597 let mut included_file =
635 let mut included_file =
598 File::create(&tmpdir_path.join("included.rc")).unwrap();
636 File::create(&tmpdir_path.join("included.rc")).unwrap();
599
637
600 included_file.write_all(b"[section]\nitem=value1").unwrap();
638 included_file.write_all(b"[section]\nitem=value1").unwrap();
601 let base_config_path = tmpdir_path.join("base.rc");
639 let base_config_path = tmpdir_path.join("base.rc");
602 let mut config_file = File::create(&base_config_path).unwrap();
640 let mut config_file = File::create(&base_config_path).unwrap();
603 let data =
641 let data =
604 b"[section]\nitem=value0\n%include included.rc\nitem=value2\n\
642 b"[section]\nitem=value0\n%include included.rc\nitem=value2\n\
605 [section2]\ncount = 4\nsize = 1.5 KB\nnot-count = 1.5\nnot-size = 1 ub";
643 [section2]\ncount = 4\nsize = 1.5 KB\nnot-count = 1.5\nnot-size = 1 ub";
606 config_file.write_all(data).unwrap();
644 config_file.write_all(data).unwrap();
607
645
608 let sources = vec![ConfigSource::AbsPath(base_config_path)];
646 let sources = vec![ConfigSource::AbsPath(base_config_path)];
609 let config = Config::load_from_explicit_sources(sources)
647 let config = Config::load_from_explicit_sources(sources)
610 .expect("expected valid config");
648 .expect("expected valid config");
611
649
612 let (_, value) = config.get_inner(b"section", b"item").unwrap();
650 let (_, value) = config.get_inner(b"section", b"item").unwrap();
613 assert_eq!(
651 assert_eq!(
614 value,
652 value,
615 &ConfigValue {
653 &ConfigValue {
616 bytes: b"value2".to_vec(),
654 bytes: b"value2".to_vec(),
617 line: Some(4)
655 line: Some(4)
618 }
656 }
619 );
657 );
620
658
621 let value = config.get(b"section", b"item").unwrap();
659 let value = config.get(b"section", b"item").unwrap();
622 assert_eq!(value, b"value2",);
660 assert_eq!(value, b"value2",);
623 assert_eq!(
661 assert_eq!(
624 config.get_all(b"section", b"item"),
662 config.get_all(b"section", b"item"),
625 [b"value2", b"value1", b"value0"]
663 [b"value2", b"value1", b"value0"]
626 );
664 );
627
665
628 assert_eq!(config.get_u32(b"section2", b"count").unwrap(), Some(4));
666 assert_eq!(config.get_u32(b"section2", b"count").unwrap(), Some(4));
629 assert_eq!(
667 assert_eq!(
630 config.get_byte_size(b"section2", b"size").unwrap(),
668 config.get_byte_size(b"section2", b"size").unwrap(),
631 Some(1024 + 512)
669 Some(1024 + 512)
632 );
670 );
633 assert!(config.get_u32(b"section2", b"not-count").is_err());
671 assert!(config.get_u32(b"section2", b"not-count").is_err());
634 assert!(config.get_byte_size(b"section2", b"not-size").is_err());
672 assert!(config.get_byte_size(b"section2", b"not-size").is_err());
635 }
673 }
636 }
674 }
@@ -1,675 +1,689 b''
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2020, Georges Racinet <georges.racinets@octobus.net>
3 // Copyright 2020, Georges Racinet <georges.racinets@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::error::CommandError;
8 use crate::error::CommandError;
9 use crate::ui::{
9 use crate::ui::{
10 format_pattern_file_warning, print_narrow_sparse_warnings, Ui,
10 format_pattern_file_warning, print_narrow_sparse_warnings, relative_paths,
11 RelativePaths, Ui,
11 };
12 };
12 use crate::utils::path_utils::RelativizePaths;
13 use crate::utils::path_utils::RelativizePaths;
13 use clap::Arg;
14 use clap::Arg;
14 use format_bytes::format_bytes;
15 use format_bytes::format_bytes;
15 use hg::config::Config;
16 use hg::config::Config;
16 use hg::dirstate::has_exec_bit;
17 use hg::dirstate::has_exec_bit;
17 use hg::dirstate::status::StatusPath;
18 use hg::dirstate::status::StatusPath;
18 use hg::dirstate::TruncatedTimestamp;
19 use hg::dirstate::TruncatedTimestamp;
19 use hg::errors::{HgError, IoResultExt};
20 use hg::errors::{HgError, IoResultExt};
20 use hg::lock::LockError;
21 use hg::lock::LockError;
21 use hg::manifest::Manifest;
22 use hg::manifest::Manifest;
22 use hg::matchers::{AlwaysMatcher, IntersectionMatcher};
23 use hg::matchers::{AlwaysMatcher, IntersectionMatcher};
23 use hg::repo::Repo;
24 use hg::repo::Repo;
24 use hg::utils::debug::debug_wait_for_file;
25 use hg::utils::debug::debug_wait_for_file;
25 use hg::utils::files::get_bytes_from_os_string;
26 use hg::utils::files::get_bytes_from_os_string;
26 use hg::utils::files::get_path_from_bytes;
27 use hg::utils::files::get_path_from_bytes;
27 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
28 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
28 use hg::DirstateStatus;
29 use hg::DirstateStatus;
29 use hg::PatternFileWarning;
30 use hg::PatternFileWarning;
30 use hg::StatusError;
31 use hg::StatusError;
31 use hg::StatusOptions;
32 use hg::StatusOptions;
32 use hg::{self, narrow, sparse};
33 use hg::{self, narrow, sparse};
33 use log::info;
34 use log::info;
34 use rayon::prelude::*;
35 use rayon::prelude::*;
35 use std::io;
36 use std::io;
36 use std::path::PathBuf;
37 use std::path::PathBuf;
37
38
38 pub const HELP_TEXT: &str = "
39 pub const HELP_TEXT: &str = "
39 Show changed files in the working directory
40 Show changed files in the working directory
40
41
41 This is a pure Rust version of `hg status`.
42 This is a pure Rust version of `hg status`.
42
43
43 Some options might be missing, check the list below.
44 Some options might be missing, check the list below.
44 ";
45 ";
45
46
46 pub fn args() -> clap::Command {
47 pub fn args() -> clap::Command {
47 clap::command!("status")
48 clap::command!("status")
48 .alias("st")
49 .alias("st")
49 .about(HELP_TEXT)
50 .about(HELP_TEXT)
50 .arg(
51 .arg(
51 Arg::new("all")
52 Arg::new("all")
52 .help("show status of all files")
53 .help("show status of all files")
53 .short('A')
54 .short('A')
54 .action(clap::ArgAction::SetTrue)
55 .action(clap::ArgAction::SetTrue)
55 .long("all"),
56 .long("all"),
56 )
57 )
57 .arg(
58 .arg(
58 Arg::new("modified")
59 Arg::new("modified")
59 .help("show only modified files")
60 .help("show only modified files")
60 .short('m')
61 .short('m')
61 .action(clap::ArgAction::SetTrue)
62 .action(clap::ArgAction::SetTrue)
62 .long("modified"),
63 .long("modified"),
63 )
64 )
64 .arg(
65 .arg(
65 Arg::new("added")
66 Arg::new("added")
66 .help("show only added files")
67 .help("show only added files")
67 .short('a')
68 .short('a')
68 .action(clap::ArgAction::SetTrue)
69 .action(clap::ArgAction::SetTrue)
69 .long("added"),
70 .long("added"),
70 )
71 )
71 .arg(
72 .arg(
72 Arg::new("removed")
73 Arg::new("removed")
73 .help("show only removed files")
74 .help("show only removed files")
74 .short('r')
75 .short('r')
75 .action(clap::ArgAction::SetTrue)
76 .action(clap::ArgAction::SetTrue)
76 .long("removed"),
77 .long("removed"),
77 )
78 )
78 .arg(
79 .arg(
79 Arg::new("clean")
80 Arg::new("clean")
80 .help("show only clean files")
81 .help("show only clean files")
81 .short('c')
82 .short('c')
82 .action(clap::ArgAction::SetTrue)
83 .action(clap::ArgAction::SetTrue)
83 .long("clean"),
84 .long("clean"),
84 )
85 )
85 .arg(
86 .arg(
86 Arg::new("deleted")
87 Arg::new("deleted")
87 .help("show only deleted files")
88 .help("show only deleted files")
88 .short('d')
89 .short('d')
89 .action(clap::ArgAction::SetTrue)
90 .action(clap::ArgAction::SetTrue)
90 .long("deleted"),
91 .long("deleted"),
91 )
92 )
92 .arg(
93 .arg(
93 Arg::new("unknown")
94 Arg::new("unknown")
94 .help("show only unknown (not tracked) files")
95 .help("show only unknown (not tracked) files")
95 .short('u')
96 .short('u')
96 .action(clap::ArgAction::SetTrue)
97 .action(clap::ArgAction::SetTrue)
97 .long("unknown"),
98 .long("unknown"),
98 )
99 )
99 .arg(
100 .arg(
100 Arg::new("ignored")
101 Arg::new("ignored")
101 .help("show only ignored files")
102 .help("show only ignored files")
102 .short('i')
103 .short('i')
103 .action(clap::ArgAction::SetTrue)
104 .action(clap::ArgAction::SetTrue)
104 .long("ignored"),
105 .long("ignored"),
105 )
106 )
106 .arg(
107 .arg(
107 Arg::new("copies")
108 Arg::new("copies")
108 .help("show source of copied files (DEFAULT: ui.statuscopies)")
109 .help("show source of copied files (DEFAULT: ui.statuscopies)")
109 .short('C')
110 .short('C')
110 .action(clap::ArgAction::SetTrue)
111 .action(clap::ArgAction::SetTrue)
111 .long("copies"),
112 .long("copies"),
112 )
113 )
113 .arg(
114 .arg(
114 Arg::new("print0")
115 Arg::new("print0")
115 .help("end filenames with NUL, for use with xargs")
116 .help("end filenames with NUL, for use with xargs")
116 .short('0')
117 .short('0')
117 .action(clap::ArgAction::SetTrue)
118 .action(clap::ArgAction::SetTrue)
118 .long("print0"),
119 .long("print0"),
119 )
120 )
120 .arg(
121 .arg(
121 Arg::new("no-status")
122 Arg::new("no-status")
122 .help("hide status prefix")
123 .help("hide status prefix")
123 .short('n')
124 .short('n')
124 .action(clap::ArgAction::SetTrue)
125 .action(clap::ArgAction::SetTrue)
125 .long("no-status"),
126 .long("no-status"),
126 )
127 )
127 .arg(
128 .arg(
128 Arg::new("verbose")
129 Arg::new("verbose")
129 .help("enable additional output")
130 .help("enable additional output")
130 .short('v')
131 .short('v')
131 .action(clap::ArgAction::SetTrue)
132 .action(clap::ArgAction::SetTrue)
132 .long("verbose"),
133 .long("verbose"),
133 )
134 )
134 }
135 }
135
136
136 /// Pure data type allowing the caller to specify file states to display
137 /// Pure data type allowing the caller to specify file states to display
137 #[derive(Copy, Clone, Debug)]
138 #[derive(Copy, Clone, Debug)]
138 pub struct DisplayStates {
139 pub struct DisplayStates {
139 pub modified: bool,
140 pub modified: bool,
140 pub added: bool,
141 pub added: bool,
141 pub removed: bool,
142 pub removed: bool,
142 pub clean: bool,
143 pub clean: bool,
143 pub deleted: bool,
144 pub deleted: bool,
144 pub unknown: bool,
145 pub unknown: bool,
145 pub ignored: bool,
146 pub ignored: bool,
146 }
147 }
147
148
148 pub const DEFAULT_DISPLAY_STATES: DisplayStates = DisplayStates {
149 pub const DEFAULT_DISPLAY_STATES: DisplayStates = DisplayStates {
149 modified: true,
150 modified: true,
150 added: true,
151 added: true,
151 removed: true,
152 removed: true,
152 clean: false,
153 clean: false,
153 deleted: true,
154 deleted: true,
154 unknown: true,
155 unknown: true,
155 ignored: false,
156 ignored: false,
156 };
157 };
157
158
158 pub const ALL_DISPLAY_STATES: DisplayStates = DisplayStates {
159 pub const ALL_DISPLAY_STATES: DisplayStates = DisplayStates {
159 modified: true,
160 modified: true,
160 added: true,
161 added: true,
161 removed: true,
162 removed: true,
162 clean: true,
163 clean: true,
163 deleted: true,
164 deleted: true,
164 unknown: true,
165 unknown: true,
165 ignored: true,
166 ignored: true,
166 };
167 };
167
168
168 impl DisplayStates {
169 impl DisplayStates {
169 pub fn is_empty(&self) -> bool {
170 pub fn is_empty(&self) -> bool {
170 !(self.modified
171 !(self.modified
171 || self.added
172 || self.added
172 || self.removed
173 || self.removed
173 || self.clean
174 || self.clean
174 || self.deleted
175 || self.deleted
175 || self.unknown
176 || self.unknown
176 || self.ignored)
177 || self.ignored)
177 }
178 }
178 }
179 }
179
180
180 fn has_unfinished_merge(repo: &Repo) -> Result<bool, CommandError> {
181 fn has_unfinished_merge(repo: &Repo) -> Result<bool, CommandError> {
181 Ok(repo.dirstate_parents()?.is_merge())
182 Ok(repo.dirstate_parents()?.is_merge())
182 }
183 }
183
184
184 fn has_unfinished_state(repo: &Repo) -> Result<bool, CommandError> {
185 fn has_unfinished_state(repo: &Repo) -> Result<bool, CommandError> {
185 // These are all the known values for the [fname] argument of
186 // These are all the known values for the [fname] argument of
186 // [addunfinished] function in [state.py]
187 // [addunfinished] function in [state.py]
187 let known_state_files: &[&str] = &[
188 let known_state_files: &[&str] = &[
188 "bisect.state",
189 "bisect.state",
189 "graftstate",
190 "graftstate",
190 "histedit-state",
191 "histedit-state",
191 "rebasestate",
192 "rebasestate",
192 "shelvedstate",
193 "shelvedstate",
193 "transplant/journal",
194 "transplant/journal",
194 "updatestate",
195 "updatestate",
195 ];
196 ];
196 if has_unfinished_merge(repo)? {
197 if has_unfinished_merge(repo)? {
197 return Ok(true);
198 return Ok(true);
198 };
199 };
199 for f in known_state_files {
200 for f in known_state_files {
200 if repo.hg_vfs().join(f).exists() {
201 if repo.hg_vfs().join(f).exists() {
201 return Ok(true);
202 return Ok(true);
202 }
203 }
203 }
204 }
204 Ok(false)
205 Ok(false)
205 }
206 }
206
207
207 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
208 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
208 // TODO: lift these limitations
209 // TODO: lift these limitations
209 if invocation
210 if invocation
210 .config
211 .config
211 .get(b"commands", b"status.terse")
212 .get(b"commands", b"status.terse")
212 .is_some()
213 .is_some()
213 {
214 {
214 return Err(CommandError::unsupported(
215 return Err(CommandError::unsupported(
215 "status.terse is not yet supported with rhg status",
216 "status.terse is not yet supported with rhg status",
216 ));
217 ));
217 }
218 }
218
219
219 let ui = invocation.ui;
220 let ui = invocation.ui;
220 let config = invocation.config;
221 let config = invocation.config;
221 let args = invocation.subcommand_args;
222 let args = invocation.subcommand_args;
222
223
223 let print0 = args.get_flag("print0");
224 let print0 = args.get_flag("print0");
224 let verbose = args.get_flag("verbose")
225 let verbose = args.get_flag("verbose")
225 || config.get_bool(b"ui", b"verbose")?
226 || config.get_bool(b"ui", b"verbose")?
226 || config.get_bool(b"commands", b"status.verbose")?;
227 || config.get_bool(b"commands", b"status.verbose")?;
227 let verbose = verbose && !print0;
228 let verbose = verbose && !print0;
228
229
229 let all = args.get_flag("all");
230 let all = args.get_flag("all");
230 let display_states = if all {
231 let display_states = if all {
231 // TODO when implementing `--quiet`: it excludes clean files
232 // TODO when implementing `--quiet`: it excludes clean files
232 // from `--all`
233 // from `--all`
233 ALL_DISPLAY_STATES
234 ALL_DISPLAY_STATES
234 } else {
235 } else {
235 let requested = DisplayStates {
236 let requested = DisplayStates {
236 modified: args.get_flag("modified"),
237 modified: args.get_flag("modified"),
237 added: args.get_flag("added"),
238 added: args.get_flag("added"),
238 removed: args.get_flag("removed"),
239 removed: args.get_flag("removed"),
239 clean: args.get_flag("clean"),
240 clean: args.get_flag("clean"),
240 deleted: args.get_flag("deleted"),
241 deleted: args.get_flag("deleted"),
241 unknown: args.get_flag("unknown"),
242 unknown: args.get_flag("unknown"),
242 ignored: args.get_flag("ignored"),
243 ignored: args.get_flag("ignored"),
243 };
244 };
244 if requested.is_empty() {
245 if requested.is_empty() {
245 DEFAULT_DISPLAY_STATES
246 DEFAULT_DISPLAY_STATES
246 } else {
247 } else {
247 requested
248 requested
248 }
249 }
249 };
250 };
250 let no_status = args.get_flag("no-status");
251 let no_status = args.get_flag("no-status");
251 let list_copies = all
252 let list_copies = all
252 || args.get_flag("copies")
253 || args.get_flag("copies")
253 || config.get_bool(b"ui", b"statuscopies")?;
254 || config.get_bool(b"ui", b"statuscopies")?;
254
255
255 let repo = invocation.repo?;
256 let repo = invocation.repo?;
256
257
257 if verbose && has_unfinished_state(repo)? {
258 if verbose && has_unfinished_state(repo)? {
258 return Err(CommandError::unsupported(
259 return Err(CommandError::unsupported(
259 "verbose status output is not supported by rhg (and is needed because we're in an unfinished operation)",
260 "verbose status output is not supported by rhg (and is needed because we're in an unfinished operation)",
260 ));
261 ));
261 }
262 }
262
263
263 let mut dmap = repo.dirstate_map_mut()?;
264 let mut dmap = repo.dirstate_map_mut()?;
264
265
265 let check_exec = hg::checkexec::check_exec(repo.working_directory_path());
266 let check_exec = hg::checkexec::check_exec(repo.working_directory_path());
266
267
267 let options = StatusOptions {
268 let options = StatusOptions {
268 check_exec,
269 check_exec,
269 list_clean: display_states.clean,
270 list_clean: display_states.clean,
270 list_unknown: display_states.unknown,
271 list_unknown: display_states.unknown,
271 list_ignored: display_states.ignored,
272 list_ignored: display_states.ignored,
272 list_copies,
273 list_copies,
273 collect_traversed_dirs: false,
274 collect_traversed_dirs: false,
274 };
275 };
275
276
276 type StatusResult<'a> =
277 type StatusResult<'a> =
277 Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
278 Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
278
279
279 let after_status = |res: StatusResult| -> Result<_, CommandError> {
280 let after_status = |res: StatusResult| -> Result<_, CommandError> {
280 let (mut ds_status, pattern_warnings) = res?;
281 let (mut ds_status, pattern_warnings) = res?;
281 for warning in pattern_warnings {
282 for warning in pattern_warnings {
282 ui.write_stderr(&format_pattern_file_warning(&warning, repo))?;
283 ui.write_stderr(&format_pattern_file_warning(&warning, repo))?;
283 }
284 }
284
285
285 for (path, error) in ds_status.bad {
286 for (path, error) in ds_status.bad {
286 let error = match error {
287 let error = match error {
287 hg::BadMatch::OsError(code) => {
288 hg::BadMatch::OsError(code) => {
288 std::io::Error::from_raw_os_error(code).to_string()
289 std::io::Error::from_raw_os_error(code).to_string()
289 }
290 }
290 hg::BadMatch::BadType(ty) => {
291 hg::BadMatch::BadType(ty) => {
291 format!("unsupported file type (type is {})", ty)
292 format!("unsupported file type (type is {})", ty)
292 }
293 }
293 };
294 };
294 ui.write_stderr(&format_bytes!(
295 ui.write_stderr(&format_bytes!(
295 b"{}: {}\n",
296 b"{}: {}\n",
296 path.as_bytes(),
297 path.as_bytes(),
297 error.as_bytes()
298 error.as_bytes()
298 ))?
299 ))?
299 }
300 }
300 if !ds_status.unsure.is_empty() {
301 if !ds_status.unsure.is_empty() {
301 info!(
302 info!(
302 "Files to be rechecked by retrieval from filelog: {:?}",
303 "Files to be rechecked by retrieval from filelog: {:?}",
303 ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>()
304 ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>()
304 );
305 );
305 }
306 }
306 let mut fixup = Vec::new();
307 let mut fixup = Vec::new();
307 if !ds_status.unsure.is_empty()
308 if !ds_status.unsure.is_empty()
308 && (display_states.modified || display_states.clean)
309 && (display_states.modified || display_states.clean)
309 {
310 {
310 let p1 = repo.dirstate_parents()?.p1;
311 let p1 = repo.dirstate_parents()?.p1;
311 let manifest = repo.manifest_for_node(p1).map_err(|e| {
312 let manifest = repo.manifest_for_node(p1).map_err(|e| {
312 CommandError::from((e, &*format!("{:x}", p1.short())))
313 CommandError::from((e, &*format!("{:x}", p1.short())))
313 })?;
314 })?;
314 let working_directory_vfs = repo.working_directory_vfs();
315 let working_directory_vfs = repo.working_directory_vfs();
315 let store_vfs = repo.store_vfs();
316 let store_vfs = repo.store_vfs();
316 let res: Vec<_> = ds_status
317 let res: Vec<_> = ds_status
317 .unsure
318 .unsure
318 .into_par_iter()
319 .into_par_iter()
319 .map(|to_check| {
320 .map(|to_check| {
320 // The compiler seems to get a bit confused with complex
321 // The compiler seems to get a bit confused with complex
321 // inference when using a parallel iterator + map
322 // inference when using a parallel iterator + map
322 // + map_err + collect, so let's just inline some of the
323 // + map_err + collect, so let's just inline some of the
323 // logic.
324 // logic.
324 match unsure_is_modified(
325 match unsure_is_modified(
325 working_directory_vfs,
326 working_directory_vfs,
326 store_vfs,
327 store_vfs,
327 check_exec,
328 check_exec,
328 &manifest,
329 &manifest,
329 &to_check.path,
330 &to_check.path,
330 ) {
331 ) {
331 Err(HgError::IoError { .. }) => {
332 Err(HgError::IoError { .. }) => {
332 // IO errors most likely stem from the file being
333 // IO errors most likely stem from the file being
333 // deleted even though we know it's in the
334 // deleted even though we know it's in the
334 // dirstate.
335 // dirstate.
335 Ok((to_check, UnsureOutcome::Deleted))
336 Ok((to_check, UnsureOutcome::Deleted))
336 }
337 }
337 Ok(outcome) => Ok((to_check, outcome)),
338 Ok(outcome) => Ok((to_check, outcome)),
338 Err(e) => Err(e),
339 Err(e) => Err(e),
339 }
340 }
340 })
341 })
341 .collect::<Result<_, _>>()?;
342 .collect::<Result<_, _>>()?;
342 for (status_path, outcome) in res.into_iter() {
343 for (status_path, outcome) in res.into_iter() {
343 match outcome {
344 match outcome {
344 UnsureOutcome::Clean => {
345 UnsureOutcome::Clean => {
345 if display_states.clean {
346 if display_states.clean {
346 ds_status.clean.push(status_path.clone());
347 ds_status.clean.push(status_path.clone());
347 }
348 }
348 fixup.push(status_path.path.into_owned())
349 fixup.push(status_path.path.into_owned())
349 }
350 }
350 UnsureOutcome::Modified => {
351 UnsureOutcome::Modified => {
351 if display_states.modified {
352 if display_states.modified {
352 ds_status.modified.push(status_path);
353 ds_status.modified.push(status_path);
353 }
354 }
354 }
355 }
355 UnsureOutcome::Deleted => {
356 UnsureOutcome::Deleted => {
356 if display_states.deleted {
357 if display_states.deleted {
357 ds_status.deleted.push(status_path);
358 ds_status.deleted.push(status_path);
358 }
359 }
359 }
360 }
360 }
361 }
361 }
362 }
362 }
363 }
363 let relative_paths = config
364
365 let relative_status = config
364 .get_option(b"commands", b"status.relative")?
366 .get_option(b"commands", b"status.relative")?
365 .unwrap_or(config.get_bool(b"ui", b"relative-paths")?);
367 .expect("commands.status.relative should have a default value");
368
369 let relativize_paths = relative_status || {
370 // TODO should be dependent on whether patterns are passed once
371 // we support those.
372 // See in Python code with `getuipathfn` usage in `commands.py`.
373 let legacy_relative_behavior = false;
374 match relative_paths(invocation.config)? {
375 RelativePaths::Legacy => legacy_relative_behavior,
376 RelativePaths::Bool(v) => v,
377 }
378 };
379
366 let output = DisplayStatusPaths {
380 let output = DisplayStatusPaths {
367 ui,
381 ui,
368 no_status,
382 no_status,
369 relativize: if relative_paths {
383 relativize: if relativize_paths {
370 Some(RelativizePaths::new(repo)?)
384 Some(RelativizePaths::new(repo)?)
371 } else {
385 } else {
372 None
386 None
373 },
387 },
374 print0,
388 print0,
375 };
389 };
376 if display_states.modified {
390 if display_states.modified {
377 output.display(b"M ", "status.modified", ds_status.modified)?;
391 output.display(b"M ", "status.modified", ds_status.modified)?;
378 }
392 }
379 if display_states.added {
393 if display_states.added {
380 output.display(b"A ", "status.added", ds_status.added)?;
394 output.display(b"A ", "status.added", ds_status.added)?;
381 }
395 }
382 if display_states.removed {
396 if display_states.removed {
383 output.display(b"R ", "status.removed", ds_status.removed)?;
397 output.display(b"R ", "status.removed", ds_status.removed)?;
384 }
398 }
385 if display_states.deleted {
399 if display_states.deleted {
386 output.display(b"! ", "status.deleted", ds_status.deleted)?;
400 output.display(b"! ", "status.deleted", ds_status.deleted)?;
387 }
401 }
388 if display_states.unknown {
402 if display_states.unknown {
389 output.display(b"? ", "status.unknown", ds_status.unknown)?;
403 output.display(b"? ", "status.unknown", ds_status.unknown)?;
390 }
404 }
391 if display_states.ignored {
405 if display_states.ignored {
392 output.display(b"I ", "status.ignored", ds_status.ignored)?;
406 output.display(b"I ", "status.ignored", ds_status.ignored)?;
393 }
407 }
394 if display_states.clean {
408 if display_states.clean {
395 output.display(b"C ", "status.clean", ds_status.clean)?;
409 output.display(b"C ", "status.clean", ds_status.clean)?;
396 }
410 }
397
411
398 let dirstate_write_needed = ds_status.dirty;
412 let dirstate_write_needed = ds_status.dirty;
399 let filesystem_time_at_status_start =
413 let filesystem_time_at_status_start =
400 ds_status.filesystem_time_at_status_start;
414 ds_status.filesystem_time_at_status_start;
401
415
402 Ok((
416 Ok((
403 fixup,
417 fixup,
404 dirstate_write_needed,
418 dirstate_write_needed,
405 filesystem_time_at_status_start,
419 filesystem_time_at_status_start,
406 ))
420 ))
407 };
421 };
408 let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?;
422 let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?;
409 let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?;
423 let (sparse_matcher, sparse_warnings) = sparse::matcher(repo)?;
410 let matcher = match (repo.has_narrow(), repo.has_sparse()) {
424 let matcher = match (repo.has_narrow(), repo.has_sparse()) {
411 (true, true) => {
425 (true, true) => {
412 Box::new(IntersectionMatcher::new(narrow_matcher, sparse_matcher))
426 Box::new(IntersectionMatcher::new(narrow_matcher, sparse_matcher))
413 }
427 }
414 (true, false) => narrow_matcher,
428 (true, false) => narrow_matcher,
415 (false, true) => sparse_matcher,
429 (false, true) => sparse_matcher,
416 (false, false) => Box::new(AlwaysMatcher),
430 (false, false) => Box::new(AlwaysMatcher),
417 };
431 };
418
432
419 print_narrow_sparse_warnings(
433 print_narrow_sparse_warnings(
420 &narrow_warnings,
434 &narrow_warnings,
421 &sparse_warnings,
435 &sparse_warnings,
422 ui,
436 ui,
423 repo,
437 repo,
424 )?;
438 )?;
425 let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) =
439 let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) =
426 dmap.with_status(
440 dmap.with_status(
427 matcher.as_ref(),
441 matcher.as_ref(),
428 repo.working_directory_path().to_owned(),
442 repo.working_directory_path().to_owned(),
429 ignore_files(repo, config),
443 ignore_files(repo, config),
430 options,
444 options,
431 after_status,
445 after_status,
432 )?;
446 )?;
433
447
434 // Development config option to test write races
448 // Development config option to test write races
435 if let Err(e) =
449 if let Err(e) =
436 debug_wait_for_file(config, "status.pre-dirstate-write-file")
450 debug_wait_for_file(config, "status.pre-dirstate-write-file")
437 {
451 {
438 ui.write_stderr(e.as_bytes()).ok();
452 ui.write_stderr(e.as_bytes()).ok();
439 }
453 }
440
454
441 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
455 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
442 && !dirstate_write_needed
456 && !dirstate_write_needed
443 {
457 {
444 // Nothing to update
458 // Nothing to update
445 return Ok(());
459 return Ok(());
446 }
460 }
447
461
448 // Update the dirstate on disk if we can
462 // Update the dirstate on disk if we can
449 let with_lock_result =
463 let with_lock_result =
450 repo.try_with_wlock_no_wait(|| -> Result<(), CommandError> {
464 repo.try_with_wlock_no_wait(|| -> Result<(), CommandError> {
451 if let Some(mtime_boundary) = filesystem_time_at_status_start {
465 if let Some(mtime_boundary) = filesystem_time_at_status_start {
452 for hg_path in fixup {
466 for hg_path in fixup {
453 use std::os::unix::fs::MetadataExt;
467 use std::os::unix::fs::MetadataExt;
454 let fs_path = hg_path_to_path_buf(&hg_path)
468 let fs_path = hg_path_to_path_buf(&hg_path)
455 .expect("HgPath conversion");
469 .expect("HgPath conversion");
456 // Specifically do not reuse `fs_metadata` from
470 // Specifically do not reuse `fs_metadata` from
457 // `unsure_is_clean` which was needed before reading
471 // `unsure_is_clean` which was needed before reading
458 // contents. Here we access metadata again after reading
472 // contents. Here we access metadata again after reading
459 // content, in case it changed in the meantime.
473 // content, in case it changed in the meantime.
460 let metadata_res = repo
474 let metadata_res = repo
461 .working_directory_vfs()
475 .working_directory_vfs()
462 .symlink_metadata(&fs_path);
476 .symlink_metadata(&fs_path);
463 let fs_metadata = match metadata_res {
477 let fs_metadata = match metadata_res {
464 Ok(meta) => meta,
478 Ok(meta) => meta,
465 Err(err) => match err {
479 Err(err) => match err {
466 HgError::IoError { .. } => {
480 HgError::IoError { .. } => {
467 // The file has probably been deleted. In any
481 // The file has probably been deleted. In any
468 // case, it was in the dirstate before, so
482 // case, it was in the dirstate before, so
469 // let's ignore the error.
483 // let's ignore the error.
470 continue;
484 continue;
471 }
485 }
472 _ => return Err(err.into()),
486 _ => return Err(err.into()),
473 },
487 },
474 };
488 };
475 if let Some(mtime) =
489 if let Some(mtime) =
476 TruncatedTimestamp::for_reliable_mtime_of(
490 TruncatedTimestamp::for_reliable_mtime_of(
477 &fs_metadata,
491 &fs_metadata,
478 &mtime_boundary,
492 &mtime_boundary,
479 )
493 )
480 .when_reading_file(&fs_path)?
494 .when_reading_file(&fs_path)?
481 {
495 {
482 let mode = fs_metadata.mode();
496 let mode = fs_metadata.mode();
483 let size = fs_metadata.len();
497 let size = fs_metadata.len();
484 dmap.set_clean(&hg_path, mode, size as u32, mtime)?;
498 dmap.set_clean(&hg_path, mode, size as u32, mtime)?;
485 dirstate_write_needed = true
499 dirstate_write_needed = true
486 }
500 }
487 }
501 }
488 }
502 }
489 drop(dmap); // Avoid "already mutably borrowed" RefCell panics
503 drop(dmap); // Avoid "already mutably borrowed" RefCell panics
490 if dirstate_write_needed {
504 if dirstate_write_needed {
491 repo.write_dirstate()?
505 repo.write_dirstate()?
492 }
506 }
493 Ok(())
507 Ok(())
494 });
508 });
495 match with_lock_result {
509 match with_lock_result {
496 Ok(closure_result) => closure_result?,
510 Ok(closure_result) => closure_result?,
497 Err(LockError::AlreadyHeld) => {
511 Err(LockError::AlreadyHeld) => {
498 // Not updating the dirstate is not ideal but not critical:
512 // Not updating the dirstate is not ideal but not critical:
499 // don’t keep our caller waiting until some other Mercurial
513 // don’t keep our caller waiting until some other Mercurial
500 // process releases the lock.
514 // process releases the lock.
501 log::info!("not writing dirstate from `status`: lock is held")
515 log::info!("not writing dirstate from `status`: lock is held")
502 }
516 }
503 Err(LockError::Other(HgError::IoError { error, .. }))
517 Err(LockError::Other(HgError::IoError { error, .. }))
504 if error.kind() == io::ErrorKind::PermissionDenied =>
518 if error.kind() == io::ErrorKind::PermissionDenied =>
505 {
519 {
506 // `hg status` on a read-only repository is fine
520 // `hg status` on a read-only repository is fine
507 }
521 }
508 Err(LockError::Other(error)) => {
522 Err(LockError::Other(error)) => {
509 // Report other I/O errors
523 // Report other I/O errors
510 Err(error)?
524 Err(error)?
511 }
525 }
512 }
526 }
513 Ok(())
527 Ok(())
514 }
528 }
515
529
516 fn ignore_files(repo: &Repo, config: &Config) -> Vec<PathBuf> {
530 fn ignore_files(repo: &Repo, config: &Config) -> Vec<PathBuf> {
517 let mut ignore_files = Vec::new();
531 let mut ignore_files = Vec::new();
518 let repo_ignore = repo.working_directory_vfs().join(".hgignore");
532 let repo_ignore = repo.working_directory_vfs().join(".hgignore");
519 if repo_ignore.exists() {
533 if repo_ignore.exists() {
520 ignore_files.push(repo_ignore)
534 ignore_files.push(repo_ignore)
521 }
535 }
522 for (key, value) in config.iter_section(b"ui") {
536 for (key, value) in config.iter_section(b"ui") {
523 if key == b"ignore" || key.starts_with(b"ignore.") {
537 if key == b"ignore" || key.starts_with(b"ignore.") {
524 let path = get_path_from_bytes(value);
538 let path = get_path_from_bytes(value);
525 // TODO: expand "~/" and environment variable here, like Python
539 // TODO: expand "~/" and environment variable here, like Python
526 // does with `os.path.expanduser` and `os.path.expandvars`
540 // does with `os.path.expanduser` and `os.path.expandvars`
527
541
528 let joined = repo.working_directory_path().join(path);
542 let joined = repo.working_directory_path().join(path);
529 ignore_files.push(joined);
543 ignore_files.push(joined);
530 }
544 }
531 }
545 }
532 ignore_files
546 ignore_files
533 }
547 }
534
548
535 struct DisplayStatusPaths<'a> {
549 struct DisplayStatusPaths<'a> {
536 ui: &'a Ui,
550 ui: &'a Ui,
537 no_status: bool,
551 no_status: bool,
538 relativize: Option<RelativizePaths>,
552 relativize: Option<RelativizePaths>,
539 print0: bool,
553 print0: bool,
540 }
554 }
541
555
542 impl DisplayStatusPaths<'_> {
556 impl DisplayStatusPaths<'_> {
543 // Probably more elegant to use a Deref or Borrow trait rather than
557 // Probably more elegant to use a Deref or Borrow trait rather than
544 // harcode HgPathBuf, but probably not really useful at this point
558 // harcode HgPathBuf, but probably not really useful at this point
545 fn display(
559 fn display(
546 &self,
560 &self,
547 status_prefix: &[u8],
561 status_prefix: &[u8],
548 label: &'static str,
562 label: &'static str,
549 mut paths: Vec<StatusPath<'_>>,
563 mut paths: Vec<StatusPath<'_>>,
550 ) -> Result<(), CommandError> {
564 ) -> Result<(), CommandError> {
551 paths.sort_unstable();
565 paths.sort_unstable();
552 // TODO: get the stdout lock once for the whole loop
566 // TODO: get the stdout lock once for the whole loop
553 // instead of in each write
567 // instead of in each write
554 for StatusPath { path, copy_source } in paths {
568 for StatusPath { path, copy_source } in paths {
555 let relative_path;
569 let relative_path;
556 let relative_source;
570 let relative_source;
557 let (path, copy_source) = if let Some(relativize) =
571 let (path, copy_source) = if let Some(relativize) =
558 &self.relativize
572 &self.relativize
559 {
573 {
560 relative_path = relativize.relativize(&path);
574 relative_path = relativize.relativize(&path);
561 relative_source =
575 relative_source =
562 copy_source.as_ref().map(|s| relativize.relativize(s));
576 copy_source.as_ref().map(|s| relativize.relativize(s));
563 (&*relative_path, relative_source.as_deref())
577 (&*relative_path, relative_source.as_deref())
564 } else {
578 } else {
565 (path.as_bytes(), copy_source.as_ref().map(|s| s.as_bytes()))
579 (path.as_bytes(), copy_source.as_ref().map(|s| s.as_bytes()))
566 };
580 };
567 // TODO: Add a way to use `write_bytes!` instead of `format_bytes!`
581 // TODO: Add a way to use `write_bytes!` instead of `format_bytes!`
568 // in order to stream to stdout instead of allocating an
582 // in order to stream to stdout instead of allocating an
569 // itermediate `Vec<u8>`.
583 // itermediate `Vec<u8>`.
570 if !self.no_status {
584 if !self.no_status {
571 self.ui.write_stdout_labelled(status_prefix, label)?
585 self.ui.write_stdout_labelled(status_prefix, label)?
572 }
586 }
573 let linebreak = if self.print0 { b"\x00" } else { b"\n" };
587 let linebreak = if self.print0 { b"\x00" } else { b"\n" };
574 self.ui.write_stdout_labelled(
588 self.ui.write_stdout_labelled(
575 &format_bytes!(b"{}{}", path, linebreak),
589 &format_bytes!(b"{}{}", path, linebreak),
576 label,
590 label,
577 )?;
591 )?;
578 if let Some(source) = copy_source.filter(|_| !self.no_status) {
592 if let Some(source) = copy_source.filter(|_| !self.no_status) {
579 let label = "status.copied";
593 let label = "status.copied";
580 self.ui.write_stdout_labelled(
594 self.ui.write_stdout_labelled(
581 &format_bytes!(b" {}{}", source, linebreak),
595 &format_bytes!(b" {}{}", source, linebreak),
582 label,
596 label,
583 )?
597 )?
584 }
598 }
585 }
599 }
586 Ok(())
600 Ok(())
587 }
601 }
588 }
602 }
589
603
590 /// Outcome of the additional check for an ambiguous tracked file
604 /// Outcome of the additional check for an ambiguous tracked file
591 enum UnsureOutcome {
605 enum UnsureOutcome {
592 /// The file is actually clean
606 /// The file is actually clean
593 Clean,
607 Clean,
594 /// The file has been modified
608 /// The file has been modified
595 Modified,
609 Modified,
596 /// The file was deleted on disk (or became another type of fs entry)
610 /// The file was deleted on disk (or became another type of fs entry)
597 Deleted,
611 Deleted,
598 }
612 }
599
613
600 /// Check if a file is modified by comparing actual repo store and file system.
614 /// Check if a file is modified by comparing actual repo store and file system.
601 ///
615 ///
602 /// This meant to be used for those that the dirstate cannot resolve, due
616 /// This meant to be used for those that the dirstate cannot resolve, due
603 /// to time resolution limits.
617 /// to time resolution limits.
604 fn unsure_is_modified(
618 fn unsure_is_modified(
605 working_directory_vfs: hg::vfs::Vfs,
619 working_directory_vfs: hg::vfs::Vfs,
606 store_vfs: hg::vfs::Vfs,
620 store_vfs: hg::vfs::Vfs,
607 check_exec: bool,
621 check_exec: bool,
608 manifest: &Manifest,
622 manifest: &Manifest,
609 hg_path: &HgPath,
623 hg_path: &HgPath,
610 ) -> Result<UnsureOutcome, HgError> {
624 ) -> Result<UnsureOutcome, HgError> {
611 let vfs = working_directory_vfs;
625 let vfs = working_directory_vfs;
612 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
626 let fs_path = hg_path_to_path_buf(hg_path).expect("HgPath conversion");
613 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
627 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
614 let is_symlink = fs_metadata.file_type().is_symlink();
628 let is_symlink = fs_metadata.file_type().is_symlink();
615
629
616 let entry = manifest
630 let entry = manifest
617 .find_by_path(hg_path)?
631 .find_by_path(hg_path)?
618 .expect("ambgious file not in p1");
632 .expect("ambgious file not in p1");
619
633
620 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the
634 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the
621 // dirstate
635 // dirstate
622 let fs_flags = if is_symlink {
636 let fs_flags = if is_symlink {
623 Some(b'l')
637 Some(b'l')
624 } else if check_exec && has_exec_bit(&fs_metadata) {
638 } else if check_exec && has_exec_bit(&fs_metadata) {
625 Some(b'x')
639 Some(b'x')
626 } else {
640 } else {
627 None
641 None
628 };
642 };
629
643
630 let entry_flags = if check_exec {
644 let entry_flags = if check_exec {
631 entry.flags
645 entry.flags
632 } else if entry.flags == Some(b'x') {
646 } else if entry.flags == Some(b'x') {
633 None
647 None
634 } else {
648 } else {
635 entry.flags
649 entry.flags
636 };
650 };
637
651
638 if entry_flags != fs_flags {
652 if entry_flags != fs_flags {
639 return Ok(UnsureOutcome::Modified);
653 return Ok(UnsureOutcome::Modified);
640 }
654 }
641 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
655 let filelog = hg::filelog::Filelog::open_vfs(&store_vfs, hg_path)?;
642 let fs_len = fs_metadata.len();
656 let fs_len = fs_metadata.len();
643 let file_node = entry.node_id()?;
657 let file_node = entry.node_id()?;
644 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| {
658 let filelog_entry = filelog.entry_for_node(file_node).map_err(|_| {
645 HgError::corrupted(format!(
659 HgError::corrupted(format!(
646 "filelog {:?} missing node {:?} from manifest",
660 "filelog {:?} missing node {:?} from manifest",
647 hg_path, file_node
661 hg_path, file_node
648 ))
662 ))
649 })?;
663 })?;
650 if filelog_entry.file_data_len_not_equal_to(fs_len) {
664 if filelog_entry.file_data_len_not_equal_to(fs_len) {
651 // No need to read file contents:
665 // No need to read file contents:
652 // it cannot be equal if it has a different length.
666 // it cannot be equal if it has a different length.
653 return Ok(UnsureOutcome::Modified);
667 return Ok(UnsureOutcome::Modified);
654 }
668 }
655
669
656 let p1_filelog_data = filelog_entry.data()?;
670 let p1_filelog_data = filelog_entry.data()?;
657 let p1_contents = p1_filelog_data.file_data()?;
671 let p1_contents = p1_filelog_data.file_data()?;
658 if p1_contents.len() as u64 != fs_len {
672 if p1_contents.len() as u64 != fs_len {
659 // No need to read file contents:
673 // No need to read file contents:
660 // it cannot be equal if it has a different length.
674 // it cannot be equal if it has a different length.
661 return Ok(UnsureOutcome::Modified);
675 return Ok(UnsureOutcome::Modified);
662 }
676 }
663
677
664 let fs_contents = if is_symlink {
678 let fs_contents = if is_symlink {
665 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string())
679 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string())
666 } else {
680 } else {
667 vfs.read(fs_path)?
681 vfs.read(fs_path)?
668 };
682 };
669
683
670 Ok(if p1_contents != &*fs_contents {
684 Ok(if p1_contents != &*fs_contents {
671 UnsureOutcome::Modified
685 UnsureOutcome::Modified
672 } else {
686 } else {
673 UnsureOutcome::Clean
687 UnsureOutcome::Clean
674 })
688 })
675 }
689 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now