Show More
@@ -11,5 +11,5 b'' | |||||
11 |
|
11 | |||
12 | mod config; |
|
12 | mod config; | |
13 | mod layer; |
|
13 | mod layer; | |
14 | pub use config::Config; |
|
14 | pub use config::{Config, ConfigValueParseError}; | |
15 | pub use layer::{ConfigError, ConfigParseError}; |
|
15 | pub use layer::{ConfigError, ConfigParseError}; |
@@ -9,7 +9,7 b'' | |||||
9 |
|
9 | |||
10 | use super::layer; |
|
10 | use super::layer; | |
11 | use crate::config::layer::{ |
|
11 | use crate::config::layer::{ | |
12 |
ConfigError, ConfigLayer, Config |
|
12 | ConfigError, ConfigLayer, ConfigOrigin, ConfigValue, | |
13 | }; |
|
13 | }; | |
14 | use crate::utils::files::get_bytes_from_os_str; |
|
14 | use crate::utils::files::get_bytes_from_os_str; | |
15 | use format_bytes::{write_bytes, DisplayBytes}; |
|
15 | use format_bytes::{write_bytes, DisplayBytes}; | |
@@ -54,6 +54,16 b' pub enum ConfigSource {' | |||||
54 | Parsed(layer::ConfigLayer), |
|
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 | pub fn parse_bool(v: &[u8]) -> Option<bool> { |
|
67 | pub fn parse_bool(v: &[u8]) -> Option<bool> { | |
58 | match v.to_ascii_lowercase().as_slice() { |
|
68 | match v.to_ascii_lowercase().as_slice() { | |
59 | b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), |
|
69 | b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true), | |
@@ -262,15 +272,19 b' impl Config {' | |||||
262 | &'config self, |
|
272 | &'config self, | |
263 | section: &[u8], |
|
273 | section: &[u8], | |
264 | item: &[u8], |
|
274 | item: &[u8], | |
|
275 | expected_type: &'static str, | |||
265 | parse: impl Fn(&'config [u8]) -> Option<T>, |
|
276 | parse: impl Fn(&'config [u8]) -> Option<T>, | |
266 | ) -> Result<Option<T>, ConfigParseError> { |
|
277 | ) -> Result<Option<T>, ConfigValueParseError> { | |
267 | match self.get_inner(§ion, &item) { |
|
278 | match self.get_inner(§ion, &item) { | |
268 | Some((layer, v)) => match parse(&v.bytes) { |
|
279 | Some((layer, v)) => match parse(&v.bytes) { | |
269 | Some(b) => Ok(Some(b)), |
|
280 | Some(b) => Ok(Some(b)), | |
270 | None => Err(ConfigParseError { |
|
281 | None => Err(ConfigValueParseError { | |
271 | origin: layer.origin.to_owned(), |
|
282 | origin: layer.origin.to_owned(), | |
272 | line: v.line, |
|
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 | None => Ok(None), |
|
290 | None => Ok(None), | |
@@ -283,8 +297,10 b' impl Config {' | |||||
283 | &self, |
|
297 | &self, | |
284 | section: &[u8], |
|
298 | section: &[u8], | |
285 | item: &[u8], |
|
299 | item: &[u8], | |
286 | ) -> Result<Option<&str>, ConfigParseError> { |
|
300 | ) -> Result<Option<&str>, ConfigValueParseError> { | |
287 |
self.get_parse(section, item, |value| |
|
301 | self.get_parse(section, item, "ASCII or UTF-8 string", |value| { | |
|
302 | str::from_utf8(value).ok() | |||
|
303 | }) | |||
288 | } |
|
304 | } | |
289 |
|
305 | |||
290 | /// Returns an `Err` if the first value found is not a valid unsigned |
|
306 | /// Returns an `Err` if the first value found is not a valid unsigned | |
@@ -293,8 +309,8 b' impl Config {' | |||||
293 | &self, |
|
309 | &self, | |
294 | section: &[u8], |
|
310 | section: &[u8], | |
295 | item: &[u8], |
|
311 | item: &[u8], | |
296 | ) -> Result<Option<u32>, ConfigParseError> { |
|
312 | ) -> Result<Option<u32>, ConfigValueParseError> { | |
297 | self.get_parse(section, item, |value| { |
|
313 | self.get_parse(section, item, "valid integer", |value| { | |
298 | str::from_utf8(value).ok()?.parse().ok() |
|
314 | str::from_utf8(value).ok()?.parse().ok() | |
299 | }) |
|
315 | }) | |
300 | } |
|
316 | } | |
@@ -306,8 +322,8 b' impl Config {' | |||||
306 | &self, |
|
322 | &self, | |
307 | section: &[u8], |
|
323 | section: &[u8], | |
308 | item: &[u8], |
|
324 | item: &[u8], | |
309 | ) -> Result<Option<u64>, ConfigParseError> { |
|
325 | ) -> Result<Option<u64>, ConfigValueParseError> { | |
310 | self.get_parse(section, item, parse_byte_size) |
|
326 | self.get_parse(section, item, "byte quantity", parse_byte_size) | |
311 | } |
|
327 | } | |
312 |
|
328 | |||
313 | /// Returns an `Err` if the first value found is not a valid boolean. |
|
329 | /// Returns an `Err` if the first value found is not a valid boolean. | |
@@ -317,8 +333,8 b' impl Config {' | |||||
317 | &self, |
|
333 | &self, | |
318 | section: &[u8], |
|
334 | section: &[u8], | |
319 | item: &[u8], |
|
335 | item: &[u8], | |
320 | ) -> Result<Option<bool>, ConfigParseError> { |
|
336 | ) -> Result<Option<bool>, ConfigValueParseError> { | |
321 | self.get_parse(section, item, parse_bool) |
|
337 | self.get_parse(section, item, "boolean", parse_bool) | |
322 | } |
|
338 | } | |
323 |
|
339 | |||
324 | /// Returns the corresponding boolean in the config. Returns `Ok(false)` |
|
340 | /// Returns the corresponding boolean in the config. Returns `Ok(false)` | |
@@ -327,7 +343,7 b' impl Config {' | |||||
327 | &self, |
|
343 | &self, | |
328 | section: &[u8], |
|
344 | section: &[u8], | |
329 | item: &[u8], |
|
345 | item: &[u8], | |
330 | ) -> Result<bool, ConfigError> { |
|
346 | ) -> Result<bool, ConfigValueParseError> { | |
331 | Ok(self.get_option(section, item)?.unwrap_or(false)) |
|
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 | use std::fmt; |
|
2 | use std::fmt; | |
2 |
|
3 | |||
3 | /// Common error cases that can happen in many different APIs |
|
4 | /// Common error cases that can happen in many different APIs | |
4 | #[derive(Debug)] |
|
5 | #[derive(Debug, derive_more::From)] | |
5 | pub enum HgError { |
|
6 | pub enum HgError { | |
6 | IoError { |
|
7 | IoError { | |
7 | error: std::io::Error, |
|
8 | error: std::io::Error, | |
@@ -29,6 +30,14 b' pub enum HgError {' | |||||
29 | /// The given string is a short explanation for users, not intended to be |
|
30 | /// The given string is a short explanation for users, not intended to be | |
30 | /// machine-readable. |
|
31 | /// machine-readable. | |
31 | Abort(String), |
|
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 | /// Details about where an I/O error happened |
|
43 | /// Details about where an I/O error happened | |
@@ -63,6 +72,7 b' impl HgError {' | |||||
63 | impl fmt::Display for HgError { |
|
72 | impl fmt::Display for HgError { | |
64 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
65 | match self { |
|
74 | match self { | |
|
75 | HgError::Abort(explanation) => write!(f, "{}", explanation), | |||
66 | HgError::IoError { error, context } => { |
|
76 | HgError::IoError { error, context } => { | |
67 | write!(f, "{}: {}", error, context) |
|
77 | write!(f, "{}: {}", error, context) | |
68 | } |
|
78 | } | |
@@ -72,7 +82,25 b' impl fmt::Display for HgError {' | |||||
72 | HgError::UnsupportedFeature(explanation) => { |
|
82 | HgError::UnsupportedFeature(explanation) => { | |
73 | write!(f, "unsupported feature: {}", explanation) |
|
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