use dependency_injector::Container;
use dependency_injector::typed::{Reg, Require, TypedBuilder, TypedContainer};
use dependency_injector::verified::{Service, ServiceModule, ServiceProvider};
use std::sync::Arc;
#[derive(Clone)]
struct Config {
debug: bool,
}
#[derive(Clone)]
struct Database {
url: String,
}
#[derive(Clone)]
struct Cache {
size: usize,
}
#[derive(Clone)]
struct UserService;
impl Require for UserService {
type Dependencies = Reg<Database, Reg<Cache, ()>>;
}
impl Service for Config {
type Dependencies = ();
fn create(_: ()) -> Self {
Config { debug: true }
}
}
impl Service for Database {
type Dependencies = Arc<Config>;
fn create(config: Arc<Config>) -> Self {
Database {
url: if config.debug {
"debug://localhost".into()
} else {
"prod://server".into()
},
}
}
}
#[derive(Clone)]
struct UserRepository {
db: Arc<Database>,
cache: Option<Arc<Cache>>,
}
impl Service for UserRepository {
type Dependencies = (Arc<Database>, Option<Arc<Cache>>);
fn create((db, cache): Self::Dependencies) -> Self {
UserRepository { db, cache }
}
}
struct DataModule;
impl ServiceModule for DataModule {
fn register(container: &Container) {
container.provide::<Config>();
container.provide::<Database>();
}
}
fn assert_dependencies<S: Require<Dependencies = D>, D>() {}
fn assert_matches_registry<S: Require<Dependencies = R>, R>(_: &TypedContainer<R>) {}
fn main() {
println!("=== Dependency Injector Compile-Time Safety Demo ===\n");
println!("Building a TypedContainer...");
let typed = TypedBuilder::new()
.singleton(Config { debug: true })
.singleton(Database {
url: "postgres://localhost:5432/app".into(),
})
.lazy(|| Cache { size: 1024 })
.build();
let db = typed.get::<Database>();
let cache = typed.get::<Cache>();
println!(" db = {}, cache size = {}", db.url, cache.size);
assert!(typed.contains::<Config>());
assert!(typed.try_get::<UserRepository>().is_none());
let child = typed.scope();
child.singleton(Cache { size: 64 });
let small = child.get::<Cache>().expect("override in child scope");
println!(" child scope overrides cache size: {}\n", small.size);
println!("Verifying UserService dependencies at compile time...");
let verified = TypedBuilder::new()
.singleton(Cache { size: 256 })
.singleton(Database {
url: "postgres://replica".into(),
})
.build();
assert_dependencies::<UserService, Reg<Database, Reg<Cache, ()>>>();
assert_matches_registry::<UserService, _>(&verified);
println!(" registry matches UserService::Dependencies\n");
println!("Providing verified services...");
let container = Container::new();
container.provide::<Config>(); container.provide::<Database>();
let registered = container.provide_singleton::<UserRepository>();
println!(" UserRepository registered: {registered}");
let repo = container.get::<UserRepository>().expect("provided above");
println!(
" repo uses {} (optional cache attached: {})",
repo.db.url,
repo.cache.is_some()
);
let empty = Container::new();
assert!(!empty.provide_singleton::<UserRepository>());
println!(" empty container rejects UserRepository: missing Database\n");
println!("Registering DataModule...");
let modular = Container::new();
DataModule::register(&modular);
assert!(modular.contains::<Config>());
assert!(modular.contains::<Database>());
let db = modular.get::<Database>().expect("registered by the module");
println!(" module provided database: {}\n", db.url);
println!("=== Demo Complete ===");
println!("\nCompile-time safety layers:");
println!(" - TypedBuilder tracks registrations in its type parameter");
println!(" - Require declares deps as a type-level Reg list");
println!(" - Service/ServiceProvider auto-resolve declared dependencies");
println!(" - ServiceModule groups registrations behind one call");
}