Schematic
derive(Config)
TODO
- Load sources from the file system or secure URLs.
- Source layering that merge into a final configuration.
- Extend additional files through an annotated setting.
- Field-level merge strategies with built-in merge functions.
- Aggregated validation with built-in validate functions (provided by garde).
- Environment variable parsing and overrides.
- Beautiful errors powered by the miette crate.
- Supports JSON, TOML, and YAML via serde.
This crate was built specifically for moon, and many of the design decisions are based around that project and its needs. Because of that, this crate is quite opinionated and won't change heavily.
Usage
Define a struct and derive the Config
trait. This struct represents the final state, after all
layers have been merged.
use Config;
Then load, parse, and validate the configuration from one or many sources. A source is either a file path, secure URL, or code block.
use ConfigLoader;
let result = yaml
.code?
.file?
.url?
.load?;
result.config;
result.layers;
Configuration
TODO
Partials
TODO
Nested
TODO
Contexts
TODO
Metadata
TODO
Serde support
By default the Config
macro will apply
#[serde(default, deny_unknown_fields, rename_all = "camelCase")]
to the
partial config. The default
and deny_unknown_fields
cannot be customized, as they
ensure proper parsing and layer merging.
However, the rename_all
field can be customized, and we also support the rename
field, both via
the top-level #[config]
attribute.
The
rename
field will also update the metadata name.
Settings
Settings are the individual fields/members of a configuration struct, and can be annotated with the
optional #[setting]
attribute.
Default values
In schematic, there are 2 forms of default values:
- The first is on the partial config, is defined with the
#[setting]
attribute, and is the first layer of the configuration to be merged. - The second is on the final config itself, and uses
Default
to generate the final value if none was provided. This acts more like a fallback.
This section will talk about the #[setting]
attribute, and the supported default
, default_str
,
and default_fn
attribute fields.
The default
attribute field is used for declaring primitive values, like numbers and booleans. It
can also be used for array and tuple literals, as well as function (mainly for from()
) and macros
calls.
For string literals, the default_str
attribute field should be used instead.
And lastly, if you need more control or need to calculate a complex value, you can use the
default_fn
attribute field, which requires a path to a function to call.
This function receives the context as the first argument (use ()
or generics if you
don't have context), and can return an optional value. If None
is returned, the Default
value
will be used instead.
Environment variables
Settings can also inherit values from environment variables via the env
attribute field. When
using this, variables take the highest precedence, and are merged as the last layer.
We also support parsing environment variables into the required type. For example, the variable may be a comma separated list of values, or a JSON string.
The parse_env
attribute field can be used, which requires a path to a function to handle the
parsing, and receives the variable value as a single argument.
We provide a handful of built-in parsing functions in the
env
module.
When defining a custom parse function, you should return an error with ConfigError::Message
if
parsing fails.
use ConfigError;
Extendable
Configs can extend other configs, generating an accurate layer chain, via the extend
attribute
field. Extended configs can either be a file path (relative from the current config) or a secure
URL. For example:
extends:
- "./another/file.yml"
- "https://domain.com/some/other/file.yml"
When defining extend
, we currently support 3 types of patterns. The first is with a single string,
which only allows a single file to be extended.
The second is with a list of strings, allowing multiple files to be extended. This is the YAML example above.
And lastly, supporting both a string or a list, using our built-in enum.
We suggest making this field optional, so that extending is not required by consumers!
Merge strategies
A common requirement for configuration is to merge multiple sources/layers into a final result. By
default schematic will replace the previous value with the next value if the next value is Some
,
but sometimes you want far more control, like shallow merging or deep merging collections.
This can be achieved with the merge
attribute field, which requires a path to a function to call.
We provide a handful of built-in merge functions in the
merge
module.
When defining a custom merge function, the previous and next values are passed as arguments, and the
function must return an optional merged result. If None
is provided, neither value will be used.
Here's an example of the merge function above.
Validation rules
What kind of configuration crate would this be without built-in validation? As such, we support it as a first-class feature, with built-in validation rules provided by garde.
In schematic, validation does not happen as part of the serde parsing process, and instead happens after the final configuration has been merged. This means we only validate the end result, not partial values (which may be incorrect).
Validation can be applied on a per-setting basis with the validate
attribute field, which requires
a path to a function to call. Furthermore, some functions are factories which can be called to
produce a validator.
We provide a handful of built-in validation functions in the
validate
module.
When defining a custom validate function, the value to check is passed as the first argument, the
current struct as the second, and the context as the third. The ValidateError
type
must be used for failures.
use ValidateError;
If validating an item in a vector or collection, you can specifiy the nested path when erroring. This is extremely useful when building error messages.
use Segment;
with_segments
Serde support
The rename
and skip
attribute fields are currently supported and will apply a #[serde]
attribute to the partial setting.
Features
The following Cargo features are available:
Parsing
json
(default) - Enables JSON.toml
- Enables TOML.yaml
- Enables YAML.
Validation
valid_email
- Enables email validation with theschematic::validate::email
function.valid_url
- Enables URL validation with theschematic::validate::url
andurl_secure
functions.