Concepts

Contributions Prototype

Some things are built from many modules: a command-line tree, a set of routes, a list of health checks. Each module contributes a piece; a target assembles them.

Capability or contribution?

CapabilityContribution
UsedWhile the application runsWhile a subsystem is being built
How manyExactly one resolved providerAny number, collected by a target
ExampleClockA CLI command

The target owns the rules

Core and Kernel know only generic shapes: a contribution, a target, and TargetComposition<T, Q> that keeps target and qualifier types intact. Everything that is specific to commands lives in the CLI target:

Generic, in Core/KernelCommand-specific, in the CLI target
Stable contribution and target identitiesCommand names are unique
Contribution and ContributionTarget contractsCommands are ordered alphabetically
Orphan diagnostics with typed target contextAn empty command tree is valid
Target errors returned without interpretationLookup and unknown-command behaviour

So a third-party target can be written against the public extension traits alone. The example's tests include one.

Assembling a command tree

let target = CliCommandTarget::<PublicCommands>::new();
let commands = TargetComposition::<_, PublicCommands>::new(vec![
    (HelloModule::ID, HelloModule::contribution()),
    (GoodbyeModule::ID, GoodbyeModule::contribution()),
])
.build(Some(&target))
.expect("the CLI target accepts these declarations")
.expect("a target produces a command tree");

println!("hello: {}", commands.execute("hello", Some(&clock)).unwrap());
println!("goodbye: {}", commands.execute("goodbye", None).unwrap());

hello reads the Clock capability; goodbye does not need one. Public and admin commands are different Rust types, and a compile-fail doc test proves a public command cannot be given to the admin target.

Orphans

A contribution marked required with no matching target is a structured error, not a silent no-op. A dormant orphan in a process you are not running does not fail the one you are (see Processes).

Cost

Assembling two declarations took a median 86 ns; the finished tree held 96 bytes for two entries and keeps only names and function pointers, not the composition wrappers. Measured on one machine — see Evidence.