1use super::*;
2use crate::builder::assembly;
3use coil_i18n::TranslationCatalog;
4use coil_template::TemplateDefinition;
5use std::env;
6use std::path::Path;
7use std::path::PathBuf;
8
9#[derive(Default)]
10pub struct Builder {
11 modules: Vec<Box<dyn PlatformModule>>,
12 customer_plugins: Vec<Box<dyn CustomerBackendPlugin>>,
13}
14
15impl Builder {
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 pub fn register_module<M>(mut self, module: M) -> Self
21 where
22 M: PlatformModule + 'static,
23 {
24 self.modules.push(Box::new(module));
25 self
26 }
27
28 pub fn register_customer_plugin<C>(mut self, plugin: C) -> Self
29 where
30 C: CustomerBackendPlugin,
31 {
32 self.customer_plugins.push(Box::new(plugin));
33 self
34 }
35
36 pub fn build_from_paths(
37 self,
38 app_root: impl AsRef<Path>,
39 config_path: impl AsRef<Path>,
40 ) -> Result<RuntimePlan, RuntimeBootstrapError> {
41 let bootstrap = CustomerRootBootstrapInputs::from_paths(app_root, config_path)?;
42 self.build_from_bootstrap_inputs(bootstrap)
43 }
44
45 pub fn run_from_env(self) -> Result<(), RuntimeBootstrapError> {
46 let bind_override = env::var("COIL_BIND").ok();
47 self.build_from_bootstrap_inputs(CustomerRootBootstrapInputs::from_env()?)?
48 .serve_from_env(bind_override)
49 }
50
51 fn build_from_bootstrap_inputs(
52 self,
53 bootstrap: CustomerRootBootstrapInputs,
54 ) -> Result<RuntimePlan, RuntimeBootstrapError> {
55 let mut builder = RuntimeBuilder::new(bootstrap.config, bootstrap.auth_package)
56 .with_template_root(bootstrap.app_root)
57 .with_translation_catalogs(bootstrap.translation_catalogs);
58 for module in self.modules {
59 builder = builder.with_boxed_module(module);
60 }
61 for plugin in self.customer_plugins {
62 builder = builder.with_boxed_customer_plugin(plugin);
63 }
64 builder = builder
65 .resolve_enabled_customer_modules(&bootstrap.enabled_modules)
66 .map_err(RuntimeBootstrapError::Build)?;
67 builder.build().map_err(RuntimeBootstrapError::Build)
68 }
69}
70
71pub struct RuntimeBuilder<P> {
72 config: PlatformConfig,
73 auth_package: P,
74 modules: Vec<Box<dyn PlatformModule>>,
75 customer_plugins: Vec<Box<dyn CustomerBackendPlugin>>,
76 translation_catalogs: Vec<TranslationCatalog>,
77 extensions: Vec<InstalledExtension>,
78 templates: Vec<TemplateDefinition>,
79 template_roots: Vec<PathBuf>,
80 storage_policies: StoragePolicySet,
81 routes: Vec<RouteDefinition>,
82 handlers: Vec<HandlerDefinition>,
83 feature_flags: Vec<FeatureFlag>,
84 maintenance_mode: Option<MaintenanceMode>,
85}
86
87pub(crate) struct RuntimeBuilderParts<P> {
88 pub(crate) config: PlatformConfig,
89 pub(crate) auth_package: P,
90 pub(crate) modules: Vec<Box<dyn PlatformModule>>,
91 pub(crate) customer_plugins: Vec<Box<dyn CustomerBackendPlugin>>,
92 pub(crate) translation_catalogs: Vec<TranslationCatalog>,
93 pub(crate) extensions: Vec<InstalledExtension>,
94 pub(crate) templates: Vec<TemplateDefinition>,
95 pub(crate) template_roots: Vec<PathBuf>,
96 pub(crate) storage_policies: StoragePolicySet,
97 pub(crate) routes: Vec<RouteDefinition>,
98 pub(crate) handlers: Vec<HandlerDefinition>,
99 pub(crate) feature_flags: Vec<FeatureFlag>,
100 pub(crate) maintenance_mode: Option<MaintenanceMode>,
101}
102
103impl<P> RuntimeBuilder<P>
104where
105 P: AuthModelPackage + 'static,
106{
107 pub fn new(config: PlatformConfig, auth_package: P) -> Self {
112 Self {
113 config,
114 auth_package,
115 modules: Vec::new(),
116 customer_plugins: Vec::new(),
117 translation_catalogs: Vec::new(),
118 extensions: Vec::new(),
119 templates: Vec::new(),
120 template_roots: Vec::new(),
121 storage_policies: StoragePolicySet::default(),
122 routes: Vec::new(),
123 handlers: Vec::new(),
124 feature_flags: Vec::new(),
125 maintenance_mode: None,
126 }
127 }
128
129 pub fn for_customer_root(
131 config: PlatformConfig,
132 auth_package: P,
133 ) -> CustomerRootRuntimeBuilder<P> {
134 CustomerRootRuntimeBuilder::new(config, auth_package)
135 }
136
137 pub fn with_module<M>(mut self, module: M) -> Self
138 where
139 M: PlatformModule + 'static,
140 {
141 self.modules.push(Box::new(module));
142 self
143 }
144
145 pub fn register_module<M>(self, module: M) -> Self
146 where
147 M: PlatformModule + 'static,
148 {
149 self.with_module(module)
150 }
151
152 pub fn with_boxed_module(mut self, module: Box<dyn PlatformModule>) -> Self {
153 self.modules.push(module);
154 self
155 }
156
157 pub fn register_customer_plugin<C>(mut self, plugin: C) -> Self
158 where
159 C: CustomerBackendPlugin,
160 {
161 self.customer_plugins.push(Box::new(plugin));
162 self
163 }
164
165 pub fn with_boxed_customer_plugin(mut self, plugin: Box<dyn CustomerBackendPlugin>) -> Self {
166 self.customer_plugins.push(plugin);
167 self
168 }
169
170 pub fn with_translation_catalog(mut self, catalog: TranslationCatalog) -> Self {
171 self.translation_catalogs.push(catalog);
172 self
173 }
174
175 pub fn with_translation_catalogs<I>(mut self, catalogs: I) -> Self
176 where
177 I: IntoIterator<Item = TranslationCatalog>,
178 {
179 self.translation_catalogs.extend(catalogs);
180 self
181 }
182
183 pub fn with_customer_plugin<C>(self, plugin: C) -> Self
184 where
185 C: CustomerBackendPlugin,
186 {
187 self.register_customer_plugin(plugin)
188 }
189
190 pub fn with_installed_extension(mut self, extension: InstalledExtension) -> Self {
191 self.extensions.push(extension);
192 self
193 }
194
195 pub fn with_template(mut self, template: TemplateDefinition) -> Self {
196 self.templates.push(template);
197 self
198 }
199
200 pub fn with_templates<I>(mut self, templates: I) -> Self
201 where
202 I: IntoIterator<Item = TemplateDefinition>,
203 {
204 self.templates.extend(templates);
205 self
206 }
207
208 pub fn with_template_root<A>(mut self, root: A) -> Self
209 where
210 A: Into<PathBuf>,
211 {
212 self.template_roots.push(root.into());
213 self
214 }
215
216 pub fn with_storage_policy_rule(mut self, rule: PathPolicyRule) -> Self {
217 self.storage_policies = self.storage_policies.with_rule(rule);
218 self
219 }
220
221 pub fn with_storage_policies(mut self, policies: StoragePolicySet) -> Self {
222 self.storage_policies = policies;
223 self
224 }
225
226 pub fn with_route(mut self, route: RouteDefinition) -> Self {
227 self.routes.push(route);
228 self
229 }
230
231 pub fn with_handler(mut self, handler: HandlerDefinition) -> Self {
232 self.handlers.push(handler);
233 self
234 }
235
236 pub fn with_feature_flag(mut self, feature_flag: FeatureFlag) -> Self {
237 self.feature_flags.push(feature_flag);
238 self
239 }
240
241 pub fn with_maintenance_mode(mut self, maintenance_mode: MaintenanceMode) -> Self {
242 self.maintenance_mode = Some(maintenance_mode);
243 self
244 }
245
246 pub(crate) fn resolve_enabled_customer_modules(
247 mut self,
248 enabled_modules: &[String],
249 ) -> Result<Self, RuntimeBuildError> {
250 self.modules = crate::builder::customer_root::resolve_enabled_customer_modules(
251 enabled_modules,
252 self.modules,
253 )?;
254 Ok(self)
255 }
256
257 pub fn build(self) -> Result<RuntimePlan, RuntimeBuildError> {
258 assembly::build_runtime_plan(self)
259 }
260
261 pub fn run_from_env(self) -> Result<(), RuntimeBootstrapError> {
262 self.build()?.serve_from_env(env::var("COIL_BIND").ok())
263 }
264
265 pub(crate) fn into_parts(self) -> RuntimeBuilderParts<P> {
266 RuntimeBuilderParts {
267 config: self.config,
268 auth_package: self.auth_package,
269 modules: self.modules,
270 customer_plugins: self.customer_plugins,
271 translation_catalogs: self.translation_catalogs,
272 extensions: self.extensions,
273 templates: self.templates,
274 template_roots: self.template_roots,
275 storage_policies: self.storage_policies,
276 routes: self.routes,
277 handlers: self.handlers,
278 feature_flags: self.feature_flags,
279 maintenance_mode: self.maintenance_mode,
280 }
281 }
282}
283
284impl RuntimeBuilder<coil_auth::LoadedAuthModelPackage> {
285 pub fn for_customer_root_from_env() -> Result<
286 CustomerRootRuntimeBuilder<coil_auth::LoadedAuthModelPackage>,
287 RuntimeBootstrapError,
288 > {
289 CustomerRootRuntimeBuilder::from_env()
290 }
291
292 pub fn for_customer_root_from_paths(
293 app_root: impl AsRef<std::path::Path>,
294 config_path: impl AsRef<std::path::Path>,
295 ) -> Result<
296 CustomerRootRuntimeBuilder<coil_auth::LoadedAuthModelPackage>,
297 RuntimeBootstrapError,
298 > {
299 CustomerRootRuntimeBuilder::from_paths(app_root, config_path)
300 }
301}