Skip to main content

rivet_core/
builder.rs

1use std::sync::Arc;
2
3use rivet_foundation::{defaults, Config, Container, ServiceProvider};
4
5use crate::application::Application;
6use crate::error::{BuildError, RivetError};
7use crate::module::RivetModule;
8use crate::state::Build;
9
10/// Compile-time provider constructor used by static provider registries.
11pub type ProviderFactory = fn() -> Box<dyn ServiceProvider>;
12
13/// Builds an [`Application`] from rivet modules and foundation backends.
14///
15/// Build order is deterministic:
16/// 1. configure modules
17/// 2. collect/register/boot providers
18/// 3. collect routes
19/// 4. collect middleware
20pub struct Builder {
21    modules: Vec<Box<dyn RivetModule>>,
22    provider_factories: Vec<ProviderFactory>,
23    container: Arc<dyn Container>,
24    config: Box<dyn Config>,
25}
26
27impl Builder {
28    /// Create a new builder using default in-memory foundation backends.
29    pub fn with_defaults() -> Self {
30        let (container, config) = defaults();
31        Self::new(container, config)
32    }
33
34    /// Create a new builder with a foundation container and config backend.
35    pub fn new(container: Arc<dyn Container>, config: Box<dyn Config>) -> Self {
36        Self {
37            modules: Vec::new(),
38            provider_factories: Vec::new(),
39            container,
40            config,
41        }
42    }
43
44    /// Register a module in build order.
45    pub fn with_module(mut self, module: Box<dyn RivetModule>) -> Self {
46        self.modules.push(module);
47        self
48    }
49
50    /// Register one compile-time provider constructor.
51    pub fn with_provider(mut self, provider: ProviderFactory) -> Self {
52        self.provider_factories.push(provider);
53        self
54    }
55
56    /// Register a static provider constructor list.
57    pub fn with_providers(mut self, providers: &[ProviderFactory]) -> Self {
58        self.provider_factories.extend(providers.iter().copied());
59        self
60    }
61
62    /// Number of modules currently queued for build.
63    pub fn module_count(&self) -> usize {
64        self.modules.len()
65    }
66
67    /// Build a runtime [`Application`] by executing all build stages.
68    #[tracing::instrument(skip(self), level = "debug")]
69    pub fn build(self) -> Result<Application, RivetError> {
70        let mut state = Build::new(self.container, self.config);
71        tracing::debug!(
72            module_count = self.modules.len(),
73            "starting rivet-core build"
74        );
75
76        Self::run_config_stage(&self.modules, &mut state)?;
77        Self::run_provider_stage(&self.modules, &self.provider_factories, &mut state)?;
78        Self::run_route_and_middleware_stage(&self.modules, &mut state)?;
79
80        Ok(Application::from_state(state.into_runtime()))
81    }
82
83    fn run_config_stage(
84        modules: &[Box<dyn RivetModule>],
85        state: &mut Build,
86    ) -> Result<(), RivetError> {
87        tracing::debug!("starting config stage");
88
89        for module in modules {
90            tracing::debug!(module = module.name(), "configuring module");
91            module
92                .configure(state.config_mut())
93                .map_err(|err| BuildError::Configure {
94                    module: module.name(),
95                    message: err.to_string(),
96                })?;
97        }
98
99        tracing::debug!("finished config stage");
100        Ok(())
101    }
102
103    fn run_provider_stage(
104        modules: &[Box<dyn RivetModule>],
105        provider_factories: &[ProviderFactory],
106        state: &mut Build,
107    ) -> Result<(), RivetError> {
108        tracing::debug!("starting provider stage");
109
110        for module in modules {
111            tracing::debug!(module = module.name(), "collecting module providers");
112            for provider in module.providers() {
113                state.push_provider(provider);
114            }
115        }
116
117        for factory in provider_factories {
118            state.push_provider(factory());
119        }
120
121        for provider in state.providers() {
122            tracing::debug!(provider = provider.name(), "registering provider");
123            provider
124                .register(state.container())
125                .map_err(|err| BuildError::ProviderRegister {
126                    provider: provider.name().to_string(),
127                    source: err,
128                })?;
129        }
130
131        for provider in state.providers() {
132            tracing::debug!(provider = provider.name(), "booting provider");
133            provider
134                .boot(state.container())
135                .map_err(|err| BuildError::ProviderBoot {
136                    provider: provider.name().to_string(),
137                    source: err,
138                })?;
139        }
140
141        tracing::debug!("finished provider stage");
142        Ok(())
143    }
144
145    fn run_route_and_middleware_stage(
146        modules: &[Box<dyn RivetModule>],
147        state: &mut Build,
148    ) -> Result<(), RivetError> {
149        tracing::debug!("starting routes+middleware stage");
150
151        for module in modules {
152            tracing::debug!(module = module.name(), "collecting module routes");
153            module
154                .routes(state.routes_mut())
155                .map_err(|err| BuildError::Routes {
156                    module: module.name(),
157                    message: err.to_string(),
158                })?;
159        }
160
161        for module in modules {
162            tracing::debug!(module = module.name(), "collecting module middleware");
163            state.extend_middleware(module.middleware());
164        }
165
166        tracing::debug!("finished routes+middleware stage");
167        Ok(())
168    }
169}