Crate smart_config

Source
Expand description

smart-config – schema-driven layered configuration system with support of multiple configuration formats.

§Overview

The task solved by the library is merging configuration input from a variety of prioritized sources (JSON and YAML files, env variables, command-line args etc.) and converting this input to strongly typed representation (i.e., config structs or enums). As with other config systems, config input follows the JSON object model (see Value), with each value enriched with its origin (e.g., a path in a specific JSON file, or a specific env var). This allows attributing errors during deserialization.

The defining feature of smart-config is its schema-driven design. Each config type has associated metadata defined with the help of the DescribeConfig derive macro; deserialization is handled by the accompanying DeserializeConfig macro. Metadata includes a variety of info extracted from the config type:

Multiple configurations are collected into a global ConfigSchema. Each configuration is mounted at a specific path. E.g., if a large app has an HTTP server component, it may be mounted at api.http. Multiple config types may be mounted at the same path (e.g., flattened configs); conversely, a single config type may be mounted at multiple places. As a result, there doesn’t need to be a god object uniting all configs in the app; they may be dynamically collected and deserialized inside relevant components.

This information provides rich human-readable info about configs. It also assists when preprocessing and merging config inputs. For example, env vars are a flat string -> string map; with the help of a schema, it’s possible to:

  • Correctly nest vars (e.g., transform the API_HTTP_PORT var into a port var inside http object inside api object)
  • Transform value types from strings to expected types.

Preprocessing and merging config sources is encapsulated in ConfigRepository.

§TL;DR

  • Rich, self-documenting configuration schema.
  • Utilizes the schema to enrich configuration sources and intelligently merge them.
  • Doesn’t require a god object uniting all configs in the app; they may be dynamically collected and deserialized inside relevant components.
  • Supports lazy parsing for complex / multi-component apps (only the used configs are parsed; other configs are not required).
  • Supports multiple configuration formats and programmable source priorities (e.g., base.yml + overrides from the overrides/ dir in the alphabetic order + env vars).
  • Rich and complete deserialization errors including locations and value origins.
  • Built-in support for secret params.

§Crate features

§primitive-types

(Off by default)

Implements deserialization for basic Ethereum types like H256 (32-byte hash) and U256 (256-bit unsigned integer).

§Examples

§Basic workflow

use smart_config::{
    config, ConfigSchema, ConfigRepository, DescribeConfig, DeserializeConfig, Yaml, Environment,
};

#[derive(Debug, DescribeConfig, DeserializeConfig)]
pub struct TestConfig {
    pub port: u16,
    #[config(default_t = "test".into())]
    pub name: String,
    #[config(default_t = true)]
    pub tracing: bool,
}

let schema = ConfigSchema::new(&TestConfig::DESCRIPTION, "test");
// Assume we use two config sources: a YAML file and env vars,
// the latter having higher priority.
let yaml = r"
test:
  port: 4000
  name: app
";
let yaml = Yaml::new("test.yml", serde_yaml::from_str(yaml)?)?;
let env = Environment::from_iter("APP_", [("APP_TEST_PORT", "8000")]);
// Add both sources to a repo.
let repo = ConfigRepository::new(&schema).with(yaml).with(env);
// Get the parser for the config.
let parser = repo.single::<TestConfig>()?;
let config = parser.parse()?;
assert_eq!(config.port, 8_000); // from the env var
assert_eq!(config.name, "app"); // from YAML
assert!(config.tracing); // from the default value

§Declaring type as well-known

use std::collections::HashMap;
use smart_config::{
    de::{Serde, WellKnown, WellKnownOption}, metadata::BasicTypes,
    DescribeConfig, DeserializeConfig,
};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
enum CustomEnum {
    First,
    Second,
}

impl WellKnown for CustomEnum {
    // signals that the type should be deserialized via `serde`
    // and the expected input is a string
    type Deserializer = Serde![str];
    const DE: Self::Deserializer = Serde![str];
}

// Signals that the type can be used with an `Option<_>`
impl WellKnownOption for CustomEnum {}

// Then, the type can be used in configs basically everywhere:
#[derive(Debug, DescribeConfig, DeserializeConfig)]
struct TestConfig {
    value: CustomEnum,
    optional: Option<CustomEnum>,
    repeated: Vec<CustomEnum>,
    map: HashMap<String, CustomEnum>,
}

Re-exports§

pub use self::de::DeserializeConfig;

Modules§

de
Configuration deserialization logic.
fallback
Fallback Value sources.
metadata
Configuration metadata.
testing
Testing tools for configurations.
validation
Parameter and config validation and filtering.
value
Enriched JSON object model that allows to associate values with origins.
visit
Visitor pattern for configs.

Macros§

Serde
Constructor of Serde types / instances.
config
Creates Json configuration input based on the provided list of path–value tuples. This is essentially a slightly fancier / more specialized version of json!.

Structs§

ByteSize
A wrapper providing a clear reminder that the wrapped value represents the number of bytes.
ConfigMut
Mutable reference to a specific configuration inside ConfigSchema.
ConfigParser
Parser of configuration input in a ConfigRepository.
ConfigRef
Reference to a specific configuration inside ConfigSchema.
ConfigRepository
Configuration repository containing zero or more configuration sources. Sources are preprocessed and merged according to the provided ConfigSchema.
ConfigSchema
Schema for configuration. Can contain multiple configs bound to different paths.
ConfigSources
Prioritized list of configuration sources. Can be used to push multiple sources at once into a ConfigRepository.
DeserializeConfigError
Marker error for DeserializeConfig operations. The error info os stored in DeserializeContext as ParseErrors.
Environment
Configuration sourced from environment variables.
Flat
Marker for key–value / flat configuration sources (e.g., env variables or command-line args).
Hierarchical
Marker for hierarchical configuration sources (e.g. JSON or YAML files).
Json
JSON-based configuration source.
ParseError
Config parameter deserialization errors.
ParseErrors
Collection of ParseErrors returned from ConfigParser::parse().
Prefixed
Wraps a hierarchical source into a prefix.
SerializerOptions
Configuration serialization options.
SourceInfo
Information about a source returned from ConfigRepository::sources().
Yaml
YAML-based configuration source.

Traits§

ConfigSource
Source of configuration parameters that can be added to a ConfigRepository.
ConfigSourceKind
Kind of a ConfigSource.
DescribeConfig
Describes a configuration (i.e., a group of related parameters).
ExampleConfig
Provides an example for this configuration. The produced config can be used in tests etc.

Type Aliases§

ErrorWithOrigin
Error together with its origin.

Derive Macros§

DescribeConfig
Derives the DescribeConfig trait for a type.
DeserializeConfig
Derives the DeserializeConfig trait for a type.
ExampleConfig
Derives the ExampleConfig trait for a type.