diff rust/hg-core/src/config/config.rs @ 46598:bc08c2331f99

rust: Add a `ConfigValueParseError` variant to common errors Configuration files are parsed into sections of key/value pairs when they are read, but at that point values are still arbitrary bytes. Only when a value is accessed by various parts of the code do we know its expected type and syntax, so values are parsed at that point. Let?s make a new error type for this latter kind of parsing error, and add a variant to the common `HgError` so that most code can propagate it without much boilerplate. Differential Revision: https://phab.mercurial-scm.org/D10009
author Simon Sapin <simon.sapin@octobus.net>
date Tue, 16 Feb 2021 15:22:20 +0100
parents 305d74c262de
children 1f55cd5b292f
line wrap: on
line diff
--- a/rust/hg-core/src/config/config.rs	Tue Feb 16 13:55:31 2021 +0100
+++ b/rust/hg-core/src/config/config.rs	Tue Feb 16 15:22:20 2021 +0100
@@ -9,7 +9,7 @@
 
 use super::layer;
 use crate::config::layer::{
-    ConfigError, ConfigLayer, ConfigParseError, ConfigValue,
+    ConfigError, ConfigLayer, ConfigOrigin, ConfigValue,
 };
 use crate::utils::files::get_bytes_from_os_str;
 use format_bytes::{write_bytes, DisplayBytes};
@@ -54,6 +54,16 @@
     Parsed(layer::ConfigLayer),
 }
 
+#[derive(Debug)]
+pub struct ConfigValueParseError {
+    pub origin: ConfigOrigin,
+    pub line: Option<usize>,
+    pub section: Vec<u8>,
+    pub item: Vec<u8>,
+    pub value: Vec<u8>,
+    pub expected_type: &'static str,
+}
+
 pub fn parse_bool(v: &[u8]) -> Option<bool> {
     match v.to_ascii_lowercase().as_slice() {
         b"1" | b"yes" | b"true" | b"on" | b"always" => Some(true),
@@ -262,15 +272,19 @@
         &'config self,
         section: &[u8],
         item: &[u8],
+        expected_type: &'static str,
         parse: impl Fn(&'config [u8]) -> Option<T>,
-    ) -> Result<Option<T>, ConfigParseError> {
+    ) -> Result<Option<T>, ConfigValueParseError> {
         match self.get_inner(&section, &item) {
             Some((layer, v)) => match parse(&v.bytes) {
                 Some(b) => Ok(Some(b)),
-                None => Err(ConfigParseError {
+                None => Err(ConfigValueParseError {
                     origin: layer.origin.to_owned(),
                     line: v.line,
-                    bytes: v.bytes.to_owned(),
+                    value: v.bytes.to_owned(),
+                    section: section.to_owned(),
+                    item: item.to_owned(),
+                    expected_type,
                 }),
             },
             None => Ok(None),
@@ -283,8 +297,10 @@
         &self,
         section: &[u8],
         item: &[u8],
-    ) -> Result<Option<&str>, ConfigParseError> {
-        self.get_parse(section, item, |value| str::from_utf8(value).ok())
+    ) -> Result<Option<&str>, ConfigValueParseError> {
+        self.get_parse(section, item, "ASCII or UTF-8 string", |value| {
+            str::from_utf8(value).ok()
+        })
     }
 
     /// Returns an `Err` if the first value found is not a valid unsigned
@@ -293,8 +309,8 @@
         &self,
         section: &[u8],
         item: &[u8],
-    ) -> Result<Option<u32>, ConfigParseError> {
-        self.get_parse(section, item, |value| {
+    ) -> Result<Option<u32>, ConfigValueParseError> {
+        self.get_parse(section, item, "valid integer", |value| {
             str::from_utf8(value).ok()?.parse().ok()
         })
     }
@@ -306,8 +322,8 @@
         &self,
         section: &[u8],
         item: &[u8],
-    ) -> Result<Option<u64>, ConfigParseError> {
-        self.get_parse(section, item, parse_byte_size)
+    ) -> Result<Option<u64>, ConfigValueParseError> {
+        self.get_parse(section, item, "byte quantity", parse_byte_size)
     }
 
     /// Returns an `Err` if the first value found is not a valid boolean.
@@ -317,8 +333,8 @@
         &self,
         section: &[u8],
         item: &[u8],
-    ) -> Result<Option<bool>, ConfigParseError> {
-        self.get_parse(section, item, parse_bool)
+    ) -> Result<Option<bool>, ConfigValueParseError> {
+        self.get_parse(section, item, "boolean", parse_bool)
     }
 
     /// Returns the corresponding boolean in the config. Returns `Ok(false)`
@@ -327,7 +343,7 @@
         &self,
         section: &[u8],
         item: &[u8],
-    ) -> Result<bool, ConfigError> {
+    ) -> Result<bool, ConfigValueParseError> {
         Ok(self.get_option(section, item)?.unwrap_or(false))
     }