Skip to main content

nestforge_core/
provider.rs

1use std::marker::PhantomData;
2
3use anyhow::{anyhow, Result};
4
5use crate::{framework_log, Container};
6
7pub struct Provider;
8
9pub struct ValueProvider<T> {
10    value: T,
11}
12
13pub struct FactoryProvider<T, F> {
14    factory: F,
15    _marker: PhantomData<fn() -> T>,
16}
17
18impl Provider {
19    pub fn value<T>(value: T) -> ValueProvider<T>
20    where
21        T: Send + Sync + 'static,
22    {
23        ValueProvider { value }
24    }
25
26    pub fn factory<T, F>(factory: F) -> FactoryProvider<T, F>
27    where
28        T: Send + Sync + 'static,
29        F: FnOnce(&Container) -> Result<T> + Send + 'static,
30    {
31        FactoryProvider {
32            factory,
33            _marker: PhantomData,
34        }
35    }
36}
37
38pub trait RegisterProvider {
39    fn register(self, container: &Container) -> Result<()>;
40}
41
42impl<T> RegisterProvider for ValueProvider<T>
43where
44    T: Send + Sync + 'static,
45{
46    fn register(self, container: &Container) -> Result<()> {
47        framework_log(format!(
48            "Registering service {}.",
49            std::any::type_name::<T>()
50        ));
51        container.register(self.value)?;
52        Ok(())
53    }
54}
55
56impl<T, F> RegisterProvider for FactoryProvider<T, F>
57where
58    T: Send + Sync + 'static,
59    F: FnOnce(&Container) -> Result<T> + Send + 'static,
60{
61    fn register(self, container: &Container) -> Result<()> {
62        framework_log(format!(
63            "Registering service {} (factory).",
64            std::any::type_name::<T>()
65        ));
66        let value = (self.factory)(container).map_err(|err| {
67            anyhow!(
68                "Failed to build provider `{}`: {}",
69                std::any::type_name::<T>(),
70                err
71            )
72        })?;
73        container.register(value)?;
74        Ok(())
75    }
76}
77
78pub fn register_provider<P>(container: &Container, provider: P) -> Result<()>
79where
80    P: RegisterProvider,
81{
82    provider.register(container)
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[derive(Clone)]
90    struct AppConfig {
91        app_name: &'static str,
92    }
93
94    struct AppService {
95        config_name: &'static str,
96    }
97
98    #[test]
99    fn registers_value_provider() {
100        let container = Container::new();
101        let result = register_provider(
102            &container,
103            Provider::value(AppConfig {
104                app_name: "nestforge",
105            }),
106        );
107
108        assert!(result.is_ok(), "value provider registration should succeed");
109        let config = container
110            .resolve::<AppConfig>()
111            .expect("config should be registered");
112        assert_eq!(config.app_name, "nestforge");
113    }
114
115    #[test]
116    fn registers_factory_provider() {
117        let container = Container::new();
118        register_provider(
119            &container,
120            Provider::value(AppConfig {
121                app_name: "nestforge",
122            }),
123        )
124        .expect("seed config");
125
126        let result = register_provider(
127            &container,
128            Provider::factory(|c| {
129                let cfg = c.resolve::<AppConfig>()?;
130                Ok(AppService {
131                    config_name: cfg.app_name,
132                })
133            }),
134        );
135
136        assert!(
137            result.is_ok(),
138            "factory provider registration should succeed"
139        );
140        let service = container
141            .resolve::<AppService>()
142            .expect("service should be registered");
143        assert_eq!(service.config_name, "nestforge");
144    }
145
146    #[test]
147    fn factory_error_includes_type_name() {
148        let container = Container::new();
149        let err = register_provider(
150            &container,
151            Provider::factory::<AppService, _>(|_| Err(anyhow!("boom"))),
152        )
153        .expect_err("factory should fail");
154
155        assert!(err.to_string().contains("AppService"));
156    }
157}