Crate confique

Source
Expand description

Confique is a type-safe, layered, light-weight, serde-based configuration library.

The core of the library is the Config trait and its derive-macro. You define your configuration value as one or more structs, each of which has to #[derive(Config)]. Then you can use different ways of loading an instancce of your root configuration struct.

§How to use

Add confique as dependency to your Cargo.toml and remember to enable the crate features for file formats you are interested in. For example: cargo add confique --features=toml.

§Defining your configuration with structs

First, define some structs that describe all your configuration values. Use the types you want to use in your code. For example, if you have a port config and your code needs that value, it should be of type u16, and not Option<u16> or String. That way, the code using that value is cleanest.

Small example:

use confique::Config;

#[derive(Config)]
struct Conf {
    // A required value. Since it's not `Option<_>`, it has to be specified when
    // loading the configuration, or else loading returns an error.
    username: String,

    // An optional value.
    welcome_message: Option<String>,

    // A required value with default value. If no other value is specified
    // (e.g. in a config file), the default value is used.
    #[config(default = 8080)]
    port: u16,
}

As your application grows, oftentimes you want to split the configuration into multiple structs. This has the added benefit that your config files are somewhat structured or have sections. You can do that by including other types that implement Config with #[config(nested)].

use std::path::PathBuf;
use confique::Config;

#[derive(Config)]
struct Conf {
    username: String,

    #[config(nested)]
    log: LogConf,

    #[config(nested)]
    db: DbConf,
}

#[derive(Config)]
struct LogConf {
    #[config(default = true)]
    stdout: bool,

    file: Option<PathBuf>,
}

#[derive(Config)]
struct DbConf {
    // ...
}

You can also attach some other attributes to fields. For example, with #[config(env = "KEY")], you can load a value from an environment variable. With #[config(validate = ...)] you can add validation checks. For more information, see the docs for the derive macro.

§Loading the configuration

Here, you have multiple options. Most of the time, you can probably use the provided high-level methods of Config, like Config::from_file and Config::builder.

use confique::Config;

// Load from a single file only.
let config = Conf::from_file("config.toml")?;

// Or load from multiple sources (higher priority sources are listed first).
let config = Conf::builder()
    .env()
    .file("config.toml")
    .file("/etc/myapp/config.toml")
    .load()?;

But you can also assemble your configuration yourself. That’s what the partial types are for (i.e. Config::Partial). These implement serde::Deserialize and can thus be loaded from a vast number of sources. One of those sources is the built-in File which gives you a bit more control when loading configuration from files. And you can always simply create an instance of the partial type by writing all values in Rust code with struct initializer syntax!

Once you have all your layers (partial types) collected, you have to combine them via Partial::with_fallback and convert them to the actual config type via Config::from_partial. And you probably also want to use Partial::default_values as the last layer.

use confique::{Config, File, FileFormat, Partial};

#[derive(Config)]
struct Conf {
    foo: f32,
}

type PartialConf = <Conf as Config>::Partial;
let from_file: PartialConf = File::with_format("/etc/foo/config", FileFormat::Toml)
    .required()
    .load()?;
let manual = PartialConf {
    // Remember: all fields in the partial types are `Option`s!
    foo: Some(3.14),
};
let defaults = PartialConf::default_values();

let merged = from_file.with_fallback(manual).with_fallback(defaults);
let config = Conf::from_partial(merged)?;

§Using your configuration

Well, this is the simple part: the loaded configuration is just an instance of your struct. And you already know how to access fields of structs!

§Cargo features

This crate has a Cargo feature for each supported file format. These are not enabled by default, so you have to specify which file formats you are interested in.

confique = { version = "...", features = ["toml"] }

All crate features:

  • toml: enables TOML support and adds the toml dependency.
  • yaml: enables YAML support and adds the serde_yaml dependency.
  • json5: enables JSON5 support and adds the json5 dependency.

Re-exports§

pub use serde;

Modules§

env
Deserialize values from environment variables.
json5
JSON5 specific features. This module only exists if the Cargo feature json5 is enabled.
meta
Types for Config::META. Represent information about a configuration type.
toml
TOML specific features. This module only exists if the Cargo feature toml is enabled.
yaml
YAML specific features. This module only exists if the Cargo feature yaml is enabled.

Structs§

Builder
Convenience builder to configure, load and merge multiple configuration sources.
Error
Type describing all errors that can occur in this library.
File
A file as source for configuration.
FormatOptions
General (non format-dependent) template-formatting options.

Enums§

FileFormat
All file formats supported by confique.

Traits§

Config
A configuration object that can be deserialized in layers via serde.
Partial
A potentially partial configuration object that can be directly deserialized via serde.

Derive Macros§

Config
Derives (automatically implements) Config for a struct.