Show More
This diff has been collapsed as it changes many lines, (669 lines changed) Show them Hide them | |||||
@@ -0,0 +1,669 b'' | |||||
|
1 | //! Code for parsing default Mercurial config items. | |||
|
2 | use itertools::Itertools; | |||
|
3 | use serde::Deserialize; | |||
|
4 | ||||
|
5 | use crate::{errors::HgError, exit_codes, FastHashMap}; | |||
|
6 | ||||
|
7 | /// Corresponds to the structure of `mercurial/configitems.toml`. | |||
|
8 | #[derive(Debug, Deserialize)] | |||
|
9 | pub struct ConfigItems { | |||
|
10 | items: Vec<DefaultConfigItem>, | |||
|
11 | templates: FastHashMap<String, Vec<TemplateItem>>, | |||
|
12 | #[serde(rename = "template-applications")] | |||
|
13 | template_applications: Vec<TemplateApplication>, | |||
|
14 | } | |||
|
15 | ||||
|
16 | /// Corresponds to a config item declaration in `mercurial/configitems.toml`. | |||
|
17 | #[derive(Clone, Debug, PartialEq, Deserialize)] | |||
|
18 | #[serde(try_from = "RawDefaultConfigItem")] | |||
|
19 | pub struct DefaultConfigItem { | |||
|
20 | /// Section of the config the item is in (e.g. `[merge-tools]`) | |||
|
21 | section: String, | |||
|
22 | /// Name of the item (e.g. `meld.gui`) | |||
|
23 | name: String, | |||
|
24 | /// Default value (can be dynamic, see [`DefaultConfigItemType`]) | |||
|
25 | default: Option<DefaultConfigItemType>, | |||
|
26 | /// If the config option is generic (e.g. `merge-tools.*`), defines | |||
|
27 | /// the priority of this item relative to other generic items. | |||
|
28 | /// If we're looking for <pattern>, then all generic items within the same | |||
|
29 | /// section will be sorted by order of priority, and the first regex match | |||
|
30 | /// against `name` is returned. | |||
|
31 | #[serde(default)] | |||
|
32 | priority: Option<isize>, | |||
|
33 | /// Aliases, if any. Each alias is a tuple of `(section, name)` for each | |||
|
34 | /// option that is aliased to this one. | |||
|
35 | #[serde(default)] | |||
|
36 | alias: Vec<(String, String)>, | |||
|
37 | /// Whether the config item is marked as experimental | |||
|
38 | #[serde(default)] | |||
|
39 | experimental: bool, | |||
|
40 | /// The (possibly empty) docstring for the item | |||
|
41 | #[serde(default)] | |||
|
42 | documentation: String, | |||
|
43 | } | |||
|
44 | ||||
|
45 | /// Corresponds to the raw (i.e. on disk) structure of config items. Used as | |||
|
46 | /// an intermediate step in deserialization. | |||
|
47 | #[derive(Clone, Debug, Deserialize)] | |||
|
48 | struct RawDefaultConfigItem { | |||
|
49 | section: String, | |||
|
50 | name: String, | |||
|
51 | default: Option<toml::Value>, | |||
|
52 | #[serde(rename = "default-type")] | |||
|
53 | default_type: Option<String>, | |||
|
54 | #[serde(default)] | |||
|
55 | priority: isize, | |||
|
56 | #[serde(default)] | |||
|
57 | generic: bool, | |||
|
58 | #[serde(default)] | |||
|
59 | alias: Vec<(String, String)>, | |||
|
60 | #[serde(default)] | |||
|
61 | experimental: bool, | |||
|
62 | #[serde(default)] | |||
|
63 | documentation: String, | |||
|
64 | } | |||
|
65 | ||||
|
66 | impl TryFrom<RawDefaultConfigItem> for DefaultConfigItem { | |||
|
67 | type Error = HgError; | |||
|
68 | ||||
|
69 | fn try_from(value: RawDefaultConfigItem) -> Result<Self, Self::Error> { | |||
|
70 | Ok(Self { | |||
|
71 | section: value.section, | |||
|
72 | name: value.name, | |||
|
73 | default: raw_default_to_concrete( | |||
|
74 | value.default_type, | |||
|
75 | value.default, | |||
|
76 | )?, | |||
|
77 | priority: if value.generic { | |||
|
78 | Some(value.priority) | |||
|
79 | } else { | |||
|
80 | None | |||
|
81 | }, | |||
|
82 | alias: value.alias, | |||
|
83 | experimental: value.experimental, | |||
|
84 | documentation: value.documentation, | |||
|
85 | }) | |||
|
86 | } | |||
|
87 | } | |||
|
88 | ||||
|
89 | impl DefaultConfigItem { | |||
|
90 | fn is_generic(&self) -> bool { | |||
|
91 | self.priority.is_some() | |||
|
92 | } | |||
|
93 | } | |||
|
94 | ||||
|
95 | impl<'a> TryFrom<&'a DefaultConfigItem> for Option<&'a str> { | |||
|
96 | type Error = HgError; | |||
|
97 | ||||
|
98 | fn try_from( | |||
|
99 | value: &'a DefaultConfigItem, | |||
|
100 | ) -> Result<Option<&'a str>, Self::Error> { | |||
|
101 | match &value.default { | |||
|
102 | Some(default) => { | |||
|
103 | let err = HgError::abort( | |||
|
104 | format!( | |||
|
105 | "programming error: wrong query on config item '{}.{}'", | |||
|
106 | value.section, | |||
|
107 | value.name | |||
|
108 | ), | |||
|
109 | exit_codes::ABORT, | |||
|
110 | Some(format!( | |||
|
111 | "asked for '&str', type of default is '{}'", | |||
|
112 | default.type_str() | |||
|
113 | )), | |||
|
114 | ); | |||
|
115 | match default { | |||
|
116 | DefaultConfigItemType::Primitive(toml::Value::String( | |||
|
117 | s, | |||
|
118 | )) => Ok(Some(s)), | |||
|
119 | _ => Err(err), | |||
|
120 | } | |||
|
121 | } | |||
|
122 | None => Ok(None), | |||
|
123 | } | |||
|
124 | } | |||
|
125 | } | |||
|
126 | ||||
|
127 | impl TryFrom<&DefaultConfigItem> for Option<bool> { | |||
|
128 | type Error = HgError; | |||
|
129 | ||||
|
130 | fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> { | |||
|
131 | match &value.default { | |||
|
132 | Some(default) => { | |||
|
133 | let err = HgError::abort( | |||
|
134 | format!( | |||
|
135 | "programming error: wrong query on config item '{}.{}'", | |||
|
136 | value.section, | |||
|
137 | value.name | |||
|
138 | ), | |||
|
139 | exit_codes::ABORT, | |||
|
140 | Some(format!( | |||
|
141 | "asked for 'bool', type of default is '{}'", | |||
|
142 | default.type_str() | |||
|
143 | )), | |||
|
144 | ); | |||
|
145 | match default { | |||
|
146 | DefaultConfigItemType::Primitive( | |||
|
147 | toml::Value::Boolean(b), | |||
|
148 | ) => Ok(Some(*b)), | |||
|
149 | _ => Err(err), | |||
|
150 | } | |||
|
151 | } | |||
|
152 | None => Ok(Some(false)), | |||
|
153 | } | |||
|
154 | } | |||
|
155 | } | |||
|
156 | ||||
|
157 | impl TryFrom<&DefaultConfigItem> for Option<u32> { | |||
|
158 | type Error = HgError; | |||
|
159 | ||||
|
160 | fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> { | |||
|
161 | match &value.default { | |||
|
162 | Some(default) => { | |||
|
163 | let err = HgError::abort( | |||
|
164 | format!( | |||
|
165 | "programming error: wrong query on config item '{}.{}'", | |||
|
166 | value.section, | |||
|
167 | value.name | |||
|
168 | ), | |||
|
169 | exit_codes::ABORT, | |||
|
170 | Some(format!( | |||
|
171 | "asked for 'u32', type of default is '{}'", | |||
|
172 | default.type_str() | |||
|
173 | )), | |||
|
174 | ); | |||
|
175 | match default { | |||
|
176 | DefaultConfigItemType::Primitive( | |||
|
177 | toml::Value::Integer(b), | |||
|
178 | ) => { | |||
|
179 | Ok(Some((*b).try_into().expect("TOML integer to u32"))) | |||
|
180 | } | |||
|
181 | _ => Err(err), | |||
|
182 | } | |||
|
183 | } | |||
|
184 | None => Ok(None), | |||
|
185 | } | |||
|
186 | } | |||
|
187 | } | |||
|
188 | ||||
|
189 | impl TryFrom<&DefaultConfigItem> for Option<u64> { | |||
|
190 | type Error = HgError; | |||
|
191 | ||||
|
192 | fn try_from(value: &DefaultConfigItem) -> Result<Self, Self::Error> { | |||
|
193 | match &value.default { | |||
|
194 | Some(default) => { | |||
|
195 | let err = HgError::abort( | |||
|
196 | format!( | |||
|
197 | "programming error: wrong query on config item '{}.{}'", | |||
|
198 | value.section, | |||
|
199 | value.name | |||
|
200 | ), | |||
|
201 | exit_codes::ABORT, | |||
|
202 | Some(format!( | |||
|
203 | "asked for 'u64', type of default is '{}'", | |||
|
204 | default.type_str() | |||
|
205 | )), | |||
|
206 | ); | |||
|
207 | match default { | |||
|
208 | DefaultConfigItemType::Primitive( | |||
|
209 | toml::Value::Integer(b), | |||
|
210 | ) => { | |||
|
211 | Ok(Some((*b).try_into().expect("TOML integer to u64"))) | |||
|
212 | } | |||
|
213 | _ => Err(err), | |||
|
214 | } | |||
|
215 | } | |||
|
216 | None => Ok(None), | |||
|
217 | } | |||
|
218 | } | |||
|
219 | } | |||
|
220 | ||||
|
221 | /// Allows abstracting over more complex default values than just primitives. | |||
|
222 | /// The former `configitems.py` contained some dynamic code that is encoded | |||
|
223 | /// in this enum. | |||
|
224 | #[derive(Debug, PartialEq, Clone, Deserialize)] | |||
|
225 | pub enum DefaultConfigItemType { | |||
|
226 | /// Some primitive type (string, integer, boolean) | |||
|
227 | Primitive(toml::Value), | |||
|
228 | /// A dynamic value that will be given by the code at runtime | |||
|
229 | Dynamic, | |||
|
230 | /// An lazily-returned array (possibly only relevant in the Python impl) | |||
|
231 | /// Example: `lambda: [b"zstd", b"zlib"]` | |||
|
232 | Lambda(Vec<String>), | |||
|
233 | /// For now, a special case for `web.encoding` that points to the | |||
|
234 | /// `encoding.encoding` module in the Python impl so that local encoding | |||
|
235 | /// is correctly resolved at runtime | |||
|
236 | LazyModule(String), | |||
|
237 | ListType, | |||
|
238 | } | |||
|
239 | ||||
|
240 | impl DefaultConfigItemType { | |||
|
241 | pub fn type_str(&self) -> &str { | |||
|
242 | match self { | |||
|
243 | DefaultConfigItemType::Primitive(primitive) => { | |||
|
244 | primitive.type_str() | |||
|
245 | } | |||
|
246 | DefaultConfigItemType::Dynamic => "dynamic", | |||
|
247 | DefaultConfigItemType::Lambda(_) => "lambda", | |||
|
248 | DefaultConfigItemType::LazyModule(_) => "lazy_module", | |||
|
249 | DefaultConfigItemType::ListType => "list_type", | |||
|
250 | } | |||
|
251 | } | |||
|
252 | } | |||
|
253 | ||||
|
254 | /// Most of the fields are shared with [`DefaultConfigItem`]. | |||
|
255 | #[derive(Debug, Clone, Deserialize)] | |||
|
256 | #[serde(try_from = "RawTemplateItem")] | |||
|
257 | struct TemplateItem { | |||
|
258 | suffix: String, | |||
|
259 | default: Option<DefaultConfigItemType>, | |||
|
260 | priority: Option<isize>, | |||
|
261 | #[serde(default)] | |||
|
262 | alias: Vec<(String, String)>, | |||
|
263 | #[serde(default)] | |||
|
264 | experimental: bool, | |||
|
265 | #[serde(default)] | |||
|
266 | documentation: String, | |||
|
267 | } | |||
|
268 | ||||
|
269 | /// Corresponds to the raw (i.e. on disk) representation of a template item. | |||
|
270 | /// Used as an intermediate step in deserialization. | |||
|
271 | #[derive(Clone, Debug, Deserialize)] | |||
|
272 | struct RawTemplateItem { | |||
|
273 | suffix: String, | |||
|
274 | default: Option<toml::Value>, | |||
|
275 | #[serde(rename = "default-type")] | |||
|
276 | default_type: Option<String>, | |||
|
277 | #[serde(default)] | |||
|
278 | priority: isize, | |||
|
279 | #[serde(default)] | |||
|
280 | generic: bool, | |||
|
281 | #[serde(default)] | |||
|
282 | alias: Vec<(String, String)>, | |||
|
283 | #[serde(default)] | |||
|
284 | experimental: bool, | |||
|
285 | #[serde(default)] | |||
|
286 | documentation: String, | |||
|
287 | } | |||
|
288 | ||||
|
289 | impl TemplateItem { | |||
|
290 | fn into_default_item( | |||
|
291 | self, | |||
|
292 | application: TemplateApplication, | |||
|
293 | ) -> DefaultConfigItem { | |||
|
294 | DefaultConfigItem { | |||
|
295 | section: application.section, | |||
|
296 | name: application | |||
|
297 | .prefix | |||
|
298 | .map(|prefix| format!("{}.{}", prefix, self.suffix)) | |||
|
299 | .unwrap_or(self.suffix), | |||
|
300 | default: self.default, | |||
|
301 | priority: self.priority, | |||
|
302 | alias: self.alias, | |||
|
303 | experimental: self.experimental, | |||
|
304 | documentation: self.documentation, | |||
|
305 | } | |||
|
306 | } | |||
|
307 | } | |||
|
308 | ||||
|
309 | impl TryFrom<RawTemplateItem> for TemplateItem { | |||
|
310 | type Error = HgError; | |||
|
311 | ||||
|
312 | fn try_from(value: RawTemplateItem) -> Result<Self, Self::Error> { | |||
|
313 | Ok(Self { | |||
|
314 | suffix: value.suffix, | |||
|
315 | default: raw_default_to_concrete( | |||
|
316 | value.default_type, | |||
|
317 | value.default, | |||
|
318 | )?, | |||
|
319 | priority: if value.generic { | |||
|
320 | Some(value.priority) | |||
|
321 | } else { | |||
|
322 | None | |||
|
323 | }, | |||
|
324 | alias: value.alias, | |||
|
325 | experimental: value.experimental, | |||
|
326 | documentation: value.documentation, | |||
|
327 | }) | |||
|
328 | } | |||
|
329 | } | |||
|
330 | ||||
|
331 | /// Transforms the on-disk string-based representation of complex default types | |||
|
332 | /// to the concrete [`DefaultconfigItemType`]. | |||
|
333 | fn raw_default_to_concrete( | |||
|
334 | default_type: Option<String>, | |||
|
335 | default: Option<toml::Value>, | |||
|
336 | ) -> Result<Option<DefaultConfigItemType>, HgError> { | |||
|
337 | Ok(match default_type.as_deref() { | |||
|
338 | None => default.as_ref().map(|default| { | |||
|
339 | DefaultConfigItemType::Primitive(default.to_owned()) | |||
|
340 | }), | |||
|
341 | Some("dynamic") => Some(DefaultConfigItemType::Dynamic), | |||
|
342 | Some("list_type") => Some(DefaultConfigItemType::ListType), | |||
|
343 | Some("lambda") => match &default { | |||
|
344 | Some(default) => Some(DefaultConfigItemType::Lambda( | |||
|
345 | default.to_owned().try_into().map_err(|e| { | |||
|
346 | HgError::abort( | |||
|
347 | e.to_string(), | |||
|
348 | exit_codes::ABORT, | |||
|
349 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
350 | ) | |||
|
351 | })?, | |||
|
352 | )), | |||
|
353 | None => { | |||
|
354 | return Err(HgError::abort( | |||
|
355 | "lambda defined with no return value".to_string(), | |||
|
356 | exit_codes::ABORT, | |||
|
357 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
358 | )) | |||
|
359 | } | |||
|
360 | }, | |||
|
361 | Some("lazy_module") => match &default { | |||
|
362 | Some(default) => { | |||
|
363 | Some(DefaultConfigItemType::LazyModule(match default { | |||
|
364 | toml::Value::String(module) => module.to_owned(), | |||
|
365 | _ => { | |||
|
366 | return Err(HgError::abort( | |||
|
367 | "lazy_module module name should be a string" | |||
|
368 | .to_string(), | |||
|
369 | exit_codes::ABORT, | |||
|
370 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
371 | )) | |||
|
372 | } | |||
|
373 | })) | |||
|
374 | } | |||
|
375 | None => { | |||
|
376 | return Err(HgError::abort( | |||
|
377 | "lazy_module should have a default value".to_string(), | |||
|
378 | exit_codes::ABORT, | |||
|
379 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
380 | )) | |||
|
381 | } | |||
|
382 | }, | |||
|
383 | Some(invalid) => { | |||
|
384 | return Err(HgError::abort( | |||
|
385 | format!("invalid default_type '{}'", invalid), | |||
|
386 | exit_codes::ABORT, | |||
|
387 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
388 | )) | |||
|
389 | } | |||
|
390 | }) | |||
|
391 | } | |||
|
392 | ||||
|
393 | #[derive(Debug, Clone, Deserialize)] | |||
|
394 | struct TemplateApplication { | |||
|
395 | template: String, | |||
|
396 | section: String, | |||
|
397 | #[serde(default)] | |||
|
398 | prefix: Option<String>, | |||
|
399 | } | |||
|
400 | ||||
|
401 | /// Represents the (dynamic) set of default core Mercurial config items from | |||
|
402 | /// `mercurial/configitems.toml`. | |||
|
403 | #[derive(Clone, Debug, Default)] | |||
|
404 | pub struct DefaultConfig { | |||
|
405 | /// Mapping of section -> (mapping of name -> item) | |||
|
406 | items: FastHashMap<String, FastHashMap<String, DefaultConfigItem>>, | |||
|
407 | } | |||
|
408 | ||||
|
409 | impl DefaultConfig { | |||
|
410 | pub fn empty() -> DefaultConfig { | |||
|
411 | Self { | |||
|
412 | items: Default::default(), | |||
|
413 | } | |||
|
414 | } | |||
|
415 | ||||
|
416 | /// Returns `Self`, given the contents of `mercurial/configitems.toml` | |||
|
417 | #[logging_timer::time("trace")] | |||
|
418 | pub fn from_contents(contents: &str) -> Result<Self, HgError> { | |||
|
419 | let mut from_file: ConfigItems = | |||
|
420 | toml::from_str(contents).map_err(|e| { | |||
|
421 | HgError::abort( | |||
|
422 | e.to_string(), | |||
|
423 | exit_codes::ABORT, | |||
|
424 | Some("Check 'mercurial/configitems.toml'".into()), | |||
|
425 | ) | |||
|
426 | })?; | |||
|
427 | ||||
|
428 | let mut flat_items = from_file.items; | |||
|
429 | ||||
|
430 | for application in from_file.template_applications.drain(..) { | |||
|
431 | match from_file.templates.get(&application.template) { | |||
|
432 | None => return Err( | |||
|
433 | HgError::abort( | |||
|
434 | format!( | |||
|
435 | "template application refers to undefined template '{}'", | |||
|
436 | application.template | |||
|
437 | ), | |||
|
438 | exit_codes::ABORT, | |||
|
439 | Some("Check 'mercurial/configitems.toml'".into()) | |||
|
440 | ) | |||
|
441 | ), | |||
|
442 | Some(template_items) => { | |||
|
443 | for template_item in template_items { | |||
|
444 | flat_items.push( | |||
|
445 | template_item | |||
|
446 | .clone() | |||
|
447 | .into_default_item(application.clone()), | |||
|
448 | ) | |||
|
449 | } | |||
|
450 | } | |||
|
451 | }; | |||
|
452 | } | |||
|
453 | ||||
|
454 | let items = flat_items.into_iter().fold( | |||
|
455 | FastHashMap::default(), | |||
|
456 | |mut acc, item| { | |||
|
457 | acc.entry(item.section.to_owned()) | |||
|
458 | .or_insert_with(|| { | |||
|
459 | let mut section = FastHashMap::default(); | |||
|
460 | section.insert(item.name.to_owned(), item.to_owned()); | |||
|
461 | section | |||
|
462 | }) | |||
|
463 | .insert(item.name.to_owned(), item); | |||
|
464 | acc | |||
|
465 | }, | |||
|
466 | ); | |||
|
467 | ||||
|
468 | Ok(Self { items }) | |||
|
469 | } | |||
|
470 | ||||
|
471 | /// Return the default config item that matches `section` and `item`. | |||
|
472 | pub fn get( | |||
|
473 | &self, | |||
|
474 | section: &[u8], | |||
|
475 | item: &[u8], | |||
|
476 | ) -> Option<&DefaultConfigItem> { | |||
|
477 | // Core items must be valid UTF-8 | |||
|
478 | let section = String::from_utf8_lossy(section); | |||
|
479 | let section_map = self.items.get(section.as_ref())?; | |||
|
480 | let item_name_lossy = String::from_utf8_lossy(item); | |||
|
481 | match section_map.get(item_name_lossy.as_ref()) { | |||
|
482 | Some(item) => Some(item), | |||
|
483 | None => { | |||
|
484 | for generic_item in section_map | |||
|
485 | .values() | |||
|
486 | .filter(|item| item.is_generic()) | |||
|
487 | .sorted_by_key(|item| match item.priority { | |||
|
488 | Some(priority) => (priority, &item.name), | |||
|
489 | _ => unreachable!(), | |||
|
490 | }) | |||
|
491 | { | |||
|
492 | if regex::bytes::Regex::new(&generic_item.name) | |||
|
493 | .expect("invalid regex in configitems") | |||
|
494 | .is_match(item) | |||
|
495 | { | |||
|
496 | return Some(generic_item); | |||
|
497 | } | |||
|
498 | } | |||
|
499 | None | |||
|
500 | } | |||
|
501 | } | |||
|
502 | } | |||
|
503 | } | |||
|
504 | ||||
|
505 | #[cfg(test)] | |||
|
506 | mod tests { | |||
|
507 | use crate::config::config_items::{ | |||
|
508 | DefaultConfigItem, DefaultConfigItemType, | |||
|
509 | }; | |||
|
510 | ||||
|
511 | use super::DefaultConfig; | |||
|
512 | ||||
|
513 | #[test] | |||
|
514 | fn test_config_read() { | |||
|
515 | let contents = r#" | |||
|
516 | [[items]] | |||
|
517 | section = "alias" | |||
|
518 | name = "abcd.*" | |||
|
519 | default = 3 | |||
|
520 | generic = true | |||
|
521 | priority = -1 | |||
|
522 | ||||
|
523 | [[items]] | |||
|
524 | section = "alias" | |||
|
525 | name = ".*" | |||
|
526 | default-type = "dynamic" | |||
|
527 | generic = true | |||
|
528 | ||||
|
529 | [[items]] | |||
|
530 | section = "cmdserver" | |||
|
531 | name = "track-log" | |||
|
532 | default-type = "lambda" | |||
|
533 | default = [ "chgserver", "cmdserver", "repocache",] | |||
|
534 | ||||
|
535 | [[items]] | |||
|
536 | section = "chgserver" | |||
|
537 | name = "idletimeout" | |||
|
538 | default = 3600 | |||
|
539 | ||||
|
540 | [[items]] | |||
|
541 | section = "cmdserver" | |||
|
542 | name = "message-encodings" | |||
|
543 | default-type = "list_type" | |||
|
544 | ||||
|
545 | [[items]] | |||
|
546 | section = "web" | |||
|
547 | name = "encoding" | |||
|
548 | default-type = "lazy_module" | |||
|
549 | default = "encoding.encoding" | |||
|
550 | ||||
|
551 | [[items]] | |||
|
552 | section = "command-templates" | |||
|
553 | name = "graphnode" | |||
|
554 | alias = [["ui", "graphnodetemplate"]] | |||
|
555 | documentation = """This is a docstring. | |||
|
556 | This is another line \ | |||
|
557 | but this is not.""" | |||
|
558 | ||||
|
559 | [[items]] | |||
|
560 | section = "censor" | |||
|
561 | name = "policy" | |||
|
562 | default = "abort" | |||
|
563 | experimental = true | |||
|
564 | ||||
|
565 | [[template-applications]] | |||
|
566 | template = "diff-options" | |||
|
567 | section = "commands" | |||
|
568 | prefix = "revert.interactive" | |||
|
569 | ||||
|
570 | [[template-applications]] | |||
|
571 | template = "diff-options" | |||
|
572 | section = "diff" | |||
|
573 | ||||
|
574 | [templates] | |||
|
575 | [[templates.diff-options]] | |||
|
576 | suffix = "nodates" | |||
|
577 | default = false | |||
|
578 | ||||
|
579 | [[templates.diff-options]] | |||
|
580 | suffix = "showfunc" | |||
|
581 | default = false | |||
|
582 | ||||
|
583 | [[templates.diff-options]] | |||
|
584 | suffix = "unified" | |||
|
585 | "#; | |||
|
586 | let res = DefaultConfig::from_contents(contents); | |||
|
587 | let config = match res { | |||
|
588 | Ok(config) => config, | |||
|
589 | Err(e) => panic!("{}", e), | |||
|
590 | }; | |||
|
591 | let expected = DefaultConfigItem { | |||
|
592 | section: "censor".into(), | |||
|
593 | name: "policy".into(), | |||
|
594 | default: Some(DefaultConfigItemType::Primitive("abort".into())), | |||
|
595 | priority: None, | |||
|
596 | alias: vec![], | |||
|
597 | experimental: true, | |||
|
598 | documentation: "".into(), | |||
|
599 | }; | |||
|
600 | assert_eq!(config.get(b"censor", b"policy"), Some(&expected)); | |||
|
601 | ||||
|
602 | // Test generic priority. The `.*` pattern is wider than `abcd.*`, but | |||
|
603 | // `abcd.*` has priority, so it should match first. | |||
|
604 | let expected = DefaultConfigItem { | |||
|
605 | section: "alias".into(), | |||
|
606 | name: "abcd.*".into(), | |||
|
607 | default: Some(DefaultConfigItemType::Primitive(3.into())), | |||
|
608 | priority: Some(-1), | |||
|
609 | alias: vec![], | |||
|
610 | experimental: false, | |||
|
611 | documentation: "".into(), | |||
|
612 | }; | |||
|
613 | assert_eq!(config.get(b"alias", b"abcdsomething"), Some(&expected)); | |||
|
614 | ||||
|
615 | //... but if it doesn't, we should fallback to `.*` | |||
|
616 | let expected = DefaultConfigItem { | |||
|
617 | section: "alias".into(), | |||
|
618 | name: ".*".into(), | |||
|
619 | default: Some(DefaultConfigItemType::Dynamic), | |||
|
620 | priority: Some(0), | |||
|
621 | alias: vec![], | |||
|
622 | experimental: false, | |||
|
623 | documentation: "".into(), | |||
|
624 | }; | |||
|
625 | assert_eq!(config.get(b"alias", b"something"), Some(&expected)); | |||
|
626 | ||||
|
627 | let expected = DefaultConfigItem { | |||
|
628 | section: "chgserver".into(), | |||
|
629 | name: "idletimeout".into(), | |||
|
630 | default: Some(DefaultConfigItemType::Primitive(3600.into())), | |||
|
631 | priority: None, | |||
|
632 | alias: vec![], | |||
|
633 | experimental: false, | |||
|
634 | documentation: "".into(), | |||
|
635 | }; | |||
|
636 | assert_eq!(config.get(b"chgserver", b"idletimeout"), Some(&expected)); | |||
|
637 | ||||
|
638 | let expected = DefaultConfigItem { | |||
|
639 | section: "cmdserver".into(), | |||
|
640 | name: "track-log".into(), | |||
|
641 | default: Some(DefaultConfigItemType::Lambda(vec![ | |||
|
642 | "chgserver".into(), | |||
|
643 | "cmdserver".into(), | |||
|
644 | "repocache".into(), | |||
|
645 | ])), | |||
|
646 | priority: None, | |||
|
647 | alias: vec![], | |||
|
648 | experimental: false, | |||
|
649 | documentation: "".into(), | |||
|
650 | }; | |||
|
651 | assert_eq!(config.get(b"cmdserver", b"track-log"), Some(&expected)); | |||
|
652 | ||||
|
653 | let expected = DefaultConfigItem { | |||
|
654 | section: "command-templates".into(), | |||
|
655 | name: "graphnode".into(), | |||
|
656 | default: None, | |||
|
657 | priority: None, | |||
|
658 | alias: vec![("ui".into(), "graphnodetemplate".into())], | |||
|
659 | experimental: false, | |||
|
660 | documentation: | |||
|
661 | "This is a docstring.\nThis is another line but this is not." | |||
|
662 | .into(), | |||
|
663 | }; | |||
|
664 | assert_eq!( | |||
|
665 | config.get(b"command-templates", b"graphnode"), | |||
|
666 | Some(&expected) | |||
|
667 | ); | |||
|
668 | } | |||
|
669 | } |
@@ -476,6 +476,12 b' dependencies = [' | |||||
476 |
|
476 | |||
477 | [[package]] |
|
477 | [[package]] | |
478 | name = "hashbrown" |
|
478 | name = "hashbrown" | |
|
479 | version = "0.12.3" | |||
|
480 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
481 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | |||
|
482 | ||||
|
483 | [[package]] | |||
|
484 | name = "hashbrown" | |||
479 | version = "0.13.1" |
|
485 | version = "0.13.1" | |
480 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
486 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
481 | checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" |
|
487 | checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" | |
@@ -517,7 +523,7 b' dependencies = [' | |||||
517 | "derive_more", |
|
523 | "derive_more", | |
518 | "flate2", |
|
524 | "flate2", | |
519 | "format-bytes", |
|
525 | "format-bytes", | |
520 | "hashbrown", |
|
526 | "hashbrown 0.13.1", | |
521 | "home", |
|
527 | "home", | |
522 | "im-rc", |
|
528 | "im-rc", | |
523 | "itertools", |
|
529 | "itertools", | |
@@ -535,9 +541,11 b' dependencies = [' | |||||
535 | "regex", |
|
541 | "regex", | |
536 | "same-file", |
|
542 | "same-file", | |
537 | "self_cell", |
|
543 | "self_cell", | |
|
544 | "serde", | |||
538 | "sha-1 0.10.0", |
|
545 | "sha-1 0.10.0", | |
539 | "tempfile", |
|
546 | "tempfile", | |
540 | "thread_local", |
|
547 | "thread_local", | |
|
548 | "toml", | |||
541 | "twox-hash", |
|
549 | "twox-hash", | |
542 | "zstd", |
|
550 | "zstd", | |
543 | ] |
|
551 | ] | |
@@ -610,6 +618,16 b' dependencies = [' | |||||
610 | ] |
|
618 | ] | |
611 |
|
619 | |||
612 | [[package]] |
|
620 | [[package]] | |
|
621 | name = "indexmap" | |||
|
622 | version = "1.9.2" | |||
|
623 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
624 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" | |||
|
625 | dependencies = [ | |||
|
626 | "autocfg", | |||
|
627 | "hashbrown 0.12.3", | |||
|
628 | ] | |||
|
629 | ||||
|
630 | [[package]] | |||
613 | name = "instant" |
|
631 | name = "instant" | |
614 | version = "0.1.12" |
|
632 | version = "0.1.12" | |
615 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
633 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -749,6 +767,15 b' dependencies = [' | |||||
749 | ] |
|
767 | ] | |
750 |
|
768 | |||
751 | [[package]] |
|
769 | [[package]] | |
|
770 | name = "nom8" | |||
|
771 | version = "0.2.0" | |||
|
772 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
773 | checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" | |||
|
774 | dependencies = [ | |||
|
775 | "memchr", | |||
|
776 | ] | |||
|
777 | ||||
|
778 | [[package]] | |||
752 | name = "num-integer" |
|
779 | name = "num-integer" | |
753 | version = "0.1.45" |
|
780 | version = "0.1.45" | |
754 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
781 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -1107,6 +1134,35 b' source = "registry+https://github.com/ru' | |||||
1107 | checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" |
|
1134 | checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" | |
1108 |
|
1135 | |||
1109 | [[package]] |
|
1136 | [[package]] | |
|
1137 | name = "serde" | |||
|
1138 | version = "1.0.152" | |||
|
1139 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1140 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" | |||
|
1141 | dependencies = [ | |||
|
1142 | "serde_derive", | |||
|
1143 | ] | |||
|
1144 | ||||
|
1145 | [[package]] | |||
|
1146 | name = "serde_derive" | |||
|
1147 | version = "1.0.152" | |||
|
1148 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1149 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" | |||
|
1150 | dependencies = [ | |||
|
1151 | "proc-macro2", | |||
|
1152 | "quote", | |||
|
1153 | "syn", | |||
|
1154 | ] | |||
|
1155 | ||||
|
1156 | [[package]] | |||
|
1157 | name = "serde_spanned" | |||
|
1158 | version = "0.6.1" | |||
|
1159 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1160 | checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" | |||
|
1161 | dependencies = [ | |||
|
1162 | "serde", | |||
|
1163 | ] | |||
|
1164 | ||||
|
1165 | [[package]] | |||
1110 | name = "sha-1" |
|
1166 | name = "sha-1" | |
1111 | version = "0.9.8" |
|
1167 | version = "0.9.8" | |
1112 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
1168 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -1160,9 +1216,9 b' checksum = "73473c0e59e6d5812c5dfe2a064a' | |||||
1160 |
|
1216 | |||
1161 | [[package]] |
|
1217 | [[package]] | |
1162 | name = "syn" |
|
1218 | name = "syn" | |
1163 |
version = "1.0.10 |
|
1219 | version = "1.0.109" | |
1164 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
1220 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
1165 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" |
|
1221 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" | |
1166 | dependencies = [ |
|
1222 | dependencies = [ | |
1167 | "proc-macro2", |
|
1223 | "proc-macro2", | |
1168 | "quote", |
|
1224 | "quote", | |
@@ -1213,6 +1269,40 b' dependencies = [' | |||||
1213 | ] |
|
1269 | ] | |
1214 |
|
1270 | |||
1215 | [[package]] |
|
1271 | [[package]] | |
|
1272 | name = "toml" | |||
|
1273 | version = "0.6.0" | |||
|
1274 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1275 | checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217" | |||
|
1276 | dependencies = [ | |||
|
1277 | "serde", | |||
|
1278 | "serde_spanned", | |||
|
1279 | "toml_datetime", | |||
|
1280 | "toml_edit", | |||
|
1281 | ] | |||
|
1282 | ||||
|
1283 | [[package]] | |||
|
1284 | name = "toml_datetime" | |||
|
1285 | version = "0.5.1" | |||
|
1286 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1287 | checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" | |||
|
1288 | dependencies = [ | |||
|
1289 | "serde", | |||
|
1290 | ] | |||
|
1291 | ||||
|
1292 | [[package]] | |||
|
1293 | name = "toml_edit" | |||
|
1294 | version = "0.18.1" | |||
|
1295 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
1296 | checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" | |||
|
1297 | dependencies = [ | |||
|
1298 | "indexmap", | |||
|
1299 | "nom8", | |||
|
1300 | "serde", | |||
|
1301 | "serde_spanned", | |||
|
1302 | "toml_datetime", | |||
|
1303 | ] | |||
|
1304 | ||||
|
1305 | [[package]] | |||
1216 | name = "twox-hash" |
|
1306 | name = "twox-hash" | |
1217 | version = "1.6.3" |
|
1307 | version = "1.6.3" | |
1218 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
1308 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -26,10 +26,12 b' rand_distr = "0.4.3"' | |||||
26 | rayon = "1.7.0" |
|
26 | rayon = "1.7.0" | |
27 | regex = "1.7.0" |
|
27 | regex = "1.7.0" | |
28 | self_cell = "1.0" |
|
28 | self_cell = "1.0" | |
|
29 | serde = { version = "1.0", features = ["derive"] } | |||
29 | sha-1 = "0.10.0" |
|
30 | sha-1 = "0.10.0" | |
30 | twox-hash = "1.6.3" |
|
31 | twox-hash = "1.6.3" | |
31 | same-file = "1.0.6" |
|
32 | same-file = "1.0.6" | |
32 | tempfile = "3.3.0" |
|
33 | tempfile = "3.3.0" | |
|
34 | toml = "0.6" | |||
33 | thread_local = "1.1.4" |
|
35 | thread_local = "1.1.4" | |
34 | crossbeam-channel = "0.5.6" |
|
36 | crossbeam-channel = "0.5.6" | |
35 | log = "0.4.17" |
|
37 | log = "0.4.17" | |
@@ -46,5 +48,5 b' features = ["zlib"]' | |||||
46 | default-features = false |
|
48 | default-features = false | |
47 |
|
49 | |||
48 | [dev-dependencies] |
|
50 | [dev-dependencies] | |
49 |
clap = { version = "4.0 |
|
51 | clap = { version = "~4.0", features = ["derive"] } | |
50 | pretty_assertions = "1.1.0" |
|
52 | pretty_assertions = "1.1.0" |
@@ -304,8 +304,9 b' pub enum ConfigOrigin {' | |||||
304 | CommandLineColor, |
|
304 | CommandLineColor, | |
305 | /// From environment variables like `$PAGER` or `$EDITOR` |
|
305 | /// From environment variables like `$PAGER` or `$EDITOR` | |
306 | Environment(Vec<u8>), |
|
306 | Environment(Vec<u8>), | |
307 | /* TODO defaults (configitems.py) |
|
307 | /// From configitems.toml | |
308 | * TODO extensions |
|
308 | Defaults, | |
|
309 | /* TODO extensions | |||
309 | * TODO Python resources? |
|
310 | * TODO Python resources? | |
310 | * Others? */ |
|
311 | * Others? */ | |
311 | } |
|
312 | } | |
@@ -323,6 +324,9 b' impl DisplayBytes for ConfigOrigin {' | |||||
323 | ConfigOrigin::Tweakdefaults => { |
|
324 | ConfigOrigin::Tweakdefaults => { | |
324 | write_bytes!(out, b"ui.tweakdefaults") |
|
325 | write_bytes!(out, b"ui.tweakdefaults") | |
325 | } |
|
326 | } | |
|
327 | ConfigOrigin::Defaults => { | |||
|
328 | write_bytes!(out, b"configitems.toml") | |||
|
329 | } | |||
326 | } |
|
330 | } | |
327 | } |
|
331 | } | |
328 | } |
|
332 | } |
@@ -9,14 +9,19 b'' | |||||
9 |
|
9 | |||
10 | //! Mercurial config parsing and interfaces. |
|
10 | //! Mercurial config parsing and interfaces. | |
11 |
|
11 | |||
|
12 | pub mod config_items; | |||
12 | mod layer; |
|
13 | mod layer; | |
13 | mod plain_info; |
|
14 | mod plain_info; | |
14 | mod values; |
|
15 | mod values; | |
15 | pub use layer::{ConfigError, ConfigOrigin, ConfigParseError}; |
|
16 | pub use layer::{ConfigError, ConfigOrigin, ConfigParseError}; | |
|
17 | use lazy_static::lazy_static; | |||
16 | pub use plain_info::PlainInfo; |
|
18 | pub use plain_info::PlainInfo; | |
17 |
|
19 | |||
|
20 | use self::config_items::DefaultConfig; | |||
|
21 | use self::config_items::DefaultConfigItem; | |||
18 | use self::layer::ConfigLayer; |
|
22 | use self::layer::ConfigLayer; | |
19 | use self::layer::ConfigValue; |
|
23 | use self::layer::ConfigValue; | |
|
24 | use crate::errors::HgError; | |||
20 | use crate::errors::{HgResultExt, IoResultExt}; |
|
25 | use crate::errors::{HgResultExt, IoResultExt}; | |
21 | use crate::utils::files::get_bytes_from_os_str; |
|
26 | use crate::utils::files::get_bytes_from_os_str; | |
22 | use format_bytes::{write_bytes, DisplayBytes}; |
|
27 | use format_bytes::{write_bytes, DisplayBytes}; | |
@@ -26,6 +31,14 b' use std::fmt;' | |||||
26 | use std::path::{Path, PathBuf}; |
|
31 | use std::path::{Path, PathBuf}; | |
27 | use std::str; |
|
32 | use std::str; | |
28 |
|
33 | |||
|
34 | lazy_static! { | |||
|
35 | static ref DEFAULT_CONFIG: Result<DefaultConfig, HgError> = { | |||
|
36 | DefaultConfig::from_contents(include_str!( | |||
|
37 | "../../../../mercurial/configitems.toml" | |||
|
38 | )) | |||
|
39 | }; | |||
|
40 | } | |||
|
41 | ||||
29 | /// Holds the config values for the current repository |
|
42 | /// Holds the config values for the current repository | |
30 | /// TODO update this docstring once we support more sources |
|
43 | /// TODO update this docstring once we support more sources | |
31 | #[derive(Clone)] |
|
44 | #[derive(Clone)] | |
@@ -347,13 +360,32 b' impl Config {' | |||||
347 | self.plain = plain; |
|
360 | self.plain = plain; | |
348 | } |
|
361 | } | |
349 |
|
362 | |||
|
363 | /// Returns the default value for the given config item, if any. | |||
|
364 | pub fn get_default( | |||
|
365 | &self, | |||
|
366 | section: &[u8], | |||
|
367 | item: &[u8], | |||
|
368 | ) -> Result<Option<&DefaultConfigItem>, HgError> { | |||
|
369 | let default_config = DEFAULT_CONFIG.as_ref().map_err(|e| { | |||
|
370 | HgError::abort( | |||
|
371 | e.to_string(), | |||
|
372 | crate::exit_codes::ABORT, | |||
|
373 | Some("`mercurial/configitems.toml` is not valid".into()), | |||
|
374 | ) | |||
|
375 | })?; | |||
|
376 | Ok(default_config.get(section, item)) | |||
|
377 | } | |||
|
378 | ||||
350 | fn get_parse<'config, T: 'config>( |
|
379 | fn get_parse<'config, T: 'config>( | |
351 | &'config self, |
|
380 | &'config self, | |
352 | section: &[u8], |
|
381 | section: &[u8], | |
353 | item: &[u8], |
|
382 | item: &[u8], | |
354 | expected_type: &'static str, |
|
383 | expected_type: &'static str, | |
355 | parse: impl Fn(&'config [u8]) -> Option<T>, |
|
384 | parse: impl Fn(&'config [u8]) -> Option<T>, | |
356 |
) -> Result<Option<T>, |
|
385 | ) -> Result<Option<T>, HgError> | |
|
386 | where | |||
|
387 | Option<T>: TryFrom<&'config DefaultConfigItem, Error = HgError>, | |||
|
388 | { | |||
357 | match self.get_inner(section, item) { |
|
389 | match self.get_inner(section, item) { | |
358 | Some((layer, v)) => match parse(&v.bytes) { |
|
390 | Some((layer, v)) => match parse(&v.bytes) { | |
359 | Some(b) => Ok(Some(b)), |
|
391 | Some(b) => Ok(Some(b)), | |
@@ -364,9 +396,15 b' impl Config {' | |||||
364 | section: section.to_owned(), |
|
396 | section: section.to_owned(), | |
365 | item: item.to_owned(), |
|
397 | item: item.to_owned(), | |
366 | expected_type, |
|
398 | expected_type, | |
367 |
}) |
|
399 | }) | |
|
400 | .into()), | |||
368 | }, |
|
401 | }, | |
369 | None => Ok(None), |
|
402 | None => match self.get_default(section, item)? { | |
|
403 | Some(default) => Ok(default.try_into()?), | |||
|
404 | None => { | |||
|
405 | Ok(None) | |||
|
406 | } | |||
|
407 | }, | |||
370 | } |
|
408 | } | |
371 | } |
|
409 | } | |
372 |
|
410 | |||
@@ -376,7 +414,7 b' impl Config {' | |||||
376 | &self, |
|
414 | &self, | |
377 | section: &[u8], |
|
415 | section: &[u8], | |
378 | item: &[u8], |
|
416 | item: &[u8], | |
379 |
) -> Result<Option<&str>, |
|
417 | ) -> Result<Option<&str>, HgError> { | |
380 | self.get_parse(section, item, "ASCII or UTF-8 string", |value| { |
|
418 | self.get_parse(section, item, "ASCII or UTF-8 string", |value| { | |
381 | str::from_utf8(value).ok() |
|
419 | str::from_utf8(value).ok() | |
382 | }) |
|
420 | }) | |
@@ -388,7 +426,7 b' impl Config {' | |||||
388 | &self, |
|
426 | &self, | |
389 | section: &[u8], |
|
427 | section: &[u8], | |
390 | item: &[u8], |
|
428 | item: &[u8], | |
391 |
) -> Result<Option<u32>, |
|
429 | ) -> Result<Option<u32>, HgError> { | |
392 | self.get_parse(section, item, "valid integer", |value| { |
|
430 | self.get_parse(section, item, "valid integer", |value| { | |
393 | str::from_utf8(value).ok()?.parse().ok() |
|
431 | str::from_utf8(value).ok()?.parse().ok() | |
394 | }) |
|
432 | }) | |
@@ -401,7 +439,7 b' impl Config {' | |||||
401 | &self, |
|
439 | &self, | |
402 | section: &[u8], |
|
440 | section: &[u8], | |
403 | item: &[u8], |
|
441 | item: &[u8], | |
404 |
) -> Result<Option<u64>, |
|
442 | ) -> Result<Option<u64>, HgError> { | |
405 | self.get_parse(section, item, "byte quantity", values::parse_byte_size) |
|
443 | self.get_parse(section, item, "byte quantity", values::parse_byte_size) | |
406 | } |
|
444 | } | |
407 |
|
445 | |||
@@ -412,7 +450,7 b' impl Config {' | |||||
412 | &self, |
|
450 | &self, | |
413 | section: &[u8], |
|
451 | section: &[u8], | |
414 | item: &[u8], |
|
452 | item: &[u8], | |
415 |
) -> Result<Option<bool>, |
|
453 | ) -> Result<Option<bool>, HgError> { | |
416 | self.get_parse(section, item, "boolean", values::parse_bool) |
|
454 | self.get_parse(section, item, "boolean", values::parse_bool) | |
417 | } |
|
455 | } | |
418 |
|
456 | |||
@@ -422,7 +460,7 b' impl Config {' | |||||
422 | &self, |
|
460 | &self, | |
423 | section: &[u8], |
|
461 | section: &[u8], | |
424 | item: &[u8], |
|
462 | item: &[u8], | |
425 |
) -> Result<bool, |
|
463 | ) -> Result<bool, HgError> { | |
426 | Ok(self.get_option(section, item)?.unwrap_or(false)) |
|
464 | Ok(self.get_option(section, item)?.unwrap_or(false)) | |
427 | } |
|
465 | } | |
428 |
|
466 |
@@ -7,7 +7,8 b'' | |||||
7 |
|
7 | |||
8 | use crate::error::CommandError; |
|
8 | use crate::error::CommandError; | |
9 | use crate::ui::{ |
|
9 | use crate::ui::{ | |
10 |
format_pattern_file_warning, print_narrow_sparse_warnings, |
|
10 | format_pattern_file_warning, print_narrow_sparse_warnings, relative_paths, | |
|
11 | RelativePaths, Ui, | |||
11 | }; |
|
12 | }; | |
12 | use crate::utils::path_utils::RelativizePaths; |
|
13 | use crate::utils::path_utils::RelativizePaths; | |
13 | use clap::Arg; |
|
14 | use clap::Arg; | |
@@ -360,13 +361,26 b' pub fn run(invocation: &crate::CliInvoca' | |||||
360 | } |
|
361 | } | |
361 | } |
|
362 | } | |
362 | } |
|
363 | } | |
363 | let relative_paths = config |
|
364 | ||
|
365 | let relative_status = config | |||
364 | .get_option(b"commands", b"status.relative")? |
|
366 | .get_option(b"commands", b"status.relative")? | |
365 | .unwrap_or(config.get_bool(b"ui", b"relative-paths")?); |
|
367 | .expect("commands.status.relative should have a default value"); | |
|
368 | ||||
|
369 | let relativize_paths = relative_status || { | |||
|
370 | // TODO should be dependent on whether patterns are passed once | |||
|
371 | // we support those. | |||
|
372 | // See in Python code with `getuipathfn` usage in `commands.py`. | |||
|
373 | let legacy_relative_behavior = false; | |||
|
374 | match relative_paths(invocation.config)? { | |||
|
375 | RelativePaths::Legacy => legacy_relative_behavior, | |||
|
376 | RelativePaths::Bool(v) => v, | |||
|
377 | } | |||
|
378 | }; | |||
|
379 | ||||
366 | let output = DisplayStatusPaths { |
|
380 | let output = DisplayStatusPaths { | |
367 | ui, |
|
381 | ui, | |
368 | no_status, |
|
382 | no_status, | |
369 | relativize: if relative_paths { |
|
383 | relativize: if relativize_paths { | |
370 | Some(RelativizePaths::new(repo)?) |
|
384 | Some(RelativizePaths::new(repo)?) | |
371 | } else { |
|
385 | } else { | |
372 | None |
|
386 | None |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now