summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuya Nishihara <yuya@tcha.org>2023-01-17 17:28:25 +0900
committerYuya Nishihara <yuya@tcha.org>2023-02-02 17:31:02 +0900
commitb24ea685907e00d8d266bd1399efb70ced7a7606 (patch)
treebacbcb14b884dde34191233e29f6dd675bc19598
parentc71ad9c77f65336c0aafb4581a2d2785991f2938 (diff)
Attach key to type error generated from Config::get_<type>()
This commit extracts low-level get_value() to preserve the origin as much as possible. get::<Value>() would run Value-to-Value deserialization, and the origin field would be lost there. For scalar types, we could instead leverage get::<T>() as the deserializer implemented for Value type invokes Value::into_<type>() function: fn get_string(&self, key: &str) -> Result<String> { self.get(key) } Signed-off-by: Yuya Nishihara <yuya@tcha.org>
-rw-r--r--src/config.rs34
-rw-r--r--tests/errors.rs54
2 files changed, 74 insertions, 14 deletions
diff --git a/src/config.rs b/src/config.rs
index 320b72e..0f44aae 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -150,7 +150,7 @@ impl Config {
Ok(())
}
- pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T> {
+ fn get_value(&self, key: &str) -> Result<Value> {
let k = key.to_lowercase();
let key = k.as_str();
// Parse the key into a path expression
@@ -159,38 +159,44 @@ impl Config {
// Traverse the cache using the path to (possibly) retrieve a value
let value = expr.get(&self.cache).cloned();
- match value {
- Some(value) => {
- // Deserialize the received value into the requested type
- T::deserialize(value).map_err(|e| e.extend_with_key(key))
- }
+ value.ok_or_else(|| ConfigError::NotFound(key.into()))
+ }
- None => Err(ConfigError::NotFound(key.into())),
- }
+ pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result<T> {
+ self.get_value(key).and_then(|value| {
+ // Deserialize the received value into the requested type
+ T::deserialize(value).map_err(|e| e.extend_with_key(key))
+ })
}
pub fn get_string(&self, key: &str) -> Result<String> {
- self.get(key).and_then(Value::into_string)
+ self.get_value(key)
+ .and_then(|value| value.into_string().map_err(|e| e.extend_with_key(key)))
}
pub fn get_int(&self, key: &str) -> Result<i64> {
- self.get(key).and_then(Value::into_int)
+ self.get_value(key)
+ .and_then(|value| value.into_int().map_err(|e| e.extend_with_key(key)))
}
pub fn get_float(&self, key: &str) -> Result<f64> {
- self.get(key).and_then(Value::into_float)
+ self.get_value(key)
+ .and_then(|value| value.into_float().map_err(|e| e.extend_with_key(key)))
}
pub fn get_bool(&self, key: &str) -> Result<bool> {
- self.get(key).and_then(Value::into_bool)
+ self.get_value(key)
+ .and_then(|value| value.into_bool().map_err(|e| e.extend_with_key(key)))
}
pub fn get_table(&self, key: &str) -> Result<Map<String, Value>> {
- self.get(key).and_then(Value::into_table)
+ self.get_value(key)
+ .and_then(|value| value.into_table().map_err(|e| e.extend_with_key(key)))
}
pub fn get_array(&self, key: &str) -> Result<Vec<Value>> {
- self.get(key).and_then(Value::into_array)
+ self.get_value(key)
+ .and_then(|value| value.into_array().map_err(|e| e.extend_with_key(key)))
}
/// Attempt to deserialize the entire configuration into the requested type.
diff --git a/tests/errors.rs b/tests/errors.rs
index 1822bff..54cb93a 100644
--- a/tests/errors.rs
+++ b/tests/errors.rs
@@ -59,6 +59,60 @@ fn test_error_type_detached() {
}
#[test]
+fn test_error_type_get_bool() {
+ let c = make();
+
+ let res = c.get_bool("boolean_s_parse");
+
+ let path: PathBuf = ["tests", "Settings.toml"].iter().collect();
+
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err().to_string(),
+ format!(
+ "invalid type: string \"fals\", expected a boolean for key `boolean_s_parse` in {}",
+ path.display()
+ )
+ );
+}
+
+#[test]
+fn test_error_type_get_table() {
+ let c = make();
+
+ let res = c.get_table("debug");
+
+ let path: PathBuf = ["tests", "Settings.toml"].iter().collect();
+
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err().to_string(),
+ format!(
+ "invalid type: boolean `true`, expected a map for key `debug` in {}",
+ path.display()
+ )
+ );
+}
+
+#[test]
+fn test_error_type_get_array() {
+ let c = make();
+
+ let res = c.get_array("debug");
+
+ let path: PathBuf = ["tests", "Settings.toml"].iter().collect();
+
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err().to_string(),
+ format!(
+ "invalid type: boolean `true`, expected an array for key `debug` in {}",
+ path.display()
+ )
+ );
+}
+
+#[test]
fn test_error_enum_de() {
#[derive(Debug, Deserialize, PartialEq, Eq)]
enum Diode {