stano-di
Lightweight dependency injection container with lazy singleton resolution, cycle detection, and environment variable loading. Pairs with stano-di-macros for automatic boilerplate generation.
Install
[]
= { = "../stano-di" }
API
Container
Container— service locator registry (TypeId-keyed internal storage).new() -> Self— create an empty container.register<T: Send+Sync+'static>(&mut self, factory: fn(&Container) -> Arc<T>)— register a concrete type with its factory. Lazily resolves on first access viaOnceLock.register_trait<T: DynComponent + ?Sized>(&mut self, factory: fn(&Container) -> Arc<T>) where Arc<T>: Send+Sync— register a trait object (e.g.,Arc<dyn MyTrait>).register_instance<T: Send+Sync+'static>(&mut self, instance: Arc<T>)— register a pre-built instance (no factory needed).register_component<T: Component>(&mut self)— register a type that implements theComponenttrait (generated by#[service]).get<T: Send+Sync+Clone+'static>(&self) -> Arc<T>— retrieve a registered type; panics if not found.try_get<T: Send+Sync+Clone+'static>(&self) -> Result<Arc<T>, ContainerError>— safe retrieval.get_trait<T: DynComponent + ?Sized>(&self) -> Arc<T> where Arc<T>: Clone— retrieve a trait object; panics if not found.try_get_trait<T: DynComponent + ?Sized>(&self) -> Result<Arc<T>, ContainerError> where Arc<T>: Clone— safe trait retrieval.has<T: 'static>(&self) -> bool— check if a concrete type is registered.has_trait<T: DynComponent + ?Sized>(&self) -> bool— check if a trait is registered.validate(&self) -> Result<(), Vec<ContainerError>>— eagerly resolve all singletons and detect cyclic dependencies. Call after all registrations and before serving traffic.dependency_graph(&self) -> DependencyGraph— returns a displayable graph of registered types and their dependencies.
Application Context
ApplicationContext— facade overContainerthat also carries anArc<dyn Environment>.- Same registration and retrieval methods as
Container. environment(&self) -> &Arc<dyn Environment>— access the environment.register_all(&mut self)— auto-register all#[service]-annotated structs discovered via theinventorycrate.container_mut(&mut self) -> &mut Container— direct access to the inner container if needed.
- Same registration and retrieval methods as
Environment
-
Environmenttrait — loads config values by key.fn get(&self, key: &str) -> Option<String>— retrieve an environment variable or config value.
-
OsEnvironment— loads from actual OS environment variables (and.envfiles viadotenvy).fn new() -> Self— creates and auto-loads.envif present.
Error Type
ContainerError:NotRegistered(&'static str)— type was not registered.DowncastFailed(&'static str)— failed to downcast a trait object.FactoryPanic(&'static str)— factory function panicked during validation.CyclicDependency(Vec<&'static str>)— cyclic dependency detected (e.g., A → B → A).
Traits (Generated by Macros)
-
Component— contract for types that can be auto-registered. Generated by#[service].fn component_type_name() -> &'static str— name of this type.fn dependency_ids() -> Vec<TypeId>— IDs of types this type depends on.fn build(container: &Container) -> Arc<Self>— construct an instance.fn register(container: &mut Container)— register the factory.
-
Injectable— trait for types that can be retrieved from container. Generated by#[component]on traits.fn get_from(container: &Container) -> Arc<Self>— retrieve from container.
-
DynComponent— marker trait for trait objects. Generated by#[component].
Usage Example
use ;
use Arc;
// Define a trait (mark with #[component]).
// Implement it (mark with #[service(dyn MyService)]).
;
// Manual registration (without the #[service] macro, for this example).
// register_trait requires the trait to implement DynComponent (via #[component]).
let mut container = new;
container.;
// Or via ApplicationContext with environment:
use OsEnvironment;
let env = new;
let mut ctx = new;
ctx.register_all; // auto-register all #[service] structs
// Validate (detects cycles, ensures all factories work).
ctx.validate?;
// Retrieve.
let svc: = ctx.get_trait;
println!; // "work done"
Notes
- Lazy resolution — singletons are resolved on first access, not at registration time.
- Cycle detection —
validate()detects circular dependencies (A → B → C → A) before they cause infinite loops at runtime. #[service]and#[component]generation — seestano-di-macrosfor the macro syntax. These generateComponenttrait impls and auto-register viainventory::submit!.- Thread-safe — all types must be
Send + Syncfor use in async contexts; factory functions run synchronously and must not block. - No feature flags — all APIs available.