injectable-rs 0.1.0

A compile-time dependency injection framework for Rust using extractor-based DI
Documentation
//! # injectable — Compile-time Dependency Injection for Rust
//!
//! A compile-time dependency injection framework using extractor-based DI,
//! inspired by Axum's typed extraction model. No `TypeId` in the public
//! API, no runtime reflection, no `HashMap<TypeId, Box<dyn Any>>`.
//!
//! # Core Philosophy
//!
//! Dependencies are resolved through **typed extractors**, not dynamic lookup.
//! Provider chains are generated at compile time. Constructor parameters
//! behave like Axum extractors. Dependency traversal is statically encoded
//! into generated provider implementations.
//!
//! # Types You Own vs. Types You Don't
//!
//! ## Types You Own — `#[injectable]`
//!
//! For types in your own crate, use the derive macro:
//!
//! ```rust,ignore
//! use injectable_rs::{Injectable, Inject, Container};
//!
//! #[injectable]
//! #[derive(Default)]
//! pub struct Database { pool_size: usize }
//!
//! #[injectable]
//! #[derive(Default)]
//! pub struct UserService { db: Arc<Database> }
//! ```
//!
//! ## Types You Don't Own — `DynProvider`
//!
//! For types from third-party crates (`reqwest::Client`, `sqlx::SqlitePool`,
//! etc.), you can't add `#[injectable]`. Instead, register a
//! dynamic provider:
//!
//! ```rust,ignore
//! use injectable_rs::{Container, DynProvider};
//!
//! let container = Container::builder()
//!     .register("", DynProvider::new(|| {
//!         Ok(reqwest::Client::new())
//!     }))
//!     .register("", DynProvider::with_ctx(|ctx| async move {
//!         let config = ctx.resolve::<AppConfig>().await?;
//!         Ok(sqlx::SqlitePool::connect(&config.db_url).await?)
//!     }))
//!     .build()
//!     .await?;
//!
//! // Resolve owned types (static path)
//! let service = container.resolve::<UserService>().await?;
//!
//! // Resolve external types (registry path)
//! let client = container.resolve_external::<reqwest::Client>().await?;
//! ```

#![doc(
    html_logo_url = "https://raw.githubusercontent.com/jymchng/injectable/refs/heads/main/assets/injectable-logo-only.png"
)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]

// Re-export runtime types
pub use injectable_rs_runtime::{
    DEFAULT_TOKEN, DynProvider, EmptySingletonStore, Extract, FactoryCtx, HookResult, Inject,
    Injectable, InjectableError, InjectableResult, PostConstruct, PreDestruct, Provider,
    ProviderRegistry, ResolveContext, SingletonStore,
};

// Re-export graph types
pub use injectable_rs_graph::{DependencyGraph, GraphError, GraphNode, ValidationError};

// Re-export proc macros — all surface area is under #[injectable(...)]
pub use injectable_rs_macros::bind;
pub use injectable_rs_macros::container;
pub use injectable_rs_macros::injectable;

// Type-safe scope markers — `#[injectable(scope = Singleton)]` etc.
pub use injectable_rs_runtime::{RequestScoped, Singleton, Transient};

mod container;

pub use container::{Container, ContainerBuilder};

#[cfg(feature = "axum")]
pub mod axum;

/// Commonly used items — `use injectable_rs::prelude::*` covers the full public API.
pub mod prelude {
    pub use crate::{
        Container,
        DynProvider,
        Extract,
        FactoryCtx,
        HookResult,
        Inject,
        // Runtime types
        Injectable,
        InjectableError,
        InjectableResult,
        RequestScoped,
        ResolveContext,
        // Scope markers
        Singleton,
        Transient,
        // Macros — all surface area lives under #[injectable(...)]
        bind,
        container,
        injectable,
    };
    // Arc is used in almost every injectable definition.
    pub use std::sync::Arc;
}