Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Structs & enums

The Config trait can be derived for structs and enums.

#![allow(unused)]
fn main() {
#[derive(Config)]
struct AppConfig {
	pub base: String,
	pub port: usize,
	pub secure: bool,
	pub allowed_hosts: Vec<String>,
}

#[derive(Config)]
enum Host {
	Local,
	#[setting(nested)]
	Remote(HostConfig),
}
}

Enum caveats

Config can only be derived for enums with tuple or unit variants, but not struct/named variants. Why not struct variants? Because with this pattern, the enum acts like a union type. This also allows for Config functionality, like partials, merging, and validation, to be applied to the contents of each variant.

If you’d like to support unit-only enums, you can use the ConfigEnum trait instead.

Attribute fields

The following fields are supported for the #[config] container attribute:

  • allow_unknown_fields - Removes the serde deny_unknown_fields from the partial struct. Defaults to false.
  • context - Sets the struct to be used as the context. Defaults to None.
  • env_prefix - Sets the prefix to use for environment variable mapping. Defaults to None.
  • partial - Forwards attributes to the partial.
#![allow(unused)]
fn main() {
#[derive(Config)]
#[config(allow_unknown_fields, env_prefix = "EXAMPLE_")]
struct ExampleConfig {
	// ...
}
}

And the following for serde compatibility:

  • rename
  • rename_all - Has no default. Field names are used exactly as written unless this is set.
  • rename_all_fields (enum only) - Applies a casing to the fields of every variant.

Serde support

By default the Config macro will apply the following #[serde] to the partial struct. The default and deny_unknown_fields ensure proper parsing and layer merging.

#![allow(unused)]
fn main() {
#[serde(default, deny_unknown_fields)]
}

However, the deny_unknown_fields field can be customized, and we also support the rename and rename_all fields, all via the top-level #[config] attribute.

#![allow(unused)]
fn main() {
#[derive(Config)]
#[config(allow_unknown_fields, rename = "ExampleConfig", rename_all = "snake_case")]
struct Example {
	// ...
}
}

These values can also be applied using #[serde], which is useful if you want to apply them to the main struct as well, and not just the partial struct.

Enum tagging

Serde’s tagging attributes are read straight off the #[serde] attribute and forwarded to the partial, so untagged, tag, content, and expecting work as they normally would.

#![allow(unused)]
fn main() {
#[derive(Config)]
#[serde(untagged)]
enum Host {
	Local,
	#[setting(nested)]
	Remote(HostConfig),
}
}

The derive registers serde as a helper attribute, so this is accepted even when the type derives only Config and not Serialize or Deserialize.

A unit-only enum is always externally tagged, no matter what you set. Marking one untagged would leave its variants deserializable only from null.