Show More
@@ -11,5 +11,5 b'' | |||
|
11 | 11 | |
|
12 | 12 | mod config; |
|
13 | 13 | mod layer; |
|
14 | pub use config::Config; | |
|
14 | pub use config::{Config, ConfigValueParseError}; | |
|
15 | 15 | pub use layer::{ConfigError, ConfigParseError}; |
@@ -9,7 +9,7 b'' | |||
|
9 | 9 | |
|
10 | 10 | use super::layer; |
|
11 | 11 | use crate::config::layer::{ |
|
12 |
ConfigError, ConfigLayer, Config |
|
|
12 | ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, | |
|
13 | 13 | }; |
|
14 | 14 | use crate::utils::files::get_bytes_from_os_str; |
|
15 | 15 | use format_bytes::{write_bytes, DisplayBytes}; |
@@ -54,6 +54,16 b' pub enum ConfigSource {' | |||
|
54 | 54 | Parsed(layer::ConfigLayer), |
|
55 | 55 | } |
|
56 | 56 | |
|
57 | #[derive(Debug)] | |
|
58 | pub struct ConfigValueParseError { | |
|
59 | pub origin: ConfigOrigin, | |
|
60 | pub line: Option<usize>, | |
|
61 | pub section: Vec<u8>, | |
|
62 | pub item: Vec<u8>, | |
|
63 | pub value: Vec<u8>, | |
|
64 | pub expected_type: &'static str, | |
|
65 | } | |
|
66 | ||
|
57 | 67 | pub fn parse_bool(v: &[u8]) -> Option<bool> { |
|
58 | 68 | match v.to_ascii_lowercase().as_slice() { |
|
59 | 69 | b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), |
@@ -262,15 +272,19 b' impl Config {' | |||
|
262 | 272 | &'config self, |
|
263 | 273 | section: &[u8], |
|
264 | 274 | item: &[u8], |
|
275 | expected_type: &'static str, | |
|
265 | 276 | parse: impl Fn(&'config [u8]) -> Option<T>, |
|
266 | ) -> Result<Option<T>, ConfigParseError> { | |
|
277 | ) -> Result<Option<T>, ConfigValueParseError> { | |
|
267 | 278 | match self.get_inner(§ion, &item) { |
|
268 | 279 | Some((layer, v)) => match parse(&v.bytes) { |
|
269 | 280 | Some(b) => Ok(Some(b)), |
|
270 | None => Err(ConfigParseError { | |
|
281 | None => Err(ConfigValueParseError { | |
|
271 | 282 | origin: layer.origin.to_owned(), |
|
272 | 283 | line: v.line, |
|
273 |
|
|
|
284 | value: v.bytes.to_owned(), | |
|
285 | section: section.to_owned(), | |
|
286 | item: item.to_owned(), | |
|
287 | expected_type, | |
|
274 | 288 | }), |
|
275 | 289 | }, |
|
276 | 290 | None => Ok(None), |
@@ -283,8 +297,10 b' impl Config {' | |||
|
283 | 297 | &self, |
|
284 | 298 | section: &[u8], |
|
285 | 299 | item: &[u8], |
|
286 | ) -> Result<Option<&str>, ConfigParseError> { | |
|
287 |
self.get_parse(section, item, |value| |
|
|
300 | ) -> Result<Option<&str>, ConfigValueParseError> { | |
|
301 | self.get_parse(section, item, "ASCII or UTF-8 string", |value| { | |
|
302 | str::from_utf8(value).ok() | |
|
303 | }) | |
|
288 | 304 | } |
|
289 | 305 | |
|
290 | 306 | /// Returns an `Err` if the first value found is not a valid unsigned |
@@ -293,8 +309,8 b' impl Config {' | |||
|
293 | 309 | &self, |
|
294 | 310 | section: &[u8], |
|
295 | 311 | item: &[u8], |
|
296 | ) -> Result<Option<u32>, ConfigParseError> { | |
|
297 | self.get_parse(section, item, |value| { | |
|
312 | ) -> Result<Option<u32>, ConfigValueParseError> { | |
|
313 | self.get_parse(section, item, "valid integer", |value| { | |
|
298 | 314 | str::from_utf8(value).ok()?.parse().ok() |
|
299 | 315 | }) |
|
300 | 316 | } |
@@ -306,8 +322,8 b' impl Config {' | |||
|
306 | 322 | &self, |
|
307 | 323 | section: &[u8], |
|
308 | 324 | item: &[u8], |
|
309 | ) -> Result<Option<u64>, ConfigParseError> { | |
|
310 | self.get_parse(section, item, parse_byte_size) | |
|
325 | ) -> Result<Option<u64>, ConfigValueParseError> { | |
|
326 | self.get_parse(section, item, "byte quantity", parse_byte_size) | |
|
311 | 327 | } |
|
312 | 328 | |
|
313 | 329 | /// Returns an `Err` if the first value found is not a valid boolean. |
@@ -317,8 +333,8 b' impl Config {' | |||
|
317 | 333 | &self, |
|
318 | 334 | section: &[u8], |
|
319 | 335 | item: &[u8], |
|
320 | ) -> Result<Option<bool>, ConfigParseError> { | |
|
321 | self.get_parse(section, item, parse_bool) | |
|
336 | ) -> Result<Option<bool>, ConfigValueParseError> { | |
|
337 | self.get_parse(section, item, "boolean", parse_bool) | |
|
322 | 338 | } |
|
323 | 339 | |
|
324 | 340 | /// Returns the corresponding boolean in the config. Returns `Ok(false)` |
@@ -327,7 +343,7 b' impl Config {' | |||
|
327 | 343 | &self, |
|
328 | 344 | section: &[u8], |
|
329 | 345 | item: &[u8], |
|
330 | ) -> Result<bool, ConfigError> { | |
|
346 | ) -> Result<bool, ConfigValueParseError> { | |
|
331 | 347 | Ok(self.get_option(section, item)?.unwrap_or(false)) |
|
332 | 348 | } |
|
333 | 349 |
@@ -1,7 +1,8 b'' | |||
|
1 | use crate::config::ConfigValueParseError; | |
|
1 | 2 | use std::fmt; |
|
2 | 3 | |
|
3 | 4 | /// Common error cases that can happen in many different APIs |
|
4 | #[derive(Debug)] | |
|
5 | #[derive(Debug, derive_more::From)] | |
|
5 | 6 | pub enum HgError { |
|
6 | 7 | IoError { |
|
7 | 8 | error: std::io::Error, |
@@ -29,6 +30,14 b' pub enum HgError {' | |||
|
29 | 30 | /// The given string is a short explanation for users, not intended to be |
|
30 | 31 | /// machine-readable. |
|
31 | 32 | Abort(String), |
|
33 | ||
|
34 | /// A configuration value is not in the expected syntax. | |
|
35 | /// | |
|
36 | /// These errors can happen in many places in the code because values are | |
|
37 | /// parsed lazily as the file-level parser does not know the expected type | |
|
38 | /// and syntax of each value. | |
|
39 | #[from] | |
|
40 | ConfigValueParseError(ConfigValueParseError), | |
|
32 | 41 | } |
|
33 | 42 | |
|
34 | 43 | /// Details about where an I/O error happened |
@@ -63,6 +72,7 b' impl HgError {' | |||
|
63 | 72 | impl fmt::Display for HgError { |
|
64 | 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
65 | 74 | match self { |
|
75 | HgError::Abort(explanation) => write!(f, "{}", explanation), | |
|
66 | 76 | HgError::IoError { error, context } => { |
|
67 | 77 | write!(f, "{}: {}", error, context) |
|
68 | 78 | } |
@@ -72,7 +82,25 b' impl fmt::Display for HgError {' | |||
|
72 | 82 | HgError::UnsupportedFeature(explanation) => { |
|
73 | 83 | write!(f, "unsupported feature: {}", explanation) |
|
74 | 84 | } |
|
75 | HgError::Abort(explanation) => explanation.fmt(f), | |
|
85 | HgError::ConfigValueParseError(ConfigValueParseError { | |
|
86 | origin: _, | |
|
87 | line: _, | |
|
88 | section, | |
|
89 | item, | |
|
90 | value, | |
|
91 | expected_type, | |
|
92 | }) => { | |
|
93 | // TODO: add origin and line number information, here and in | |
|
94 | // corresponding python code | |
|
95 | write!( | |
|
96 | f, | |
|
97 | "config error: {}.{} is not a {} ('{}')", | |
|
98 | String::from_utf8_lossy(section), | |
|
99 | String::from_utf8_lossy(item), | |
|
100 | expected_type, | |
|
101 | String::from_utf8_lossy(value) | |
|
102 | ) | |
|
103 | } | |
|
76 | 104 | } |
|
77 | 105 | } |
|
78 | 106 | } |
General Comments 0
You need to be logged in to leave comments.
Login now