Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How should we work with nested library configurations? #123

Open
gshipilov opened this issue Sep 11, 2024 · 0 comments
Open

How should we work with nested library configurations? #123

gshipilov opened this issue Sep 11, 2024 · 0 comments

Comments

@gshipilov
Copy link

I own an internal library crate and an application that uses that library, and I'm trying to move both to use figment. I checked out the For Library Authors section of the doc, but I'm a bit confused how to use it in practice.

What I've got

I've set up the library config roughly like this:

#[derive(Deserialize)]
pub struct LibConfig {
    pub first_value: String,
    pub second_value: String,
}

impl LibConfig {
    pub fn new() -> figment::Result<Self> {
        Self::from_provider(Self::figment())
    }

    pub fn from_provider(provider: impl Provider) -> figment::Result<Self> {
        Figment::from(provider).extract()
    }

    pub fn figment() -> Figment {
        Figment::new().merge(Env::prefixed("MY_LIB_"))
    }
}

This works great. I can call LibConfig::new() and it extracts the values out of the environment and creates the config struct.

What I want

When I start integrating this with my app, I want to have a config file like this:

---
app_value: "a"
my_lib:
    first_value: "b"
    # second_value set using env vars.

And have my rust code set up like this:

#[derive(Deserialize)]
pub struct AppConfig {
    pub app_value: String,
    pub my_lib: LibConfig,
}

impl AppConfig {
    pub fn new() -> figment::Result<Self> {
        Figment::new()
            .merge(Yaml::file("app_config.yaml"))
            .merge(MyLib::figment()) // <-- Is this right??
            .extract()
    }
}

This doesn't work as expected. first_value in the lib config is set correctly, but second_value isn't. From what I can tell, this is because the LibConfig env keys aren't nested correctly. So it's trying to set the value at the top level instead of the nested object.

I thought maybe focus() could this—something like

Figment::new()
    .merge(Yaml::file(...))
    .focus("my_lib")
    .merge(MyLib::figment())

but that returns a new figment that can't be "unfocused"—so that works to extract the LibConfig struct but not the wrapping AppConfig struct.

It looks like figment::util::nest() exists, but that works on values, not figments.

What I think I need is something like a merge_under(&self, prefix: &str, provider: impl Provider) that does merge(), but nests the keys in provider under prefix.

Questions

Now that I've set the context, my actual questions are:

  1. Is there any way to do what I want here? I think the library is pushing me towards having multiple different config structs and getting them out via extract_inner or focus. This works, but it's not quite as nice as being able to deserialize the whole thing.
  2. Is it possible to get a more complete example in the "For Library Authors" section of the docs? Something that shows not just how the library should be set up, but how it's expected to be used by a dependent.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant