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:
- Parameter info: name (including aliases and renaming), help (extracted from doc comments), type, deserializer for the param etc.
- Nested configurations.
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 aport
var insidehttp
object insideapi
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 theoverrides/
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;
pub use self::error::ParseErrorCategory;
Modules§
- Configuration deserialization logic.
- Fallback
Value
sources. - Configuration metadata.
- Testing tools for configurations.
- Parameter and config validation and filtering.
- Enriched JSON object model that allows to associate values with origins.
- Visitor pattern for configs.
Macros§
- Constructor of
Serde
types / instances.
Structs§
- A wrapper providing a clear reminder that the wrapped value represents the number of bytes.
- Mutable reference to a specific configuration inside
ConfigSchema
. - Parser of configuration input in a
ConfigRepository
. - Reference to a specific configuration inside
ConfigSchema
. - Configuration repository containing zero or more configuration sources. Sources are preprocessed and merged according to the provided
ConfigSchema
. - Schema for configuration. Can contain multiple configs bound to different paths.
- Prioritized list of configuration sources. Can be used to push multiple sources at once into a
ConfigRepository
. - Marker error for
DeserializeConfig
operations. The error info os stored inDeserializeContext
asParseErrors
. - Configuration sourced from environment variables.
- Marker for key–value / flat configuration sources (e.g., env variables or command-line args).
- Marker for hierarchical configuration sources (e.g. JSON or YAML files).
- JSON-based configuration source.
- Config parameter deserialization errors.
- Collection of
ParseError
s returned fromConfigParser::parse()
. - Wraps a hierarchical source into a prefix.
- Configuration serialization options.
- Information about a source returned from
ConfigRepository::sources()
. - YAML-based configuration source.
Traits§
- Source of configuration parameters that can be added to a
ConfigRepository
. - Kind of a
ConfigSource
. - Describes a configuration (i.e., a group of related parameters).
- Provides an example for this configuration. The produced config can be used in tests etc.
Type Aliases§
- Error together with its origin.
Derive Macros§
- Derives the
DescribeConfig
trait for a type. - Derives the
DeserializeConfig
trait for a type. - Derives the
ExampleConfig
trait for a type.