Modules Prototype
A module is the unit you add to an application. It says who it is, what it needs and what it offers, using ordinary trait implementations.
Identity
Every module has a stable, human-readable identity. Diagnostics, inspection output and explicit provider selection all refer to it.
use rustclamp_core::{Module, ModuleId};
pub struct GreeterModule;
impl Module for GreeterModule {
const ID: ModuleId = ModuleId::new("example.module.greeter");
}
Requires and Provides
Relationships are additive: each one is a separate trait impl, so a module grows by adding declarations, not by editing a central list (ADR 0004).
pub struct FixedClockModule(pub SystemTime);
impl Module for FixedClockModule {
const ID: ModuleId = ModuleId::new("example.module.clock");
}
impl Clock for FixedClockModule {
fn now(&self) -> SystemTime {
self.0
}
}
impl Provides<ClockCapability> for FixedClockModule {
fn provided_value(&self) -> &(dyn Clock + 'static) {
self
}
}
impl Requires<ClockCapability> for GreeterModule {}
Requires carries no body: the declaration is the point. Kernel reads it to find a provider, reports it in diagnostics, and uses it to order startup.
Resolution
Kernel adapts the declarations into typed provisions and requirements and resolves them:
let provisions = [Provision::<ClockCapability>::from_module(&clock)];
let requirement = CapabilityRequirement::<ClockCapability>::from_module::<GreeterModule>();
let clock = Resolver::resolve::<ClockCapability>(
requirement.required_by(),
&provisions,
requirement.selected_provider(),
)?;
| Situation | Result |
|---|---|
| One provider | Resolves directly. |
| No provider | Missing-provider error naming the module and the capability. |
| Two providers | Ambiguity error with the candidates sorted — the same in either registration order. |
| Explicit selection | The selected provider wins; selecting an unavailable one lists what is available. |
| Cycle | Reported as a closed module path; validated iteratively up to 2,048 modules without stack growth. |
What a module does not do
- It does not register itself in a global. There is no registry,
TypeIdmap orAnylookup. - It does not need
Send,Sync,Arcor'static. Provisions borrow their provider; a non-thread-safeRc<Cell<_>>clock resolves fine on the calling thread. - It does not depend on the facade. A module built only against Core and Kernel compiles and tests on its own.
In the design, not the code yet
Modules will also participate in lifecycle contracts (initialize, start, stop) and declare which resources they own. The lifecycle prototype explores the first; resource ownership is not implemented. Planned