Skip to main content

nestforge_core/
injectable.rs

1use anyhow::Result;
2
3use crate::{framework_log_event, Container};
4
5pub trait IntoInjectableResult<T> {
6    fn into_injectable_result(self) -> Result<T>;
7}
8
9impl<T> IntoInjectableResult<T> for T {
10    fn into_injectable_result(self) -> Result<T> {
11        Ok(self)
12    }
13}
14
15impl<T> IntoInjectableResult<T> for Result<T> {
16    fn into_injectable_result(self) -> Result<T> {
17        self
18    }
19}
20
21/**
22 * `Injectable` is the core trait of NestForge's dependency injection system.
23 *
24 * The `#[injectable]` macro automatically implements this trait for the decorated struct.
25 * It tells NestForge how to create an instance of a service and register it within
26 * the global `Container`.
27 *
28 * Manual implementation is possible for complex registration logic that the macro
29 * does not yet support.
30 *
31 * ### Manual Implementation Example
32 * ```rust
33 * use nestforge::{Injectable, Container};
34 *
35 * struct MyService;
36 *
37 * impl Injectable for MyService {
38 *     fn register(container: &Container) -> anyhow::Result<()> {
39 *         // Registration logic goes here
40 *         container.register(MyService)?;
41 *         Ok(())
42 *     }
43 * }
44 * ```
45 */
46pub trait Injectable: Send + Sync + 'static {
47    /// Registers the provider into the provided `Container`.
48    /// This is usually called during the module initialization phase.
49    fn register(container: &Container) -> anyhow::Result<()>;
50}
51
52pub fn register_injectable<T>(container: &Container) -> Result<()>
53where
54    T: Injectable,
55{
56    framework_log_event(
57        "injectable_register",
58        &[("type", std::any::type_name::<T>().to_string())],
59    );
60    T::register(container)
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[derive(Default)]
68    struct DefaultService;
69
70    impl Injectable for DefaultService {
71        fn register(container: &Container) -> Result<()> {
72            container.register(Self)?;
73            Ok(())
74        }
75    }
76
77    struct FactoryService(&'static str);
78
79    impl Injectable for FactoryService {
80        fn register(container: &Container) -> Result<()> {
81            let value: FactoryService =
82                Result::<FactoryService>::Ok(FactoryService("ready")).into_injectable_result()?;
83            container.register(value)?;
84            Ok(())
85        }
86    }
87
88    #[test]
89    fn register_injectable_stores_service_in_container() {
90        let container = Container::new();
91
92        register_injectable::<DefaultService>(&container).expect("injectable should register");
93
94        assert!(container.resolve::<DefaultService>().is_ok());
95    }
96
97    #[test]
98    fn into_injectable_result_accepts_plain_values() {
99        let value = FactoryService("plain")
100            .into_injectable_result()
101            .expect("plain value should convert");
102
103        assert_eq!(value.0, "plain");
104    }
105
106    #[test]
107    fn into_injectable_result_accepts_anyhow_results() {
108        let container = Container::new();
109
110        register_injectable::<FactoryService>(&container).expect("factory should register");
111
112        let service = container
113            .resolve::<FactoryService>()
114            .expect("factory service should resolve");
115        assert_eq!(service.0, "ready");
116    }
117}