Skip to main content

chartml_core/pipeline/
mod.rs

1//! Three-stage rendering pipeline types (chartml 5.0 phase 2).
2//!
3//! The pipeline splits chart rendering into three explicit stages so each
4//! has a single typed responsibility:
5//!
6//! ```text
7//!   YAML ──► FETCH ──► FetchedChart ──► TRANSFORM ──► PreparedChart ──► render ──► SVG
8//!            (async)                    (async)                          (sync)
9//! ```
10//!
11//! Phase 2 introduces the type shapes and exposes the explicit
12//! `ChartML::fetch` / `transform` / `render_prepared_to_svg` /
13//! `render_to_svg_async` methods. No provider trait yet — `fetch` reads
14//! from pre-registered sources only. Phase 3 adds the provider trait,
15//! resolver, cache, and parallel fetch.
16//!
17//! All types derive `Clone` because:
18//! - `DataTable` is `Arc`-backed internally, so cloning sources is cheap;
19//! - `Clone` enables the "fetch + transform once, resize-render N times"
20//!   use case (e.g. responsive layouts) without re-running upstream stages.
21
22use std::collections::HashMap;
23// `web_time::SystemTime` is a wasm32-compatible drop-in for `std::time::SystemTime`.
24use web_time::SystemTime;
25
26use indexmap::IndexMap;
27
28use crate::data::DataTable;
29use crate::spec::ChartSpec;
30
31mod render_options;
32
33pub use render_options::RenderOptions;
34
35/// Stage 1 output: the spec plus every named source resolved to a `DataTable`.
36///
37/// `sources` always has at least one entry. The key is the user-chosen name
38/// from the YAML `data:` map; for unnamed flat data the canonical key is
39/// `"source"`. Insertion order matches the YAML — preserved via `IndexMap`
40/// so transform middleware can rely on stable ordering.
41#[derive(Debug, Clone)]
42pub struct FetchedChart {
43    pub spec: ChartSpec,
44    pub sources: IndexMap<String, DataTable>,
45    pub metadata: FetchMetadata,
46}
47
48/// Stage 2 output: spec plus the single transformed table the renderer will consume.
49///
50/// `data` is either the lone source (passthrough when no transform is declared)
51/// or the result of running registered `TransformMiddleware` over the
52/// fetched sources.
53#[derive(Debug, Clone)]
54pub struct PreparedChart {
55    pub spec: ChartSpec,
56    pub data: DataTable,
57    pub metadata: PreparedMetadata,
58}
59
60/// Per-fetch telemetry. `per_source` stays empty in phase 2 (no provider
61/// dispatch yet); phase 3 populates it from `FetchResult.metadata` per
62/// declared source.
63#[derive(Debug, Clone)]
64pub struct FetchMetadata {
65    pub refreshed_at: SystemTime,
66    /// Source names served from cache. Empty in phase 2 (no resolver yet).
67    pub cache_hits: Vec<String>,
68    /// Source names that produced a fresh fetch. Empty in phase 2 (no resolver yet).
69    pub cache_misses: Vec<String>,
70    /// Per-source provider metadata (e.g. `bytes_billed`, `rows_returned`).
71    /// Empty in phase 2 — populated once providers exist.
72    pub per_source: HashMap<String, HashMap<String, serde_json::Value>>,
73}
74
75impl FetchMetadata {
76    /// Construct empty metadata stamped at the current wall clock. Used by
77    /// phase 2's `fetch` (which only reads from pre-registered sources).
78    pub fn empty_now() -> Self {
79        Self {
80            refreshed_at: SystemTime::now(),
81            cache_hits: Vec::new(),
82            cache_misses: Vec::new(),
83            per_source: HashMap::new(),
84        }
85    }
86}
87
88/// Per-transform telemetry. Captures whether the middleware actually ran
89/// (vs. passthrough) plus the names of the sources the transform consumed.
90#[derive(Debug, Clone)]
91pub struct PreparedMetadata {
92    pub refreshed_at: SystemTime,
93    /// `true` when transform middleware was invoked; `false` for the
94    /// single-source passthrough path.
95    pub transform_applied: bool,
96    /// Names of every source that fed the transform stage. Insertion order
97    /// matches the YAML — preserved by carrying the `IndexMap` keys
98    /// directly out of stage 1.
99    pub sources_used: Vec<String>,
100}