pub trait Subcommands: Sized { }Expand description
The Subcommands trait represents one or more subcommands that can be added to a Conf
structure. To use it, put #[derive(Subcommands)] on your enum, and then add a
#[conf(subcommands)] field to your Conf structure whose type is your enum type, or
Option<T> where T is your enum type.
Each variant of the enum corresponds to a subcommand, and must contain a single un-named Conf
structure.
The subcommand name is the name of the enum variant, but you can use attributes to change this name.
A Subcommands enum can then be added as a field to a top-level Conf structure and marked using
the #[conf(subcommands)] attribute.
Hand-written implementations of this trait are not supported.
You should think of this trait as a “non-exhaustive trait” with hidden required items.
conf is free to add, modify, or remove these implementation details without considering it
a semver breaking change, so the only stable way to get an impl is via the derive macro.
§derive Subcommands proc-macro reference
The #[derive(Subcommands)] macro can only be placed on an enum.
When using #[derive(Subcommands)], the result is adjusted by various #[conf(...)] attributes that can be applied.
These are documented here.
The #[conf(...)] attributes conform to Rust’s structured attribute convention.
§Where can conf attributes be used?
The #[conf(...)] attributes can appear on an enum or a variant of the enum.
use conf::Subcommands;
#[derive(Subcommands)]
#[conf(serde)] // This is an enum-level attribute
pub enum MySubcommands {
Run(RunConfig),
// This is a variant-level attribute
#[conf(name = "migrate")]
RunMigrations(MigrateConfig),
// This is also a variant-level attribute
#[conf(name = "validate")]
RunValidation(ValidateConfig),
// This variant has no associated config (zero fields)
#[conf(name = "stat")]
Stat,
}Each enum variant can be one of:
- Unit variant (no fields) - The subcommand takes no additional arguments.
- Single unnamed field - The field must be a
structtype which implementsConf. - Named fields - The fields are defined inline and support the same
#[arg(...)]and#[conf(...)]attributes as struct fields. Works similarly to clap’s named field variants.
§Enum-level attributes
-
example:
#[conf(serde)],#[subcommands(serde)]Enable the serde integration on this enum.
The interaction with
serdeis:- Each subcommand that is not
#[conf(serde(skip))]now has a serialization name as well. - If that key appears in the serde document, and the subcommand appears in the CLI args, then the subcommand variant reads from the corresponding corresponding value in the serde document.
- If the key appears in the serde document, but the subcommand does not appear in the CLI args, then this serde value is simply ignored, and it is not an error.
This allows the previous example to work with a TOML config file structured like this:
[run] run_param = "..." [run_migrations] migrations_param = "..." [run_validation] validation_param = "..."If you invoke
./my_prog run, therunsubcommand will pick up values from the[run]block, and the other sections won’t cause an error even though they are unused. Similarly./my_prog migratewould pick up values from the[run_migrations]block, without errors.You can change the serialization name of a subcommand using
#[conf(serde(rename = "..."))].You may also prefer that two or more subcommands that have a lot of overlap read from the same section of the config file. For this, you can just make the serialization names the same 1.
- Each subcommand that is not
§Variant-level attributes
-
example:
#[conf(name = "migrate")]Set the name of this subcommand, which is used to activate the subcommand and is documented in the help.
If this attribute is not present, the name is the kebab-case of the variant name.
-
alias(string argument, repeating)example:
#[conf(alias = "mig")],#[conf(alias = "mig", alias = "m")]Adds alternative names that can be used to invoke this subcommand on the command line. This is useful for providing shorter or alternative names for frequently used subcommands.
The
aliasattribute can be specified multiple times to add multiple alternative names.Example:
#[derive(Subcommands)] #[conf(serde)] pub enum Command { #[conf(name = "migrate", alias = "mig", alias = "m")] Migrate(MigrateConfig), }In this example, the subcommand can be invoked as
./my_prog migrate,./my_prog mig, or./my_prog m. -
serde(optional additional attributes)example:
#[conf(serde(rename = "foo"))]Configuration specific to the serde integration.
-
example:
#[conf(serde(rename = "foo"))]Similar to
#[serde(rename)], changes the name used in serialization.If this attribute is not present, the serialization name is the lower snake-case of the variant name.
-
alias(string argument, repeating)example:
#[conf(serde(alias = "old_name"))],#[conf(serde(alias = "old_name", alias = "older_name"))]Similar to
#[serde(alias)], adds alternative names that can be used when deserializing from serde documents. This is useful for maintaining backwards compatibility when renaming subcommands in configuration files.Example:
#[derive(Subcommands)] #[conf(serde)] pub enum Command { #[conf(serde(rename = "new_command", alias = "old_command", alias = "legacy_command"))] NewCommand(NewCommandConfig), }In this example, the subcommand can be specified in serde documents as
new_command(the primary name),old_command, orlegacy_command(aliases), and all will deserialize to theNewCommandvariant. -
example:
#[conf(serde(skip))]Similar to
#[serde(skip)], this subcommand won’t read data from the serde value source.
-
Normally, making two fields have the same serialization name won’t work in
serde. Inserdeit is only possible to deserialize a value at most once, so you can’t populate two different fields with the same deserializer content. Also it would likely breakSerialize. In this case, we aren’t serializing anything, and the enum semantics ensure that we will only deserialize this value at most once. ↩
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.