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 application shutdown hooks.
46    pub async fn shutdown(&self) -> Result<()> {
47        self.lifecycle.shutdown().await
48    }
49}
50
51/// Framework bootstrap entrypoint.
52pub struct Nidus;
53
54impl Nidus {
55    /// Bootstraps a Nidus application from a root module definition.
56    ///
57    /// The module graph is validated and the container is populated with the root
58    /// module graph's typed providers. Synchronous providers are registered here;
59    /// providers that require async initialization are not run by this synchronous
60    /// entrypoint and need [`Nidus::bootstrap_with_lifecycle`] (or the facade
61    /// builder) to construct.
62    pub fn bootstrap<M: Module>() -> Result<Application> {
63        let graph = ModuleGraph::from_root::<M>()?;
64        let mut container = Container::new();
65        register_module_providers(&mut container, &graph)?;
66        Ok(Application::new(container, graph))
67    }
68
69    /// Bootstraps a Nidus application from a root module and explicit graph definitions.
70    ///
71    /// Like [`Nidus::bootstrap`], this validates the graph and registers synchronous
72    /// typed providers. Async provider initializers are not run by this synchronous
73    /// entrypoint.
74    pub fn bootstrap_with_modules<M, I>(modules: I) -> Result<Application>
75    where
76        M: Module,
77        I: IntoIterator<Item = ModuleDefinition>,
78    {
79        let graph = ModuleGraph::from_root_and_modules::<M, I>(modules)?;
80        let mut container = Container::new();
81        register_module_providers(&mut container, &graph)?;
82        Ok(Application::new(container, graph))
83    }
84
85    /// Bootstraps a Nidus application and runs startup lifecycle hooks.
86    ///
87    /// Typed providers are registered and async provider initializers run before
88    /// startup hooks, so hooks can resolve fully initialized providers.
89    pub async fn bootstrap_with_lifecycle<M: Module>(
90        lifecycle: LifecycleRunner,
91    ) -> Result<Application> {
92        let graph = ModuleGraph::from_root::<M>()?;
93        let mut container = Container::new();
94        register_module_providers(&mut container, &graph)?;
95        initialize_module_providers(&mut container, &graph).await?;
96        lifecycle.startup().await?;
97        Ok(Application::with_lifecycle(container, graph, lifecycle))
98    }
99
100    /// Bootstraps a Nidus application from an explicit module graph and runs startup hooks.
101    ///
102    /// Typed providers are registered and async provider initializers run before
103    /// startup hooks, so hooks can resolve fully initialized providers.
104    pub async fn bootstrap_with_modules_and_lifecycle<M, I>(
105        modules: I,
106        lifecycle: LifecycleRunner,
107    ) -> Result<Application>
108    where
109        M: Module,
110        I: IntoIterator<Item = ModuleDefinition>,
111    {
112        let graph = ModuleGraph::from_root_and_modules::<M, I>(modules)?;
113        let mut container = Container::new();
114        register_module_providers(&mut container, &graph)?;
115        initialize_module_providers(&mut container, &graph).await?;
116        lifecycle.startup().await?;
117        Ok(Application::with_lifecycle(container, graph, lifecycle))
118    }
119}
120
121fn register_module_providers(container: &mut Container, graph: &ModuleGraph) -> Result<()> {
122    for module in graph.modules() {
123        for registrar in module.provider_registrars() {
124            registrar(container)?;
125        }
126    }
127    Ok(())
128}
129
130async fn initialize_module_providers(container: &mut Container, graph: &ModuleGraph) -> Result<()> {
131    for module in graph.modules() {
132        for initializer in module.async_initializers() {
133            initializer(container).await?;
134        }
135    }
136    Ok(())
137}