1use crate::error::IoError;
2use crate::traits::{LoadStrategy, SpreadsheetReader, SpreadsheetWriter};
3use chrono::Timelike;
4use formualizer_common::{
5 LiteralValue, RangeAddress,
6 error::{ExcelError, ExcelErrorKind},
7};
8use formualizer_eval::engine::eval::EvalPlan;
9use formualizer_eval::engine::named_range::{NameScope, NamedDefinition};
10use std::collections::BTreeMap;
11
12#[derive(Default, Debug, Clone, Copy)]
14pub struct WBResolver;
15
16impl formualizer_eval::traits::ReferenceResolver for WBResolver {
17 fn resolve_cell_reference(
18 &self,
19 _sheet: Option<&str>,
20 _row: u32,
21 _col: u32,
22 ) -> Result<LiteralValue, formualizer_common::error::ExcelError> {
23 Err(formualizer_common::error::ExcelError::from(
24 formualizer_common::error::ExcelErrorKind::NImpl,
25 ))
26 }
27}
28impl formualizer_eval::traits::RangeResolver for WBResolver {
29 fn resolve_range_reference(
30 &self,
31 _sheet: Option<&str>,
32 _sr: Option<u32>,
33 _sc: Option<u32>,
34 _er: Option<u32>,
35 _ec: Option<u32>,
36 ) -> Result<Box<dyn formualizer_eval::traits::Range>, formualizer_common::error::ExcelError>
37 {
38 Err(formualizer_common::error::ExcelError::from(
39 formualizer_common::error::ExcelErrorKind::NImpl,
40 ))
41 }
42}
43impl formualizer_eval::traits::NamedRangeResolver for WBResolver {
44 fn resolve_named_range_reference(
45 &self,
46 _name: &str,
47 ) -> Result<Vec<Vec<LiteralValue>>, formualizer_common::error::ExcelError> {
48 Err(ExcelError::new(ExcelErrorKind::Name))
49 }
50}
51impl formualizer_eval::traits::TableResolver for WBResolver {
52 fn resolve_table_reference(
53 &self,
54 _tref: &formualizer_parse::parser::TableReference,
55 ) -> Result<Box<dyn formualizer_eval::traits::Table>, formualizer_common::error::ExcelError>
56 {
57 Err(formualizer_common::error::ExcelError::from(
58 formualizer_common::error::ExcelErrorKind::NImpl,
59 ))
60 }
61}
62impl formualizer_eval::traits::SourceResolver for WBResolver {}
63impl formualizer_eval::traits::FunctionProvider for WBResolver {
64 fn get_function(
65 &self,
66 ns: &str,
67 name: &str,
68 ) -> Option<std::sync::Arc<dyn formualizer_eval::function::Function>> {
69 formualizer_eval::function_registry::get(ns, name)
70 }
71}
72impl formualizer_eval::traits::Resolver for WBResolver {}
73impl formualizer_eval::traits::EvaluationContext for WBResolver {}
74
75pub struct Workbook {
77 engine: formualizer_eval::engine::Engine<WBResolver>,
78 enable_changelog: bool,
79 log: formualizer_eval::engine::ChangeLog,
80 undo: formualizer_eval::engine::graph::editor::undo_engine::UndoEngine,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum WorkbookMode {
85 Ephemeral,
87 Interactive,
89}
90
91#[derive(Clone, Debug)]
92pub struct WorkbookConfig {
93 pub eval: formualizer_eval::engine::EvalConfig,
94 pub enable_changelog: bool,
95}
96
97impl WorkbookConfig {
98 pub fn ephemeral() -> Self {
99 Self {
100 eval: formualizer_eval::engine::EvalConfig::default(),
101 enable_changelog: false,
102 }
103 }
104
105 pub fn interactive() -> Self {
106 let mut eval = formualizer_eval::engine::EvalConfig::default();
107 eval.defer_graph_building = true;
108 Self {
109 eval,
110 enable_changelog: true,
111 }
112 }
113}
114
115impl Default for Workbook {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl Workbook {
122 pub fn new_with_config(mut config: WorkbookConfig) -> Self {
123 config.eval.arrow_storage_enabled = true;
124 config.eval.delta_overlay_enabled = true;
125 config.eval.write_formula_overlay_enabled = true;
126 let engine = formualizer_eval::engine::Engine::new(WBResolver, config.eval);
127 let mut log = formualizer_eval::engine::ChangeLog::new();
128 log.set_enabled(config.enable_changelog);
129 Self {
130 engine,
131 enable_changelog: config.enable_changelog,
132 log,
133 undo: formualizer_eval::engine::graph::editor::undo_engine::UndoEngine::new(),
134 }
135 }
136 pub fn new_with_mode(mode: WorkbookMode) -> Self {
137 let config = match mode {
138 WorkbookMode::Ephemeral => WorkbookConfig::ephemeral(),
139 WorkbookMode::Interactive => WorkbookConfig::interactive(),
140 };
141 Self::new_with_config(config)
142 }
143 pub fn new() -> Self {
144 Self::new_with_mode(WorkbookMode::Interactive)
145 }
146
147 pub fn engine(&self) -> &formualizer_eval::engine::Engine<WBResolver> {
148 &self.engine
149 }
150 pub fn engine_mut(&mut self) -> &mut formualizer_eval::engine::Engine<WBResolver> {
151 &mut self.engine
152 }
153 pub fn eval_config(&self) -> &formualizer_eval::engine::EvalConfig {
154 &self.engine.config
155 }
156
157 pub fn set_changelog_enabled(&mut self, enabled: bool) {
159 self.enable_changelog = enabled;
160 self.log.set_enabled(enabled);
161 }
162 pub fn begin_action(&mut self, description: impl Into<String>) {
163 if self.enable_changelog {
164 self.log.begin_compound(description.into());
165 }
166 }
167 pub fn end_action(&mut self) {
168 if self.enable_changelog {
169 self.log.end_compound();
170 }
171 }
172 pub fn undo(&mut self) -> Result<(), IoError> {
173 if self.enable_changelog {
174 self.engine
175 .undo_logged(&mut self.undo, &mut self.log)
176 .map_err(|e| IoError::from_backend("editor", e))?;
177 self.resync_all_overlays();
178 }
179 Ok(())
180 }
181 pub fn redo(&mut self) -> Result<(), IoError> {
182 if self.enable_changelog {
183 self.engine
184 .redo_logged(&mut self.undo, &mut self.log)
185 .map_err(|e| IoError::from_backend("editor", e))?;
186 self.resync_all_overlays();
187 }
188 Ok(())
189 }
190
191 fn resync_all_overlays(&mut self) {
192 let sheet_names: Vec<String> = self
194 .engine
195 .sheet_store()
196 .sheets
197 .iter()
198 .map(|s| s.name.as_ref().to_string())
199 .collect();
200 for s in sheet_names {
201 self.resync_overlay_for_sheet(&s);
202 }
203 }
204 fn resync_overlay_for_sheet(&mut self, sheet: &str) {
205 if let Some(asheet) = self.engine.sheet_store().sheet(sheet) {
206 let rows = asheet.nrows as usize;
207 let cols = asheet.columns.len();
208 for r0 in 0..rows {
209 let r = (r0 as u32) + 1;
210 for c0 in 0..cols {
211 let c = (c0 as u32) + 1;
212 let v = self
213 .engine
214 .graph_cell_value(sheet, r, c)
215 .unwrap_or(LiteralValue::Empty);
216 self.mirror_value_to_overlay(sheet, r, c, &v);
217 }
218 }
219 }
220 }
222
223 fn ensure_arrow_sheet_capacity(&mut self, sheet: &str, min_rows: usize, min_cols: usize) {
224 use formualizer_eval::arrow_store::ArrowSheet;
225
226 if self.engine.sheet_store().sheet(sheet).is_none() {
227 self.engine.sheet_store_mut().sheets.push(ArrowSheet {
228 name: std::sync::Arc::<str>::from(sheet),
229 columns: Vec::new(),
230 nrows: 0,
231 chunk_starts: Vec::new(),
232 chunk_rows: 32 * 1024,
233 });
234 }
235
236 let asheet = self
237 .engine
238 .sheet_store_mut()
239 .sheet_mut(sheet)
240 .expect("ArrowSheet must exist");
241
242 if min_rows > asheet.nrows as usize {
244 asheet.ensure_row_capacity(min_rows);
245 }
246
247 let cur_cols = asheet.columns.len();
249 if min_cols > cur_cols {
250 asheet.insert_columns(cur_cols, min_cols - cur_cols);
251 }
252 }
253
254 fn mirror_value_to_overlay(&mut self, sheet: &str, row: u32, col: u32, value: &LiteralValue) {
255 use formualizer_eval::arrow_store::OverlayValue;
256 if !(self.engine.config.arrow_storage_enabled && self.engine.config.delta_overlay_enabled) {
257 return;
258 }
259 let date_system = self.engine.config.date_system;
260 let row0 = row.saturating_sub(1) as usize;
261 let col0 = col.saturating_sub(1) as usize;
262 self.ensure_arrow_sheet_capacity(sheet, row0 + 1, col0 + 1);
263 let asheet = self
264 .engine
265 .sheet_store_mut()
266 .sheet_mut(sheet)
267 .expect("ArrowSheet must exist");
268 if let Some((ch_idx, in_off)) = asheet.chunk_of_row(row0) {
269 let ov = match value {
270 LiteralValue::Empty => OverlayValue::Empty,
271 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
272 LiteralValue::Number(n) => OverlayValue::Number(*n),
273 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
274 LiteralValue::Text(s) => OverlayValue::Text(std::sync::Arc::from(s.clone())),
275 LiteralValue::Error(e) => {
276 OverlayValue::Error(formualizer_eval::arrow_store::map_error_code(e.kind))
277 }
278 LiteralValue::Date(d) => {
279 let dt = d.and_hms_opt(0, 0, 0).unwrap();
280 let serial = formualizer_eval::builtins::datetime::datetime_to_serial_for(
281 date_system,
282 &dt,
283 );
284 OverlayValue::Number(serial)
285 }
286 LiteralValue::DateTime(dt) => {
287 let serial = formualizer_eval::builtins::datetime::datetime_to_serial_for(
288 date_system,
289 dt,
290 );
291 OverlayValue::Number(serial)
292 }
293 LiteralValue::Time(t) => {
294 let serial = t.num_seconds_from_midnight() as f64 / 86_400.0;
295 OverlayValue::Number(serial)
296 }
297 LiteralValue::Duration(d) => {
298 let serial = d.num_seconds() as f64 / 86_400.0;
299 OverlayValue::Number(serial)
300 }
301 LiteralValue::Pending => OverlayValue::Pending,
302 LiteralValue::Array(_) => {
303 OverlayValue::Error(formualizer_eval::arrow_store::map_error_code(
304 formualizer_common::ExcelErrorKind::Value,
305 ))
306 }
307 };
308 if let Some(ch) = asheet.ensure_column_chunk_mut(col0, ch_idx) {
310 ch.overlay.set(in_off, ov);
311 }
312 }
313 }
314
315 pub fn sheet_names(&self) -> Vec<String> {
317 self.engine
318 .sheet_store()
319 .sheets
320 .iter()
321 .map(|s| s.name.as_ref().to_string())
322 .collect()
323 }
324 pub fn sheet_dimensions(&self, name: &str) -> Option<(u32, u32)> {
326 self.engine
327 .sheet_store()
328 .sheet(name)
329 .map(|s| (s.nrows, s.columns.len() as u32))
330 }
331 pub fn has_sheet(&self, name: &str) -> bool {
332 self.engine.sheet_id(name).is_some()
333 }
334 pub fn add_sheet(&mut self, name: &str) -> Result<(), ExcelError> {
335 self.engine.add_sheet(name)?;
336 self.ensure_arrow_sheet_capacity(name, 0, 0);
337 Ok(())
338 }
339 pub fn delete_sheet(&mut self, name: &str) -> Result<(), ExcelError> {
340 if let Some(id) = self.engine.sheet_id(name) {
341 self.engine.remove_sheet(id)?;
342 }
343 self.engine
345 .sheet_store_mut()
346 .sheets
347 .retain(|s| s.name.as_ref() != name);
348 Ok(())
349 }
350 pub fn rename_sheet(&mut self, old: &str, new: &str) -> Result<(), ExcelError> {
351 if let Some(id) = self.engine.sheet_id(old) {
352 self.engine.rename_sheet(id, new)?;
353 }
354 if let Some(asheet) = self.engine.sheet_store_mut().sheet_mut(old) {
355 asheet.name = std::sync::Arc::<str>::from(new);
356 }
357 Ok(())
358 }
359
360 pub fn set_value(
362 &mut self,
363 sheet: &str,
364 row: u32,
365 col: u32,
366 value: LiteralValue,
367 ) -> Result<(), IoError> {
368 self.ensure_arrow_sheet_capacity(sheet, row as usize, col as usize);
369 if self.enable_changelog {
370 let sheet_id = self
372 .engine
373 .sheet_id(sheet)
374 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
375 let cell = formualizer_eval::reference::CellRef::new(
376 sheet_id,
377 formualizer_eval::reference::Coord::from_excel(row, col, true, true),
378 );
379 self.engine.edit_with_logger(&mut self.log, |editor| {
380 editor.set_cell_value(cell, value.clone());
381 });
382 self.mirror_value_to_overlay(sheet, row, col, &value);
383 self.engine.mark_data_edited();
384 Ok(())
385 } else {
386 self.engine
387 .set_cell_value(sheet, row, col, value)
388 .map_err(IoError::Engine)
389 }
390 }
391
392 pub fn set_formula(
393 &mut self,
394 sheet: &str,
395 row: u32,
396 col: u32,
397 formula: &str,
398 ) -> Result<(), IoError> {
399 self.ensure_arrow_sheet_capacity(sheet, row as usize, col as usize);
400 if self.engine.config.defer_graph_building {
401 if self.engine.get_cell(sheet, row, col).is_some() {
402 let with_eq = if formula.starts_with('=') {
403 formula.to_string()
404 } else {
405 format!("={formula}")
406 };
407 let ast = formualizer_parse::parser::parse(&with_eq)
408 .map_err(|e| IoError::from_backend("parser", e))?;
409 if self.enable_changelog {
410 let sheet_id = self
411 .engine
412 .sheet_id(sheet)
413 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
414 let cell = formualizer_eval::reference::CellRef::new(
415 sheet_id,
416 formualizer_eval::reference::Coord::from_excel(row, col, true, true),
417 );
418 self.engine.edit_with_logger(&mut self.log, |editor| {
419 editor.set_cell_formula(cell, ast);
420 });
421 self.engine.mark_data_edited();
422 Ok(())
423 } else {
424 self.engine
425 .set_cell_formula(sheet, row, col, ast)
426 .map_err(IoError::Engine)
427 }
428 } else {
429 self.engine
430 .stage_formula_text(sheet, row, col, formula.to_string());
431 Ok(())
432 }
433 } else {
434 let with_eq = if formula.starts_with('=') {
435 formula.to_string()
436 } else {
437 format!("={formula}")
438 };
439 let ast = formualizer_parse::parser::parse(&with_eq)
440 .map_err(|e| IoError::from_backend("parser", e))?;
441 if self.enable_changelog {
442 let sheet_id = self
443 .engine
444 .sheet_id(sheet)
445 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
446 let cell = formualizer_eval::reference::CellRef::new(
447 sheet_id,
448 formualizer_eval::reference::Coord::from_excel(row, col, true, true),
449 );
450 self.engine.edit_with_logger(&mut self.log, |editor| {
451 editor.set_cell_formula(cell, ast);
452 });
453 self.engine.mark_data_edited();
454 Ok(())
455 } else {
456 self.engine
457 .set_cell_formula(sheet, row, col, ast)
458 .map_err(IoError::Engine)
459 }
460 }
461 }
462
463 pub fn get_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
464 self.engine.get_cell_value(sheet, row, col)
465 }
466 pub fn get_formula(&self, sheet: &str, row: u32, col: u32) -> Option<String> {
467 if let Some(s) = self.engine.get_staged_formula_text(sheet, row, col) {
468 return Some(s);
469 }
470 self.engine
471 .get_cell(sheet, row, col)
472 .and_then(|(ast, _)| ast.map(|a| formualizer_parse::pretty::canonical_formula(&a)))
473 }
474
475 pub fn read_range(&self, addr: &RangeAddress) -> Vec<Vec<LiteralValue>> {
477 let mut out = Vec::with_capacity(addr.height() as usize);
478 if let Some(asheet) = self.engine.sheet_store().sheet(&addr.sheet) {
479 let sr0 = addr.start_row.saturating_sub(1) as usize;
480 let sc0 = addr.start_col.saturating_sub(1) as usize;
481 let er0 = addr.end_row.saturating_sub(1) as usize;
482 let ec0 = addr.end_col.saturating_sub(1) as usize;
483 let view = asheet.range_view(sr0, sc0, er0, ec0);
484 let (h, w) = view.dims();
485 for rr in 0..h {
486 let mut row = Vec::with_capacity(w);
487 for cc in 0..w {
488 row.push(view.get_cell(rr, cc));
489 }
490 out.push(row);
491 }
492 } else {
493 for r in addr.start_row..=addr.end_row {
495 let mut row = Vec::with_capacity(addr.width() as usize);
496 for c in addr.start_col..=addr.end_col {
497 row.push(
498 self.engine
499 .get_cell_value(&addr.sheet, r, c)
500 .unwrap_or(LiteralValue::Empty),
501 );
502 }
503 out.push(row);
504 }
505 }
506 out
507 }
508 pub fn write_range(
509 &mut self,
510 sheet: &str,
511 _start: (u32, u32),
512 cells: BTreeMap<(u32, u32), crate::traits::CellData>,
513 ) -> Result<(), IoError> {
514 if self.enable_changelog {
515 let sheet_id = self
516 .engine
517 .sheet_id(sheet)
518 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
519 let defer_graph_building = self.engine.config.defer_graph_building;
520
521 let mut overlay_ops: Vec<(u32, u32, LiteralValue)> = Vec::new();
522 let mut staged_forms: Vec<(u32, u32, String)> = Vec::new();
523
524 self.engine
525 .edit_with_logger(&mut self.log, |editor| -> Result<(), IoError> {
526 for ((r, c), d) in cells.into_iter() {
527 let cell = formualizer_eval::reference::CellRef::new(
528 sheet_id,
529 formualizer_eval::reference::Coord::from_excel(r, c, true, true),
530 );
531 if let Some(v) = d.value.clone() {
532 editor.set_cell_value(cell, v.clone());
533 overlay_ops.push((r, c, v));
534 }
535 if let Some(f) = d.formula.as_ref() {
536 if defer_graph_building {
537 staged_forms.push((r, c, f.clone()));
538 } else {
539 let with_eq = if f.starts_with('=') {
540 f.clone()
541 } else {
542 format!("={f}")
543 };
544 let ast = formualizer_parse::parser::parse(&with_eq)
545 .map_err(|e| IoError::from_backend("parser", e))?;
546 editor.set_cell_formula(cell, ast);
547 }
548 }
549 }
550 Ok(())
551 })?;
552
553 for (r, c, v) in overlay_ops {
554 self.mirror_value_to_overlay(sheet, r, c, &v);
555 }
556 for (r, c, f) in staged_forms {
557 self.engine.stage_formula_text(sheet, r, c, f);
558 }
559 self.engine.mark_data_edited();
560 Ok(())
561 } else {
562 for ((r, c), d) in cells.into_iter() {
563 if let Some(v) = d.value.clone() {
564 self.engine
565 .set_cell_value(sheet, r, c, v)
566 .map_err(IoError::Engine)?;
567 }
568 if let Some(f) = d.formula.as_ref() {
569 if self.engine.config.defer_graph_building {
570 self.engine.stage_formula_text(sheet, r, c, f.clone());
571 } else {
572 let with_eq = if f.starts_with('=') {
573 f.clone()
574 } else {
575 format!("={f}")
576 };
577 let ast = formualizer_parse::parser::parse(&with_eq)
578 .map_err(|e| IoError::from_backend("parser", e))?;
579 self.engine
580 .set_cell_formula(sheet, r, c, ast)
581 .map_err(IoError::Engine)?;
582 }
583 }
584 }
585 Ok(())
586 }
587 }
588
589 pub fn set_values(
591 &mut self,
592 sheet: &str,
593 start_row: u32,
594 start_col: u32,
595 rows: &[Vec<LiteralValue>],
596 ) -> Result<(), IoError> {
597 if self.enable_changelog {
598 let sheet_id = self
599 .engine
600 .sheet_id(sheet)
601 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
602 let mut overlay_ops: Vec<(u32, u32, LiteralValue)> = Vec::new();
603
604 self.engine.edit_with_logger(&mut self.log, |editor| {
605 for (ri, rvals) in rows.iter().enumerate() {
606 let r = start_row + ri as u32;
607 for (ci, v) in rvals.iter().enumerate() {
608 let c = start_col + ci as u32;
609 let cell = formualizer_eval::reference::CellRef::new(
610 sheet_id,
611 formualizer_eval::reference::Coord::from_excel(r, c, true, true),
612 );
613 editor.set_cell_value(cell, v.clone());
614 overlay_ops.push((r, c, v.clone()));
615 }
616 }
617 });
618
619 for (r, c, v) in overlay_ops {
620 self.mirror_value_to_overlay(sheet, r, c, &v);
621 }
622 self.engine.mark_data_edited();
623 Ok(())
624 } else {
625 for (ri, rvals) in rows.iter().enumerate() {
626 let r = start_row + ri as u32;
627 for (ci, v) in rvals.iter().enumerate() {
628 let c = start_col + ci as u32;
629 self.engine
630 .set_cell_value(sheet, r, c, v.clone())
631 .map_err(IoError::Engine)?;
632 }
633 }
634 Ok(())
635 }
636 }
637
638 pub fn set_formulas(
640 &mut self,
641 sheet: &str,
642 start_row: u32,
643 start_col: u32,
644 rows: &[Vec<String>],
645 ) -> Result<(), IoError> {
646 let height = rows.len();
647 let width = rows.iter().map(|r| r.len()).max().unwrap_or(0);
648 if height == 0 || width == 0 {
649 return Ok(());
650 }
651 let end_row = start_row.saturating_add((height - 1) as u32);
652 let end_col = start_col.saturating_add((width - 1) as u32);
653 self.ensure_arrow_sheet_capacity(sheet, end_row as usize, end_col as usize);
654
655 if self.engine.config.defer_graph_building {
656 for (ri, rforms) in rows.iter().enumerate() {
657 let r = start_row + ri as u32;
658 for (ci, f) in rforms.iter().enumerate() {
659 let c = start_col + ci as u32;
660 self.engine.stage_formula_text(sheet, r, c, f.clone());
661 }
662 }
663 Ok(())
664 } else if self.enable_changelog {
665 let sheet_id = self
666 .engine
667 .sheet_id(sheet)
668 .unwrap_or_else(|| self.engine.add_sheet(sheet).expect("add sheet"));
669
670 self.engine
671 .edit_with_logger(&mut self.log, |editor| -> Result<(), IoError> {
672 for (ri, rforms) in rows.iter().enumerate() {
673 let r = start_row + ri as u32;
674 for (ci, f) in rforms.iter().enumerate() {
675 let c = start_col + ci as u32;
676 let cell = formualizer_eval::reference::CellRef::new(
677 sheet_id,
678 formualizer_eval::reference::Coord::from_excel(r, c, true, true),
679 );
680 let with_eq = if f.starts_with('=') {
681 f.clone()
682 } else {
683 format!("={f}")
684 };
685 let ast = formualizer_parse::parser::parse(&with_eq)
686 .map_err(|e| IoError::from_backend("parser", e))?;
687 editor.set_cell_formula(cell, ast);
688 }
689 }
690 Ok(())
691 })?;
692
693 self.engine.mark_data_edited();
694 Ok(())
695 } else {
696 for (ri, rforms) in rows.iter().enumerate() {
697 let r = start_row + ri as u32;
698 for (ci, f) in rforms.iter().enumerate() {
699 let c = start_col + ci as u32;
700 let with_eq = if f.starts_with('=') {
701 f.clone()
702 } else {
703 format!("={f}")
704 };
705 let ast = formualizer_parse::parser::parse(&with_eq)
706 .map_err(|e| IoError::from_backend("parser", e))?;
707 self.engine
708 .set_cell_formula(sheet, r, c, ast)
709 .map_err(IoError::Engine)?;
710 }
711 }
712 Ok(())
713 }
714 }
715
716 pub fn prepare_graph_all(&mut self) -> Result<(), IoError> {
718 self.engine
719 .build_graph_all()
720 .map_err(|e| IoError::from_backend("parser", e))
721 }
722 pub fn prepare_graph_for_sheets<'a, I: IntoIterator<Item = &'a str>>(
723 &mut self,
724 sheets: I,
725 ) -> Result<(), IoError> {
726 self.engine
727 .build_graph_for_sheets(sheets)
728 .map_err(|e| IoError::from_backend("parser", e))
729 }
730 pub fn evaluate_cell(
731 &mut self,
732 sheet: &str,
733 row: u32,
734 col: u32,
735 ) -> Result<LiteralValue, IoError> {
736 self.engine
737 .evaluate_cell(sheet, row, col)
738 .map_err(IoError::Engine)
739 .map(|value| value.unwrap_or(LiteralValue::Empty))
740 }
741 pub fn evaluate_cells(
742 &mut self,
743 targets: &[(&str, u32, u32)],
744 ) -> Result<Vec<LiteralValue>, IoError> {
745 self.engine
746 .evaluate_cells(targets)
747 .map_err(IoError::Engine)
748 .map(|values| {
749 values
750 .into_iter()
751 .map(|v| v.unwrap_or(LiteralValue::Empty))
752 .collect()
753 })
754 }
755
756 pub fn evaluate_cells_cancellable(
757 &mut self,
758 targets: &[(&str, u32, u32)],
759 cancel_flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
760 ) -> Result<Vec<LiteralValue>, IoError> {
761 self.engine
762 .evaluate_cells_cancellable(targets, cancel_flag)
763 .map_err(IoError::Engine)
764 .map(|values| {
765 values
766 .into_iter()
767 .map(|v| v.unwrap_or(LiteralValue::Empty))
768 .collect()
769 })
770 }
771 pub fn evaluate_all(&mut self) -> Result<formualizer_eval::engine::EvalResult, IoError> {
772 self.engine.evaluate_all().map_err(IoError::Engine)
773 }
774
775 pub fn evaluate_all_cancellable(
776 &mut self,
777 cancel_flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
778 ) -> Result<formualizer_eval::engine::EvalResult, IoError> {
779 self.engine
780 .evaluate_all_cancellable(cancel_flag)
781 .map_err(IoError::Engine)
782 }
783
784 pub fn evaluate_with_plan(
785 &mut self,
786 plan: &formualizer_eval::engine::RecalcPlan,
787 ) -> Result<formualizer_eval::engine::EvalResult, IoError> {
788 self.engine
789 .evaluate_recalc_plan(plan)
790 .map_err(IoError::Engine)
791 }
792
793 pub fn get_eval_plan(&self, targets: &[(&str, u32, u32)]) -> Result<EvalPlan, IoError> {
794 self.engine.get_eval_plan(targets).map_err(IoError::Engine)
795 }
796
797 pub fn define_named_range(
799 &mut self,
800 name: &str,
801 address: &RangeAddress,
802 scope: crate::traits::NamedRangeScope,
803 ) -> Result<(), IoError> {
804 let (definition, scope) = self.named_definition_with_scope(address, scope)?;
805 if self.enable_changelog {
806 let result = self.engine.edit_with_logger(&mut self.log, |editor| {
807 editor.define_name(name, definition, scope)
808 });
809 result.map_err(|e| IoError::from_backend("editor", e))
810 } else {
811 self.engine
812 .define_name(name, definition, scope)
813 .map_err(IoError::Engine)
814 }
815 }
816
817 pub fn update_named_range(
818 &mut self,
819 name: &str,
820 address: &RangeAddress,
821 scope: crate::traits::NamedRangeScope,
822 ) -> Result<(), IoError> {
823 let (definition, scope) = self.named_definition_with_scope(address, scope)?;
824 if self.enable_changelog {
825 let result = self.engine.edit_with_logger(&mut self.log, |editor| {
826 editor.update_name(name, definition, scope)
827 });
828 result.map_err(|e| IoError::from_backend("editor", e))
829 } else {
830 self.engine
831 .update_name(name, definition, scope)
832 .map_err(IoError::Engine)
833 }
834 }
835
836 pub fn delete_named_range(
837 &mut self,
838 name: &str,
839 scope: crate::traits::NamedRangeScope,
840 sheet: Option<&str>,
841 ) -> Result<(), IoError> {
842 let scope = self.name_scope_from_hint(scope, sheet)?;
843 if self.enable_changelog {
844 let result = self
845 .engine
846 .edit_with_logger(&mut self.log, |editor| editor.delete_name(name, scope));
847 result.map_err(|e| IoError::from_backend("editor", e))
848 } else {
849 self.engine
850 .delete_name(name, scope)
851 .map_err(IoError::Engine)
852 }
853 }
854
855 pub fn named_range_address(&self, name: &str) -> Option<RangeAddress> {
857 if let Some((_, named)) = self
858 .engine
859 .named_ranges_iter()
860 .find(|(n, _)| n.as_str() == name)
861 {
862 return self.named_definition_to_address(&named.definition);
863 }
864
865 let mut resolved: Option<RangeAddress> = None;
866 for ((_sheet_id, candidate), named) in self.engine.sheet_named_ranges_iter() {
867 if candidate == name
868 && let Some(address) = self.named_definition_to_address(&named.definition)
869 {
870 if resolved.is_some() {
871 return None; }
873 resolved = Some(address);
874 }
875 }
876 resolved
877 }
878
879 fn named_definition_with_scope(
880 &mut self,
881 address: &RangeAddress,
882 scope: crate::traits::NamedRangeScope,
883 ) -> Result<(NamedDefinition, NameScope), IoError> {
884 let sheet_id = self.ensure_sheet_for_address(address)?;
885 let scope = match scope {
886 crate::traits::NamedRangeScope::Workbook => NameScope::Workbook,
887 crate::traits::NamedRangeScope::Sheet => NameScope::Sheet(sheet_id),
888 };
889 let sr0 = address.start_row.saturating_sub(1);
890 let sc0 = address.start_col.saturating_sub(1);
891 let er0 = address.end_row.saturating_sub(1);
892 let ec0 = address.end_col.saturating_sub(1);
893 let start_ref = formualizer_eval::reference::CellRef::new(
894 sheet_id,
895 formualizer_eval::reference::Coord::new(sr0, sc0, true, true),
896 );
897 if sr0 == er0 && sc0 == ec0 {
898 Ok((NamedDefinition::Cell(start_ref), scope))
899 } else {
900 let end_ref = formualizer_eval::reference::CellRef::new(
901 sheet_id,
902 formualizer_eval::reference::Coord::new(er0, ec0, true, true),
903 );
904 let range_ref = formualizer_eval::reference::RangeRef::new(start_ref, end_ref);
905 Ok((NamedDefinition::Range(range_ref), scope))
906 }
907 }
908
909 fn name_scope_from_hint(
910 &mut self,
911 scope: crate::traits::NamedRangeScope,
912 sheet: Option<&str>,
913 ) -> Result<NameScope, IoError> {
914 match scope {
915 crate::traits::NamedRangeScope::Workbook => Ok(NameScope::Workbook),
916 crate::traits::NamedRangeScope::Sheet => {
917 let sheet = sheet.ok_or_else(|| IoError::Backend {
918 backend: "workbook".to_string(),
919 message: "Sheet scope requires a sheet name".to_string(),
920 })?;
921 let sheet_id = self
922 .engine
923 .sheet_id(sheet)
924 .ok_or_else(|| IoError::Backend {
925 backend: "workbook".to_string(),
926 message: "Sheet not found".to_string(),
927 })?;
928 Ok(NameScope::Sheet(sheet_id))
929 }
930 }
931 }
932
933 fn ensure_sheet_for_address(
934 &mut self,
935 address: &RangeAddress,
936 ) -> Result<formualizer_eval::SheetId, IoError> {
937 let sheet_id = self
938 .engine
939 .sheet_id(&address.sheet)
940 .or_else(|| self.engine.add_sheet(&address.sheet).ok())
941 .ok_or_else(|| IoError::Backend {
942 backend: "workbook".to_string(),
943 message: "Sheet not found".to_string(),
944 })?;
945 self.ensure_arrow_sheet_capacity(
946 &address.sheet,
947 address.end_row as usize,
948 address.end_col as usize,
949 );
950 Ok(sheet_id)
951 }
952
953 fn named_definition_to_address(&self, definition: &NamedDefinition) -> Option<RangeAddress> {
954 match definition {
955 NamedDefinition::Cell(cell) => {
956 let sheet = self.engine.sheet_name(cell.sheet_id).to_string();
957 let row = cell.coord.row() + 1;
958 let col = cell.coord.col() + 1;
959 RangeAddress::new(sheet, row, col, row, col).ok()
960 }
961 NamedDefinition::Range(range) => {
962 if range.start.sheet_id != range.end.sheet_id {
963 return None;
964 }
965 let sheet = self.engine.sheet_name(range.start.sheet_id).to_string();
966 let start_row = range.start.coord.row() + 1;
967 let start_col = range.start.coord.col() + 1;
968 let end_row = range.end.coord.row() + 1;
969 let end_col = range.end.coord.col() + 1;
970 RangeAddress::new(sheet, start_row, start_col, end_row, end_col).ok()
971 }
972 NamedDefinition::Formula { .. } => {
973 #[cfg(feature = "tracing")]
974 tracing::debug!("formula-backed named ranges are not yet supported");
975 None
976 }
977 }
978 }
979
980 pub fn begin_tx<'a, W: SpreadsheetWriter>(
982 &'a mut self,
983 writer: &'a mut W,
984 ) -> crate::transaction::WriteTransaction<'a, W> {
985 crate::transaction::WriteTransaction::new(writer)
986 }
987
988 pub fn from_reader<B>(
990 mut backend: B,
991 _strategy: LoadStrategy,
992 config: WorkbookConfig,
993 ) -> Result<Self, IoError>
994 where
995 B: SpreadsheetReader + formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>,
996 IoError: From<<B as formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>>::Error>,
997 {
998 let mut wb = Self::new_with_config(config);
999 backend
1000 .stream_into_engine(&mut wb.engine)
1001 .map_err(IoError::from)?;
1002 Ok(wb)
1003 }
1004
1005 pub fn from_reader_with_config<B>(
1006 backend: B,
1007 strategy: LoadStrategy,
1008 config: WorkbookConfig,
1009 ) -> Result<Self, IoError>
1010 where
1011 B: SpreadsheetReader + formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>,
1012 IoError: From<<B as formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>>::Error>,
1013 {
1014 Self::from_reader(backend, strategy, config)
1015 }
1016
1017 pub fn from_reader_with_mode<B>(
1018 backend: B,
1019 strategy: LoadStrategy,
1020 mode: WorkbookMode,
1021 ) -> Result<Self, IoError>
1022 where
1023 B: SpreadsheetReader + formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>,
1024 IoError: From<<B as formualizer_eval::engine::ingest::EngineLoadStream<WBResolver>>::Error>,
1025 {
1026 let config = match mode {
1027 WorkbookMode::Ephemeral => WorkbookConfig::ephemeral(),
1028 WorkbookMode::Interactive => WorkbookConfig::interactive(),
1029 };
1030 Self::from_reader(backend, strategy, config)
1031 }
1032}
1033
1034impl SpreadsheetWriter for Workbook {
1036 type Error = IoError;
1037
1038 fn write_cell(
1039 &mut self,
1040 sheet: &str,
1041 row: u32,
1042 col: u32,
1043 data: crate::traits::CellData,
1044 ) -> Result<(), Self::Error> {
1045 if let Some(v) = data.value {
1046 self.set_value(sheet, row, col, v)?;
1047 }
1048 if let Some(f) = data.formula {
1049 self.set_formula(sheet, row, col, &f)?;
1050 }
1051 Ok(())
1052 }
1053 fn write_range(
1054 &mut self,
1055 sheet: &str,
1056 cells: BTreeMap<(u32, u32), crate::traits::CellData>,
1057 ) -> Result<(), Self::Error> {
1058 for ((r, c), d) in cells {
1059 self.write_cell(sheet, r, c, d)?;
1060 }
1061 Ok(())
1062 }
1063 fn clear_range(
1064 &mut self,
1065 sheet: &str,
1066 start: (u32, u32),
1067 end: (u32, u32),
1068 ) -> Result<(), Self::Error> {
1069 for r in start.0..=end.0 {
1070 for c in start.1..=end.1 {
1071 self.set_value(sheet, r, c, LiteralValue::Empty)?;
1072 }
1073 }
1074 Ok(())
1075 }
1076 fn create_sheet(&mut self, name: &str) -> Result<(), Self::Error> {
1077 self.add_sheet(name).map_err(IoError::Engine)
1078 }
1079 fn delete_sheet(&mut self, name: &str) -> Result<(), Self::Error> {
1080 self.delete_sheet(name).map_err(IoError::Engine)
1081 }
1082 fn rename_sheet(&mut self, old: &str, new: &str) -> Result<(), Self::Error> {
1083 self.rename_sheet(old, new).map_err(IoError::Engine)
1084 }
1085 fn flush(&mut self) -> Result<(), Self::Error> {
1086 Ok(())
1087 }
1088}