formualizer_workbook/
resolver.rs

1use crate::traits::SpreadsheetReader;
2use formualizer_common::{
3    LiteralValue,
4    error::{ExcelError, ExcelErrorKind},
5};
6use formualizer_eval::function::Function;
7use formualizer_eval::traits::{
8    FunctionProvider, InMemoryRange, NamedRangeResolver, Range, RangeResolver, ReferenceResolver,
9    Resolver, Table, TableResolver,
10};
11use formualizer_parse::parser::TableReference;
12use parking_lot::RwLock;
13use std::sync::Arc;
14
15/// Minimal resolver for ranges/tables (NOT cells - Engine handles cells from graph)
16pub struct IoResolver<B: SpreadsheetReader> {
17    backend: RwLock<B>,
18}
19
20impl<B: SpreadsheetReader> IoResolver<B> {
21    pub fn new(backend: B) -> Self {
22        Self {
23            backend: RwLock::new(backend),
24        }
25    }
26}
27
28// IoResolver does NOT implement ReferenceResolver (Engine handles cells from graph)
29
30impl<B: SpreadsheetReader> RangeResolver for IoResolver<B> {
31    fn resolve_range_reference(
32        &self,
33        sheet: Option<&str>,
34        sr: Option<u32>,
35        sc: Option<u32>,
36        er: Option<u32>,
37        ec: Option<u32>,
38    ) -> Result<Box<dyn Range>, ExcelError> {
39        let sheet_name = sheet.ok_or_else(|| {
40            ExcelError::new(ExcelErrorKind::Ref).with_message("Missing sheet name")
41        })?;
42        let (sr, sc, er, ec) = normalize_range(sr, sc, er, ec)?;
43
44        // Read from backend with interior mutability
45        let mut guard = self.backend.write();
46        let map = guard
47            .read_range(sheet_name, (sr, sc), (er, ec))
48            .map_err(|e| ExcelError::new(ExcelErrorKind::NImpl).with_message(e.to_string()))?;
49
50        let height = (er - sr + 1) as usize;
51        let width = (ec - sc + 1) as usize;
52        let mut rows = vec![vec![LiteralValue::Empty; width]; height];
53        for ((r, c), cell) in map.into_iter() {
54            let rr = (r - sr) as usize;
55            let cc = (c - sc) as usize;
56            if let Some(v) = cell.value {
57                rows[rr][cc] = v;
58            } else {
59                rows[rr][cc] = LiteralValue::Empty;
60            }
61        }
62        Ok(Box::new(InMemoryRange::new(rows)))
63    }
64}
65
66impl<B: SpreadsheetReader> NamedRangeResolver for IoResolver<B> {
67    fn resolve_named_range_reference(
68        &self,
69        _name: &str,
70    ) -> Result<Vec<Vec<LiteralValue>>, ExcelError> {
71        // Check if backend supports named ranges
72        if !self.backend.read().capabilities().named_ranges {
73            return Err(ExcelError::new(ExcelErrorKind::Name)
74                .with_message("Backend doesn't support named ranges"));
75        }
76
77        // TODO: Implement named range support
78        Err(ExcelError::new(ExcelErrorKind::Name).with_message("Named ranges not yet implemented"))
79    }
80}
81
82impl<B: SpreadsheetReader> TableResolver for IoResolver<B> {
83    fn resolve_table_reference(
84        &self,
85        _tref: &TableReference,
86    ) -> Result<Box<dyn Table>, ExcelError> {
87        // Check if backend supports tables
88        if !self.backend.read().capabilities().tables {
89            return Err(ExcelError::new(ExcelErrorKind::NImpl)
90                .with_message("Backend doesn't support tables"));
91        }
92
93        // TODO: Implement table support
94        Err(ExcelError::new(ExcelErrorKind::NImpl).with_message("Tables not yet implemented"))
95    }
96}
97
98impl<B: SpreadsheetReader> FunctionProvider for IoResolver<B> {
99    fn get_function(&self, ns: &str, name: &str) -> Option<Arc<dyn Function>> {
100        // Delegate to global registry
101        formualizer_eval::function_registry::get(ns, name)
102    }
103}
104
105// IoResolver needs to implement ReferenceResolver for cells
106// Even though Engine handles cells from graph, trait requires it
107impl<B: SpreadsheetReader> ReferenceResolver for IoResolver<B> {
108    fn resolve_cell_reference(
109        &self,
110        _sheet: Option<&str>,
111        _row: u32,
112        _col: u32,
113    ) -> Result<LiteralValue, ExcelError> {
114        // IoResolver doesn't handle cells - Engine reads from graph
115        // This is just to satisfy the trait requirement
116        Err(ExcelError::new(ExcelErrorKind::Ref)
117            .with_message("IoResolver doesn't handle cell references"))
118    }
119}
120
121impl<B: SpreadsheetReader> Resolver for IoResolver<B> {}
122
123fn normalize_range(
124    sr: Option<u32>,
125    sc: Option<u32>,
126    er: Option<u32>,
127    ec: Option<u32>,
128) -> Result<(u32, u32, u32, u32), ExcelError> {
129    // Default to single cell if not specified
130    let sr = sr.unwrap_or(1);
131    let sc = sc.unwrap_or(1);
132    let er = er.unwrap_or(sr);
133    let ec = ec.unwrap_or(sc);
134
135    // Validate range
136    if sr > er || sc > ec {
137        return Err(ExcelError::new(ExcelErrorKind::Ref).with_message("Invalid range: start > end"));
138    }
139
140    Ok((sr, sc, er, ec))
141}