Skip to main content

Crate reinhardt_di

Crate reinhardt_di 

Source
Expand description

§Reinhardt Dependency Injection

FastAPI-inspired dependency injection system for Reinhardt.

§Features

  • Type-safe: Full compile-time type checking
  • Async-first: Built for async/await
  • Scoped: Request-scoped and singleton dependencies
  • Composable: Dependencies can depend on other dependencies
  • Cache: Automatic caching within request scope
  • Circular Dependency Detection: Automatic runtime detection with optimized performance

§Development Tools (dev-tools feature)

When the dev-tools feature is enabled, additional debugging and profiling tools are available:

  • Visualization: Generate dependency graphs in DOT format for Graphviz
  • Profiling: Track dependency resolution performance and identify bottlenecks
  • Advanced Caching: LRU and TTL-based caching strategies

§Generator Support (generator feature) ✅

Generator-based dependency resolution for lazy, streaming dependency injection.

Note: Uses genawaiter crate as a workaround for unstable native async yield. Will be migrated to native syntax when Rust stabilizes async generators.

// let gen = DependencyGenerator::new(|co| async move {
//     let db = resolve_database().await;
//     co.yield_(db).await;
//
//     let cache = resolve_cache().await;
//     co.yield_(cache).await;
// });

§Example

// Define a dependency
// struct Database {
//     pool: DbPool,
// }
//
// #[async_trait]
// impl Injectable for Database {
//     async fn inject(ctx: &InjectionContext) -> Result<Self> {
//         Ok(Database {
//             pool: get_pool().await?,
//         })
//     }
// }
//
// Use in endpoint
// #[endpoint(GET "/users")]
// async fn list_users(
//     db: Depends<Database>,
// ) -> Result<Vec<User>> {
//     db.query("SELECT * FROM users").await
// }

§InjectionContext Construction

InjectionContext is constructed using the builder pattern with a required singleton scope:

use reinhardt_di::{InjectionContext, SingletonScope};
use std::sync::Arc;

// Create singleton scope
let singleton = Arc::new(SingletonScope::new());

// Build injection context with singleton scope
let ctx = InjectionContext::builder(singleton).build();

Optional request and param context can be added:

use reinhardt_di::{InjectionContext, SingletonScope};
use reinhardt_http::Request;
use std::sync::Arc;

let singleton = Arc::new(SingletonScope::new());

// Create a dummy request for demonstration
let request = Request::builder()
    .method(hyper::Method::GET)
    .uri("/")
    .version(hyper::Version::HTTP_11)
    .headers(hyper::HeaderMap::new())
    .body(bytes::Bytes::new())
    .build()
    .unwrap();

let ctx = InjectionContext::builder(singleton)
    .with_request(request)
    .build();

§Circular Dependency Detection

The DI system automatically detects circular dependencies at runtime using an optimized thread-local mechanism:

#[derive(Clone)]
struct ServiceA {
    b: Arc<ServiceB>,
}

#[derive(Clone)]
struct ServiceB {
    a: Arc<ServiceA>,  // Circular dependency!
}

#[async_trait]
impl Injectable for ServiceA {
    async fn inject(ctx: &InjectionContext) -> DiResult<Self> {
        let b = ctx.resolve::<ServiceB>().await?;
        Ok(ServiceA { b })
    }
}

#[async_trait]
impl Injectable for ServiceB {
    async fn inject(ctx: &InjectionContext) -> DiResult<Self> {
        let a = ctx.resolve::<ServiceA>().await?;
        Ok(ServiceB { a })
    }
}

let singleton = Arc::new(SingletonScope::new());
let ctx = InjectionContext::builder(singleton).build();

// This will return Err with DiError::CircularDependency
let result = ctx.resolve::<ServiceA>().await;
assert!(result.is_err());

§Performance Characteristics

  • Cache Hit: < 5% overhead (cycle detection completely skipped)
  • Cache Miss: 10-20% overhead (O(1) detection using HashSet)
  • Deep Chains: Sampling reduces linear cost (checks every 10th at depth 50+)
  • Thread Safety: Thread-local storage eliminates lock contention

§Development Tools Example

// fn visualize_dependencies() {
//     let mut graph = DependencyGraph::new();
//     graph.add_node("Database", "singleton");
//     graph.add_node("UserService", "request");
//     graph.add_dependency("UserService", "Database");
//
//     println!("{}", graph.to_dot());
// }
//
// fn profile_resolution() {
//     let mut profiler = DependencyProfiler::new();
//     profiler.start_resolve("Database");
//     // ... perform resolution ...
//     profiler.end_resolve("Database");
//
//     let report = profiler.generate_report();
//     println!("{}", report.to_string());
// }

Re-exports§

pub use context::InjectionContext;
pub use context::InjectionContextBuilder;
pub use context::RequestContext;
pub use cycle_detection::CycleError;
pub use cycle_detection::ResolutionGuard;
pub use cycle_detection::begin_resolution;
pub use cycle_detection::register_type_name;
pub use cycle_detection::with_cycle_detection_scope;
pub use function_handle::FunctionHandle;
pub use override_registry::OverrideRegistry;
pub use depends::Depends;
pub use depends::DependsBuilder;
pub use injectable::Injectable;
pub use injected::DependencyScope as InjectedScope;
pub use injected::Injected;
pub use injected::InjectionMetadata;
pub use injected::OptionalInjected;
pub use provider::Provider;
pub use provider::ProviderFn;
pub use registry::DependencyRegistration;
pub use registry::DependencyRegistry;
pub use registry::DependencyScope;
pub use registry::FactoryTrait;
pub use registry::global_registry;
pub use scope::RequestScope;
pub use scope::Scope;
pub use scope::SingletonScope;
pub use inventory;

Modules§

context
Injection context for dependency resolution
cycle_detection
Task-local circular dependency detection mechanism
depends
Depends wrapper for dependency injection
function_handle
Function handle for fluent override API
graph
Dependency graph analysis and visualization
injectable
Injectable trait for dependencies
injected
Injected wrapper for dependency injection
override_registry
Override registry for dependency injection
params
Reinhardt Parameter Extraction
provider
Dependency providers
registry
Global dependency registry for FastAPI-style dependency injection
scope
Dependency scopes

Macros§

submit_registration
Helper macro for submitting registrations to inventory

Enums§

DiError

Type Aliases§

DiResult