Skip to main content

a3s_boot/app/
factory.rs

1use super::{BootApplication, BootApplicationBuilder};
2use crate::{BoxFuture, HttpAdapter, MessageTransport, Module, ModuleRef, Result};
3use std::net::SocketAddr;
4use std::sync::Arc;
5
6/// NestFactory-style entrypoint for building managed Boot applications.
7pub struct BootFactory;
8
9impl BootFactory {
10    pub fn create<M>(module: M) -> Result<BootApplicationHandle>
11    where
12        M: Module,
13    {
14        Self::create_with_builder(BootApplication::builder().import(module))
15    }
16
17    pub fn create_arc(module: Arc<dyn Module>) -> Result<BootApplicationHandle> {
18        Self::create_with_builder(BootApplication::builder().import_arc(module))
19    }
20
21    pub fn create_with_builder(builder: BootApplicationBuilder) -> Result<BootApplicationHandle> {
22        Ok(BootApplicationHandle::from_app(builder.build()?))
23    }
24
25    pub async fn create_async<M>(module: M) -> Result<BootApplicationHandle>
26    where
27        M: Module,
28    {
29        Self::create_with_builder_async(BootApplication::builder().import(module)).await
30    }
31
32    pub async fn create_arc_async(module: Arc<dyn Module>) -> Result<BootApplicationHandle> {
33        Self::create_with_builder_async(BootApplication::builder().import_arc(module)).await
34    }
35
36    pub async fn create_with_builder_async(
37        builder: BootApplicationBuilder,
38    ) -> Result<BootApplicationHandle> {
39        Ok(BootApplicationHandle::from_app(
40            builder.build_async().await?,
41        ))
42    }
43
44    pub fn create_application_context<M>(module: M) -> Result<BootApplicationContext>
45    where
46        M: Module,
47    {
48        Self::create_application_context_with_builder(BootApplication::builder().import(module))
49    }
50
51    pub fn create_application_context_arc(
52        module: Arc<dyn Module>,
53    ) -> Result<BootApplicationContext> {
54        Self::create_application_context_with_builder(BootApplication::builder().import_arc(module))
55    }
56
57    pub fn create_application_context_with_builder(
58        builder: BootApplicationBuilder,
59    ) -> Result<BootApplicationContext> {
60        Ok(BootApplicationContext {
61            handle: Self::create_with_builder(builder)?,
62        })
63    }
64
65    pub async fn create_application_context_async<M>(module: M) -> Result<BootApplicationContext>
66    where
67        M: Module,
68    {
69        Self::create_application_context_with_builder_async(
70            BootApplication::builder().import(module),
71        )
72        .await
73    }
74
75    pub async fn create_application_context_arc_async(
76        module: Arc<dyn Module>,
77    ) -> Result<BootApplicationContext> {
78        Self::create_application_context_with_builder_async(
79            BootApplication::builder().import_arc(module),
80        )
81        .await
82    }
83
84    pub async fn create_application_context_with_builder_async(
85        builder: BootApplicationBuilder,
86    ) -> Result<BootApplicationContext> {
87        Ok(BootApplicationContext {
88            handle: Self::create_with_builder_async(builder).await?,
89        })
90    }
91
92    pub fn create_microservice<M, T>(module: M, transport: T) -> Result<BootMicroservice<T>>
93    where
94        M: Module,
95        T: MessageTransport,
96    {
97        Self::create_microservice_with_builder(BootApplication::builder().import(module), transport)
98    }
99
100    pub fn create_microservice_with_builder<T>(
101        builder: BootApplicationBuilder,
102        transport: T,
103    ) -> Result<BootMicroservice<T>>
104    where
105        T: MessageTransport,
106    {
107        Ok(BootMicroservice::new(builder.build()?, transport))
108    }
109
110    pub async fn create_microservice_async<M, T>(
111        module: M,
112        transport: T,
113    ) -> Result<BootMicroservice<T>>
114    where
115        M: Module,
116        T: MessageTransport,
117    {
118        Self::create_microservice_with_builder_async(
119            BootApplication::builder().import(module),
120            transport,
121        )
122        .await
123    }
124
125    pub async fn create_microservice_with_builder_async<T>(
126        builder: BootApplicationBuilder,
127        transport: T,
128    ) -> Result<BootMicroservice<T>>
129    where
130        T: MessageTransport,
131    {
132        Ok(BootMicroservice::new(
133            builder.build_async().await?,
134            transport,
135        ))
136    }
137}
138
139/// Managed application with idempotent startup and shutdown.
140pub struct BootApplicationHandle {
141    app: BootApplication,
142    initialized: bool,
143    microservices: Vec<Box<dyn ConnectedMicroservice>>,
144}
145
146impl BootApplicationHandle {
147    pub fn from_app(app: BootApplication) -> Self {
148        Self {
149            app,
150            initialized: false,
151            microservices: Vec::new(),
152        }
153    }
154
155    pub fn app(&self) -> &BootApplication {
156        &self.app
157    }
158
159    pub fn module_ref(&self) -> &ModuleRef {
160        self.app.module_ref()
161    }
162
163    pub fn into_app(self) -> BootApplication {
164        self.app
165    }
166
167    pub fn get<T>(&self) -> Result<Arc<T>>
168    where
169        T: Send + Sync + 'static,
170    {
171        self.app.get::<T>()
172    }
173
174    pub fn get_named<T>(&self, token: &str) -> Result<Arc<T>>
175    where
176        T: Send + Sync + 'static,
177    {
178        self.app.get_named::<T>(token)
179    }
180
181    pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>>
182    where
183        T: Send + Sync + 'static,
184    {
185        self.app.get_optional::<T>()
186    }
187
188    pub fn get_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
189    where
190        T: Send + Sync + 'static,
191    {
192        self.app.get_optional_named::<T>(token)
193    }
194
195    pub fn is_initialized(&self) -> bool {
196        self.initialized
197    }
198
199    pub async fn init(&mut self) -> Result<()> {
200        if self.initialized {
201            return Ok(());
202        }
203
204        if let Err(error) = self.app.bootstrap().await {
205            let _ = self.app.shutdown().await;
206            return Err(error);
207        }
208
209        self.initialized = true;
210        Ok(())
211    }
212
213    pub async fn close(&mut self) -> Result<()> {
214        if !self.initialized {
215            return Ok(());
216        }
217
218        self.app.shutdown().await?;
219        self.initialized = false;
220        Ok(())
221    }
222
223    pub async fn listen_with<A>(&mut self, adapter: &A, addr: SocketAddr) -> Result<()>
224    where
225        A: HttpAdapter,
226    {
227        self.init().await?;
228        let serve_result = adapter.serve(self.app.clone(), addr).await;
229        let close_result = self.close().await;
230
231        match (serve_result, close_result) {
232            (Err(error), _) => Err(error),
233            (Ok(()), Err(error)) => Err(error),
234            (Ok(()), Ok(())) => Ok(()),
235        }
236    }
237
238    pub fn connect_microservice<T>(&mut self, transport: T) -> usize
239    where
240        T: MessageTransport + Send + Sync + 'static,
241    {
242        let index = self.microservices.len();
243        self.microservices
244            .push(Box::new(ConnectedMessageTransport { transport }));
245        index
246    }
247
248    pub fn connected_microservice_count(&self) -> usize {
249        self.microservices.len()
250    }
251
252    pub async fn start_all_microservices(&mut self) -> Result<()> {
253        self.init().await?;
254        for microservice in &self.microservices {
255            microservice.serve(self.app.clone()).await?;
256        }
257        Ok(())
258    }
259}
260
261/// Managed application context for provider-only hosts and workers.
262pub struct BootApplicationContext {
263    handle: BootApplicationHandle,
264}
265
266impl BootApplicationContext {
267    pub fn app(&self) -> &BootApplication {
268        self.handle.app()
269    }
270
271    pub fn module_ref(&self) -> &ModuleRef {
272        self.handle.module_ref()
273    }
274
275    pub fn into_app(self) -> BootApplication {
276        self.handle.into_app()
277    }
278
279    pub fn get<T>(&self) -> Result<Arc<T>>
280    where
281        T: Send + Sync + 'static,
282    {
283        self.handle.get::<T>()
284    }
285
286    pub fn get_named<T>(&self, token: &str) -> Result<Arc<T>>
287    where
288        T: Send + Sync + 'static,
289    {
290        self.handle.get_named::<T>(token)
291    }
292
293    pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>>
294    where
295        T: Send + Sync + 'static,
296    {
297        self.handle.get_optional::<T>()
298    }
299
300    pub fn get_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
301    where
302        T: Send + Sync + 'static,
303    {
304        self.handle.get_optional_named::<T>(token)
305    }
306
307    pub fn is_initialized(&self) -> bool {
308        self.handle.is_initialized()
309    }
310
311    pub async fn init(&mut self) -> Result<()> {
312        self.handle.init().await
313    }
314
315    pub async fn close(&mut self) -> Result<()> {
316        self.handle.close().await
317    }
318}
319
320/// Managed standalone microservice built from Boot message patterns.
321pub struct BootMicroservice<T> {
322    app: BootApplication,
323    transport: T,
324    initialized: bool,
325}
326
327impl<T> BootMicroservice<T>
328where
329    T: MessageTransport,
330{
331    pub fn new(app: BootApplication, transport: T) -> Self {
332        Self {
333            app,
334            transport,
335            initialized: false,
336        }
337    }
338
339    pub fn app(&self) -> &BootApplication {
340        &self.app
341    }
342
343    pub fn transport(&self) -> &T {
344        &self.transport
345    }
346
347    pub fn into_app(self) -> BootApplication {
348        self.app
349    }
350
351    pub fn build_client(&self) -> Result<T::Output> {
352        self.transport.build(self.app.clone())
353    }
354
355    pub fn is_initialized(&self) -> bool {
356        self.initialized
357    }
358
359    pub async fn init(&mut self) -> Result<()> {
360        if self.initialized {
361            return Ok(());
362        }
363
364        if let Err(error) = self.app.bootstrap().await {
365            let _ = self.app.shutdown().await;
366            return Err(error);
367        }
368
369        self.initialized = true;
370        Ok(())
371    }
372
373    pub async fn close(&mut self) -> Result<()> {
374        if !self.initialized {
375            return Ok(());
376        }
377
378        self.app.shutdown().await?;
379        self.initialized = false;
380        Ok(())
381    }
382
383    pub async fn listen(&mut self) -> Result<()> {
384        self.init().await?;
385        let serve_result = self.transport.serve(self.app.clone()).await;
386        let close_result = self.close().await;
387
388        match (serve_result, close_result) {
389            (Err(error), _) => Err(error),
390            (Ok(()), Err(error)) => Err(error),
391            (Ok(()), Ok(())) => Ok(()),
392        }
393    }
394}
395
396trait ConnectedMicroservice: Send + Sync {
397    fn serve(&self, app: BootApplication) -> BoxFuture<'static, Result<()>>;
398}
399
400struct ConnectedMessageTransport<T> {
401    transport: T,
402}
403
404impl<T> ConnectedMicroservice for ConnectedMessageTransport<T>
405where
406    T: MessageTransport + Send + Sync + 'static,
407{
408    fn serve(&self, app: BootApplication) -> BoxFuture<'static, Result<()>> {
409        self.transport.serve(app)
410    }
411}