#proc-macro #cli #cli-config

yanked scuffle-config

Extensible config solution

1 unstable release

0.0.1 Jan 24, 2024

#29 in #cli-config

MIT license

120KB
3.5K SLoC

⚙️ Config

Once someone said

Config Solutions Absolutly Suck

This crate tries to solve this problem and make it suck less.

It comes with built-in support for parsing cli arguments, environment variables and files without requiring you to write more than just the definition of your configuration structs. Another goal of this crate is to be extensible and allow you to write your own sources. You could, for example, implement config sources like databases or remote services. See Advanced Usage for more information.

🚀 How to use it

Since this crate is built on top of serde, please add serde as a dependency to your Cargo.toml.

cargo add serde --features derive

After that, you can use the #[derive(Config)] macro call to derive the Config trait for your configuration struct. All structs implementing Config are also required to implement serde::Deserialize.

#[derive(config::Config, serde::Deserialize)]
struct MyConfig {
    // ...
}

Now you can use the ConfigBuilder to parse your configuration from various sources and merge them together into your configuration struct.

use config::sources;

#[derive(config::Config, serde::Deserialize)]
struct MyConfig {
    // ...
}

fn main() -> Result<(), config::ConfigError> {
    let mut builder = config::ConfigBuilder::new();
    // From CLI arguments
    builder.add_source(sources::CliSource::new()?);
    // From environment variables
    builder.add_source(sources::EnvSource::with_prefix("TEST")?);
    // From config file
    builder.add_source(sources::FileSource::with_path("config.toml")?);

    // Build the final configuration
    let config: MyConfig = builder.build()?;

    // ...

    Ok(())
}
  • The first line creates a new ConfigBuilder.
  • The next line adds the CliSource to the builder which parses cli arguments using the clap crate. You can call your executable with --help to see the generated help message.
  • The third line adds the EnvSource to the builder which parses environment variables with the given prefix.
  • The third source, called FileSource, is added to parse the configuration from a file with the given path. The file format will be detected automatically during runtime. It can be TOML, YAML or JSON.
  • Finally, you can call build to build the final configuration. This will parse all sources and merge them together into your configuration struct. The ealier a source is added, the higher its priority is. This means that values from sources added later will not overwrite values from sources added earlier.

Example

See the examples folder for examples.

🔧 Advanced usage

How to define your own config source

To define your own config source, you need to implement the Source trait.

This requires you to implement the get_key method which returns a Value for a given KeyPath.

That's it. It's as simple as that. Now you can add your source to a ConfigBuilder and use it to parse your configuration.

🔬 How it works under the hood

As soon as your type implements the Config trait, it supports getting its keys as a KeyGraph. This is a graph of all keys in that type. It is used by the ConfigBuilder to retrieve the values from the added sources by iterating all keys.

Please see the docs for Config, ConfigBuilder, Source and KeyGraph for more information.

Dependencies

~5–6.5MB
~122K SLoC