#configuration #environment #command-line #secrets #seal

irx-config

The library provides convenient way to represent/parse configuration from different sources

16 stable releases

3.4.0 Dec 11, 2023
3.3.0 Feb 24, 2023
3.1.1 Dec 18, 2022
3.1.0 Oct 8, 2022
1.0.2 Dec 31, 2021

#23 in Configuration

Download history 329/week @ 2023-10-31 217/week @ 2023-11-07 451/week @ 2023-11-14 414/week @ 2023-11-21 695/week @ 2023-11-28 632/week @ 2023-12-05 1273/week @ 2023-12-12 1767/week @ 2023-12-19 309/week @ 2023-12-26 715/week @ 2024-01-02 377/week @ 2024-01-09 347/week @ 2024-01-16 309/week @ 2024-01-23 362/week @ 2024-01-30 311/week @ 2024-02-06 628/week @ 2024-02-13

1,659 downloads per month
Used in svd2rust

BSD-2-Clause

100KB
2K SLoC

irx-config library

GitHub top language Crates.io Crates.io Crates.io Libraries.io dependency status for latest release docs.rs

The irx-config library provides convenient way to represent/parse configuration from different sources. The main goals is to be very easy to use and to be extendable.

Features

  • Fully compatible with serde
  • Full deep merge of nested dictionaries/mappings
  • Case sensitive/insensitive parameters names matching/merging
  • Sealing secrets during display/debugging
  • Get all configuration parameters or just cherry pick few
  • Several embedded parsers available via library features:
    • Command-line argument (via clap)
    • Environment variables
    • File based parsers: JSON, JSON5, YAML and TOML
  • Could be extended with custom parsers

Examples

JSON with environment variables

To enable parsers used in example below, one has to add the following to Cargo.toml:

[dependencies]
irx-config = { version = "3.4", features = ["env", "json"] }
use irx_config::parsers::{env, json};
use irx_config::ConfigBuilder;
use serde::Deserialize;

#[derive(Deserialize)]
struct Conf {
    id: u32,
    logger: String,
    tag: String,
}

// Data from two parsers will be merged. The values from parser appended first (`JSON`)
// will take precedence if values have a same names
let config = ConfigBuilder::default()
    .append_parser(
        json::ParserBuilder::default()
            .default_path("config.json")
            .build()?,
    )
    .append_parser(
        env::ParserBuilder::default()
            .default_prefix("APP_")
            .build()?,
    )
    .load()?;

let conf_data: Conf = config.get()?;

Command-line, TOML and environment variables

To enable parsers used in example below, one has to add the following to Cargo.toml:

[dependencies]
irx-config = { version = "3.4", features = ["cmd", "env", "toml-parser"] }
use clap::app_from_crate;
use irx_config::parsers::{cmd, env, toml};
use irx_config::ConfigBuilder;
use serde::Deserialize;

fn localhost() -> String {
    "localhost".into()
}

#[derive(Deserialize)]
struct Logger {
    level: String,
    path: String,
}

#[derive(Deserialize)]
struct Connection {
    #[serde(default = "localhost")]
    host: String,
    port: u16,
}

#[derive(Deserialize)]
struct Conf {
    id: u32,
    logger: Logger,
    connection: Connection,
}

let app = app_from_crate!();

// Data from three parsers will be merged. The values from parser appended first (`cmd`)
// will take precedence if values have a same names
let config = ConfigBuilder::default()
    .append_parser(
        cmd::ParserBuilder::new(app)
            .exit_on_error(true)
            .build()?,
    )
    .append_parser(
        toml::ParserBuilder::default()
            .default_path("config.toml")
            .path_option("config")
            .build()?,
    )
    .append_parser(
        env::ParserBuilder::default()
            .default_prefix("APP_")
            .prefix_option("prefix")
            .build()?,
    )
    .load()?;

let conf_data: Conf = config.get()?;

Custom parser

use irx_config::{AnyResult, Case, ConfigBuilder, Parse, Value};
use serde::Deserialize;
use std::borrow::Cow;

#[derive(Deserialize)]
struct Conf {
    id: u32,
    logger: String,
    tag: String,
}

struct JsonStringParser<'a> {
    data: Cow<'a, str>,
}

impl<'a> JsonStringParser<'a> {
    pub fn new(data: impl Into<Cow<'a, str>>) -> Self {
        JsonStringParser { data: data.into() }
    }
}

impl Case for JsonStringParser<'_> {}

impl Parse for JsonStringParser<'_> {
    fn parse(&mut self, _value: &Value) -> AnyResult<Value> {
        Ok(serde_json::from_str(&self.data)?)
    }
}

let data = r#"{ "id": 42, "logger": "file", "tag": "test" }"#;
let config = ConfigBuilder::load_one(JsonStringParser::new(data))?;
let conf_data: Conf = config.get()?;

JSON parser get partial data

To enable parsers used in example below, one has to add the following to Cargo.toml:

[dependencies]
irx-config = { version = "3.4", features = ["json"] }
use irx_config::parsers::json;
use irx_config::ConfigBuilder;
use serde::Deserialize;

fn localhost() -> String {
    "localhost".into()
}

#[derive(Deserialize)]
struct Logger {
    level: String,
    path: String,
}

#[derive(Deserialize)]
struct Connection {
    #[serde(default = "localhost")]
    host: String,
    port: u16,
}

let config = ConfigBuilder::load_one(
    json::ParserBuilder::default()
        .default_path("config.json")
        .build()?,
)?;

let logger: Logger = config.get_by_key_path("logger")?.unwrap();
let port: u16 = config.get_by_key_path("connection:port")?.unwrap();

Dependencies

~2–4MB
~89K SLoC