Skip to main content

nidus_core/app/
mod.rs

1//! Application bootstrap primitives.
2
3use crate::{Container, LifecycleRunner, Module, ModuleDefinition, ModuleGraph, Result};
4
5/// Bootstrapped Nidus application.
6pub struct Application {
7    container: Container,
8    modules: ModuleGraph,
9    lifecycle: LifecycleRunner,
10}
11
12impl Application {
13    /// Creates an application from an already validated container and graph.
14    pub fn new(container: Container, modules: ModuleGraph) -> Self {
15        Self {
16            container,
17            modules,
18            lifecycle: LifecycleRunner::empty(),
19        }
20    }
21
22    /// Creates an application from an already validated container, graph, and lifecycle runner.
23    pub fn with_lifecycle(
24        container: Container,
25        modules: ModuleGraph,
26        lifecycle: LifecycleRunner,
27    ) -> Self {
28        Self {
29            container,
30            modules,
31            lifecycle,
32        }
33    }
34
35    /// Returns the application dependency container.
36    pub fn container(&self) -> &Container {
37        &self.container
38    }
39
40    /// Returns the validated module graph.
41    pub fn modules(&self) -> &ModuleGraph {
42        &self.modules
43    }
44
45    /// Runs every application shutdown hook in reverse registration order.
46    ///
47    /// If hooks fail, cleanup continues and the first shutdown error is
48    /// returned after all hooks have been attempted.
49    pub async fn shutdown(&self) -> Result<()> {
50        self.lifecycle.shutdown().await
51    }
52}
53
54/// Framework bootstrap entrypoint.
55pub struct Nidus;
56
57impl Nidus {
58    /// Bootstraps a Nidus application from a root module definition.
59    ///
60    /// The module graph is validated and the container is populated with the root
61    /// module graph's typed providers. Synchronous providers are registered here;
62    /// providers that require async initialization are not run by this synchronous
63    /// entrypoint and need [`Nidus::bootstrap_with_lifecycle`] (or the facade
64    /// builder) to construct.
65    pub fn bootstrap<M: Module>() -> Result<Application> {
66        let graph = ModuleGraph::from_root::<M>()?;
67        let mut container = Container::new();
68        register_module_providers(&mut container, &graph)?;
69        Ok(Application::new(container, graph))
70    }
71
72    /// Bootstraps a Nidus application from a root module and explicit graph definitions.
73    ///
74    /// Like [`Nidus::bootstrap`], this validates the graph and registers synchronous
75    /// typed providers. Async provider initializers are not run by this synchronous
76    /// entrypoint.
77    pub fn bootstrap_with_modules<M, I>(modules: I) -> Result<Application>
78    where
79        M: Module,
80        I: IntoIterator<Item = ModuleDefinition>,
81    {
82        let graph = ModuleGraph::from_root_and_modules::<M, I>(modules)?;
83        let mut container = Container::new();
84        register_module_providers(&mut container, &graph)?;
85        Ok(Application::new(container, graph))
86    }
87
88    /// Bootstraps a Nidus application and runs startup lifecycle hooks.
89    ///
90    /// Typed providers are registered and async provider initializers run before
91    /// startup hooks, so hooks can resolve fully initialized providers.
92    pub async fn bootstrap_with_lifecycle<M: Module>(
93        lifecycle: LifecycleRunner,
94    ) -> Result<Application> {
95        let graph = ModuleGraph::from_root::<M>()?;
96        let mut container = Container::new();
97        register_module_providers(&mut container, &graph)?;
98        initialize_module_providers(&mut container, &graph).await?;
99        lifecycle.startup().await?;
100        Ok(Application::with_lifecycle(container, graph, lifecycle))
101    }
102
103    /// Bootstraps a Nidus application from an explicit module graph and runs startup hooks.
104    ///
105    /// Typed providers are registered and async provider initializers run before
106    /// startup hooks, so hooks can resolve fully initialized providers.
107    pub async fn bootstrap_with_modules_and_lifecycle<M, I>(
108        modules: I,
109        lifecycle: LifecycleRunner,
110    ) -> Result<Application>
111    where
112        M: Module,
113        I: IntoIterator<Item = ModuleDefinition>,
114    {
115        let graph = ModuleGraph::from_root_and_modules::<M, I>(modules)?;
116        let mut container = Container::new();
117        register_module_providers(&mut container, &graph)?;
118        initialize_module_providers(&mut container, &graph).await?;
119        lifecycle.startup().await?;
120        Ok(Application::with_lifecycle(container, graph, lifecycle))
121    }
122}
123
124fn register_module_providers(container: &mut Container, graph: &ModuleGraph) -> Result<()> {
125    for module in graph.modules() {
126        for registrar in module.provider_registrars() {
127            registrar(container)?;
128        }
129    }
130    Ok(())
131}
132
133async fn initialize_module_providers(container: &mut Container, graph: &ModuleGraph) -> Result<()> {
134    for module in graph.modules() {
135        for initializer in module.async_initializers() {
136            initializer(container).await?;
137        }
138    }
139    Ok(())
140}