From 1fe75f085f27110a6e08a4ef62d5ff41d4428faa Mon Sep 17 00:00:00 2001 From: Tommaso Allevi Date: Thu, 9 Jan 2025 17:31:16 +0100 Subject: [PATCH] feat: prepend error with path --- Cargo.lock | 31 +++++++++++++++++++++---------- Cargo.toml | 1 + src/config.rs | 16 +++++++++++++++- tests/testsuite/errors.rs | 4 ++-- tests/testsuite/log.rs | 2 +- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 774fc905..df704892 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,6 +254,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "serde_path_to_error", "snapbox", "temp-env", "tokio", @@ -1352,18 +1353,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1626,18 +1627,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.189" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1656,6 +1657,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -1776,9 +1787,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.38" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6059a158..a587764b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,6 +135,7 @@ indexmap = { version = "2.2", features = ["serde"], optional = true } convert_case = { version = "0.6", optional = true } pathdiff = "0.2" winnow = "0.6.20" +serde_path_to_error = "0.1" [dev-dependencies] serde_derive = "1.0" diff --git a/src/config.rs b/src/config.rs index 64d6b7f1..5233fe00 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use serde::de::Deserialize; use serde::ser::Serialize; +use serde_path_to_error::{Deserializer, Track}; use crate::builder::{ConfigBuilder, DefaultState}; use crate::error::{ConfigError, Result}; @@ -142,7 +143,20 @@ impl Config { /// Attempt to deserialize the entire configuration into the requested type. pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result { - T::deserialize(self) + let mut track = Track::new(); + match T::deserialize(Deserializer::new(self, &mut track)) { + Ok(t) => Ok(t), + Err(ConfigError::Message(e)) => { + let path = track.path(); + if path.iter().count() == 0 { + return Err(ConfigError::Message(e)); + } + Err(ConfigError::Message(format!( + "failed reading field `{path}`: {e}" + ))) + } + Err(err) => Err(err), + } } /// Attempt to serialize the entire configuration from the given type. diff --git a/tests/testsuite/errors.rs b/tests/testsuite/errors.rs index ca55dae6..5ed43442 100644 --- a/tests/testsuite/errors.rs +++ b/tests/testsuite/errors.rs @@ -341,7 +341,7 @@ fn test_json_error_with_path() { "inner": { "value": 42 } } "#, - FileFormat::Json, + FileFormat::Json5, )) .build() .unwrap(); @@ -349,6 +349,6 @@ fn test_json_error_with_path() { let with_path = c.clone().try_deserialize::(); assert_data_eq!( with_path.unwrap_err().to_string(), - str!["missing field `value2`"] + str!["failed reading field `inner`: missing field `value2`"] ); } diff --git a/tests/testsuite/log.rs b/tests/testsuite/log.rs index 29bf7eca..aa6278d2 100644 --- a/tests/testsuite/log.rs +++ b/tests/testsuite/log.rs @@ -53,6 +53,6 @@ fn test_load_level_lowercase() { assert!(s.is_err()); assert_data_eq!( s.unwrap_err().to_string(), - str!["enum Level does not have variant constructor error"] + str!["failed reading field `log`: enum Level does not have variant constructor error"] ); }