##// END OF EJS Templates
rust: Add a `ConfigValueParseError` variant to common errors...
Simon Sapin -
r47340:bc08c233 default
parent child Browse files
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, ConfigParseError, ConfigValue,
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(&section, &item) {
278 match self.get_inner(&section, &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 bytes: v.bytes.to_owned(),
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| str::from_utf8(value).ok())
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