Skip to content

Commit

Permalink
feat: add 'try_deserialize_with_error_path' method
Browse files Browse the repository at this point in the history
  • Loading branch information
allevo committed Jan 9, 2025
1 parent a3ff970 commit 15cad69
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 10 deletions.
31 changes: 21 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ convert-case = ["convert_case"]
preserve_order = ["indexmap", "toml?/preserve_order", "serde_json?/preserve_order", "ron?/indexmap"]
async = ["async-trait"]
toml = ["dep:toml"]
path_to_error = ["serde_path_to_error"]

[dependencies]
serde = "1.0"
Expand All @@ -135,6 +136,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 = { version = "0.1", optional = true }

[dev-dependencies]
serde_derive = "1.0"
Expand Down
15 changes: 15 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ impl Config {
T::deserialize(self)
}

#[cfg(feature = "path_to_error")]
/// Attempt to deserialize the entire configuration into the requested type.
/// If fails, the returned error will contain the path where it occurred.
pub fn try_deserialize_with_error_path<'de, T: Deserialize<'de>>(
self,
) -> std::result::Result<T, Box<serde_path_to_error::Error<ConfigError>>> {
use serde_path_to_error::{Deserializer, Error, Track};

let mut track = Track::new();
match T::deserialize(Deserializer::new(self, &mut track)) {
Ok(t) => Ok(t),
Err(err) => Err(Box::new(Error::new(track.path(), err))),
}
}

/// Attempt to serialize the entire configuration from the given type.
pub fn try_from<T: Serialize>(from: &T) -> Result<Self> {
let mut serializer = ConfigSerializer::default();
Expand Down
46 changes: 46 additions & 0 deletions tests/testsuite/file_json_error_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#![cfg(all(feature = "json5", feature = "path_to_error"))]

use serde_derive::Deserialize;

use config::{Config, File, FileFormat};

#[derive(Debug, Deserialize)]
struct InnerSettings {
#[allow(dead_code)]
value: String,
}

#[derive(Debug, Deserialize)]
struct Settings {
#[allow(dead_code)]
inner: InnerSettings,
}

#[test]
fn test_json_foo() {
let c = Config::builder()
.add_source(File::from_str(
r#"
{
"inner": { "value": {} }
}
"#,
FileFormat::Json,
))
.build()
.unwrap();

match c.try_deserialize_with_error_path::<Settings>() {
Ok(_) => panic!("expected a type error"),
Err(err) => {
let path = err.path().to_string();
assert_eq!(path, "inner.value");

let s = format!("{err}");
assert_eq!(
s,
"inner.value: invalid type: unit value, expected a string for key `inner.value`"
);
}
}
}
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod file;
pub mod file_ini;
pub mod file_json;
pub mod file_json5;
pub mod file_json_error_path;
pub mod file_ron;
pub mod file_toml;
pub mod file_yaml;
Expand Down

0 comments on commit 15cad69

Please sign in to comment.