Skip to main content

backbone_accounting/
lib.rs

1//! Accounting Module
2//!
3//! Generated by metaphor-schema. Enhanced with runtime implementations.
4//!
5//! This module provides:
6//! - Domain entities and repositories
7//! - Application services
8//! - HTTP and gRPC handlers
9//! - Route configuration
10//! - State machine enforcement
11//! - Validation rules runtime
12//! - RBAC middleware
13//! - Trigger execution system
14//! - Computed fields
15//! - Workflow orchestrator
16
17#![recursion_limit = "1024"]
18#![allow(unused_imports)]
19
20// Generated modules
21pub mod domain;
22pub mod infrastructure;
23pub mod application;
24pub mod presentation;
25pub mod seeders;
26pub mod exports;
27
28// Re-exports for convenience - Domain entities
29pub use domain::entity::*;
30
31// Re-exports - Infrastructure
32pub use infrastructure::persistence::*;
33
34// Re-exports - Application services
35pub use application::service::AccountService;
36pub use application::service::AccountingPostService;
37pub use application::service::CostCenterService;
38pub use application::service::EmvQrConfigService;
39pub use application::service::BankCheckSequenceService;
40pub use application::service::PrintedCheckService;
41pub use application::service::TaxTagRepairRunService;
42pub use application::service::FinancialStatementService;
43pub use application::service::FiscalPeriodService;
44pub use application::service::JournalService;
45pub use application::service::JournalLineService;
46pub use application::service::LedgerService;
47pub use application::service::ReconciliationService;
48pub use application::service::ReconciliationItemService;
49pub use application::service::FullReconcileService;
50pub use application::service::PartialReconcileService;
51
52// Re-exports - Validation
53pub use application::validator::{ValidationError, ValidationResult};
54
55// Re-exports - Workflows
56pub use application::workflows::*;
57
58use std::sync::Arc;
59use axum::Router;
60use sqlx::PgPool;
61
62/// Accounting module configuration
63///
64/// Use the builder pattern to configure and register this module:
65///
66/// ```text
67/// let accounting = AccountingModule::builder()
68///     .with_database(pool.clone())
69///     .build()?;
70///
71/// // Unguarded full CRUD (trusted/admin); compose a guarded router for production.
72/// let router = accounting.all_crud_routes();
73/// ```
74pub struct AccountingModule {
75    pub(crate) account_service: Arc<AccountService>,
76    pub(crate) accounting_post_service: Arc<AccountingPostService>,
77    pub(crate) cost_center_service: Arc<CostCenterService>,
78    pub(crate) emv_qr_config_service: Arc<EmvQrConfigService>,
79    pub(crate) bank_check_sequence_service: Arc<BankCheckSequenceService>,
80    pub(crate) printed_check_service: Arc<PrintedCheckService>,
81    pub(crate) tax_tag_repair_run_service: Arc<TaxTagRepairRunService>,
82    pub(crate) financial_statement_service: Arc<FinancialStatementService>,
83    pub(crate) fiscal_period_service: Arc<FiscalPeriodService>,
84    pub(crate) journal_service: Arc<JournalService>,
85    pub(crate) journal_line_service: Arc<JournalLineService>,
86    pub(crate) ledger_service: Arc<LedgerService>,
87    pub(crate) reconciliation_service: Arc<ReconciliationService>,
88    pub(crate) reconciliation_item_service: Arc<ReconciliationItemService>,
89    pub(crate) full_reconcile_service: Arc<FullReconcileService>,
90    pub(crate) partial_reconcile_service: Arc<PartialReconcileService>,
91    // <<< CUSTOM FIELDS
92    pub(crate) db_pool: PgPool,
93    pub(crate) reconcile_write_service:
94        Arc<crate::application::service::reconcile_write_service::ReconcileWriteService>,
95    /// The check printing surface: per-bank-journal check-number sequences and
96    /// the printed-check registry.
97    pub(crate) check_printing_service:
98        Arc<crate::application::service::check_printing_service::CheckPrintingService>,
99    /// The EMV(QRCPS)/QRIS display surface: merchant QR configuration and the
100    /// invoice payload render.
101    pub(crate) emv_qr_service:
102        Arc<crate::application::service::emv_qr_service::EmvQrService>,
103    /// The tax-tag legal-change repair verb and its audit ledger read.
104    pub(crate) tax_tag_repair_service:
105        Arc<crate::application::service::tax_tag_repair_service::TaxTagRepairService>,
106    /// Host-implemented cash-basis tax deferral lookup, kept so post-build
107    /// service variants (e.g. the exchange-account one) keep the flip wired.
108    pub(crate) deferred_tax: Option<Arc<dyn crate::domain::repositories::DeferredTaxLookup>>,
109    /// Host-implemented budget control, kept so every posting path the host
110    /// builds from this module (the HTTP posting verb, the journal workflow,
111    /// the period close) can arm the consult from one registration.
112    pub(crate) budget_control: Option<Arc<dyn crate::domain::repositories::BudgetControlPort>>,
113    /// Chart install engine over the datasets registered via `with_chart_dataset`.
114    pub(crate) chart_install_service:
115        Arc<crate::application::service::chart_install_service::ChartInstallService>,
116    /// On-the-fly financial reports (TB tree, BS, P&L, GL, partner ledger, aged AR/AP).
117    pub(crate) reporting_service:
118        Arc<crate::application::service::reporting_service::ReportingService>,
119    // END CUSTOM
120}
121
122impl AccountingModule {
123    /// Create a new module builder
124    pub fn builder() -> AccountingModuleBuilder {
125        AccountingModuleBuilder::new()
126    }
127
128    /// Mount ALL generated CRUD endpoints (12 per entity) with NO domain
129    /// validation — the fully **unguarded** surface. A well-formed request can
130    /// create invalid rows or soft-delete a referenced master out from under its
131    /// dependents. Prefer a guarded composition (read + validated writes) for any
132    /// real deployment; use this only in trusted/admin/seeding contexts.
133    pub fn all_crud_routes(&self) -> Router {
134        use presentation::http::{
135            create_account_routes,
136            create_accounting_post_routes,
137            create_cost_center_routes,
138            create_emv_qr_config_routes,
139            create_bank_check_sequence_routes,
140            create_printed_check_routes,
141            create_tax_tag_repair_run_routes,
142            create_financial_statement_routes,
143            create_fiscal_period_routes,
144            create_journal_routes,
145            create_journal_line_routes,
146            create_ledger_routes,
147            create_reconciliation_routes,
148            create_reconciliation_item_routes,
149            create_full_reconcile_routes,
150            create_partial_reconcile_routes,
151        };
152
153        Router::new()
154            .merge(create_account_routes(self.account_service.clone()))
155            .merge(create_accounting_post_routes(self.accounting_post_service.clone()))
156            .merge(create_cost_center_routes(self.cost_center_service.clone()))
157            .merge(create_emv_qr_config_routes(self.emv_qr_config_service.clone()))
158            .merge(create_bank_check_sequence_routes(self.bank_check_sequence_service.clone()))
159            .merge(create_printed_check_routes(self.printed_check_service.clone()))
160            .merge(create_tax_tag_repair_run_routes(self.tax_tag_repair_run_service.clone()))
161            .merge(create_financial_statement_routes(self.financial_statement_service.clone()))
162            .merge(create_fiscal_period_routes(self.fiscal_period_service.clone()))
163            .merge(create_journal_routes(self.journal_service.clone()))
164            .merge(create_journal_line_routes(self.journal_line_service.clone()))
165            .merge(create_ledger_routes(self.ledger_service.clone()))
166            .merge(create_reconciliation_routes(self.reconciliation_service.clone()))
167            .merge(create_reconciliation_item_routes(self.reconciliation_item_service.clone()))
168            .merge(create_full_reconcile_routes(self.full_reconcile_service.clone()))
169            .merge(create_partial_reconcile_routes(self.partial_reconcile_service.clone()))
170    }
171
172    /// Deprecated alias for [`Self::all_crud_routes`]. `routes()` reads like
173    /// "the routes" but mounts UNVALIDATED generic CRUD on every entity — a naive
174    /// mount exposes unguarded writes. Compose a guarded router (read + validated
175    /// writes) for production, or call `all_crud_routes()` to opt into the full
176    /// unguarded surface explicitly.
177    #[deprecated(note = "mounts unvalidated generic CRUD; prefer readonly_routes() + validated writes, or all_crud_routes() for the full/unguarded surface")]
178    pub fn routes(&self) -> Router {
179        self.all_crud_routes()
180    }
181
182    /// Read-only routes for every entity (GET endpoints only) — the safe base.
183    ///
184    /// Generic mutation can't reach here, so this surface cannot bypass a
185    /// validated write service's invariants. Use this as the production base and
186    /// merge validated write routes (or a write service's HTTP layer) onto it.
187    pub fn readonly_routes(&self) -> Router {
188        use presentation::http::{
189            create_account_read_routes,
190            create_accounting_post_read_routes,
191            create_cost_center_read_routes,
192            create_emv_qr_config_read_routes,
193            create_bank_check_sequence_read_routes,
194            create_printed_check_read_routes,
195            create_tax_tag_repair_run_read_routes,
196            create_financial_statement_read_routes,
197            create_fiscal_period_read_routes,
198            create_journal_read_routes,
199            create_journal_line_read_routes,
200            create_ledger_read_routes,
201            create_reconciliation_read_routes,
202            create_reconciliation_item_read_routes,
203            create_full_reconcile_read_routes,
204            create_partial_reconcile_read_routes,
205        };
206
207        Router::new()
208            .merge(create_account_read_routes(self.account_service.clone()))
209            .merge(create_accounting_post_read_routes(self.accounting_post_service.clone()))
210            .merge(create_cost_center_read_routes(self.cost_center_service.clone()))
211            .merge(create_emv_qr_config_read_routes(self.emv_qr_config_service.clone()))
212            .merge(create_bank_check_sequence_read_routes(self.bank_check_sequence_service.clone()))
213            .merge(create_printed_check_read_routes(self.printed_check_service.clone()))
214            .merge(create_tax_tag_repair_run_read_routes(self.tax_tag_repair_run_service.clone()))
215            .merge(create_financial_statement_read_routes(self.financial_statement_service.clone()))
216            .merge(create_fiscal_period_read_routes(self.fiscal_period_service.clone()))
217            .merge(create_journal_read_routes(self.journal_service.clone()))
218            .merge(create_journal_line_read_routes(self.journal_line_service.clone()))
219            .merge(create_ledger_read_routes(self.ledger_service.clone()))
220            .merge(create_reconciliation_read_routes(self.reconciliation_service.clone()))
221            .merge(create_reconciliation_item_read_routes(self.reconciliation_item_service.clone()))
222            .merge(create_full_reconcile_read_routes(self.full_reconcile_service.clone()))
223            .merge(create_partial_reconcile_read_routes(self.partial_reconcile_service.clone()))
224    }
225
226    // <<< CUSTOM METHODS
227    /// The registered budget-control port, if any — the single registration a
228    /// host threads into every `PostingService` it builds from this module
229    /// (`.with_budget_control_if_set(m.budget_control())`), so the HTTP
230    /// posting verb, the journal workflow, and the period close all consult
231    /// the same control.
232    pub fn budget_control(
233        &self,
234    ) -> Option<Arc<dyn crate::domain::repositories::BudgetControlPort>> {
235        self.budget_control.clone()
236    }
237
238    /// The on-the-fly financial reports (Trial Balance tree, Balance Sheet, Income
239    /// Statement, General Ledger, Partner Ledger, Aged AR/AP). Read-only; scoped reads.
240    pub fn reporting_service(
241        &self,
242    ) -> std::sync::Arc<crate::application::service::reporting_service::ReportingService> {
243        self.reporting_service.clone()
244    }
245
246    /// The reconciliation write service — the ONLY sanctioned writer of the
247    /// reconciliation graph (guards + CLAMP + side-effecting unlink). The graph tables
248    /// carry no CRUD surface anywhere; `guarded_routes` mounts its verb routes.
249    pub fn reconcile_write_service(
250        &self,
251    ) -> std::sync::Arc<crate::application::service::reconcile_write_service::ReconcileWriteService>
252    {
253        self.reconcile_write_service.clone()
254    }
255
256    /// Build a reconcile write service with the FX (exchange-difference) account
257    /// configured. Without one, any rate divergence that demands an exchange move fails
258    /// closed (`exchange_account_unconfigured`) — normal single-currency posts never
259    /// generate one, so the default service is complete for them.
260    pub fn reconcile_write_service_with_exchange_account(
261        &self,
262        exchange_account_id: uuid::Uuid,
263    ) -> std::sync::Arc<crate::application::service::reconcile_write_service::ReconcileWriteService>
264    {
265        std::sync::Arc::new(
266            crate::application::service::reconcile_write_service::ReconcileWriteService::new(
267                std::sync::Arc::new(
268                    crate::infrastructure::persistence::reconcile_graph_repository::SqlxReconcileGraphRepository::new(),
269                ),
270                std::sync::Arc::new(
271                    crate::infrastructure::persistence::posting_repository::SqlxPostingRepository::new(
272                        self.db_pool.clone(),
273                    ),
274                ),
275                self.db_pool.clone(),
276                Some(exchange_account_id),
277            )
278            .with_deferred_tax_if_set(self.deferred_tax.clone()),
279        )
280    }
281
282    /// The chart install engine — validates, gates, and writes a registered chart
283    /// dataset into the accounts, idempotently. The install VERB itself is
284    /// host-mounted (it orchestrates beyond this module); this handle is the engine
285    /// the host verb drives.
286    pub fn chart_install_service(
287        &self,
288    ) -> std::sync::Arc<crate::application::service::chart_install_service::ChartInstallService>
289    {
290        self.chart_install_service.clone()
291    }
292
293    /// The check printing surface — sequence registration, atomic number
294    /// allocation, the printed-check registry, and voids. `guarded_routes`
295    /// mounts its verb routes; there is no CRUD path to these tables.
296    pub fn check_printing_service(
297        &self,
298    ) -> std::sync::Arc<crate::application::service::check_printing_service::CheckPrintingService>
299    {
300        self.check_printing_service.clone()
301    }
302
303    /// The EMV(QRCPS)/QRIS display surface — merchant QR configuration and the
304    /// invoice payload render (payloads are built on demand, never stored).
305    pub fn emv_qr_service(
306        &self,
307    ) -> std::sync::Arc<crate::application::service::emv_qr_service::EmvQrService> {
308        self.emv_qr_service.clone()
309    }
310
311    /// The tax-tag legal-change repair verb (reason mandatory, locked periods
312    /// refused, closed periods overridden explicitly, every run audit-stamped)
313    /// plus its audit-ledger read.
314    pub fn tax_tag_repair_service(
315        &self,
316    ) -> std::sync::Arc<crate::application::service::tax_tag_repair_service::TaxTagRepairService>
317    {
318        self.tax_tag_repair_service.clone()
319    }
320    // END CUSTOM
321}
322
323/// Builder for AccountingModule
324pub struct AccountingModuleBuilder {
325    db_pool: Option<PgPool>,
326    // <<< CUSTOM FIELDS
327    deferred_tax: Option<Arc<dyn crate::domain::repositories::DeferredTaxLookup>>,
328    budget_control: Option<Arc<dyn crate::domain::repositories::BudgetControlPort>>,
329    chart_datasets: Vec<Arc<crate::domain::chart_dataset::ChartDataset>>,
330    // END CUSTOM
331}
332
333impl AccountingModuleBuilder {
334    /// Create a new builder
335    pub fn new() -> Self {
336        Self {
337            db_pool: None,
338            // <<< CUSTOM INIT
339            deferred_tax: None,
340            budget_control: None,
341            chart_datasets: Vec::new(),
342            // END CUSTOM
343        }
344    }
345
346    /// Set the database connection pool
347    pub fn with_database(mut self, pool: PgPool) -> Self {
348        self.db_pool = Some(pool);
349        self
350    }
351
352    // <<< CUSTOM - custom builder methods
353    /// Register a chart-of-accounts dataset installable through the chart install
354    /// engine. Datasets are data (commonly shipped by an l10n module); registering
355    /// one makes it listable via `GET /accounting/charts` and installable by the
356    /// host-mounted install verb.
357    pub fn with_chart_dataset(
358        mut self,
359        dataset: Arc<crate::domain::chart_dataset::ChartDataset>,
360    ) -> Self {
361        self.chart_datasets.push(dataset);
362        self
363    }
364
365    /// Wire the host's cash-basis tax deferral lookup: partial reconciliations
366    /// landing on receivable/payable lines flip deferred (on_payment) tax
367    /// pro-rata from the transition account onto the real tax account. Hosts
368    /// without a tax module leave it unset — no flips, everything else works.
369    pub fn with_deferred_tax(
370        mut self,
371        port: Arc<dyn crate::domain::repositories::DeferredTaxLookup>,
372    ) -> Self {
373        self.deferred_tax = Some(port);
374        self
375    }
376
377    /// Wire the host's budget control: postings through the GL-posting
378    /// chokepoint are checked against confirmed budget positions (account x
379    /// cost center x fiscal period) after the pure double-entry rules pass.
380    /// Warn-enforcement breaches log and proceed; block-enforcement breaches
381    /// refuse the posting. Hosts without a budget module leave it unset — no
382    /// check, everything else works (fail-open).
383    pub fn with_budget_control(
384        mut self,
385        port: Arc<dyn crate::domain::repositories::BudgetControlPort>,
386    ) -> Self {
387        self.budget_control = Some(port);
388        self
389    }
390    // END CUSTOM
391
392    /// Build the module with configured dependencies
393    pub fn build(self) -> anyhow::Result<AccountingModule> {
394        let db_pool = self.db_pool
395            .ok_or_else(|| anyhow::anyhow!("Database pool not configured"))?;
396
397        // Account service
398        let account_repository = Arc::new(AccountRepository::new(db_pool.clone()));
399        let account_service = Arc::new(AccountService::with_repository(account_repository.clone()));
400
401        // AccountingPost service
402        let accounting_post_repository = Arc::new(AccountingPostRepository::new(db_pool.clone()));
403        let accounting_post_service = Arc::new(AccountingPostService::with_repository(accounting_post_repository.clone()));
404
405        // CostCenter service
406        let cost_center_repository = Arc::new(CostCenterRepository::new(db_pool.clone()));
407        let cost_center_service = Arc::new(CostCenterService::with_repository(cost_center_repository.clone()));
408
409        // EmvQrConfig service
410        let emv_qr_config_repository = Arc::new(EmvQrConfigRepository::new(db_pool.clone()));
411        let emv_qr_config_service = Arc::new(EmvQrConfigService::with_repository(emv_qr_config_repository.clone()));
412
413        // BankCheckSequence service
414        let bank_check_sequence_repository = Arc::new(BankCheckSequenceRepository::new(db_pool.clone()));
415        let bank_check_sequence_service = Arc::new(BankCheckSequenceService::with_repository(bank_check_sequence_repository.clone()));
416
417        // PrintedCheck service
418        let printed_check_repository = Arc::new(PrintedCheckRepository::new(db_pool.clone()));
419        let printed_check_service = Arc::new(PrintedCheckService::with_repository(printed_check_repository.clone()));
420
421        // TaxTagRepairRun service
422        let tax_tag_repair_run_repository = Arc::new(TaxTagRepairRunRepository::new(db_pool.clone()));
423        let tax_tag_repair_run_service = Arc::new(TaxTagRepairRunService::with_repository(tax_tag_repair_run_repository.clone()));
424
425        // FinancialStatement service
426        let financial_statement_repository = Arc::new(FinancialStatementRepository::new(db_pool.clone()));
427        let financial_statement_service = Arc::new(FinancialStatementService::with_repository(financial_statement_repository.clone()));
428
429        // FiscalPeriod service
430        let fiscal_period_repository = Arc::new(FiscalPeriodRepository::new(db_pool.clone()));
431        let fiscal_period_service = Arc::new(FiscalPeriodService::with_repository(fiscal_period_repository.clone()));
432
433        // Journal service
434        let journal_repository = Arc::new(JournalRepository::new(db_pool.clone()));
435        let journal_service = Arc::new(JournalService::with_repository(journal_repository.clone()));
436
437        // JournalLine service
438        let journal_line_repository = Arc::new(JournalLineRepository::new(db_pool.clone()));
439        let journal_line_service = Arc::new(JournalLineService::with_repository(journal_line_repository.clone()));
440
441        // Ledger service
442        let ledger_repository = Arc::new(LedgerRepository::new(db_pool.clone()));
443        let ledger_service = Arc::new(LedgerService::with_repository(ledger_repository.clone()));
444
445        // Reconciliation service
446        let reconciliation_repository = Arc::new(ReconciliationRepository::new(db_pool.clone()));
447        let reconciliation_service = Arc::new(ReconciliationService::with_repository(reconciliation_repository.clone()));
448
449        // ReconciliationItem service
450        let reconciliation_item_repository = Arc::new(ReconciliationItemRepository::new(db_pool.clone()));
451        let reconciliation_item_service = Arc::new(ReconciliationItemService::with_repository(reconciliation_item_repository.clone()));
452
453        // FullReconcile service
454        let full_reconcile_repository = Arc::new(FullReconcileRepository::new(db_pool.clone()));
455        let full_reconcile_service = Arc::new(FullReconcileService::with_repository(full_reconcile_repository.clone()));
456
457        // PartialReconcile service
458        let partial_reconcile_repository = Arc::new(PartialReconcileRepository::new(db_pool.clone()));
459        let partial_reconcile_service = Arc::new(PartialReconcileService::with_repository(partial_reconcile_repository.clone()));
460
461        // <<< CUSTOM
462        // Reconciliation write service: graph port + posting port (reversals of
463        // generated moves) over the shared pool; no FX account by default (fail-closed).
464        let reconcile_graph_repository = std::sync::Arc::new(
465            crate::infrastructure::persistence::reconcile_graph_repository::SqlxReconcileGraphRepository::new(),
466        );
467        let reconcile_posting_repository = std::sync::Arc::new(
468            crate::infrastructure::persistence::posting_repository::SqlxPostingRepository::new(
469                db_pool.clone(),
470            ),
471        );
472        let reconcile_write_service = std::sync::Arc::new(
473            crate::application::service::reconcile_write_service::ReconcileWriteService::new(
474                reconcile_graph_repository,
475                reconcile_posting_repository,
476                db_pool.clone(),
477                None,
478            )
479            .with_deferred_tax_if_set(self.deferred_tax.clone()),
480        );
481        // Chart install engine: datasets registered by the host drive idempotent
482        // chart installs onto fresh companies (provenance columns identify own rows).
483        let chart_install_service = std::sync::Arc::new(
484            crate::application::service::chart_install_service::ChartInstallService::new(
485                std::sync::Arc::new(
486                    crate::infrastructure::persistence::chart_install_repository::SqlxChartInstallRepository::new(),
487                ),
488                db_pool.clone(),
489                self.chart_datasets,
490            ),
491        );
492        // Financial reports: computed on the fly over the scoped ledger reads.
493        let reporting_service = std::sync::Arc::new(
494            crate::application::service::reporting_service::ReportingService::new(
495                std::sync::Arc::new(
496                    crate::infrastructure::persistence::reporting_repository::SqlxReportingRepository::new(
497                        db_pool.clone(),
498                    ),
499                ),
500            ),
501        );
502        // Check printing: per-bank-journal number sequences + the printed-check
503        // registry, all writes through the guarded verb set.
504        let check_printing_service = std::sync::Arc::new(
505            crate::application::service::check_printing_service::CheckPrintingService::new(
506                db_pool.clone(),
507            ),
508        );
509        // EMV(QRCPS)/QRIS display: merchant QR config + invoice payload render.
510        let emv_qr_service = std::sync::Arc::new(
511            crate::application::service::emv_qr_service::EmvQrService::new(db_pool.clone()),
512        );
513        // Tax-tag legal-change repair verb + its audit ledger read.
514        let tax_tag_repair_service = std::sync::Arc::new(
515            crate::application::service::tax_tag_repair_service::TaxTagRepairService::new(
516                db_pool.clone(),
517            ),
518        );
519        // END CUSTOM
520
521        Ok(AccountingModule {
522            account_service,
523            accounting_post_service,
524            cost_center_service,
525            emv_qr_config_service,
526            bank_check_sequence_service,
527            printed_check_service,
528            tax_tag_repair_run_service,
529            financial_statement_service,
530            fiscal_period_service,
531            journal_service,
532            journal_line_service,
533            ledger_service,
534            reconciliation_service,
535            reconciliation_item_service,
536            full_reconcile_service,
537            partial_reconcile_service,
538            // <<< CUSTOM
539            db_pool,
540            reconcile_write_service,
541            deferred_tax: self.deferred_tax,
542            budget_control: self.budget_control,
543            chart_install_service,
544            reporting_service,
545            check_printing_service,
546            emv_qr_service,
547            tax_tag_repair_service,
548            // END CUSTOM
549        })
550    }
551}
552
553impl Default for AccountingModuleBuilder {
554    fn default() -> Self {
555        Self::new()
556    }
557}