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