dmsc/core/
app_builder.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//! 
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//! 
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! you may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//! 
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//! 
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Application Builder
21//!
22//! This module provides the application builder for constructing DMSC applications.
23//! The `DMSCAppBuilder` follows the builder pattern for fluent configuration,
24//! enabling developers to compose applications from various modules, configuration
25//! sources, and runtime settings in a declarative manner.
26//!
27//! ## Builder Pattern
28//!
29//! The builder pattern allows step-by-step construction of complex objects.
30//! Each method on `DMSCAppBuilder` configures a specific aspect of the application
31//! and returns the builder for method chaining. This results in a fluent API
32//! that is both readable and type-safe.
33//!
34//! ## Module Registration
35//!
36//! Modules are the primary extension point for DMSC applications. The builder
37//! supports multiple types of modules:
38//!
39//! - **Synchronous modules**: Implement `ServiceModule` trait for sync operations
40//! - **Asynchronous modules**: Implement `AsyncServiceModule` trait for async operations
41//! - **DMSC modules**: Implement public `DMSCModule` trait (converted to async internally)
42//! - **Python modules**: Modules created from Python code (with pyo3 feature)
43//!
44//! ## Configuration Management
45//!
46//! The builder supports multiple configuration sources with a defined priority order:
47//!
48//! 1. Configuration files (lowest priority): `dms.yaml`, `dms.yml`, `dms.toml`, `dms.json`
49//! 2. Custom configuration via `with_config()` method
50//! 3. Environment variables (highest priority)
51//!
52//! ## Usage Example
53//!
54//! ```rust
55//! use dmsc::prelude::*;
56//!
57//! #[tokio::main]
58//! async fn main() -> DMSCResult<()> {
59//!     let app = DMSCAppBuilder::new()
60//!         .with_config("config.yaml")?
61//!         .with_module(Box::new(MySyncModule::new()))
62//!         .with_async_module(Box::new(MyAsyncModule::new()))
63//!         .build()?;
64//!
65//!     app.run(|ctx| async move {
66//!         ctx.logger().info("service", "DMSC service started")?;
67//!         Ok(())
68//!     }).await
69//! }
70//! ```
71//!
72//! ## Thread Safety
73//!
74//! The `DMSCAppBuilder` is designed to be used in a single-threaded context
75//! during application construction. After calling `build()`, the resulting
76//! `DMSCAppRuntime` is safe to use across multiple threads.
77//!
78//! ## Error Handling
79//!
80//! All builder methods that can fail return `DMSCResult`, enabling proper
81//! error handling through the `?` operator or explicit match statements.
82
83use crate::core::{DMSCResult, DMSCServiceContext, ServiceModule, AsyncServiceModule};
84use super::module_sorter::sort_modules;
85use super::module_types::{ModuleSlot, ModuleType};
86use super::lifecycle::DMSCLifecycleObserver;
87use super::analytics::DMSCLogAnalyticsModule;
88use std::sync::Arc;
89
90#[cfg(feature = "pyo3")]
91use pyo3::PyResult;
92#[cfg(feature = "pyo3")]
93use crate::core::app_runtime::DMSCAppRuntime;
94
95/// Public-facing application builder for DMSC.
96/// 
97/// The `DMSCAppBuilder` provides a fluent API for configuring and building DMSC applications.
98/// It follows the builder pattern, allowing users to configure various aspects of the application
99/// before building the final runtime.
100/// 
101/// ## Usage
102/// 
103/// ```rust
104/// use dmsc::prelude::*;
105/// 
106/// #[tokio::main]
107/// async fn main() -> DMSCResult<()> {
108///     let app = DMSCAppBuilder::new()
109///         .with_config("config.yaml")?
110///         .with_module(Box::new(MySyncModule::new()))
111///         .with_async_module(Box::new(MyAsyncModule::new()))
112///         .build()?;
113///     
114///     app.run(|ctx| async move {
115///         ctx.logger().info("service", "DMSC service started")?;
116///         Ok(())
117///     }).await
118/// }
119/// ```
120
121#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
122pub struct DMSCAppBuilder {
123    /// Vector of modules with their state, including both sync and async modules
124    modules: Vec<ModuleSlot>, 
125    /// Configuration file paths to load
126    config_paths: Vec<String>, 
127    /// Custom logging configuration (optional)
128    logging_config: Option<crate::log::DMSCLogConfig>, 
129    /// Custom observability configuration (optional)
130    observability_config: Option<crate::observability::DMSCObservabilityConfig>, 
131}
132 
133impl Default for DMSCAppBuilder {
134    fn default() -> Self {
135        Self::new()
136    }
137}
138 
139impl DMSCAppBuilder {
140    /// Create a new empty application builder.
141    /// 
142    /// # Returns
143    /// 
144    /// A new `DMSCAppBuilder` instance with default settings.
145    pub fn new() -> Self {
146        DMSCAppBuilder {
147            modules: Vec::new(),
148            config_paths: Vec::new(),
149            logging_config: None,
150            observability_config: None,
151        }
152    }
153 
154    /// Add a synchronous module to the application.
155    /// 
156    /// # Parameters
157    /// 
158    /// - `module`: A boxed synchronous module implementing `ServiceModule`
159    /// 
160    /// # Returns
161    /// 
162    /// The updated `DMSCAppBuilder` instance for method chaining.
163    pub fn with_module(mut self, module: Box<dyn ServiceModule>) -> Self {
164        self.modules.push(ModuleSlot { module: ModuleType::Sync(module), failed: false });
165        self
166    }
167    
168    /// Add a Python module to the application.
169    /// 
170    /// This method adds a module created from Python code to the application.
171    /// The module will be treated as an asynchronous DMSC module.
172    /// 
173    /// # Parameters
174    /// 
175    /// - `module`: A Python module adapter implementing module configuration
176    /// 
177    /// # Returns
178    /// 
179    /// The updated `DMSCAppBuilder` instance for method chaining.
180    #[cfg(feature = "pyo3")]
181    pub fn with_python_module(mut self, module: crate::core::module::DMSCPythonModuleAdapter) -> Self {
182        self.modules.push(ModuleSlot { 
183            module: ModuleType::Async(Box::new(module)), 
184            failed: false 
185        });
186        self
187    }
188 
189    /// Add an asynchronous module to the application.
190    /// 
191    /// # Parameters
192    /// 
193    /// - `module`: A boxed asynchronous module implementing `AsyncServiceModule`
194    /// 
195    /// # Returns
196    /// 
197    /// The updated `DMSCAppBuilder` instance for method chaining.
198    pub fn with_async_module(mut self, module: Box<dyn AsyncServiceModule>) -> Self {
199        self.modules.push(ModuleSlot { module: ModuleType::Async(module), failed: false });
200        self
201    }
202 
203    /// Add a DMSC module to the application.
204    /// 
205    /// This method adds a module implementing the public `DMSCModule` trait to the application.
206    /// The module will be treated as an asynchronous module.
207    /// 
208    /// # Parameters
209    /// 
210    /// - `module`: A boxed module implementing `DMSCModule`
211    /// 
212    /// # Returns
213    /// 
214    /// The updated `DMSCAppBuilder` instance for method chaining.
215    pub fn with_dms_module(mut self, module: Box<dyn crate::core::DMSCModule>) -> Self {
216        // Wrap DMSCModule into AsyncServiceModule adapter
217        struct DMSCModuleAdapter(Box<dyn crate::core::DMSCModule + Send + Sync + 'static>);
218        
219        #[async_trait::async_trait]
220        impl AsyncServiceModule for DMSCModuleAdapter {
221            fn name(&self) -> &str {
222                self.0.name()
223            }
224            
225            fn is_critical(&self) -> bool {
226                self.0.is_critical()
227            }
228            
229            fn priority(&self) -> i32 {
230                self.0.priority()
231            }
232            
233            fn dependencies(&self) -> Vec<&str> {
234                self.0.dependencies()
235            }
236            
237            async fn init(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
238                self.0.init(ctx).await
239            }
240            
241            async fn before_start(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
242                self.0.before_start(ctx).await
243            }
244            
245            async fn start(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
246                self.0.start(ctx).await
247            }
248            
249            async fn after_start(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
250                self.0.after_start(ctx).await
251            }
252            
253            async fn before_shutdown(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
254                self.0.before_shutdown(ctx).await
255            }
256            
257            async fn shutdown(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
258                self.0.shutdown(ctx).await
259            }
260            
261            async fn after_shutdown(&mut self, ctx: &mut DMSCServiceContext) -> DMSCResult<()> {
262                self.0.after_shutdown(ctx).await
263            }
264        }
265        
266        self.modules.push(ModuleSlot { 
267            module: ModuleType::Async(Box::new(DMSCModuleAdapter(module))), 
268            failed: false 
269        });
270        self
271    }
272 
273    /// Add multiple synchronous modules to the application.
274    /// 
275    /// # Parameters
276    /// 
277    /// - `modules`: A vector of boxed synchronous modules implementing `ServiceModule`
278    /// 
279    /// # Returns
280    /// 
281    /// The updated `DMSCAppBuilder` instance for method chaining.
282    pub fn with_modules(mut self, modules: Vec<Box<dyn ServiceModule>>) -> Self {
283        for module in modules {
284            self.modules.push(ModuleSlot { module: ModuleType::Sync(module), failed: false });
285        }
286        self
287    }
288 
289    /// Add multiple asynchronous modules to the application.
290    /// 
291    /// # Parameters
292    /// 
293    /// - `modules`: A vector of boxed asynchronous modules implementing `AsyncServiceModule`
294    /// 
295    /// # Returns
296    /// 
297    /// The updated `DMSCAppBuilder` instance for method chaining.
298    pub fn with_async_modules(mut self, modules: Vec<Box<dyn AsyncServiceModule>>) -> Self {
299        for module in modules {
300            self.modules.push(ModuleSlot { module: ModuleType::Async(module), failed: false });
301        }
302        self
303    }
304    
305    /// Add multiple DMSC modules to the application.
306    /// 
307    /// This method adds multiple modules implementing the public `DMSCModule` trait to the application.
308    /// Each module will be treated as an asynchronous module.
309    /// 
310    /// # Parameters
311    /// 
312    /// - `modules`: A vector of boxed modules implementing `DMSCModule`
313    /// 
314    /// # Returns
315    /// 
316    /// The updated `DMSCAppBuilder` instance for method chaining.
317    pub fn with_dms_modules(mut self, modules: Vec<Box<dyn crate::core::DMSCModule>>) -> Self {
318        for module in modules {
319            self = self.with_dms_module(module);
320        }
321        self
322    }
323 
324    /// Add a configuration file to the application.
325    /// 
326    /// # Parameters
327    /// 
328    /// - `config_path`: Path to the configuration file
329    /// 
330    /// # Returns
331    /// 
332    /// A `DMSCResult` containing the updated `DMSCAppBuilder` instance for method chaining.
333    /// 
334    /// # Errors
335    /// 
336    /// This method currently never returns an error, but returns `DMSCResult` for consistency
337    /// with other builder methods and to allow for future error handling.
338    pub fn with_config(mut self, config_path: impl Into<String>) -> DMSCResult<Self> {
339        self.config_paths.push(config_path.into());
340        Ok(self)
341    }
342 
343    /// Set custom logging configuration for the application.
344    /// 
345    /// # Parameters
346    /// 
347    /// - `logging_config`: Custom logging configuration
348    /// 
349    /// # Returns
350    /// 
351    /// The updated `DMSCAppBuilder` instance for method chaining.
352    pub fn with_logging(mut self, logging_config: crate::log::DMSCLogConfig) -> Self {
353        self.logging_config = Some(logging_config);
354        self
355    }
356 
357    /// Set custom observability configuration for the application.
358    /// 
359    /// # Parameters
360    /// 
361    /// - `observability_config`: Custom observability configuration
362    /// 
363    /// # Returns
364    /// 
365    /// The updated `DMSCAppBuilder` instance for method chaining.
366    pub fn with_observability(mut self, observability_config: crate::observability::DMSCObservabilityConfig) -> Self {
367        self.observability_config = Some(observability_config);
368        self
369    }
370
371    /// Add cache module with configuration.
372    /// 
373    /// This method adds a cache module to the application with custom configuration.
374    /// The configuration is provided via a closure that receives a cache config builder.
375    /// 
376    /// # Parameters
377    /// 
378    /// - `config_fn`: Closure for configuring the cache module
379    /// 
380    /// # Returns
381    /// 
382    /// The updated `DMSCAppBuilder` instance for method chaining.
383    pub fn with_cache_module<F>(mut self, config_fn: F) -> Self
384    where
385        F: FnOnce(&mut crate::cache::DMSCCacheConfig) -> &mut crate::cache::DMSCCacheConfig,
386    {
387        let mut config = crate::cache::DMSCCacheConfig::default();
388        config_fn(&mut config);
389        let cache_module = crate::cache::DMSCCacheModule::with_config(config);
390        self.modules.push(ModuleSlot {
391            module: ModuleType::Sync(Box::new(cache_module)),
392            failed: false,
393        });
394        self
395    }
396
397    /// Add authentication module with configuration.
398    /// 
399    /// This method adds an authentication module to the application with custom configuration.
400    /// 
401    /// # Parameters
402    /// 
403    /// - `config_fn`: Closure for configuring the auth module
404    /// 
405    /// # Returns
406    /// 
407    /// The updated `DMSCAppBuilder` instance for method chaining.
408    pub fn with_auth_module<F>(mut self, config_fn: F) -> Self
409    where
410        F: FnOnce(&mut crate::auth::DMSCAuthConfig) -> &mut crate::auth::DMSCAuthConfig,
411    {
412        let mut config = crate::auth::DMSCAuthConfig::default();
413        config_fn(&mut config);
414        let auth_module = crate::auth::DMSCAuthModule::with_config(config);
415        self.modules.push(ModuleSlot {
416            module: ModuleType::Sync(Box::new(auth_module)),
417            failed: false,
418        });
419        self
420    }
421
422    /// Add queue module with configuration.
423    /// 
424    /// This method adds a message queue module to the application with custom configuration.
425    /// 
426    /// # Parameters
427    /// 
428    /// - `config_fn`: Closure for configuring the queue module
429    /// 
430    /// # Returns
431    /// 
432    /// The updated `DMSCAppBuilder` instance for method chaining.
433    pub fn with_queue_module<F>(mut self, config_fn: F) -> Self
434    where
435        F: FnOnce(&mut crate::queue::DMSCQueueConfig) -> &mut crate::queue::DMSCQueueConfig,
436    {
437        let mut config = crate::queue::DMSCQueueConfig::default();
438        config_fn(&mut config);
439        match crate::queue::DMSCQueueModule::with_config(config) {
440            Ok(queue_module) => {
441                self.modules.push(ModuleSlot {
442                    module: ModuleType::Sync(Box::new(queue_module)),
443                    failed: false,
444                });
445            }
446            Err(e) => {
447                log::error!("Failed to create queue module: {}", e);
448            }
449        }
450        self
451    }
452
453    /// Add device control module with configuration.
454    /// 
455    /// This method adds a device control module to the application with custom configuration.
456    /// 
457    /// # Parameters
458    /// 
459    /// - `config_fn`: Closure for configuring the device module
460    /// 
461    /// # Returns
462    /// 
463    /// The updated `DMSCAppBuilder` instance for method chaining.
464    pub fn with_device_module<F>(mut self, config_fn: F) -> Self
465    where
466        F: FnOnce(&mut crate::device::DMSCDeviceControlConfig) -> &mut crate::device::DMSCDeviceControlConfig,
467    {
468        let mut config = crate::device::DMSCDeviceControlConfig::default();
469        config_fn(&mut config);
470        let device_module = crate::device::DMSCDeviceControlModule::new().with_config(config);
471        self.modules.push(ModuleSlot {
472            module: ModuleType::Sync(Box::new(device_module)),
473            failed: false,
474        });
475        self
476    }
477 
478    /// Build the application runtime.
479    /// 
480    /// This method performs the following steps:
481    /// 1. Creates and configures the configuration manager
482    /// 2. Loads configuration from specified files and environment variables
483    /// 3. Creates the service context with core functionalities
484    /// 4. Adds core modules (analytics and lifecycle observer)
485    /// 5. Sorts modules based on dependencies and priority
486    /// 6. Creates and returns the application runtime
487    /// 
488    /// # Returns
489    /// 
490    /// A `DMSCResult` containing the built `DMSCAppRuntime` instance, or an error if building fails.
491    /// 
492    /// # Errors
493    /// 
494    /// - If configuration loading fails
495    /// - If service context creation fails
496    /// - If module sorting fails due to circular dependencies
497    pub fn build(mut self) -> DMSCResult<super::app_runtime::DMSCAppRuntime> {
498        // Create config manager with specified config paths
499        let mut config_manager = crate::config::DMSCConfigManager::new();
500        
501        // Add specified config files
502        for path in &self.config_paths {
503            config_manager.add_file_source(path);
504        }
505        
506        // Add default config sources if no paths specified
507        if self.config_paths.is_empty() {
508            if let Ok(cwd) = std::env::current_dir() {
509                let config_dir = cwd.join("config");
510                
511                // Add all supported config files in order of priority (lowest to highest)
512                config_manager.add_file_source(config_dir.join("dms.yaml"));
513                config_manager.add_file_source(config_dir.join("dms.yml"));
514                config_manager.add_file_source(config_dir.join("dms.toml"));
515                config_manager.add_file_source(config_dir.join("dms.json"));
516            }
517        }
518        
519        // Add environment variables as highest priority
520        config_manager.add_environment_source();
521        
522        // Load configuration
523        config_manager.load()?;
524 
525        // Create service context with custom configuration
526        let ctx = self.create_service_context(config_manager)?;
527        
528        // Add core modules
529        self.modules.push(ModuleSlot { module: ModuleType::Sync(Box::new(DMSCLogAnalyticsModule::new())), failed: false });
530        self.modules.push(ModuleSlot { module: ModuleType::Sync(Box::new(DMSCLifecycleObserver::new())), failed: false });
531        
532        // Sort modules based on dependencies and priority
533        self.modules = sort_modules(self.modules)?;
534        
535        let runtime = super::app_runtime::DMSCAppRuntime::new(ctx, self.modules);
536        Ok(runtime)
537    }
538    
539    /// Create the service context with the given configuration manager.
540    /// 
541    /// This method creates the service context with the following components:
542    /// 1. File system accessor
543    /// 2. Logger (using custom config if provided, otherwise from configuration)
544    /// 3. Configuration manager
545    /// 4. Hook bus for lifecycle events
546    /// 
547    /// # Parameters
548    /// 
549    /// - `config_manager`: Configuration manager with loaded configuration
550    /// 
551    /// # Returns
552    /// 
553    /// A `DMSCResult` containing the created `DMSCServiceContext` instance, or an error if creation fails.
554    /// 
555    /// # Errors
556    /// 
557    /// - If project root directory detection fails
558    /// - If file system creation fails
559    /// - If logger creation fails
560    fn create_service_context(&self, config_manager: crate::config::DMSCConfigManager) -> DMSCResult<DMSCServiceContext> {
561        let cfg = config_manager.config();
562 
563        let project_root = std::env::current_dir()
564            .map_err(|e| crate::core::DMSCError::Other(format!("detect project root failed: {e}")))?;
565        let app_data_root = if let Some(root_str) = cfg.get_str("fs.app_data_root") {
566            project_root.join(root_str)
567        } else {
568            project_root.join(".dms")
569        };
570 
571        let fs = crate::fs::DMSCFileSystem::new_with_roots(project_root, app_data_root);
572
573        // Use custom logging config if provided, otherwise create from config
574        let log_config: crate::log::DMSCLogConfig = if let Some(log_config) = &self.logging_config {
575            log_config.clone()
576        } else {
577            crate::log::DMSCLogConfig::from_config(&cfg)
578        };
579        let logger = crate::log::DMSCLogger::new(&log_config, fs.clone());
580        let hooks = crate::hooks::DMSCHookBus::new();
581        let metrics_registry = Some(Arc::new(crate::observability::DMSCMetricsRegistry::new()));
582        
583        Ok(DMSCServiceContext::new_with(fs, logger, config_manager, hooks, metrics_registry))
584    }
585}
586 
587#[cfg(feature = "pyo3")]
588#[pyo3::prelude::pymethods]
589impl DMSCAppBuilder {
590    #[new]
591    fn py_new() -> Self {
592        Self::new()
593    }
594
595    fn py_with_config(&mut self, config_path: &str) -> PyResult<Self> {
596        self.config_paths.push(config_path.to_string());
597        Ok(std::mem::take(self))
598    }
599
600    fn py_with_logging(&mut self, logging_config: crate::log::DMSCLogConfig) -> PyResult<Self> {
601        self.logging_config = Some(logging_config);
602        Ok(std::mem::take(self))
603    }
604
605    fn py_with_observability(&mut self, observability_config: crate::observability::DMSCObservabilityConfig) -> PyResult<Self> {
606        self.observability_config = Some(observability_config);
607        Ok(std::mem::take(self))
608    }
609
610    fn py_build(&mut self) -> PyResult<DMSCAppRuntime> {
611        let builder = std::mem::take(self);
612        DMSCAppBuilder::build(builder).map_err(|e| pyo3::prelude::PyErr::from(e))
613    }
614
615    fn py_with_module(&mut self, module: super::module::DMSCPythonServiceModule) -> PyResult<Self> {
616        self.modules.push(crate::core::module_types::ModuleSlot {
617            module: crate::core::module_types::ModuleType::Sync(Box::new(module)),
618            failed: false,
619        });
620        Ok(std::mem::take(self))
621    }
622
623    fn py_with_python_module(&mut self, module: super::module::DMSCPythonModuleAdapter) -> PyResult<Self> {
624        self.modules.push(crate::core::module_types::ModuleSlot {
625            module: crate::core::module_types::ModuleType::Async(Box::new(module)),
626            failed: false,
627        });
628        Ok(std::mem::take(self))
629    }
630
631    fn py_with_async_module(&mut self, module: super::module::DMSCPythonAsyncServiceModule) -> PyResult<Self> {
632        self.modules.push(crate::core::module_types::ModuleSlot {
633            module: crate::core::module_types::ModuleType::Async(Box::new(module)),
634            failed: false,
635        });
636        Ok(std::mem::take(self))
637    }
638
639    fn py_with_dms_module(&mut self, module: super::module::DMSCPythonModuleAdapter) -> PyResult<Self> {
640        self.modules.push(crate::core::module_types::ModuleSlot {
641            module: crate::core::module_types::ModuleType::Async(Box::new(module)),
642            failed: false,
643        });
644        Ok(std::mem::take(self))
645    }
646
647    fn py_with_modules(&mut self, modules: Vec<super::module::DMSCPythonServiceModule>) -> PyResult<Self> {
648        for module in modules {
649            self.modules.push(crate::core::module_types::ModuleSlot {
650                module: crate::core::module_types::ModuleType::Sync(Box::new(module)),
651                failed: false,
652            });
653        }
654        Ok(std::mem::take(self))
655    }
656
657    fn py_with_async_modules(&mut self, modules: Vec<super::module::DMSCPythonAsyncServiceModule>) -> PyResult<Self> {
658        for module in modules {
659            self.modules.push(crate::core::module_types::ModuleSlot {
660                module: crate::core::module_types::ModuleType::Async(Box::new(module)),
661                failed: false,
662            });
663        }
664        Ok(std::mem::take(self))
665    }
666
667    fn py_with_dms_modules(&mut self, modules: Vec<super::module::DMSCPythonModuleAdapter>) -> PyResult<Self> {
668        for module in modules {
669            self.modules.push(crate::core::module_types::ModuleSlot {
670                module: crate::core::module_types::ModuleType::Async(Box::new(module)),
671                failed: false,
672            });
673        }
674        Ok(std::mem::take(self))
675    }
676}