Derive Macro smart_config::DescribeConfig
source · #[derive(DescribeConfig)]
{
// Attributes available to this derive:
#[config]
}
Expand description
Derives the DescribeConfig
trait for a type.
This macro supports both structs and enums. It is conceptually similar to Deserialize
macro from serde
.
Macro behavior can be configured with #[config(_)]
attributes. Multiple #[config(_)]
attributes
on a single item are supported.
Each field in the struct / each enum variant is considered a configuration param (by default),
or a sub-config (if #[config(nest)]
or #[config(flatten)]
is present for the field).
§Container attributes
§validate
Type: One of the following:
- Expression evaluating to a
Validate
implementation (e.g., aRange
; see theValidate
docs for implementations). An optional human-readable string validation description may be provided delimited by the comma (e.g., to make the description more domain-specific). - Pointer to a function with the
fn(&_) -> Result<(), ErrorWithOrigin>
signature and the validation description separated by a comma. - Pointer to a function with the
fn(&_) -> bool
signature and the validation description separated by a comma. Validation fails if the function returnsfalse
.
See the examples in the validation
module.
Specifies a post-deserialization validation for the config. This is useful to check invariants involving multiple params. Multiple validations are supported by specifying the attribute multiple times.
§tag
Type: string
Specifies the param name holding the enum tag, similar to the corresponding attribute in serde
.
Unlike serde
, this attribute is required for enums; this is to ensure that source merging is well-defined.
§rename_all
Type: string; one of lowercase
, UPPERCASE
, camelCase
, snake_case
, SCREAMING_SNAKE_CASE
,
kebab-case
, SCREAMING-KEBAB-CASE
Renames all variants in an enum config according to the provided transform. Unlike in serde
, this attribute
only works on enum variants. Params / sub-configs are always expected to have snake_case
naming.
Caveats:
rename_all
assumes that original variant names are inPascalCase
(i.e., follow Rust naming conventions).rename_all
requires original variant names to consist of ASCII chars.- Each letter of capitalized acronyms (e.g., “HTTP” in
HTTPServer
) is treated as a separate word. E.g.,rename_all = "snake_case"
will renameHTTPServer
toh_t_t_p_server
. Note that it is recommended to not capitalize acronyms (i.e., useHttpServer
). - No spacing is inserted before numbers or other non-letter chars. E.g.,
rename_all = "snake_case"
will renameStatus500
tostatus500
, not tostatus_500
.
§derive(Default)
Derives Default
according to the default values of params (+ the default variant for enum configs).
To work, all params must have a default value specified.
§Variant attributes
§rename
, alias
Type: string
Have the same meaning as in serde
; i.e. allow to rename / specify additional names for the tag(s)
corresponding to the variant. alias
can be specified multiple times.
§default
If specified, marks the variant as default – one which will be used if the tag param is not set in the input. At most one variant can be marked as default.
§Field attributes
§rename
, alias
Type: string
Have the same meaning as in serde
; i.e. allow to rename / specify additional names for the param or a nested config.
Names are validated in compile time.
In addition to simple names, path aliases are supported as well. A path alias starts with .
and consists of dot-separated segments,
e.g. .experimental.value
or ..value
. The paths are resolved relative to the config prefix. As in Python, more than one dot
at the start of the path signals that the path is relative to the parent(s) of the config.
alias = ".experimental.value"
with config prefixtest
resolves to the absolute pathtest.experimental.value
.alias = "..value"
with config prefixtest.experimental
resolves to the absolute pathtest.value
.
If an alias requires more parents than is present in the config prefix, the alias is not applicable.
(E.g., alias = "...value"
with config prefix test
.)
Path aliases are somewhat difficult to reason about, so avoid using them unless necessary.
§deprecated
Type: string
Similar to alias
, with the difference that the alias is marked as deprecated in the schema docs,
and its usages are logged on the WARN
level.
§default
Type: path to function (optional)
Has the same meaning as in serde
, i.e. allows to specify a constructor of the default value for the param.
Without a value, Default
is used for this purpose. Unlike serde
, the path shouldn’t be quoted.
§default_t
Type: expression with param type
Allows to specify the default typed value for the param. The provided expression doesn’t need to be constant.
§example
Type: expression with field type
Allows to specify the example value for the param. The example value can be specified together with the default
/ default_t
attribute. In this case, the example value can be more “complex” than the default, to better illustrate how the configuration works.
§fallback
Type: constant expression evaluating to &'static dyn
FallbackSource
Allows to provide a fallback source for the param. See the fallback
module docs for the discussion of fallbacks
and intended use cases.
§with
Type: const expression implementing DeserializeParam
Allows changing the param deserializer. See de
module docs for the overview of available deserializers.
For Option
s, with
refers to the internal type deserializer; it will be wrapped into an Optional
automatically.
Note that there is an alternative: implementing WellKnown
for the param type.
§nest
If specified, the field is treated as a nested sub-config rather than a param. Correspondingly, its type must
implement DescribeConfig
, or wrap such a type in an Option
.
§flatten
If specified, the field is treated as a flattened sub-config rather than a param. Unlike nest
, its params
will be added to the containing config instead of a separate object. The sub-config type must implement DescribeConfig
.
§validate
Has same semantics as config validations, but applies to a specific config parameter.
§deserialize_if
Type: same as config validations
Filters an Option
al value. This is useful to coerce semantically invalid values (e.g., empty strings for URLs)
to None
in the case automated null coercion doesn’t apply.
See the validation
module for examples of usage.
§Validations
The following validations are performed by the macro in compile time:
- Param / sub-config names and aliases must be non-empty, consist of lowercase ASCII alphanumeric chars or underscore
and not start with a digit (i.e., follow the
[a-z_][a-z0-9_]*
regex). - Param names / aliases cannot coincide with nested config names.
§Examples
use smart_config::metadata::TimeUnit;
#[derive(DescribeConfig, DeserializeConfig)]
struct TestConfig {
/// Doc comments are parsed as a description.
#[config(default_t = 3)]
int: u32,
#[config(default)] // multiple `config` attrs are supported
#[config(rename = "str", alias = "string")]
renamed: String,
/// Nested sub-config. E.g., the tag will be read from path `nested.version`.
#[config(nest)]
nested: NestedConfig,
/// Flattened sub-config. E.g., `array` param will be read from `array`, not `flat.array`.
#[config(flatten)]
flat: FlattenedConfig,
}
#[derive(DescribeConfig, DeserializeConfig)]
#[config(tag = "version", rename_all = "snake_case", derive(Default))]
enum NestedConfig {
#[config(default)]
V0,
#[config(alias = "latest")]
V1 {
/// Param with a custom deserializer. In this case, it will deserialize
/// a duration from a number with milliseconds unit of measurement.
#[config(default_t = Duration::from_millis(50), with = TimeUnit::Millis)]
latency_ms: Duration,
/// `Vec`s, sets and other containers are supported out of the box.
set: HashSet<NonZeroUsize>,
},
}
#[derive(DescribeConfig, DeserializeConfig)]
struct FlattenedConfig {
#[config(default = FlattenedConfig::default_array)]
array: [f32; 2],
}
impl FlattenedConfig {
const fn default_array() -> [f32; 2] { [1.0, 2.0] }
}