Skip to content

Commit

Permalink
Use consistent path/key wording. Fix error paths.
Browse files Browse the repository at this point in the history
This commit improves 'Figment::extract_inner()' so that error messages
that arise from the method's use include the target path.

This commit improves 'Error::with_path(path)' so that `path` is properly
expanded before being pushed into the error's path.
  • Loading branch information
SergioBenitez committed Apr 16, 2024
1 parent 060742b commit 94d09e1
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 12 deletions.
17 changes: 12 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ pub enum Kind {
}

impl Error {
pub(crate) fn prefixed(mut self, path: &str) -> Self {
self.path.insert(0, path.into());
pub(crate) fn prefixed(mut self, key: &str) -> Self {
self.path.insert(0, key.into());
self
}

Expand Down Expand Up @@ -190,11 +190,18 @@ impl Error {
/// ```rust
/// use figment::Error;
///
/// let error = Error::from("an error message".to_string())
/// .with_path("some_path");
/// let error = Error::from("an error message").with_path("some_path");
/// assert_eq!(error.path, vec!["some_path"]);
///
/// let error = Error::from("an error message").with_path("some.path");
/// assert_eq!(error.path, vec!["some", "path"]);
/// ```
pub fn with_path(mut self, path: &str) -> Self {
self.path.push(path.into());
let paths = path.split('.')
.filter(|v| !v.is_empty())
.map(|v| v.to_string());

self.path.extend(paths);
self
}

Expand Down
64 changes: 57 additions & 7 deletions src/figment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,9 @@ impl Figment {
/// Ok(())
/// });
/// ```
pub fn extract_inner<'a, T: Deserialize<'a>>(&self, key: &str) -> Result<T> {
T::deserialize(ConfiguredValueDe::from(self, &self.find_value(key)?))
pub fn extract_inner<'a, T: Deserialize<'a>>(&self, path: &str) -> Result<T> {
T::deserialize(ConfiguredValueDe::from(self, &self.find_value(path)?))
.map_err(|e| e.with_path(path))
}

/// Returns an iterator over the metadata for all of the collected values in
Expand Down Expand Up @@ -600,8 +601,14 @@ impl Figment {
.flatten()
}

/// Finds the value at `key` path in the combined value. See
/// [`Value::find()`] for details on the syntax for `key`.
/// Finds the value at `path` in the combined value.
///
/// If there is an error evaluating the combined figment, that error is
/// returned. Otherwise if there is a value at `path`, returns `Ok(value)`,
/// and if there is no value at `path`, returns `Err` of kind
/// `MissingField`.
///
/// See [`Value::find()`] for details on the syntax for `path`.
///
/// # Example
///
Expand Down Expand Up @@ -640,10 +647,53 @@ impl Figment {
/// Ok(())
/// });
/// ```
pub fn find_value(&self, key: &str) -> Result<Value> {
pub fn find_value(&self, path: &str) -> Result<Value> {
self.merged()?
.find(key)
.ok_or_else(|| Kind::MissingField(key.to_string().into()).into())
.find(path)
.ok_or_else(|| Kind::MissingField(path.to_string().into()).into())
}

/// Returns `true` if the combined figment evaluates successfully and
/// contains a value at `path`.
///
/// See [`Value::find()`] for details on the syntax for `path`.
///
/// # Example
///
/// ```rust
/// use serde::Deserialize;
///
/// use figment::{Figment, providers::{Format, Toml, Json, Env}};
///
/// figment::Jail::expect_with(|jail| {
/// jail.create_file("Config.toml", r#"
/// name = "test"
///
/// [package]
/// name = "my-package"
/// "#)?;
///
/// jail.create_file("Config.json", r#"
/// {
/// "author": { "name": "Bob" }
/// }
/// "#)?;
///
/// let figment = Figment::new()
/// .merge(Toml::file("Config.toml"))
/// .join(Json::file("Config.json"));
///
/// assert!(figment.contains("name"));
/// assert!(figment.contains("package"));
/// assert!(figment.contains("package.name"));
/// assert!(figment.contains("author"));
/// assert!(figment.contains("author.name"));
/// assert!(!figment.contains("author.title"));
/// Ok(())
/// });
/// ```
pub fn contains(&self, path: &str) -> bool {
self.merged().map_or(false, |v| v.find_ref(path).is_some())
}

/// Finds the metadata for the value at `key` path. See [`Value::find()`]
Expand Down
20 changes: 20 additions & 0 deletions tests/profile-tag.rs → tests/tagged.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ fn check_values_are_tagged_with_profile() {
Ok(())
});
}

#[test]
fn check_errors_are_tagged_with_path() {
Jail::expect_with(|_| {
let figment = Figment::new()
.merge(("foo", 123))
.merge(("foo.bar", 789))
.merge(("baz", 789));

let err = figment.extract_inner::<String>("foo").unwrap_err();
assert_eq!(err.path, vec!["foo"]);

let err = figment.extract_inner::<String>("foo.bar").unwrap_err();
assert_eq!(err.path, vec!["foo", "bar"]);

let err = figment.extract_inner::<usize>("foo.bar.baz").unwrap_err();
assert!(err.path.is_empty());
Ok(())
});
}

0 comments on commit 94d09e1

Please sign in to comment.