Skip to main content

injectable_rs/
lib.rs

1//! # injectable — Compile-time Dependency Injection for Rust
2//!
3//! A compile-time dependency injection framework using extractor-based DI,
4//! inspired by Axum's typed extraction model. No `TypeId` in the public
5//! API, no runtime reflection, no `HashMap<TypeId, Box<dyn Any>>`.
6//!
7//! # Core Philosophy
8//!
9//! Dependencies are resolved through **typed extractors**, not dynamic lookup.
10//! Provider chains are generated at compile time. Constructor parameters
11//! behave like Axum extractors. Dependency traversal is statically encoded
12//! into generated provider implementations.
13//!
14//! # Types You Own vs. Types You Don't
15//!
16//! ## Types You Own — `#[injectable]`
17//!
18//! For types in your own crate, use the derive macro:
19//!
20//! ```rust,ignore
21//! use injectable_rs::{Injectable, Inject, Container};
22//!
23//! #[injectable]
24//! #[derive(Default)]
25//! pub struct Database { pool_size: usize }
26//!
27//! #[injectable]
28//! #[derive(Default)]
29//! pub struct UserService { db: Arc<Database> }
30//! ```
31//!
32//! ## Types You Don't Own — `DynProvider`
33//!
34//! For types from third-party crates (`reqwest::Client`, `sqlx::SqlitePool`,
35//! etc.), you can't add `#[injectable]`. Instead, register a
36//! dynamic provider:
37//!
38//! ```rust,ignore
39//! use injectable_rs::{Container, DynProvider};
40//!
41//! let container = Container::builder()
42//!     .register("", DynProvider::new(|| {
43//!         Ok(reqwest::Client::new())
44//!     }))
45//!     .register("", DynProvider::with_ctx(|ctx| async move {
46//!         let config = ctx.resolve::<AppConfig>().await?;
47//!         Ok(sqlx::SqlitePool::connect(&config.db_url).await?)
48//!     }))
49//!     .build()
50//!     .await?;
51//!
52//! // Resolve owned types (static path)
53//! let service = container.resolve::<UserService>().await?;
54//!
55//! // Resolve external types (registry path)
56//! let client = container.resolve_external::<reqwest::Client>().await?;
57//! ```
58
59#![doc(
60    html_logo_url = "https://raw.githubusercontent.com/jymchng/injectable/refs/heads/main/assets/injectable-logo-only.png"
61)]
62#![forbid(unsafe_code)]
63#![deny(missing_docs)]
64
65// Re-export runtime types
66pub use injectable_rs_runtime::{
67    DEFAULT_TOKEN, DynProvider, EmptySingletonStore, Extract, FactoryCtx, HookResult, Inject,
68    Injectable, InjectableError, InjectableResult, PostConstruct, PreDestruct, Provider,
69    ProviderRegistry, ResolveContext, SingletonStore,
70};
71
72// Re-export graph types
73pub use injectable_rs_graph::{DependencyGraph, GraphError, GraphNode, ValidationError};
74
75// Re-export proc macros — all surface area is under #[injectable(...)]
76pub use injectable_rs_macros::bind;
77pub use injectable_rs_macros::container;
78pub use injectable_rs_macros::injectable;
79
80// Type-safe scope markers — `#[injectable(scope = Singleton)]` etc.
81pub use injectable_rs_runtime::{RequestScoped, Singleton, Transient};
82
83mod container;
84
85pub use container::{Container, ContainerBuilder};
86
87#[cfg(feature = "axum")]
88pub mod axum;
89
90/// Commonly used items — `use injectable_rs::prelude::*` covers the full public API.
91pub mod prelude {
92    pub use crate::{
93        Container,
94        DynProvider,
95        Extract,
96        FactoryCtx,
97        HookResult,
98        Inject,
99        // Runtime types
100        Injectable,
101        InjectableError,
102        InjectableResult,
103        RequestScoped,
104        ResolveContext,
105        // Scope markers
106        Singleton,
107        Transient,
108        // Macros — all surface area lives under #[injectable(...)]
109        bind,
110        container,
111        injectable,
112    };
113    // Arc is used in almost every injectable definition.
114    pub use std::sync::Arc;
115}