Skip to main content

pmcp_workbook_runtime/
range_ref.rs

1//! The owned, structured A1 range reference [`RangeRef`] + the canonical cell-key
2//! helper [`cell_key`] — RELOCATED into `workbook-runtime` (Phase 11, Plan 05).
3//!
4//! These two were originally in `workbook-compiler`'s `ingest/cell_map.rs`, but
5//! the IR (`Expr::Range`) and the runtime executor reach them, and `ingest`
6//! links `umya`. Relocating the TYPE + the key builder here keeps the runtime
7//! crate umya-free; `workbook-compiler` re-exports both so existing
8//! `crate::ingest::RangeRef` / `cell_key` call sites resolve unchanged.
9//!
10//! Owned, serde/schemars-clean: no `umya`/`quick-xml`/`zip`/`pmcp-code-mode`
11//! type appears here.
12
13use serde::{Deserialize, Serialize};
14
15/// Build the canonical manifest cell key `sheet!addr` (e.g. `"1_Inputs!E6"`).
16///
17/// The single home for this shape: the linter, synthesis overlap check, range
18/// resolution, and the runtime executor all key cells the same way, so they
19/// share one helper instead of re-inlining `format!("{}!{}", …)`.
20pub fn cell_key(sheet: &str, addr: &str) -> String {
21    format!("{sheet}!{addr}")
22}
23
24/// A structured, owned A1-range reference — the SINGLE range type reused for
25/// merges, CF ranges, tables, named-range targets, and the IR's `Expr::Range`
26/// (replaces all `(String,String)` tuples; Codex HIGH). For a single-cell range
27/// `start == end`.
28///
29/// Example: `RangeRef { sheet: "1_Inputs", start: "E6", end: "E6" }`.
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
31pub struct RangeRef {
32    /// The sheet the range lives on.
33    pub sheet: String,
34    /// The top-left A1 cell of the range (e.g. `"E6"`).
35    pub start: String,
36    /// The bottom-right A1 cell of the range (`== start` for a single cell).
37    pub end: String,
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn cell_key_builds_sheet_bang_addr() {
46        assert_eq!(cell_key("2_Constants", "C17"), "2_Constants!C17");
47    }
48
49    #[test]
50    fn range_ref_round_trips_through_serde() {
51        let r = RangeRef {
52            sheet: "5_Quantities".to_string(),
53            start: "B2".to_string(),
54            end: "B10".to_string(),
55        };
56        let v = serde_json::to_value(&r).expect("serialize");
57        assert_eq!(v["sheet"], "5_Quantities");
58        assert_eq!(v["start"], "B2");
59        assert_eq!(v["end"], "B10");
60        let back: RangeRef = serde_json::from_value(v).expect("deserialize");
61        assert_eq!(r, back);
62    }
63}