Skip to main content

dmc/engine/
mod.rs

1use std::path::Path;
2
3use dmc_diagnostic::{Code, DiagResult};
4use duck_diagnostic::{DiagnosticEngine, diag};
5use rayon::prelude::*;
6
7use crate::engine::config::EngineConfig;
8
9pub mod accumulator;
10pub mod cache;
11pub mod collection;
12pub mod compile;
13pub mod config;
14pub mod index;
15pub mod schema_ts;
16pub mod sidecar;
17pub mod utils;
18
19pub struct Engine;
20
21impl Engine {
22  /// Execute one build: optionally clean `output_dir`, process every
23  /// collection in parallel via rayon, then emit `index.js` + `index.d.ts`
24  /// re-exporting each `<name>.json`. With a TS/JS `config_path`, the
25  /// generated `index.d.ts` infers record types via `typeof import(...)`.
26  pub fn run(cfg: &EngineConfig, config_path: Option<&Path>, diag_engine: &mut DiagnosticEngine<Code>) -> DiagResult {
27    if cfg.clean && cfg.output_dir.exists() {
28      let paths: Vec<_> = ["index.js", "index.d.ts", "index.cjs"]
29        .iter()
30        .map(|n| cfg.output_dir.join(n))
31        .chain(cfg.collections.iter().map(|c| cfg.output_dir.join(format!("{}.json", c.name))))
32        .collect();
33
34      let errors: Vec<_> = paths
35        .par_iter()
36        .filter_map(|p| match std::fs::remove_file(p) {
37          Err(e) if e.kind() != std::io::ErrorKind::NotFound => Some(e),
38          _ => None,
39        })
40        .collect();
41
42      for e in errors {
43        diag_engine.emit(diag!(Code::IoWrite, format!("clean: remove failed: {e}")));
44      }
45    }
46
47    std::fs::create_dir_all(&cfg.output_dir).map_err(|e| {
48      diag!(
49        Code::Custom { code: String::from("N001"), severity: duck_diagnostic::Severity::Note },
50        format!("output_dir error: {}", e.to_string())
51      )
52    })?;
53
54    let math_cache_path = cfg.output_dir.join(".cache").join("math.json");
55    #[cfg(feature = "math")]
56    if cfg.cache_enabled {
57      dmc_transform::Math::load_cache(&math_cache_path)?;
58    }
59
60    for c in &cfg.collections {
61      let _ = c.process(cfg, diag_engine);
62    }
63
64    // Flush math cache so the next build starts warm.
65    #[cfg(feature = "math")]
66    if cfg.cache_enabled {
67      dmc_transform::Math::save_cache(&math_cache_path)?;
68    }
69    let _ = math_cache_path;
70
71    let format = cfg.output_format.as_deref().unwrap_or("esm");
72    index::write_index(&cfg.output_dir, &cfg.collections, format, config_path)?;
73
74    Ok(())
75  }
76}