diff options
author | Yuya Nishihara <yuya@tcha.org> | 2023-01-17 17:28:25 +0900 |
---|---|---|
committer | Yuya Nishihara <yuya@tcha.org> | 2023-02-02 17:31:02 +0900 |
commit | b24ea685907e00d8d266bd1399efb70ced7a7606 (patch) | |
tree | bacbcb14b884dde34191233e29f6dd675bc19598 | |
parent | c71ad9c77f65336c0aafb4581a2d2785991f2938 (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.rs | 34 | ||||
-rw-r--r-- | tests/errors.rs | 54 |
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 { |