Skip to main content

injectable_rs_runtime/
lifecycle.rs

1//! Lifecycle hook traits for `post_construct` and `pre_destruct`.
2//!
3//! These traits are automatically implemented by the `#[derive(Injectable)]`
4//! macro when `#[injectable(post_construct)]` or `#[injectable(pre_destruct)]` annotations are
5//! present on methods.
6//!
7//! # Error Handling
8//!
9//! Both hooks return `Result<(), Box<dyn std::error::Error + Send + Sync>>`,
10//! allowing errors to be propagated:
11//!
12//! - **`post_construct`**: If a hook fails, the error is wrapped in
13//!   [`InjectableError::LifecycleHookFailed`](crate::InjectableError::LifecycleHookFailed)
14//!   and the entire resolution fails.
15//!
16//! - **`pre_destruct`**: If a hook fails, the error is collected. All
17//!   remaining destructors still run (best-effort cleanup). The accumulated
18//!   errors are returned from [`Container::shutdown`](crate::Container::shutdown).
19//!
20//! Hooks that cannot fail may return `Ok(())` — the macro generates code
21//! that adapts both `-> ()` and `-> Result<...>` methods automatically.
22
23/// A specialized result type for lifecycle hooks.
24///
25/// Uses `Box<dyn Error + Send + Sync>` so that hooks can return any
26/// error type without being constrained to a specific error enum.
27pub type HookResult = Result<(), Box<dyn std::error::Error + Send + Sync>>;
28
29/// Trait for post-construction lifecycle hooks.
30///
31/// When a type has a method annotated with `#[injectable(post_construct)]`, the
32/// derive macro generates an implementation of this trait that calls
33/// the annotated method.
34///
35/// # Execution Order
36///
37/// Post-construct hooks run **after** the constructor returns but
38/// **before** the value is returned from the provider. This ensures
39/// the instance is fully initialized before any consumer receives it.
40///
41/// # Error Handling
42///
43/// If a `post_construct` hook returns an error, the entire resolution
44/// fails with `InjectableError::LifecycleHookFailed`. The instance
45/// is discarded — it will not be available to consumers.
46///
47/// # Use Cases
48///
49/// - Database connection establishment
50/// - Cache warming
51/// - Spawning background workers
52/// - Registering with external services
53///
54/// # Example
55///
56/// ```rust,ignore
57/// #[derive(Injectable)]
58/// pub struct Database {
59///     pool_size: usize,
60/// }
61///
62/// impl Database {
63///     #[injectable(ctor)]
64///     pub async fn new() -> Self { Self { pool_size: 10 } }
65///
66///     #[injectable(post_construct)]
67///     async fn connect(&self) -> Result<(), std::io::Error> {
68///         self.establish_connection().await?;
69///         Ok(())
70///     }
71/// }
72/// ```
73///
74/// Hooks that cannot fail may return `()`:
75///
76/// ```rust,ignore
77/// #[injectable(post_construct)]
78/// fn log_startup(&self) {
79///     println!("Service started");
80/// }
81/// ```
82#[async_trait::async_trait]
83pub trait PostConstruct: Send + Sync {
84    /// Run the post-construction hook.
85    ///
86    /// Return `Ok(())` on success, or an error to fail the resolution.
87    async fn post_construct(&self) -> HookResult;
88}
89
90/// Trait for pre-destruction lifecycle hooks.
91///
92/// When a type has a method annotated with `#[injectable(pre_destruct)]`, the
93/// derive macro generates an implementation of this trait that calls
94/// the annotated method.
95///
96/// # Execution Order
97///
98/// Pre-destruct hooks run in **reverse topological order** during
99/// container shutdown. Dependencies are destroyed before the types
100/// that depend on them.
101///
102/// # Error Handling
103///
104/// If a `pre_destruct` hook returns an error, it is collected. All
105/// remaining destructors still run (best-effort cleanup). After all
106/// destructors have been called, the accumulated errors are returned
107/// from `Container::shutdown()`.
108///
109/// # Use Cases
110///
111/// - Graceful database disconnection
112/// - Flushing buffers
113/// - Stopping background workers
114/// - Releasing external resources
115///
116/// # Example
117///
118/// ```rust,ignore
119/// impl Database {
120///     #[injectable(pre_destruct)]
121///     async fn shutdown(&self) -> Result<(), std::io::Error> {
122///         self.close_connections().await?;
123///         Ok(())
124///     }
125/// }
126/// ```
127#[async_trait::async_trait]
128pub trait PreDestruct: Send + Sync {
129    /// Run the pre-destruction hook.
130    ///
131    /// Return `Ok(())` on success, or an error to report cleanup failures.
132    /// All destructors run even if some fail (best-effort cleanup).
133    async fn pre_destruct(&self) -> HookResult;
134}