1#![deny(missing_docs)]
2
3use std::collections::HashMap;
4use std::vec::Vec;
5
6use crate::expressions::parser::static_analysis::run_static_analysis_on_node;
7use crate::{
8 calc_result::{CalcResult, Range},
9 cell::CellValue,
10 constants::{self, LAST_COLUMN, LAST_ROW},
11 expressions::{
12 lexer::LexerMode,
13 parser::{
14 move_formula::{move_formula, MoveContext},
15 new_parser_english,
16 static_analysis::StaticResult,
17 stringify::{
18 rename_defined_name_in_node, to_english_string, to_localized_string, to_rc_format,
19 },
20 ArrayNode, CompletionContext, NamedVariable, Node, Parser,
21 },
22 token::{get_error_by_name, Error, OpProduct, OpSum, OpUnary},
23 types::*,
24 utils::{self, is_valid_column_number, is_valid_identifier, is_valid_row},
25 },
26 formatter::{
27 format::{format_number, parse_formatted_number},
28 lexer::is_likely_date_number_format,
29 },
30 implicit_intersection::implicit_intersection,
31 language::{get_default_language, get_language, Language},
32 locale::{get_default_locale, get_locale, Locale},
33 types::*,
34 utils as common,
35};
36
37use crate::{cf_types::CfCellResult, tz::Tz};
38
39#[cfg(any(test, feature = "mock_time"))]
40pub use crate::mock_time::get_milliseconds_since_epoch;
41
42#[cfg(not(any(test, feature = "mock_time")))]
48#[cfg(not(target_arch = "wasm32"))]
49#[allow(clippy::expect_used)]
50pub fn get_milliseconds_since_epoch() -> i64 {
51 use std::time::{SystemTime, UNIX_EPOCH};
52 SystemTime::now()
53 .duration_since(UNIX_EPOCH)
54 .expect("problem with system time")
55 .as_millis() as i64
56}
57
58#[cfg(not(any(test, feature = "mock_time")))]
64#[cfg(target_arch = "wasm32")]
65pub fn get_milliseconds_since_epoch() -> i64 {
66 use js_sys::Date;
67 Date::now() as i64
68}
69
70pub(crate) enum CellStructure {
78 SingleCell,
79 ArrayFormula {
80 range: (i32, i32),
81 },
82 DynamicFormula {
83 range: (i32, i32),
84 },
85 SpillArray {
86 anchor: (i32, i32),
87 range: (i32, i32),
88 },
89 SpillDynamic {
90 anchor: (i32, i32),
91 range: (i32, i32),
92 },
93}
94
95#[derive(Clone)]
97pub(crate) enum CellState {
98 Evaluated,
100 Evaluating,
102}
103
104#[derive(Clone)]
106pub(crate) enum ParsedDefinedName {
107 CellReference(CellReferenceIndex),
109 RangeReference(Range),
111 LambdaDefinition(Vec<NamedVariable>, Node),
113 InvalidDefinedNameFormula,
115}
116
117pub struct FmtSettings {
119 pub currency: String,
121 pub currency_format: String,
123 pub short_date: String,
125 pub short_date_example: String,
127 pub long_date: String,
129 pub long_date_example: String,
131 pub number_fmt: String,
133 pub number_example: String,
135}
136
137fn array_node_to_formula_value(node: ArrayNode) -> FormulaValue {
138 match node {
139 ArrayNode::Boolean(b) => FormulaValue::Boolean(b),
140 ArrayNode::Number(n) => FormulaValue::Number(n),
141 ArrayNode::String(s) => FormulaValue::Text(s),
142 ArrayNode::Error(ei) => FormulaValue::Error {
143 ei,
144 o: String::new(),
145 m: String::new(),
146 },
147 ArrayNode::Empty => FormulaValue::Number(0.0),
148 }
149}
150
151fn array_node_to_spill_value(node: ArrayNode) -> SpillValue {
152 match node {
153 ArrayNode::Boolean(b) => SpillValue::Boolean(b),
154 ArrayNode::Number(n) => SpillValue::Number(n),
155 ArrayNode::String(s) => SpillValue::Text(s),
156 ArrayNode::Error(ei) => SpillValue::Error(ei),
157 ArrayNode::Empty => SpillValue::Number(0.0),
158 }
159}
160
161fn formula_value_to_spill_value(v: &FormulaValue) -> SpillValue {
162 match v {
163 FormulaValue::Unevaluated => SpillValue::Error(Error::ERROR),
164 FormulaValue::Boolean(b) => SpillValue::Boolean(*b),
165 FormulaValue::Number(n) => SpillValue::Number(*n),
166 FormulaValue::Text(s) => SpillValue::Text(s.clone()),
167 FormulaValue::Error { ei, .. } => SpillValue::Error(ei.clone()),
168 }
169}
170
171pub(crate) enum CellOrRange {
172 Cell((u32, i32, i32)),
174 Range((u32, i32, i32, i32, i32)),
176}
177
178pub struct Model<'a> {
191 pub workbook: Workbook,
193 pub parsed_formulas: Vec<Vec<(Node, StaticResult)>>,
195 pub(crate) parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
197 pub(crate) shared_strings: HashMap<String, usize>,
199 pub(crate) parser: Parser<'a>,
201 pub(crate) cells: HashMap<(u32, i32, i32), CellState>,
203 pub(crate) locale: &'a Locale,
205 pub(crate) language: &'a Language,
207 pub(crate) tz: Tz,
209 pub(crate) view_id: u32,
211 pub(crate) variable_stack: HashMap<usize, CalcResult>,
213 pub(crate) last_variable_id: usize,
215 pub(crate) lambdas: HashMap<usize, (Vec<NamedVariable>, Node)>,
217 pub(crate) last_lambda_id: usize,
219 pub(crate) spill_cells: Vec<CellReferenceIndex>,
221 pub(crate) support: HashMap<CellReferenceIndex, Vec<CellOrRange>>,
223 pub(crate) cf_cache: HashMap<(u32, i32, i32), Vec<CfCellResult>>,
226 pub(crate) links: HashMap<(u32, i32, i32), Link>,
228}
229
230pub struct CellIndex {
233 pub index: u32,
235 pub row: i32,
237 pub column: i32,
239}
240
241impl<'a> Model<'a> {
242 pub(crate) fn get_next_variable_id(&mut self) -> usize {
243 let id = self.last_variable_id;
244 self.last_variable_id += 1;
245 id
246 }
247 fn clear_variable_stack(&mut self) {
248 self.variable_stack.clear();
249 self.last_variable_id = 0;
250 }
251 pub(crate) fn get_next_lambda_id(&mut self) -> usize {
252 let id = self.last_lambda_id;
253 self.last_lambda_id += 1;
254 id
255 }
256 fn clear_lambdas(&mut self) {
257 self.lambdas.clear();
258 self.last_lambda_id = 0;
259 }
260 pub(crate) fn evaluate_node_with_reference(
261 &mut self,
262 node: &Node,
263 cell: CellReferenceIndex,
264 ) -> CalcResult {
265 match node {
266 Node::ReferenceKind {
267 sheet_name: _,
268 sheet_index,
269 absolute_row,
270 absolute_column,
271 row,
272 column,
273 } => {
274 let mut row1 = *row;
275 let mut column1 = *column;
276 if !absolute_row {
277 row1 += cell.row;
278 }
279 if !absolute_column {
280 column1 += cell.column;
281 }
282 CalcResult::Range {
283 left: CellReferenceIndex {
284 sheet: *sheet_index,
285 row: row1,
286 column: column1,
287 },
288 right: CellReferenceIndex {
289 sheet: *sheet_index,
290 row: row1,
291 column: column1,
292 },
293 }
294 }
295 Node::RangeKind {
296 sheet_name: _,
297 sheet_index,
298 absolute_row1,
299 absolute_column1,
300 row1,
301 column1,
302 absolute_row2,
303 absolute_column2,
304 row2,
305 column2,
306 } => {
307 let mut row_left = *row1;
308 let mut column_left = *column1;
309 if !absolute_row1 {
310 row_left += cell.row;
311 }
312 if !absolute_column1 {
313 column_left += cell.column;
314 }
315 let mut row_right = *row2;
316 let mut column_right = *column2;
317 if !absolute_row2 {
318 row_right += cell.row;
319 }
320 if !absolute_column2 {
321 column_right += cell.column;
322 }
323 CalcResult::Range {
325 left: CellReferenceIndex {
326 sheet: *sheet_index,
327 row: row_left,
328 column: column_left,
329 },
330 right: CellReferenceIndex {
331 sheet: *sheet_index,
332 row: row_right,
333 column: column_right,
334 },
335 }
336 }
337 Node::ImplicitIntersection {
338 automatic: _,
339 child,
340 } => match self.evaluate_node_with_reference(child, cell) {
341 CalcResult::Range { left, right } => CalcResult::Range { left, right },
342 _ => CalcResult::new_error(
343 Error::ERROR,
344 cell,
345 format!("Error with Implicit Intersection in cell {cell:?}"),
346 ),
347 },
348 _ => self.evaluate_node_in_context(node, cell),
349 }
350 }
351
352 fn get_range(&mut self, left: &Node, right: &Node, cell: CellReferenceIndex) -> CalcResult {
353 let left_result = self.evaluate_node_with_reference(left, cell);
354 let right_result = self.evaluate_node_with_reference(right, cell);
355 match (left_result, right_result) {
356 (
357 CalcResult::Range {
358 left: left1,
359 right: right1,
360 },
361 CalcResult::Range {
362 left: left2,
363 right: right2,
364 },
365 ) => {
366 if left1.row == right1.row
367 && left1.column == right1.column
368 && left2.row == right2.row
369 && left2.column == right2.column
370 {
371 return CalcResult::Range {
372 left: left1,
373 right: right2,
374 };
375 }
376 CalcResult::Error {
377 error: Error::VALUE,
378 origin: cell,
379 message: "Invalid range".to_string(),
380 }
381 }
382 _ => CalcResult::Error {
383 error: Error::VALUE,
384 origin: cell,
385 message: "Invalid range".to_string(),
386 },
387 }
388 }
389
390 pub(crate) fn formula_without_prefix<'b>(&self, value: &'b str) -> Option<&'b str> {
391 if let Some(stripped) = value.strip_prefix('=') {
392 if stripped.is_empty() {
393 None
394 } else {
395 Some(stripped)
396 }
397 } else if let Some(stripped) = value.strip_prefix(['+', '-']) {
398 if stripped.is_empty() || self.cast_number(stripped).is_some() {
399 None
400 } else {
401 Some(value)
402 }
403 } else {
404 None
405 }
406 }
407
408 pub(crate) fn parse_internal_formula(&mut self, body: &str, context: &CellReferenceRC) -> Node {
417 let locale = self.locale;
418 let language = self.language;
419 self.parser.set_locale(get_default_locale());
420 self.parser.set_language(get_default_language());
421 let node = self.parser.parse(body, context);
422 self.parser.set_locale(locale);
423 self.parser.set_language(language);
424 node
425 }
426
427 pub(crate) fn user_formula_to_internal(
436 &mut self,
437 formula: &str,
438 context: &CellReferenceRC,
439 ) -> Result<String, String> {
440 let trimmed = formula.trim();
441 let had_equals = trimmed.starts_with('=');
442 let body = trimmed.strip_prefix('=').unwrap_or(trimmed);
443 let mut node = self.parser.parse(body, context);
444 if let Node::ParseErrorKind { .. } = node {
445 node = self.parse_internal_formula(body, context);
448 }
449 if let Node::ParseErrorKind { .. } = node {
450 return Err(format!("Invalid formula: '{formula}'"));
451 }
452 let english = to_english_string(&node, context);
453 Ok(if had_equals {
454 format!("={english}")
455 } else {
456 english
457 })
458 }
459
460 pub fn formula_completion(
467 &mut self,
468 sheet: u32,
469 row: i32,
470 column: i32,
471 formula: &str,
472 cursor: usize,
473 ) -> Result<CompletionContext, String> {
474 let sheet_name = self.workbook.worksheet(sheet)?.get_name();
475 let cell_reference = CellReferenceRC {
476 sheet: sheet_name,
477 row,
478 column,
479 };
480 let (body, cursor) = match formula.strip_prefix('=') {
483 Some(rest) => (rest, cursor.saturating_sub(1)),
484 None => (formula, cursor),
485 };
486 Ok(self.parser.parse_at_cursor(body, cursor, &cell_reference))
487 }
488
489 pub(crate) fn internal_formula_to_display(
493 &self,
494 formula: &str,
495 context: &CellReferenceRC,
496 ) -> String {
497 let trimmed = formula.trim();
498 let had_equals = trimmed.starts_with('=');
499 let body = trimmed.strip_prefix('=').unwrap_or(trimmed);
500 if body.is_empty() {
501 return formula.to_string();
502 }
503 let worksheet_names = self
505 .workbook
506 .worksheets
507 .iter()
508 .map(|s| s.get_name())
509 .collect();
510 let defined_names = self.workbook.get_defined_names_with_scope();
511 let mut parser =
512 new_parser_english(worksheet_names, defined_names, self.workbook.tables.clone());
513 let node = parser.parse(body, context);
514 if let Node::ParseErrorKind { .. } = node {
515 return formula.to_string();
516 }
517 let local = to_localized_string(&node, context, self.locale, self.language);
518 if had_equals {
519 format!("={local}")
520 } else {
521 local
522 }
523 }
524
525 pub(crate) fn evaluate_formula(&mut self, formula: &str, sheet: u32) -> Option<f64> {
529 let body = formula.trim().strip_prefix('=').unwrap_or(formula.trim());
530 if body.is_empty() {
531 return None;
532 }
533 let sheet_name = self.workbook.worksheets.get(sheet as usize)?.get_name();
534 let context_rc = CellReferenceRC {
535 sheet: sheet_name,
536 row: 1,
537 column: 1,
538 };
539 let node = self.parse_internal_formula(body, &context_rc);
540 let context_index = CellReferenceIndex {
541 sheet,
542 row: 1,
543 column: 1,
544 };
545 match self.evaluate_node_in_context(&node, context_index) {
546 CalcResult::Number(n) => Some(n),
547 _ => None,
548 }
549 }
550
551 pub(crate) fn evaluate_node_in_context(
552 &mut self,
553 node: &Node,
554 cell: CellReferenceIndex,
555 ) -> CalcResult {
556 use Node::*;
557 match node {
558 OpSumKind { kind, left, right } => match kind {
559 OpSum::Add => self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 + f2)),
560 OpSum::Minus => self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 - f2)),
561 },
562 NumberKind(value) => CalcResult::Number(*value),
563 StringKind(value) => CalcResult::String(value.replace(r#""""#, r#"""#)),
564 BooleanKind(value) => CalcResult::Boolean(*value),
565 ReferenceKind {
566 sheet_name: _,
567 sheet_index,
568 absolute_row,
569 absolute_column,
570 row,
571 column,
572 } => {
573 let mut row1 = *row;
574 let mut column1 = *column;
575 if !absolute_row {
576 row1 += cell.row;
577 }
578 if !absolute_column {
579 column1 += cell.column;
580 }
581 self.support
582 .entry(cell)
583 .or_default()
584 .push(CellOrRange::Cell((*sheet_index, row1, column1)));
585 self.evaluate_cell(CellReferenceIndex {
586 sheet: *sheet_index,
587 row: row1,
588 column: column1,
589 })
590 }
591 WrongReferenceKind { .. } => {
592 CalcResult::new_error(Error::REF, cell, "Wrong reference".to_string())
593 }
594 OpRangeKind { left, right } => self.get_range(left, right, cell),
595 WrongRangeKind { .. } => {
596 CalcResult::new_error(Error::REF, cell, "Wrong range".to_string())
597 }
598 RangeKind {
599 sheet_index,
600 row1,
601 column1,
602 row2,
603 column2,
604 absolute_column1,
605 absolute_row2,
606 absolute_row1,
607 absolute_column2,
608 sheet_name: _,
609 } => {
610 let r1 = if *absolute_row1 {
611 *row1
612 } else {
613 *row1 + cell.row
614 };
615 let r2 = if *absolute_row2 {
616 *row2
617 } else {
618 *row2 + cell.row
619 };
620 let c1 = if *absolute_column1 {
621 *column1
622 } else {
623 *column1 + cell.column
624 };
625 let c2 = if *absolute_column2 {
626 *column2
627 } else {
628 *column2 + cell.column
629 };
630 self.support
631 .entry(cell)
632 .or_default()
633 .push(CellOrRange::Range((
634 *sheet_index,
635 r1.min(r2),
636 c1.min(c2),
637 r1.max(r2),
638 c1.max(c2),
639 )));
640 CalcResult::Range {
641 left: CellReferenceIndex {
642 sheet: *sheet_index,
643 row: r1.min(r2),
644 column: c1.min(c2),
645 },
646 right: CellReferenceIndex {
647 sheet: *sheet_index,
648 row: r1.max(r2),
649 column: c1.max(c2),
650 },
651 }
652 }
653 OpConcatenateKind { left, right } => self.handle_concatenate(left, right, cell),
654 OpProductKind { kind, left, right } => match kind {
655 OpProduct::Times => {
656 self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 * f2))
657 }
658 OpProduct::Divide => self.handle_arithmetic(left, right, cell, &|f1, f2| {
659 if f2 == 0.0 {
660 Err(Error::DIV)
661 } else {
662 Ok(f1 / f2)
663 }
664 }),
665 },
666 OpPowerKind { left, right } => {
667 self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1.powf(f2)))
668 }
669 FunctionKind { kind, args } => self.evaluate_function(kind, args, cell),
670 NamedFunctionKind { name, args, id } => {
671 let lambda_result = if let Some(var_id) = id {
672 match self.variable_stack.get(&(*var_id as usize)) {
674 Some(v) => v.clone(),
675 None => {
676 return CalcResult::new_error(
677 Error::NAME,
678 cell,
679 format!("Variable \"{name}\" not found in scope."),
680 )
681 }
682 }
683 } else {
684 let name_lower = name.to_lowercase();
688 let found = self
689 .parsed_defined_names
690 .get(&(Some(cell.sheet), name_lower.clone()))
691 .or_else(|| self.parsed_defined_names.get(&(None, name_lower)))
692 .cloned();
693 match found {
694 Some(ParsedDefinedName::LambdaDefinition(param_names, body)) => {
695 let lambda_id = self.get_next_lambda_id();
696 self.lambdas.insert(lambda_id, (param_names, body));
697 CalcResult::Lambda(lambda_id)
698 }
699 _ => {
700 return CalcResult::new_error(
701 Error::NAME,
702 cell,
703 format!("Invalid function: {name}"),
704 )
705 }
706 }
707 };
708 self.call_lambda(lambda_result, args, cell)
709 }
710 ArrayKind(s) => CalcResult::Array(s.to_owned()),
711 DefinedNameKind((name, scope, _)) => {
712 if let Ok(Some(parsed_defined_name)) = self.get_parsed_defined_name(name, *scope) {
713 match parsed_defined_name {
714 ParsedDefinedName::CellReference(reference) => {
715 self.evaluate_cell(reference)
716 }
717 ParsedDefinedName::RangeReference(range) => CalcResult::Range {
718 left: range.left,
719 right: range.right,
720 },
721 ParsedDefinedName::LambdaDefinition(param_names, body) => {
722 let lambda_id = self.get_next_lambda_id();
723 self.lambdas.insert(lambda_id, (param_names, body));
724 CalcResult::Lambda(lambda_id)
725 }
726 ParsedDefinedName::InvalidDefinedNameFormula => CalcResult::new_error(
727 Error::NAME,
728 cell,
729 format!("Defined name \"{name}\" is not a reference."),
730 ),
731 }
732 } else {
733 CalcResult::new_error(
734 Error::NAME,
735 cell,
736 format!("Defined name \"{name}\" not found."),
737 )
738 }
739 }
740 TableNameKind(s) => CalcResult::new_error(
741 Error::NAME,
742 cell,
743 format!("table name \"{s}\" not supported."),
744 ),
745 NamedVariableKind { name, id: Some(id) } => {
746 match self.variable_stack.get(&(*id as usize)) {
747 Some(v) => v.clone(),
748 None => CalcResult::new_error(
749 Error::NAME,
750 cell,
751 format!("Variable \"{name}\" not found in scope."),
752 ),
753 }
754 }
755 NamedVariableKind { name, id: None } => CalcResult::new_error(
756 Error::NAME,
757 cell,
758 format!("Variable name \"{name}\" not found."),
759 ),
760 CompareKind { kind, left, right } => self.handle_comparison(left, right, cell, kind),
761 UnaryKind { kind, right } => {
762 let r = match self.get_number(right, cell) {
763 Ok(f) => f,
764 Err(s) => {
765 return s;
766 }
767 };
768 match kind {
769 OpUnary::Minus => CalcResult::Number(-r),
770 OpUnary::Percentage => CalcResult::Number(r / 100.0),
771 }
772 }
773 ErrorKind(kind) => CalcResult::new_error(kind.clone(), cell, "".to_string()),
774 ParseErrorKind {
775 formula, message, ..
776 } => CalcResult::new_error(
777 Error::ERROR,
778 cell,
779 format!("Error parsing {formula}: {message}"),
780 ),
781 EmptyArgKind => CalcResult::EmptyArg,
782 SpillRangeOperator { child } => match self.evaluate_node_with_reference(child, cell) {
783 CalcResult::Range { left, right } => {
784 if left != right {
785 return CalcResult::new_error(
786 Error::ERROR,
787 cell,
788 format!("Error with Spill Range Operator in cell {cell:?}"),
789 );
790 }
791 let sheet = left.sheet;
793 let row = left.row;
794 let column = left.column;
795 let worksheet = match self.workbook.worksheet(sheet) {
796 Ok(s) => s,
797 Err(e) => {
798 return CalcResult::new_error(
799 Error::REF,
800 cell,
801 format!("Sheet index {sheet} not found: {e}"),
802 );
803 }
804 };
805 match worksheet.get_cell_spill(row, column) {
806 Ok((width, height)) => CalcResult::Range {
807 left: CellReferenceIndex { sheet, row, column },
808 right: CellReferenceIndex {
809 sheet,
810 row: row + height - 1,
811 column: column + width - 1,
812 },
813 },
814 Err(e) => CalcResult::new_error(
815 Error::REF,
816 cell,
817 format!("Cell {sheet}!{row},{column} not found: {e}"),
818 ),
819 }
820 }
821 _ => CalcResult::new_error(
822 Error::ERROR,
823 cell,
824 format!("Error with Spill Range Operator in cell {cell:?}"),
825 ),
826 },
827 ImplicitIntersection {
828 automatic: _,
829 child,
830 } => match self.evaluate_node_with_reference(child, cell) {
831 CalcResult::Range { left, right } => {
832 match implicit_intersection(&cell, &Range { left, right }) {
833 Some(cell_reference) => self.evaluate_cell(cell_reference),
834 None => CalcResult::new_error(
835 Error::VALUE,
836 cell,
837 format!("Error with Implicit Intersection in cell {cell:?}"),
838 ),
839 }
840 }
841 _ => self.evaluate_node_in_context(child, cell),
842 },
843 LambdaDefKind { parameters, body } => {
844 let id = self.get_next_lambda_id();
845 self.lambdas.insert(id, (parameters.clone(), *body.clone()));
846 CalcResult::Lambda(id)
847 }
848 LambdaCallKind { lambda, args } => {
849 let lambda_result = self.evaluate_node_in_context(lambda, cell);
850 self.call_lambda(lambda_result, args, cell)
851 }
852 }
853 }
854
855 fn cell_reference_to_string(
856 &self,
857 cell_reference: &CellReferenceIndex,
858 ) -> Result<String, String> {
859 let sheet = self.workbook.worksheet(cell_reference.sheet)?;
860 let column = utils::number_to_column(cell_reference.column)
861 .ok_or_else(|| "Invalid column".to_string())?;
862 if !is_valid_row(cell_reference.row) {
863 return Err("Invalid row".to_string());
864 }
865 Ok(format!("{}!{}{}", sheet.name, column, cell_reference.row))
866 }
867
868 fn get_value_from_array(
869 &self,
870 array: &[Vec<ArrayNode>],
871 row: i32,
872 column: i32,
873 ) -> Option<ArrayNode> {
874 let width = array[0].len() as i32;
875 let height = array.len() as i32;
876 if row < 1 || row > height || column < 1 || column > width {
877 return None;
878 }
879 let value = &array[(row - 1) as usize][(column - 1) as usize];
880 Some(value.clone())
881 }
882
883 fn set_cells_with_result(
891 &mut self,
892 cell_reference: CellReferenceIndex,
893 cell: &Cell,
894 result: &CalcResult,
895 ) -> Result<(), String> {
896 let CellReferenceIndex { sheet, column, row } = cell_reference;
897 let original_range = match cell {
898 Cell::ArrayFormula {
899 r,
900 kind: ArrayKind::Cse,
901 ..
902 } => Some((false, (r.0, r.1))),
903 Cell::ArrayFormula {
904 r,
905 kind: ArrayKind::Dynamic,
906 ..
907 } => Some((true, (r.0, r.1))),
908 _ => None,
909 };
910 let s = cell.get_style();
911 let formula = match cell.get_formula() {
912 Some(f) => f,
913 None => return Ok(()),
914 };
915 if let CalcResult::Array(array) = result {
919 if array.is_empty() || array[0].is_empty() {
920 return self.set_cells_with_result(
921 cell_reference,
922 cell,
923 &CalcResult::new_error(
924 Error::CALC,
925 cell_reference,
926 "Formula produced a zero-size array".to_string(),
927 ),
928 );
929 }
930 let array_width = array[0].len() as i32;
931 let array_height = array.len() as i32;
932
933 match original_range {
934 Some((true, _)) => {
935 if row + array_height - 1 > LAST_ROW || column + array_width - 1 > LAST_COLUMN {
936 return self.set_cells_with_result(
937 cell_reference,
938 cell,
939 &CalcResult::new_error(
940 Error::SPILL,
941 cell_reference,
942 "Spill would exceed worksheet bounds".to_string(),
943 ),
944 );
945 }
946 let sheet_data = &self.workbook.worksheets[sheet as usize].sheet_data;
949 for r in row..row + array_height {
950 let row_data = sheet_data.get(&r);
951 for c in column..column + array_width {
952 if r == row && c == column {
953 continue;
954 }
955 let blocking = row_data
962 .and_then(|row_map| row_map.get(&c))
963 .map(|cell| match cell {
964 Cell::EmptyCell { .. } => false,
965 Cell::SpillCell { a, .. } if *a == (row, column) => false,
966 _ => true,
967 })
968 .unwrap_or(false);
969 if blocking {
970 return self.set_cells_with_result(
971 cell_reference,
972 cell,
973 &CalcResult::new_error(
974 Error::SPILL,
975 cell_reference,
976 "Cannot spill array result".to_string(),
977 ),
978 );
979 }
980 }
981 }
982 let worksheet = &mut self.workbook.worksheets[sheet as usize];
983 for r in row..row + array_height {
986 for c in column..column + array_width {
987 let value = array[(r - row) as usize][(c - column) as usize].clone();
988 let cell = if r == row && c == column {
989 Cell::ArrayFormula {
990 f: formula,
991 s,
992 r: (array_width, array_height),
993 kind: ArrayKind::Dynamic,
994 v: array_node_to_formula_value(value),
995 }
996 } else {
997 let existing_style = worksheet.get_style(r, c);
998 Cell::SpillCell {
999 a: (row, column),
1000 s: existing_style,
1001 v: array_node_to_spill_value(value),
1002 }
1003 };
1004 worksheet.update_cell(r, c, cell)?;
1005 }
1006 }
1007 return Ok(());
1008 }
1009 Some((false, (original_width, original_height))) => {
1010 for r in row..row + original_height {
1013 for c in column..column + original_width {
1014 let rel_row = r - row + 1;
1015 let rel_col = c - column + 1;
1016 let value = self.get_value_from_array(array, rel_row, rel_col);
1017 let new_cell = if r == row && c == column {
1018 let fv = match value {
1019 Some(node) => array_node_to_formula_value(node),
1020 None => FormulaValue::Error {
1021 ei: Error::NIMPL,
1022 o: "".to_string(),
1023 m: "Unexpected array result".to_string(),
1024 },
1025 };
1026 Cell::ArrayFormula {
1027 f: formula,
1028 s,
1029 r: (original_width, original_height),
1030 kind: ArrayKind::Cse,
1031 v: fv,
1032 }
1033 } else {
1034 let sv = match value {
1035 Some(node) => array_node_to_spill_value(node),
1036 None => SpillValue::Error(Error::VALUE),
1037 };
1038 let existing_style =
1039 self.workbook.worksheets[sheet as usize].get_style(r, c);
1040 Cell::SpillCell {
1041 s: existing_style,
1042 a: (row, column),
1043 v: sv,
1044 }
1045 };
1046 *self.workbook.worksheets[sheet as usize]
1047 .sheet_data
1048 .get_mut(&r)
1049 .ok_or("expected a row")?
1050 .get_mut(&c)
1051 .ok_or("expected a column")? = new_cell;
1052 }
1053 }
1054 return Ok(());
1056 }
1057 None => {
1058 let coerced = if array_width == 1 && array_height == 1 {
1067 match self.get_value_from_array(array, 1, 1) {
1068 Some(node) => array_node_to_formula_value(node),
1069 None => FormulaValue::Error {
1070 ei: Error::VALUE,
1071 o: "".to_string(),
1072 m: "Unexpected array result".to_string(),
1073 },
1074 }
1075 } else {
1076 debug_assert!(
1083 false,
1084 "Larger-than-1x1 array reached scalar-context cell \
1085 (sheet={sheet}, row={row}, column={column}, \
1086 {array_width}x{array_height}); implicit intersection \
1087 was expected to collapse it.",
1088 );
1089 FormulaValue::Error {
1090 ei: Error::VALUE,
1091 o: "".to_string(),
1092 m: "Array result in scalar context".to_string(),
1093 }
1094 };
1095 *self.workbook.worksheets[sheet as usize]
1096 .sheet_data
1097 .get_mut(&row)
1098 .ok_or("expected a row")?
1099 .get_mut(&column)
1100 .ok_or("expected a column")? = Cell::CellFormula {
1101 f: formula,
1102 s,
1103 v: coerced,
1104 };
1105 return Ok(());
1106 }
1107 }
1108 }
1109
1110 let formula_value = match result {
1111 CalcResult::Number(value) => {
1112 if value.is_nan() || value.is_infinite() {
1114 return self.set_cells_with_result(
1116 cell_reference,
1117 cell,
1118 &CalcResult::Error {
1119 error: Error::NUM,
1120 origin: cell_reference,
1121 message: "".to_string(),
1122 },
1123 );
1124 }
1125 FormulaValue::Number(*value)
1126 }
1127 CalcResult::String(value) => FormulaValue::Text(value.clone()),
1128 CalcResult::Boolean(value) => FormulaValue::Boolean(*value),
1129 CalcResult::Error {
1130 error,
1131 origin,
1132 message,
1133 } => {
1134 let o = match self.cell_reference_to_string(origin) {
1135 Ok(s) => s,
1136 Err(_) => "".to_string(),
1137 };
1138 FormulaValue::Error {
1139 ei: error.clone(),
1140 o,
1141 m: message.to_string(),
1142 }
1143 }
1144 CalcResult::Range { .. } => {
1145 debug_assert!(false, "Unexpected range result in non-array formula");
1147 return Err("Cannot set a range as cell value".to_string());
1148 }
1149 CalcResult::EmptyCell | CalcResult::EmptyArg => {
1150 return self.set_cells_with_result(cell_reference, cell, &CalcResult::Number(0.0));
1152 }
1153 CalcResult::Array(_) | CalcResult::Lambda(_) => {
1155 debug_assert!(false, "Unexpected array result in non-array formula");
1156 return Err("Unexpected array result in non-array formula".to_string());
1157 }
1158 };
1159
1160 let new_cell = match original_range {
1161 Some((is_dynamic, (width, height))) => {
1162 let (kind, r) = if is_dynamic {
1163 (ArrayKind::Dynamic, (1, 1))
1164 } else {
1165 (ArrayKind::Cse, (width, height))
1166 };
1167 Cell::ArrayFormula {
1168 f: formula,
1169 s,
1170 r,
1171 kind,
1172 v: formula_value.clone(),
1173 }
1174 }
1175 None => Cell::CellFormula {
1176 f: formula,
1177 s,
1178 v: formula_value.clone(),
1179 },
1180 };
1181
1182 if let Some((false, (width, height))) = original_range {
1184 let spill_value = formula_value_to_spill_value(&formula_value);
1185 let ws = &mut self.workbook.worksheets[sheet as usize];
1186 for r in row..row + height {
1187 for c in column..column + width {
1188 if r == row && c == column {
1189 continue;
1190 }
1191 let existing_style = ws.get_style(r, c);
1192 ws.update_cell(
1193 r,
1194 c,
1195 Cell::SpillCell {
1196 a: (row, column),
1197 s: existing_style,
1198 v: spill_value.clone(),
1199 },
1200 )?;
1201 }
1202 }
1203 }
1204
1205 self.workbook.worksheets[sheet as usize].update_cell(row, column, new_cell)?;
1206 Ok(())
1207 }
1208
1209 pub fn set_sheet_color(&mut self, sheet: u32, color: &Color) -> Result<(), String> {
1224 let worksheet = self.workbook.worksheet_mut(sheet)?;
1225 worksheet.color = color.clone();
1226 Ok(())
1227 }
1228
1229 pub fn set_sheet_state(&mut self, sheet: u32, state: SheetState) -> Result<(), String> {
1231 let worksheet = self.workbook.worksheet_mut(sheet)?;
1232 worksheet.state = state;
1233 Ok(())
1234 }
1235
1236 pub fn set_theme(&mut self, theme: crate::types::Theme) {
1238 self.workbook.theme = theme;
1239 self.evaluate_conditional_formatting();
1240 }
1241
1242 pub fn get_theme(&self) -> Theme {
1244 self.workbook.theme.clone()
1245 }
1246
1247 pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<(), String> {
1249 let worksheet = self.workbook.worksheet_mut(sheet)?;
1250 worksheet.show_grid_lines = show_grid_lines;
1251 Ok(())
1252 }
1253
1254 fn get_cell_value(&self, cell: &Cell, cell_reference: CellReferenceIndex) -> CalcResult {
1256 use Cell::*;
1257 match cell {
1258 EmptyCell { .. } => CalcResult::EmptyCell,
1259 BooleanCell { v, .. } => CalcResult::Boolean(*v),
1260 NumberCell { v, .. } => CalcResult::Number(*v),
1261 ErrorCell { ei, .. } => {
1262 let message = ei.to_localized_error_string(self.language);
1263 CalcResult::new_error(ei.clone(), cell_reference, message)
1264 }
1265 SharedString { si, .. } => {
1266 if let Some(s) = self.workbook.shared_strings.get(*si as usize) {
1267 CalcResult::String(s.clone())
1268 } else {
1269 let message = "Invalid shared string".to_string();
1270 CalcResult::new_error(Error::ERROR, cell_reference, message)
1271 }
1272 }
1273 CellFormula {
1274 v: FormulaValue::Unevaluated,
1275 ..
1276 }
1277 | ArrayFormula {
1278 v: FormulaValue::Unevaluated,
1279 ..
1280 } => CalcResult::Error {
1281 error: Error::ERROR,
1282 origin: cell_reference,
1283 message: "Unevaluated formula".to_string(),
1284 },
1285 CellFormula {
1286 v: FormulaValue::Boolean(v),
1287 ..
1288 }
1289 | ArrayFormula {
1290 v: FormulaValue::Boolean(v),
1291 ..
1292 } => CalcResult::Boolean(*v),
1293 CellFormula {
1294 v: FormulaValue::Number(v),
1295 ..
1296 }
1297 | ArrayFormula {
1298 v: FormulaValue::Number(v),
1299 ..
1300 } => CalcResult::Number(*v),
1301 CellFormula {
1302 v: FormulaValue::Text(v),
1303 ..
1304 }
1305 | ArrayFormula {
1306 v: FormulaValue::Text(v),
1307 ..
1308 } => CalcResult::String(v.clone()),
1309 CellFormula {
1310 v: FormulaValue::Error { ei, o, m },
1311 ..
1312 }
1313 | ArrayFormula {
1314 v: FormulaValue::Error { ei, o, m },
1315 ..
1316 } => {
1317 if let Some(cell_reference) = self.parse_reference(o) {
1318 CalcResult::new_error(ei.clone(), cell_reference, m.clone())
1319 } else {
1320 CalcResult::Error {
1321 error: ei.clone(),
1322 origin: cell_reference,
1323 message: ei.to_localized_error_string(self.language),
1324 }
1325 }
1326 }
1327 SpillCell {
1328 v: SpillValue::Number(v),
1329 ..
1330 } => CalcResult::Number(*v),
1331 SpillCell {
1332 v: SpillValue::Boolean(v),
1333 ..
1334 } => CalcResult::Boolean(*v),
1335 SpillCell {
1336 v: SpillValue::Text(v),
1337 ..
1338 } => CalcResult::String(v.clone()),
1339 SpillCell {
1340 v: SpillValue::Error(ei),
1341 ..
1342 } => {
1343 let message = ei.to_localized_error_string(self.language);
1344 CalcResult::new_error(ei.clone(), cell_reference, message)
1345 }
1346 }
1347 }
1348
1349 pub fn is_empty_cell(&self, sheet: u32, row: i32, column: i32) -> Result<bool, String> {
1364 self.workbook.worksheet(sheet)?.is_empty_cell(row, column)
1365 }
1366
1367 pub(crate) fn evaluate_range(
1369 &mut self,
1370 left: CellReferenceIndex,
1371 right: CellReferenceIndex,
1372 ) -> Vec<Vec<ArrayNode>> {
1373 let mut result = Vec::new();
1374 for r in left.row..=right.row {
1375 let mut row_result = Vec::new();
1376 for c in left.column..=right.column {
1377 let cell_reference = CellReferenceIndex {
1378 sheet: left.sheet,
1379 row: r,
1380 column: c,
1381 };
1382 let value = match self.evaluate_cell(cell_reference) {
1383 CalcResult::Number(n) => ArrayNode::Number(n),
1384 CalcResult::Boolean(b) => ArrayNode::Boolean(b),
1385 CalcResult::String(s) => ArrayNode::String(s),
1386 CalcResult::Error { error, .. } => ArrayNode::Error(error),
1387 CalcResult::EmptyCell | CalcResult::EmptyArg => ArrayNode::Empty,
1388 CalcResult::Range { .. } | CalcResult::Array(_) | CalcResult::Lambda(_) => {
1389 debug_assert!(false, "Unexpected array result in non-array formula");
1391 ArrayNode::Error(Error::NIMPL)
1392 }
1393 };
1394 row_result.push(value);
1395 }
1396 result.push(row_result);
1397 }
1398 result
1399 }
1400
1401 #[inline(always)]
1402 fn fetch_cell(&self, cell_reference: CellReferenceIndex) -> Option<&Cell> {
1403 self.workbook.worksheets[cell_reference.sheet as usize]
1404 .sheet_data
1405 .get(&cell_reference.row)?
1406 .get(&cell_reference.column)
1407 }
1408
1409 pub(crate) fn evaluate_cell(&mut self, cell_reference: CellReferenceIndex) -> CalcResult {
1412 let original_cell = match self.fetch_cell(cell_reference) {
1413 Some(c) => c.clone(),
1414 None => return CalcResult::EmptyCell,
1415 };
1416
1417 if let Cell::SpillCell { a, .. } = original_cell {
1418 let anchor_cell_reference = CellReferenceIndex {
1422 sheet: cell_reference.sheet,
1423 column: a.1,
1424 row: a.0,
1425 };
1426 let _ = self.evaluate_cell(anchor_cell_reference);
1428 let cell = match self.fetch_cell(cell_reference) {
1430 Some(c) => c,
1431 None => return CalcResult::EmptyCell,
1432 };
1433 return self.get_cell_value(cell, cell_reference);
1435 };
1436
1437 match original_cell.get_formula() {
1438 Some(f) => {
1439 let key = (
1440 cell_reference.sheet,
1441 cell_reference.row,
1442 cell_reference.column,
1443 );
1444 if let Some(state) = self.cells.get(&key) {
1445 match state {
1446 CellState::Evaluating => {
1447 return CalcResult::new_error(
1448 Error::CIRC,
1449 cell_reference,
1450 "Circular reference detected".to_string(),
1451 );
1452 }
1453 CellState::Evaluated => {
1454 return self.get_cell_value(&original_cell, cell_reference);
1455 }
1456 }
1457 }
1458 if let Cell::ArrayFormula {
1462 r,
1463 kind: ArrayKind::Dynamic,
1464 ..
1465 } = &original_cell
1466 {
1467 let (width, height) = *r;
1468 let ws = match self.workbook.worksheet_mut(cell_reference.sheet) {
1469 Ok(ws) => ws,
1470 Err(_) => {
1471 return CalcResult::new_error(
1472 Error::ERROR,
1473 cell_reference,
1474 "Invalid sheet".to_string(),
1475 )
1476 }
1477 };
1478 for r in cell_reference.row..cell_reference.row + height {
1479 for c in cell_reference.column..cell_reference.column + width {
1480 if r == cell_reference.row && c == cell_reference.column {
1481 continue;
1482 }
1483 let is_own_spill = ws
1487 .sheet_data
1488 .get(&r)
1489 .and_then(|row_data| row_data.get(&c))
1490 .map(|cell| {
1491 matches!(cell, Cell::SpillCell { a, .. }
1492 if *a == (cell_reference.row, cell_reference.column))
1493 })
1494 .unwrap_or(false);
1495 if is_own_spill {
1496 let _ = ws.cell_clear_contents(r, c);
1497 }
1498 }
1499 }
1500 }
1501 self.cells.insert(key, CellState::Evaluating);
1503 let (node, _static_result) =
1504 &self.parsed_formulas[cell_reference.sheet as usize][f as usize];
1505 let result = self.evaluate_node_in_context(&node.clone(), cell_reference);
1506
1507 let result = if let CalcResult::Range { left, right } = result {
1509 if left.sheet == right.sheet
1510 && left.row == right.row
1511 && left.column == right.column
1512 {
1513 self.evaluate_cell(left)
1515 } else {
1516 let array_height = right.row - left.row + 1;
1517 let array_width = right.column - left.column + 1;
1518 let last_row = cell_reference.row + array_height - 1;
1519 let last_col = cell_reference.column + array_width - 1;
1520 if last_row > LAST_ROW || last_col > LAST_COLUMN {
1521 CalcResult::new_error(
1522 Error::SPILL,
1523 cell_reference,
1524 "Spill would exceed worksheet bounds".to_string(),
1525 )
1526 } else {
1527 let array = self.evaluate_range(left, right);
1528 CalcResult::Array(array)
1529 }
1530 }
1531 } else if matches!(result, CalcResult::Lambda(_)) {
1532 CalcResult::new_error(
1533 Error::CALC,
1534 cell_reference,
1535 "A LAMBDA was returned but not called".to_string(),
1536 )
1537 } else {
1538 result
1539 };
1540
1541 if let Err(e) = self.set_cells_with_result(cell_reference, &original_cell, &result)
1542 {
1543 self.cells.insert(key, CellState::Evaluated);
1544 return CalcResult::new_error(Error::ERROR, cell_reference, e);
1546 };
1547
1548 self.cells.insert(key, CellState::Evaluated);
1550
1551 match result {
1553 CalcResult::Array(a) => {
1554 let is_array_formula = matches!(original_cell, Cell::ArrayFormula { .. });
1565 let array_height = a.len();
1566 let array_width = if array_height > 0 { a[0].len() } else { 0 };
1567 if !is_array_formula && (array_width != 1 || array_height != 1) {
1568 debug_assert!(
1577 false,
1578 "Larger-than-1x1 array reached scalar-context cell \
1579 ({cell_reference:?}, {array_width}x{array_height}); \
1580 implicit intersection was expected to collapse it.",
1581 );
1582 CalcResult::new_error(
1583 Error::VALUE,
1584 cell_reference,
1585 "Array result in scalar context".to_string(),
1586 )
1587 } else if array_height == 0 || array_width == 0 {
1588 CalcResult::new_error(
1589 Error::CALC,
1590 cell_reference,
1591 "Formula produced a zero-size array".to_string(),
1592 )
1593 } else {
1594 match a[0][0] {
1595 ArrayNode::Number(n) => CalcResult::Number(n),
1596 ArrayNode::Boolean(b) => CalcResult::Boolean(b),
1597 ArrayNode::String(ref s) => CalcResult::String(s.clone()),
1598 ArrayNode::Error(ref error) => {
1599 let message = error.to_localized_error_string(self.language);
1600 CalcResult::new_error(error.clone(), cell_reference, message)
1601 }
1602 ArrayNode::Empty => CalcResult::EmptyCell,
1603 }
1604 }
1605 }
1606 _ => result,
1607 }
1608 }
1609 None => self.get_cell_value(&original_cell, cell_reference),
1610 }
1611 }
1612
1613 pub(crate) fn get_sheet_index_by_name(&self, name: &str) -> Option<u32> {
1614 let worksheets = &self.workbook.worksheets;
1615 for (index, worksheet) in worksheets.iter().enumerate() {
1616 if worksheet.get_name().to_uppercase() == name.to_uppercase() {
1617 return Some(index as u32);
1618 }
1619 }
1620 None
1621 }
1622
1623 pub fn from_bytes(s: &[u8], language_id: &'a str) -> Result<Model<'a>, String> {
1645 let workbook: Workbook =
1646 bitcode::decode(s).map_err(|e| format!("Error parsing workbook: {e}"))?;
1647 Model::from_workbook(workbook, language_id)
1648 }
1649
1650 pub fn from_workbook(workbook: Workbook, language_id: &str) -> Result<Model<'_>, String> {
1669 let parsed_formulas = Vec::new();
1670 let worksheets = &workbook.worksheets;
1671
1672 let worksheet_names = worksheets.iter().map(|s| s.get_name()).collect();
1673
1674 let defined_names = workbook.get_defined_names_with_scope();
1675 let cells = HashMap::new();
1686 let locale =
1687 get_locale(&workbook.settings.locale).map_err(|_| "Invalid locale".to_string())?;
1688 let tz = Tz::parse(&workbook.settings.tz)?;
1689
1690 let language = match get_language(language_id) {
1691 Ok(lang) => lang,
1692 Err(_) => return Err("Invalid language".to_string()),
1693 };
1694 let parser = Parser::new(
1695 worksheet_names,
1696 defined_names,
1697 workbook.tables.clone(),
1698 locale,
1699 language,
1700 );
1701 let mut shared_strings = HashMap::new();
1702 for (index, s) in workbook.shared_strings.iter().enumerate() {
1703 shared_strings.insert(s.to_string(), index);
1704 }
1705
1706 let mut model = Model {
1707 workbook,
1708 parsed_formulas,
1709 shared_strings,
1710 parsed_defined_names: HashMap::new(),
1711 parser,
1712 cells,
1713 language,
1714 locale,
1715 tz,
1716 view_id: 0,
1717 variable_stack: HashMap::new(),
1718 last_variable_id: 0,
1719 lambdas: HashMap::new(),
1720 last_lambda_id: 0,
1721 spill_cells: Vec::new(),
1722 support: HashMap::new(),
1723 cf_cache: HashMap::new(),
1724 links: HashMap::new(),
1725 };
1726
1727 model.parse_formulas();
1728 model.parse_defined_names();
1729 model.evaluate_conditional_formatting();
1730
1731 Ok(model)
1732 }
1733
1734 pub fn parse_reference(&self, s: &str) -> Option<CellReferenceIndex> {
1750 let bytes = s.as_bytes();
1751 let mut sheet_name = "".to_string();
1752 let mut column = "".to_string();
1753 let mut row = "".to_string();
1754 let mut state = "sheet"; for &byte in bytes {
1756 match state {
1757 "sheet" => {
1758 if byte == b'!' {
1759 state = "col"
1760 } else {
1761 sheet_name.push(byte as char);
1762 }
1763 }
1764 "col" => {
1765 if byte.is_ascii_alphabetic() {
1766 column.push(byte as char);
1767 } else {
1768 state = "row";
1769 row.push(byte as char);
1770 }
1771 }
1772 _ => {
1773 row.push(byte as char);
1774 }
1775 }
1776 }
1777 let sheet = self.get_sheet_index_by_name(&sheet_name)?;
1778 let row = match row.parse::<i32>() {
1779 Ok(r) => r,
1780 Err(_) => return None,
1781 };
1782 if !(1..=constants::LAST_ROW).contains(&row) {
1783 return None;
1784 }
1785
1786 let column = match utils::column_to_number(&column) {
1787 Ok(column) => {
1788 if is_valid_column_number(column) {
1789 column
1790 } else {
1791 return None;
1792 }
1793 }
1794 Err(_) => return None,
1795 };
1796
1797 Some(CellReferenceIndex { sheet, row, column })
1798 }
1799
1800 pub fn move_cell_value_to_area(
1822 &mut self,
1823 value: &str,
1824 source: &CellReferenceIndex,
1825 target: &CellReferenceIndex,
1826 area: &Area,
1827 ) -> Result<String, String> {
1828 let source_sheet_name = self
1829 .workbook
1830 .worksheet(source.sheet)
1831 .map_err(|e| format!("Could not find source worksheet: {e}"))?
1832 .get_name();
1833 if source.sheet != area.sheet {
1834 return Err("Source and area are in different sheets".to_string());
1835 }
1836 if source.row < area.row || source.row >= area.row + area.height {
1837 return Err("Source is outside the area".to_string());
1838 }
1839 if source.column < area.column || source.column >= area.column + area.width {
1840 return Err("Source is outside the area".to_string());
1841 }
1842 let target_sheet_name = self
1843 .workbook
1844 .worksheet(target.sheet)
1845 .map_err(|e| format!("Could not find target worksheet: {e}"))?
1846 .get_name();
1847 if let Some(formula) = self.formula_without_prefix(value) {
1848 let cell_reference = CellReferenceRC {
1849 sheet: source_sheet_name.to_owned(),
1850 row: source.row,
1851 column: source.column,
1852 };
1853 let formula_str = move_formula(
1854 &self.parser.parse(formula, &cell_reference),
1855 &MoveContext {
1856 source_sheet_name: &source_sheet_name,
1857 row: source.row,
1858 column: source.column,
1859 area,
1860 target_sheet_name: &target_sheet_name,
1861 row_delta: target.row - source.row,
1862 column_delta: target.column - source.column,
1863 },
1864 self.locale,
1865 self.language,
1866 );
1867 Ok(format!("={formula_str}"))
1868 } else {
1869 Ok(value.to_string())
1870 }
1871 }
1872
1873 pub fn extend_to(
1894 &self,
1895 sheet: u32,
1896 row: i32,
1897 column: i32,
1898 target_row: i32,
1899 target_column: i32,
1900 ) -> Result<String, String> {
1901 let cell = self.workbook.worksheet(sheet)?.cell(row, column);
1902 let result = match cell {
1903 Some(cell) => match cell.get_formula() {
1904 None => cell.get_localized_text(
1905 &self.workbook.shared_strings,
1906 self.locale,
1907 self.language,
1908 ),
1909 Some(i) => {
1910 let (formula, _static_result) =
1911 &self.parsed_formulas[sheet as usize][i as usize];
1912 let cell_ref = CellReferenceRC {
1913 sheet: self.workbook.worksheets[sheet as usize].get_name(),
1914 row: target_row,
1915 column: target_column,
1916 };
1917 format!(
1918 "={}",
1919 to_localized_string(formula, &cell_ref, self.locale, self.language)
1920 )
1921 }
1922 },
1923 None => "".to_string(),
1924 };
1925 Ok(result)
1926 }
1927
1928 pub fn extend_copied_value(
1949 &mut self,
1950 value: &str,
1951 source: &CellReferenceIndex,
1952 target: &CellReferenceIndex,
1953 ) -> Result<String, String> {
1954 let source_sheet_name = match self.workbook.worksheets.get(source.sheet as usize) {
1955 Some(ws) => ws.get_name(),
1956 None => {
1957 return Err("Invalid worksheet index".to_owned());
1958 }
1959 };
1960 let target_sheet_name = match self.workbook.worksheets.get(target.sheet as usize) {
1961 Some(ws) => ws.get_name(),
1962 None => {
1963 return Err("Invalid worksheet index".to_owned());
1964 }
1965 };
1966
1967 if let Some(formula_str) = self.formula_without_prefix(value) {
1968 let cell_reference = CellReferenceRC {
1969 sheet: source_sheet_name.to_string(),
1970 row: source.row,
1971 column: source.column,
1972 };
1973 let formula = &self.parser.parse(formula_str, &cell_reference);
1974 let cell_reference = CellReferenceRC {
1975 sheet: target_sheet_name,
1976 row: target.row,
1977 column: target.column,
1978 };
1979 return Ok(format!(
1980 "={}",
1981 to_localized_string(formula, &cell_reference, self.locale, self.language)
1982 ));
1983 }
1984 Ok(value.to_string())
1985 }
1986
1987 pub fn get_cell_formula(
2007 &self,
2008 sheet: u32,
2009 row: i32,
2010 column: i32,
2011 ) -> Result<Option<String>, String> {
2012 let worksheet = self.workbook.worksheet(sheet)?;
2013 match worksheet.cell(row, column) {
2014 Some(cell) => match cell.get_formula() {
2015 Some(formula_index) => {
2016 let (formula, _static_result) = &self
2017 .parsed_formulas
2018 .get(sheet as usize)
2019 .ok_or("missing sheet")?
2020 .get(formula_index as usize)
2021 .ok_or("missing formula")?;
2022 let cell_ref = CellReferenceRC {
2023 sheet: worksheet.get_name(),
2024 row,
2025 column,
2026 };
2027 Ok(Some(format!(
2028 "={}",
2029 to_localized_string(formula, &cell_ref, self.locale, self.language)
2030 )))
2031 }
2032 None => Ok(None),
2033 },
2034 None => Ok(None),
2035 }
2036 }
2037
2038 pub(crate) fn get_english_cell_formula(
2043 &self,
2044 sheet: u32,
2045 row: i32,
2046 column: i32,
2047 ) -> Result<Option<String>, String> {
2048 let worksheet = self.workbook.worksheet(sheet)?;
2049 match worksheet.cell(row, column) {
2050 Some(cell) => match cell.get_formula() {
2051 Some(formula_index) => {
2052 let (formula, _static_result) = &self
2053 .parsed_formulas
2054 .get(sheet as usize)
2055 .ok_or("missing sheet")?
2056 .get(formula_index as usize)
2057 .ok_or("missing formula")?;
2058 let cell_ref = CellReferenceRC {
2059 sheet: worksheet.get_name(),
2060 row,
2061 column,
2062 };
2063 let language_en = get_default_language();
2064 Ok(Some(format!(
2065 "={}",
2066 to_localized_string(formula, &cell_ref, self.locale, language_en)
2067 )))
2068 }
2069 None => Ok(None),
2070 },
2071 None => Ok(None),
2072 }
2073 }
2074
2075 pub fn update_cell_with_text(
2100 &mut self,
2101 sheet: u32,
2102 row: i32,
2103 column: i32,
2104 value: &str,
2105 ) -> Result<(), String> {
2106 let style_index = self.get_cell_style_index(sheet, row, column)?;
2107 let new_style_index;
2108 if common::value_needs_quoting(value, self.language) {
2109 new_style_index = self
2110 .workbook
2111 .styles
2112 .get_style_with_quote_prefix(style_index)?;
2113 } else if self.workbook.styles.style_is_quote_prefix(style_index) {
2114 new_style_index = self
2115 .workbook
2116 .styles
2117 .get_style_without_quote_prefix(style_index)?;
2118 } else {
2119 new_style_index = style_index;
2120 }
2121
2122 self.set_cell_with_string(sheet, row, column, value, new_style_index)
2123 }
2124
2125 pub fn update_cell_with_bool(
2150 &mut self,
2151 sheet: u32,
2152 row: i32,
2153 column: i32,
2154 value: bool,
2155 ) -> Result<(), String> {
2156 let style_index = self.get_cell_style_index(sheet, row, column)?;
2157 let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
2158 self.workbook
2159 .styles
2160 .get_style_without_quote_prefix(style_index)?
2161 } else {
2162 style_index
2163 };
2164 self.set_cell_with_boolean(sheet, row, column, value, new_style_index)
2165 }
2166
2167 pub fn update_cell_with_number(
2192 &mut self,
2193 sheet: u32,
2194 row: i32,
2195 column: i32,
2196 value: f64,
2197 ) -> Result<(), String> {
2198 let style_index = self.get_cell_style_index(sheet, row, column)?;
2199 let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
2200 self.workbook
2201 .styles
2202 .get_style_without_quote_prefix(style_index)?
2203 } else {
2204 style_index
2205 };
2206 self.set_cell_with_number(sheet, row, column, value, new_style_index)
2207 }
2208
2209 pub fn update_cell_with_formula(
2237 &mut self,
2238 sheet: u32,
2239 row: i32,
2240 column: i32,
2241 formula: String,
2242 ) -> Result<(), String> {
2243 let mut style_index = self.get_cell_style_index(sheet, row, column)?;
2244 if self.workbook.styles.style_is_quote_prefix(style_index) {
2245 style_index = self
2246 .workbook
2247 .styles
2248 .get_style_without_quote_prefix(style_index)?;
2249 }
2250
2251 if let Some(new_formula) = self.formula_without_prefix(&formula) {
2252 self.set_cell_with_formula(sheet, row, column, new_formula, style_index)?;
2253 Ok(())
2254 } else {
2255 Err(format!("\"{formula}\" is not a valid formula"))
2256 }
2257 }
2258
2259 fn prepare_cell_for_user_input(
2267 &mut self,
2268 sheet: u32,
2269 row: i32,
2270 column: i32,
2271 ) -> Result<(), String> {
2272 match self.get_cell_structure(sheet, row, column)? {
2273 CellStructure::SingleCell => {
2274 }
2276 CellStructure::ArrayFormula { range } => {
2277 let (width, height) = range;
2279 if width > 1 || height > 1 {
2280 return Err(
2281 "Cannot write in a cell that is part of an array formula".to_string()
2282 );
2283 }
2284 }
2285 CellStructure::DynamicFormula { range } => {
2286 let (width, height) = range;
2288 let ws = self.workbook.worksheet_mut(sheet)?;
2289 for r in row..row + height {
2290 for c in column..column + width {
2291 let _ = ws.cell_clear_contents(r, c);
2293 }
2294 }
2295 }
2296 CellStructure::SpillArray { .. } => {
2297 return Err("Cannot write in a cell that is part of an array formula".to_string());
2298 }
2299 CellStructure::SpillDynamic { anchor, range } => {
2300 let (anchor_row, anchor_column) = anchor;
2304 let (width, height) = range;
2305 let ws = self.workbook.worksheet_mut(sheet)?;
2306 let (formula_index, anchor_style) = {
2308 let anchor_cell = ws
2309 .cell(anchor_row, anchor_column)
2310 .ok_or_else(|| "Dynamic formula anchor not found".to_string())?;
2311 let fi = anchor_cell
2312 .get_formula()
2313 .ok_or_else(|| "Dynamic formula anchor has no formula".to_string())?;
2314 let s = anchor_cell.get_style();
2315 (fi, s)
2316 };
2317 ws.set_cell_with_dynamic_formula(
2318 anchor_row,
2319 anchor_column,
2320 formula_index,
2321 anchor_style,
2322 1,
2323 1,
2324 )?;
2325 for r in anchor_row..anchor_row + height {
2326 for c in anchor_column..anchor_column + width {
2327 if r == anchor_row && c == anchor_column {
2328 continue;
2329 }
2330 let _ = ws.cell_clear_contents(r, c);
2332 }
2333 }
2334 }
2335 };
2336 Ok(())
2337 }
2338
2339 pub fn set_user_input(
2371 &mut self,
2372 sheet: u32,
2373 row: i32,
2374 column: i32,
2375 value: String,
2376 ) -> Result<(), String> {
2377 self.prepare_cell_for_user_input(sheet, row, column)?;
2379 if value.is_empty() {
2380 let ws = self.workbook.worksheet_mut(sheet)?;
2383 ws.cell_clear_contents(row, column)?;
2384 ws.links.remove(&(row, column));
2385 return Ok(());
2386 }
2387
2388 let style_index = self.get_cell_style_index(sheet, row, column)?;
2390 if let Some(new_value) = value.strip_prefix('\'') {
2391 let new_style = self
2392 .workbook
2393 .styles
2394 .get_style_with_quote_prefix(style_index)?;
2395 self.set_cell_with_string(sheet, row, column, new_value, new_style)?;
2396 } else {
2397 let mut new_style_index = style_index;
2398 if self.workbook.styles.style_is_quote_prefix(style_index) {
2399 new_style_index = self
2400 .workbook
2401 .styles
2402 .get_style_without_quote_prefix(style_index)?;
2403 }
2404 if let Some(formula) = self.formula_without_prefix(&value) {
2405 let formula_index =
2406 self.set_cell_with_formula(sheet, row, column, formula, new_style_index)?;
2407 let cell = CellReferenceIndex { sheet, row, column };
2409 let (parsed_formula, _static_result) =
2410 &self.parsed_formulas[sheet as usize][formula_index as usize];
2411 if let Some(units) = self.compute_node_units(parsed_formula, &cell) {
2412 let new_style_index = self
2413 .workbook
2414 .styles
2415 .get_style_with_format(new_style_index, &units.get_num_fmt())?;
2416 let style = self.workbook.styles.get_style(new_style_index)?;
2417 self.set_cell_style(sheet, row, column, &style)?;
2418 }
2419 } else {
2420 let mut currencies = vec!["$", "€"];
2422 let currency = &self.locale.currency.symbol;
2423 if !currencies.iter().any(|e| e == currency) {
2424 currencies.push(currency);
2425 }
2426
2427 if let Ok((v, number_format)) =
2429 parse_formatted_number(&value, ¤cies, self.locale)
2430 {
2431 if let Some(num_fmt) = number_format {
2432 let should_apply_format = !(is_likely_date_number_format(
2435 &self.workbook.styles.get_style(new_style_index)?.num_fmt,
2436 ) && is_likely_date_number_format(&num_fmt));
2437 if should_apply_format {
2438 new_style_index = self
2439 .workbook
2440 .styles
2441 .get_style_with_format(new_style_index, &num_fmt)?;
2442 }
2443 }
2444 let worksheet = self.workbook.worksheet_mut(sheet)?;
2445 worksheet.set_cell_with_number(row, column, v, new_style_index)?;
2446 return Ok(());
2447 }
2448 if let Ok(v) = value.to_lowercase().parse::<bool>() {
2450 let worksheet = self.workbook.worksheet_mut(sheet)?;
2451 worksheet.set_cell_with_boolean(row, column, v, new_style_index)?;
2452 return Ok(());
2453 }
2454 let upper = value.to_uppercase();
2456 let worksheet = self.workbook.worksheet_mut(sheet)?;
2457 match get_error_by_name(&upper, self.language) {
2458 Some(error) => {
2459 worksheet.set_cell_with_error(row, column, error, new_style_index)?;
2460 }
2461 None => {
2462 self.set_cell_with_string(sheet, row, column, &value, new_style_index)?;
2463 self.auto_link_cell(sheet, row, column, &value)?;
2467 }
2468 }
2469 }
2470 }
2471 Ok(())
2472 }
2473
2474 pub fn set_user_array_formula(
2476 &mut self,
2477 sheet: u32,
2478 row: i32,
2479 column: i32,
2480 width: i32,
2481 height: i32,
2482 value: &str,
2483 ) -> Result<(), String> {
2484 self.prepare_cell_for_user_input(sheet, row, column)?;
2485 let style_index = self.get_cell_style_index(sheet, row, column)?;
2487 if value.strip_prefix('\'').is_none() {
2488 let mut new_style_index = style_index;
2489 if self.workbook.styles.style_is_quote_prefix(style_index) {
2490 new_style_index = self
2491 .workbook
2492 .styles
2493 .get_style_without_quote_prefix(style_index)?;
2494 }
2495 if let Some(formula) = value.strip_prefix('=') {
2496 let formula_index = self.set_cell_with_array_formula(
2498 sheet,
2499 row,
2500 column,
2501 formula,
2502 new_style_index,
2503 width,
2504 height,
2505 )?;
2506
2507 let cell = CellReferenceIndex { sheet, row, column };
2509 let (parsed_formula, _static_result) =
2510 &self.parsed_formulas[sheet as usize][formula_index as usize];
2511
2512 if let Some(units) = self.compute_node_units(parsed_formula, &cell) {
2513 let new_style_index = self
2514 .workbook
2515 .styles
2516 .get_style_with_format(new_style_index, &units.get_num_fmt())?;
2517 let style = self.workbook.styles.get_style(new_style_index)?;
2518 self.set_cell_style(sheet, row, column, &style)?;
2519 }
2520 for r in row..row + height {
2522 for c in column..column + width {
2523 if r == row && c == column {
2524 continue;
2525 }
2526 let mut new_style_index_spill = self.get_cell_style_index(sheet, r, c)?;
2527 if self
2528 .workbook
2529 .styles
2530 .style_is_quote_prefix(new_style_index_spill)
2531 {
2532 new_style_index_spill = self
2533 .workbook
2534 .styles
2535 .get_style_without_quote_prefix(new_style_index_spill)?;
2536 }
2537
2538 self.set_cell_with_string(sheet, r, c, "", new_style_index_spill)?;
2539 }
2540 }
2541 return Ok(());
2542 }
2543 }
2544 for r in row..row + height {
2546 for c in column..column + width {
2547 self.set_user_input(sheet, r, c, value.to_string())?;
2548 }
2549 }
2550
2551 Ok(())
2552 }
2553
2554 pub(crate) fn get_cell_structure(
2555 &self,
2556 sheet: u32,
2557 row: i32,
2558 column: i32,
2559 ) -> Result<CellStructure, String> {
2560 let worksheet = self.workbook.worksheet(sheet)?;
2561 worksheet.get_cell_structure(row, column)
2562 }
2563
2564 fn set_cell_with_formula(
2565 &mut self,
2566 sheet: u32,
2567 row: i32,
2568 column: i32,
2569 formula: &str,
2570 style: i32,
2571 ) -> Result<i32, String> {
2572 let worksheet = self.workbook.worksheet_mut(sheet)?;
2573 let cell_reference = CellReferenceRC {
2574 sheet: worksheet.get_name(),
2575 row,
2576 column,
2577 };
2578 let shared_formulas = &mut worksheet.shared_formulas;
2579 let mut parsed_formula = self.parser.parse(formula, &cell_reference);
2580 if let Node::ParseErrorKind { .. } = parsed_formula {
2583 let new_parsed_formula = self.parser.parse(&format!("{formula})"), &cell_reference);
2584 match new_parsed_formula {
2585 Node::ParseErrorKind { .. } => {}
2586 _ => parsed_formula = new_parsed_formula,
2587 }
2588 }
2589 let static_result = run_static_analysis_on_node(&parsed_formula);
2590 let is_dynamic = !matches!(static_result, StaticResult::Scalar);
2591
2592 let s = to_rc_format(&parsed_formula);
2593 let mut formula_index: i32 = -1;
2594 if let Some(index) = shared_formulas.iter().position(|x| x == &s) {
2595 formula_index = index as i32;
2596 }
2597 if formula_index == -1 {
2598 shared_formulas.push(s);
2599 self.parsed_formulas[sheet as usize].push((parsed_formula, static_result));
2600 formula_index = (shared_formulas.len() as i32) - 1;
2601 }
2602 if is_dynamic {
2603 worksheet.set_cell_with_dynamic_formula(row, column, formula_index, style, 1, 1)?;
2604 } else {
2605 worksheet.set_cell_with_formula(row, column, formula_index, style)?;
2606 }
2607 Ok(formula_index)
2608 }
2609
2610 #[allow(clippy::too_many_arguments)]
2612 pub(crate) fn set_cell_with_array_formula(
2613 &mut self,
2614 sheet: u32,
2615 row: i32,
2616 column: i32,
2617 formula: &str,
2618 style: i32,
2619 width: i32,
2620 height: i32,
2621 ) -> Result<i32, String> {
2622 let worksheet = self.workbook.worksheet_mut(sheet)?;
2623 let cell_reference = CellReferenceRC {
2624 sheet: worksheet.get_name(),
2625 row,
2626 column,
2627 };
2628 let shared_formulas = &mut worksheet.shared_formulas;
2629 let mut parsed_formula = self.parser.parse(formula, &cell_reference);
2630 if let Node::ParseErrorKind { .. } = parsed_formula {
2633 let new_parsed_formula = self.parser.parse(&format!("{formula})"), &cell_reference);
2634 match new_parsed_formula {
2635 Node::ParseErrorKind { .. } => {}
2636 _ => parsed_formula = new_parsed_formula,
2637 }
2638 }
2639 let static_result = run_static_analysis_on_node(&parsed_formula);
2640
2641 let s = to_rc_format(&parsed_formula);
2642 let mut formula_index: i32 = -1;
2643 if let Some(index) = shared_formulas.iter().position(|x| x == &s) {
2644 formula_index = index as i32;
2645 }
2646 if formula_index == -1 {
2647 shared_formulas.push(s);
2648 self.parsed_formulas[sheet as usize].push((parsed_formula, static_result));
2649 formula_index = (shared_formulas.len() as i32) - 1;
2650 }
2651 worksheet.set_cell_with_array_formula(row, column, formula_index, style, width, height)?;
2652 Ok(formula_index)
2653 }
2654
2655 pub(crate) fn set_cell_with_string(
2656 &mut self,
2657 sheet: u32,
2658 row: i32,
2659 column: i32,
2660 value: &str,
2661 style: i32,
2662 ) -> Result<(), String> {
2663 match self.shared_strings.get(value) {
2664 Some(string_index) => {
2665 self.workbook.worksheet_mut(sheet)?.set_cell_with_string(
2666 row,
2667 column,
2668 *string_index as i32,
2669 style,
2670 )?;
2671 }
2672 None => {
2673 let string_index = self.workbook.shared_strings.len();
2674 self.workbook.shared_strings.push(value.to_string());
2675 self.shared_strings.insert(value.to_string(), string_index);
2676 self.workbook.worksheet_mut(sheet)?.set_cell_with_string(
2677 row,
2678 column,
2679 string_index as i32,
2680 style,
2681 )?;
2682 }
2683 }
2684 Ok(())
2685 }
2686
2687 fn set_cell_with_boolean(
2688 &mut self,
2689 sheet: u32,
2690 row: i32,
2691 column: i32,
2692 value: bool,
2693 style: i32,
2694 ) -> Result<(), String> {
2695 self.workbook
2696 .worksheet_mut(sheet)?
2697 .set_cell_with_boolean(row, column, value, style)
2698 }
2699
2700 fn set_cell_with_number(
2701 &mut self,
2702 sheet: u32,
2703 row: i32,
2704 column: i32,
2705 value: f64,
2706 style: i32,
2707 ) -> Result<(), String> {
2708 self.workbook
2709 .worksheet_mut(sheet)?
2710 .set_cell_with_number(row, column, value, style)
2711 }
2712
2713 fn get_parsed_defined_name(
2715 &self,
2716 name: &str,
2717 scope: Option<u32>,
2718 ) -> Result<Option<ParsedDefinedName>, String> {
2719 let name_upper = name.to_uppercase();
2720
2721 for (key, df) in &self.parsed_defined_names {
2722 if key.1.to_uppercase() == name_upper && key.0 == scope {
2723 return Ok(Some(df.clone()));
2724 }
2725 }
2726 Ok(None)
2727 }
2728
2729 pub(crate) fn get_defined_name_formula(
2731 &self,
2732 name: &str,
2733 scope: Option<u32>,
2734 ) -> Result<String, String> {
2735 let name_upper = name.to_uppercase();
2736 let defined_names = &self.workbook.defined_names;
2737 let sheet_id = match scope {
2738 Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
2739 None => None,
2740 };
2741 for df in defined_names {
2742 if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
2743 return Ok(df.formula.clone());
2744 }
2745 }
2746 Err("Defined name not found".to_string())
2747 }
2748
2749 pub fn get_defined_name_list(&self) -> Vec<(String, Option<u32>, String)> {
2754 let context = self.defined_name_context();
2755 self.workbook
2756 .get_defined_names_with_scope()
2757 .into_iter()
2758 .map(|(name, scope, formula)| {
2759 let formula = self.internal_formula_to_display(&formula, &context);
2760 (name, scope, formula)
2761 })
2762 .collect()
2763 }
2764
2765 pub fn get_cell_value_by_ref(&self, cell_ref: &str) -> Result<CellValue, String> {
2770 let cell_reference = match self.parse_reference(cell_ref) {
2771 Some(c) => c,
2772 None => return Err(format!("Error parsing reference: '{cell_ref}'")),
2773 };
2774 let sheet_index = cell_reference.sheet;
2775 let column = cell_reference.column;
2776 let row = cell_reference.row;
2777
2778 self.get_cell_value_by_index(sheet_index, row, column)
2779 }
2780
2781 pub fn get_cell_value_by_index(
2786 &self,
2787 sheet_index: u32,
2788 row: i32,
2789 column: i32,
2790 ) -> Result<CellValue, String> {
2791 let cell = self
2792 .workbook
2793 .worksheet(sheet_index)?
2794 .cell(row, column)
2795 .cloned()
2796 .unwrap_or_default();
2797 let cell_value = cell.value(&self.workbook.shared_strings, self.language);
2798 Ok(cell_value)
2799 }
2800
2801 pub fn get_formatted_cell_value(
2822 &self,
2823 sheet_index: u32,
2824 row: i32,
2825 column: i32,
2826 ) -> Result<String, String> {
2827 match self.workbook.worksheet(sheet_index)?.cell(row, column) {
2828 Some(cell) => {
2829 let format = self.get_style_for_cell(sheet_index, row, column)?.num_fmt;
2830 let formatted_value =
2831 cell.formatted_value(&self.workbook.shared_strings, self.language, |value| {
2832 format_number(value, &format, self.locale).text
2833 });
2834 Ok(formatted_value)
2835 }
2836 None => Ok("".to_string()),
2837 }
2838 }
2839
2840 pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<CellType, String> {
2842 Ok(match self.workbook.worksheet(sheet)?.cell(row, column) {
2843 Some(c) => c.get_type(),
2844 None => CellType::Number,
2845 })
2846 }
2847
2848 pub fn get_localized_cell_content(
2855 &self,
2856 sheet: u32,
2857 row: i32,
2858 column: i32,
2859 ) -> Result<String, String> {
2860 let worksheet = self.workbook.worksheet(sheet)?;
2861 let cell = match worksheet.cell(row, column) {
2862 Some(c) => c,
2863 None => return Ok("".to_string()),
2864 };
2865 match cell.get_formula() {
2866 Some(formula_index) => {
2867 let formula = &self.parsed_formulas[sheet as usize][formula_index as usize].0;
2868 let cell_ref = CellReferenceRC {
2869 sheet: worksheet.get_name(),
2870 row,
2871 column,
2872 };
2873 Ok(format!(
2874 "={}",
2875 to_localized_string(formula, &cell_ref, self.locale, self.language)
2876 ))
2877 }
2878 None => {
2879 let style_index = cell.get_style();
2880 let style = self.workbook.styles.get_style(style_index)?;
2881 if style.quote_prefix {
2882 Ok(format!(
2883 "'{}",
2884 cell.get_localized_text(
2885 &self.workbook.shared_strings,
2886 self.locale,
2887 self.language,
2888 )
2889 ))
2890 } else {
2891 if is_likely_date_number_format(&style.num_fmt) {
2893 let value = cell.value(&self.workbook.shared_strings, self.language);
2894 if let CellValue::Number(n) = value {
2895 let formatted = format_number(n, &style.num_fmt, self.locale);
2896 if formatted.error.is_none() {
2897 return Ok(formatted.text);
2898 }
2899 }
2900 }
2901 Ok(cell.get_localized_text(
2902 &self.workbook.shared_strings,
2903 self.locale,
2904 self.language,
2905 ))
2906 }
2907 }
2908 }
2909 }
2910
2911 pub fn get_all_cells(&self) -> Vec<CellIndex> {
2913 let mut cells = Vec::new();
2914 for (index, sheet) in self.workbook.worksheets.iter().enumerate() {
2915 let mut sorted_rows: Vec<_> = sheet.sheet_data.keys().collect();
2916 sorted_rows.sort_unstable();
2917 for row in sorted_rows {
2918 let row_data = &sheet.sheet_data[row];
2919 let mut sorted_columns: Vec<_> = row_data.keys().collect();
2920 sorted_columns.sort_unstable();
2921 for column in sorted_columns {
2922 cells.push(CellIndex {
2923 index: index as u32,
2924 row: *row,
2925 column: *column,
2926 });
2927 }
2928 }
2929 }
2930 cells
2931 }
2932
2933 fn collect_spill_cells(&mut self) {
2936 let mut spill_cells = Vec::new();
2937 for (sheet_index, worksheet) in self.workbook.worksheets.iter().enumerate() {
2938 let mut sorted_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
2939 sorted_rows.sort_unstable();
2940 for row in &sorted_rows {
2941 let row_data = &worksheet.sheet_data[row];
2942 let mut sorted_cols: Vec<i32> = row_data.keys().copied().collect();
2943 sorted_cols.sort_unstable();
2944 for col in &sorted_cols {
2945 if matches!(
2946 &row_data[col],
2947 Cell::ArrayFormula {
2948 kind: ArrayKind::Dynamic,
2949 ..
2950 }
2951 ) {
2952 spill_cells.push(CellReferenceIndex {
2953 sheet: sheet_index as u32,
2954 row: *row,
2955 column: *col,
2956 });
2957 }
2958 }
2959 }
2960 }
2961 self.spill_cells = spill_cells;
2962 }
2963
2964 fn get_spill_area(&self, cell_ref: CellReferenceIndex) -> Vec<CellReferenceIndex> {
2967 let ws = match self.workbook.worksheet(cell_ref.sheet) {
2968 Ok(ws) => ws,
2969 Err(_) => return Vec::new(),
2970 };
2971 let (width, height) = match ws.cell(cell_ref.row, cell_ref.column) {
2972 Some(Cell::ArrayFormula {
2973 r,
2974 kind: ArrayKind::Dynamic,
2975 ..
2976 }) => *r,
2977 _ => return Vec::new(),
2978 };
2979 (cell_ref.row..cell_ref.row + height)
2980 .flat_map(|r| {
2981 (cell_ref.column..cell_ref.column + width).map(move |c| CellReferenceIndex {
2982 sheet: cell_ref.sheet,
2983 row: r,
2984 column: c,
2985 })
2986 })
2987 .collect()
2988 }
2989
2990 fn position_in_support(
2992 &self,
2993 cell: CellReferenceIndex,
2994 positions: &[CellReferenceIndex],
2995 ) -> bool {
2996 let deps = match self.support.get(&cell) {
2997 Some(d) => d,
2998 None => return false,
2999 };
3000 for dep in deps {
3001 match *dep {
3002 CellOrRange::Cell((sheet, row, col)) => {
3003 if positions
3004 .iter()
3005 .any(|p| p.sheet == sheet && p.row == row && p.column == col)
3006 {
3007 return true;
3008 }
3009 }
3010 CellOrRange::Range((sheet, r1, c1, r2, c2)) => {
3011 if positions.iter().any(|p| {
3012 p.sheet == sheet
3013 && p.row >= r1
3014 && p.row <= r2
3015 && p.column >= c1
3016 && p.column <= c2
3017 }) {
3018 return true;
3019 }
3020 }
3021 }
3022 }
3023 false
3024 }
3025
3026 pub fn evaluate(&mut self) {
3037 self.collect_spill_cells();
3038
3039 let n = self.spill_cells.len();
3040 let max_restarts = n * n + 1;
3042 let mut retry = true;
3043 let mut restart_count = 0;
3044
3045 while retry && restart_count < max_restarts {
3046 retry = false;
3047 self.cells.clear();
3048 self.support.clear();
3049 self.links.clear();
3051 self.clear_variable_stack();
3052 self.clear_lambdas();
3053
3054 for i in 0..self.spill_cells.len() {
3056 let spill_cell = self.spill_cells[i];
3057 self.evaluate_cell(spill_cell);
3058
3059 let spill_area = self.get_spill_area(spill_cell);
3061
3062 for j in 0..i {
3065 let prev = self.spill_cells[j];
3066 if self.position_in_support(prev, &spill_area) {
3067 let moved = self.spill_cells.remove(i);
3068 self.spill_cells.insert(j, moved);
3069 retry = true;
3070 restart_count += 1;
3071 break;
3072 }
3073 }
3074 if retry {
3075 break;
3076 }
3077 }
3078 }
3079
3080 let all_cells = self.get_all_cells();
3083 for cell in all_cells {
3084 self.evaluate_cell(CellReferenceIndex {
3085 sheet: cell.index,
3086 row: cell.row,
3087 column: cell.column,
3088 });
3089 }
3090 self.evaluate_conditional_formatting();
3091 }
3092
3093 pub fn range_clear_contents(&mut self, range: &Area) -> Result<(), String> {
3122 if !self.can_clear_range(range)? {
3123 return Err("Cannot clear the range because it contains array formulas".to_string());
3124 }
3125 let sheet = range.sheet;
3126 let ws = self.workbook.worksheet_mut(sheet)?;
3127 for row in range.row..range.row + range.height {
3128 for column in range.column..range.column + range.width {
3129 let structure = ws.get_cell_structure(row, column)?;
3130 match structure {
3131 CellStructure::DynamicFormula { range }
3132 | CellStructure::ArrayFormula { range, .. } => {
3133 let (width, height) = range;
3134 for r in row..row + height {
3135 for c in column..column + width {
3136 let _ = ws.cell_clear_contents(r, c);
3137 }
3138 }
3139 }
3140 _ => {
3141 let _ = ws.cell_clear_contents(row, column);
3142 }
3143 }
3144 }
3145 }
3146 ws.links.retain(|&(row, column), _| {
3148 row < range.row
3149 || row >= range.row + range.height
3150 || column < range.column
3151 || column >= range.column + range.width
3152 });
3153 Ok(())
3154 }
3155
3156 pub(crate) fn can_clear_range(&self, range: &Area) -> Result<bool, String> {
3159 let sheet = range.sheet;
3160 for row in range.row..range.row + range.height {
3161 for column in range.column..range.column + range.width {
3162 match self.get_cell_structure(sheet, row, column)? {
3163 CellStructure::ArrayFormula { range: r } => {
3164 let (width, height) = r;
3165 if column + width > range.column + range.width
3166 || row + height > range.row + range.height
3167 {
3168 return Ok(false);
3169 }
3170 }
3171 CellStructure::SpillArray {
3172 anchor: a,
3173 range: r,
3174 } => {
3175 let (anchor_row, anchor_column) = a;
3176 let (width, height) = r;
3177 if anchor_column < range.column
3178 || anchor_row < range.row
3179 || anchor_column + width > range.column + range.width
3180 || anchor_row + height > range.row + range.height
3181 {
3182 return Ok(false);
3183 }
3184 }
3185 _ => {
3186 }
3188 }
3189 }
3190 }
3191 Ok(true)
3192 }
3193
3194 pub fn range_clear_all(&mut self, area: &Area) -> Result<(), String> {
3224 if !self.can_clear_range(area)? {
3225 return Err("Cannot clear the range because it contains array formulas".to_string());
3226 }
3227 let worksheet = self.workbook.worksheet_mut(area.sheet)?;
3228
3229 let sheet_data = &mut worksheet.sheet_data;
3230 let mut cells_to_clear = Vec::new();
3231 for row in area.row..area.row + area.height {
3232 if let Some(row_data) = sheet_data.get_mut(&row) {
3233 for column in area.column..area.column + area.width {
3234 if let Some(Cell::ArrayFormula {
3236 r,
3237 kind: ArrayKind::Dynamic,
3238 ..
3239 }) = row_data.get(&column)
3240 {
3241 let (width, height) = r;
3243 for r in row..row + height {
3244 for c in column..column + width {
3245 cells_to_clear.push((r, c));
3246 }
3247 }
3248 }
3249 row_data.remove(&column);
3250 }
3251 if row_data.is_empty() {
3252 sheet_data.remove(&row);
3253 };
3254 }
3255 }
3256 for (row, column) in cells_to_clear {
3257 let _ = worksheet.cell_clear_contents(row, column);
3259 }
3260 worksheet.links.retain(|&(row, column), _| {
3262 row < area.row
3263 || row >= area.row + area.height
3264 || column < area.column
3265 || column >= area.column + area.width
3266 });
3267 Ok(())
3268 }
3269
3270 pub(crate) fn reset_dynamic_array_spills(&mut self, sheet: u32) -> Result<(), String> {
3274 let anchors: Vec<(i32, i32, i32, i32, i32, i32)> = {
3276 let ws = self.workbook.worksheet(sheet)?;
3277 let mut result = Vec::new();
3278 for (row, row_data) in &ws.sheet_data {
3279 for (column, cell) in row_data {
3280 if let Cell::ArrayFormula {
3281 r,
3282 f,
3283 s,
3284 kind: ArrayKind::Dynamic,
3285 ..
3286 } = cell
3287 {
3288 let (width, height) = *r;
3289 result.push((*row, *column, *f, *s, width, height));
3290 }
3291 }
3292 }
3293 result
3294 };
3295
3296 for (row, column, f, s, width, height) in anchors {
3297 let ws = self.workbook.worksheet_mut(sheet)?;
3298 if let Some(row_data) = ws.sheet_data.get_mut(&row) {
3300 row_data.insert(
3301 column,
3302 Cell::ArrayFormula {
3303 f,
3304 s,
3305 r: (1, 1),
3306 kind: ArrayKind::Dynamic,
3307 v: FormulaValue::Unevaluated,
3308 },
3309 );
3310 }
3311 for r in row..row + height {
3313 for c in column..column + width {
3314 if r == row && c == column {
3315 continue;
3316 }
3317 let _ = ws.cell_clear_contents(r, c);
3318 }
3319 }
3320 }
3321 Ok(())
3322 }
3323
3324 pub fn get_cell_style_index(&self, sheet: u32, row: i32, column: i32) -> Result<i32, String> {
3326 let cell = self.workbook.worksheet(sheet)?.cell(row, column);
3328
3329 match cell {
3330 Some(cell) => Ok(cell.get_style()),
3331 None => {
3332 let rows = &self.workbook.worksheet(sheet)?.rows;
3333 for r in rows {
3334 if r.r == row {
3335 if r.custom_format {
3336 return Ok(r.s);
3337 }
3338 break;
3339 }
3340 }
3341 let cols = &self.workbook.worksheet(sheet)?.cols;
3342 for c in cols.iter() {
3343 let min = c.min;
3344 let max = c.max;
3345 if column >= min && column <= max {
3346 return Ok(c.style.unwrap_or(0));
3347 }
3348 }
3349 Ok(0)
3350 }
3351 }
3352 }
3353
3354 pub fn get_style_for_cell(&self, sheet: u32, row: i32, column: i32) -> Result<Style, String> {
3357 let style_index = self.get_cell_style_index(sheet, row, column)?;
3358 let style = self.workbook.styles.get_style(style_index)?;
3359 Ok(style)
3360 }
3361
3362 pub fn get_cell_style_or_none(
3364 &self,
3365 sheet: u32,
3366 row: i32,
3367 column: i32,
3368 ) -> Result<Option<Style>, String> {
3369 let style = self
3370 .workbook
3371 .worksheet(sheet)?
3372 .cell(row, column)
3373 .map(|c| self.workbook.styles.get_style(c.get_style()))
3374 .transpose();
3375 style
3376 }
3377
3378 pub fn to_bytes(&self) -> Vec<u8> {
3383 bitcode::encode(&self.workbook)
3384 }
3385
3386 pub fn get_worksheets_properties(&self) -> Vec<SheetProperties> {
3388 self.workbook
3389 .worksheets
3390 .iter()
3391 .map(|worksheet| SheetProperties {
3392 name: worksheet.get_name(),
3393 state: worksheet.state.to_string(),
3394 color: worksheet.color.clone(),
3395 sheet_id: worksheet.sheet_id,
3396 })
3397 .collect()
3398 }
3399
3400 pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
3402 let worksheet = self.workbook.worksheet(sheet)?;
3403 let dimension = worksheet.dimension();
3404
3405 let mut rows = Vec::new();
3406
3407 for row in 1..(dimension.max_row + 1) {
3408 let mut row_markup: Vec<String> = Vec::new();
3409
3410 for column in 1..(dimension.max_column + 1) {
3411 let mut cell_markup = match self.get_cell_formula(sheet, row, column)? {
3412 Some(formula) => formula,
3413 None => self.get_formatted_cell_value(sheet, row, column)?,
3414 };
3415 let style = self.get_style_for_cell(sheet, row, column)?;
3416 if style.font.b {
3417 cell_markup = format!("**{cell_markup}**")
3418 }
3419 row_markup.push(cell_markup);
3420 }
3421
3422 rows.push(row_markup.join("|"));
3423 }
3424
3425 Ok(rows.join("\n"))
3426 }
3427
3428 pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32, String> {
3430 if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3431 Ok(worksheet.frozen_rows)
3432 } else {
3433 Err("Invalid sheet".to_string())
3434 }
3435 }
3436
3437 pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32, String> {
3439 if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3440 Ok(worksheet.frozen_columns)
3441 } else {
3442 Err("Invalid sheet".to_string())
3443 }
3444 }
3445
3446 pub fn set_frozen_rows(&mut self, sheet: u32, frozen_rows: i32) -> Result<(), String> {
3449 if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
3450 if frozen_rows < 0 {
3451 return Err("Frozen rows cannot be negative".to_string());
3452 }
3453 if frozen_rows >= LAST_ROW {
3454 return Err("Too many rows".to_string());
3455 }
3456 worksheet.frozen_rows = frozen_rows;
3457 Ok(())
3458 } else {
3459 Err("Invalid sheet".to_string())
3460 }
3461 }
3462
3463 pub fn set_frozen_columns(&mut self, sheet: u32, frozen_columns: i32) -> Result<(), String> {
3466 if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
3467 if frozen_columns < 0 {
3468 return Err("Frozen columns cannot be negative".to_string());
3469 }
3470 if frozen_columns >= LAST_COLUMN {
3471 return Err("Too many columns".to_string());
3472 }
3473 worksheet.frozen_columns = frozen_columns;
3474 Ok(())
3475 } else {
3476 Err("Invalid sheet".to_string())
3477 }
3478 }
3479
3480 #[inline]
3482 pub fn get_column_width(&self, sheet: u32, column: i32) -> Result<f64, String> {
3483 self.workbook.worksheet(sheet)?.get_column_width(column)
3484 }
3485
3486 #[inline]
3488 pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<(), String> {
3489 self.workbook
3490 .worksheet_mut(sheet)?
3491 .set_column_width(column, width)
3492 }
3493
3494 #[inline]
3496 pub fn set_column_hidden(
3497 &mut self,
3498 sheet: u32,
3499 column: i32,
3500 hidden: bool,
3501 ) -> Result<(), String> {
3502 self.workbook
3503 .worksheet_mut(sheet)?
3504 .set_column_hidden(column, hidden)
3505 }
3506
3507 #[inline]
3509 pub fn set_row_hidden(&mut self, sheet: u32, row: i32, hidden: bool) -> Result<(), String> {
3510 self.workbook
3511 .worksheet_mut(sheet)?
3512 .set_row_hidden(row, hidden)
3513 }
3514
3515 #[inline]
3517 pub fn is_column_hidden(&self, sheet: u32, column: i32) -> Result<bool, String> {
3518 self.workbook.worksheet(sheet)?.is_column_hidden(column)
3519 }
3520
3521 #[inline]
3523 pub fn is_row_hidden(&self, sheet: u32, row: i32) -> Result<bool, String> {
3524 self.workbook.worksheet(sheet)?.is_row_hidden(row)
3525 }
3526
3527 #[inline]
3529 pub fn get_row_height(&self, sheet: u32, row: i32) -> Result<f64, String> {
3530 self.workbook.worksheet(sheet)?.row_height(row)
3531 }
3532
3533 #[inline]
3535 pub fn set_row_height(&mut self, sheet: u32, column: i32, height: f64) -> Result<(), String> {
3536 self.workbook
3537 .worksheet_mut(sheet)?
3538 .set_row_height(column, height)
3539 }
3540
3541 pub fn new_defined_name(
3544 &mut self,
3545 name: &str,
3546 scope: Option<u32>,
3547 formula: &str,
3548 ) -> Result<(), String> {
3549 let sheet_id = self.is_valid_defined_name(name, scope, formula)?;
3550 let context = self.defined_name_context();
3553 let internal_formula = self.user_formula_to_internal(formula, &context)?;
3554 self.workbook.defined_names.push(DefinedName {
3555 name: name.to_string(),
3556 formula: internal_formula,
3557 sheet_id,
3558 });
3559 self.reset_parsed_structures();
3560
3561 Ok(())
3562 }
3563
3564 pub(crate) fn defined_name_context(&self) -> CellReferenceRC {
3567 CellReferenceRC {
3568 sheet: self
3569 .workbook
3570 .worksheets
3571 .first()
3572 .map(|ws| ws.get_name())
3573 .unwrap_or_else(|| "Sheet1".to_string()),
3574 row: 1,
3575 column: 1,
3576 }
3577 }
3578
3579 pub fn is_valid_defined_name(
3581 &mut self,
3582 name: &str,
3583 scope: Option<u32>,
3584 formula: &str,
3585 ) -> Result<Option<u32>, String> {
3586 if !is_valid_identifier(name) {
3587 return Err("Name: Invalid defined name".to_string());
3588 }
3589 let name_upper = name.to_uppercase();
3590 let defined_names = &self.workbook.defined_names;
3591 let sheet_id = match scope {
3592 Some(index) => match self.workbook.worksheet(index) {
3593 Ok(ws) => Some(ws.sheet_id),
3594 Err(_) => return Err("Scope: Invalid sheet index".to_string()),
3595 },
3596 None => None,
3597 };
3598 for df in defined_names {
3600 if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3601 return Err("Name: Defined name already exists".to_string());
3602 }
3603 }
3604
3605 let is_reference =
3607 common::ParsedReference::parse_reference_formula(None, formula, self.locale, |name| {
3608 self.get_sheet_index_by_name(name)
3609 })
3610 .is_ok();
3611
3612 if !is_reference {
3613 use crate::expressions::types::CellReferenceRC;
3616 let formula_body = formula.strip_prefix('=').unwrap_or(formula);
3617 let dummy_ref = CellReferenceRC {
3618 sheet: self
3619 .workbook
3620 .worksheets
3621 .first()
3622 .map(|ws| ws.get_name())
3623 .unwrap_or_else(|| "Sheet1".to_string()),
3624 row: 1,
3625 column: 1,
3626 };
3627 let mut node = self.parser.parse(formula_body, &dummy_ref);
3631 if let Node::ParseErrorKind { .. } = node {
3632 node = self.parse_internal_formula(formula_body, &dummy_ref);
3633 }
3634 if !matches!(node, Node::LambdaDefKind { .. }) {
3635 return Err("Formula: Invalid defined name formula".to_string());
3636 }
3637 }
3638
3639 Ok(sheet_id)
3640 }
3641
3642 pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
3644 let name_upper = name.to_uppercase();
3645 let defined_names = &self.workbook.defined_names;
3646 let sheet_id = match scope {
3647 Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
3648 None => None,
3649 };
3650 let mut index = None;
3651 for (i, df) in defined_names.iter().enumerate() {
3652 if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3653 index = Some(i);
3654 }
3655 }
3656 if let Some(i) = index {
3657 self.workbook.defined_names.remove(i);
3658 self.reset_parsed_structures();
3659 Ok(())
3660 } else {
3661 Err("Defined name not found".to_string())
3662 }
3663 }
3664
3665 pub fn update_defined_name(
3667 &mut self,
3668 name: &str,
3669 scope: Option<u32>,
3670 new_name: &str,
3671 new_scope: Option<u32>,
3672 new_formula: &str,
3673 ) -> Result<(), String> {
3674 if !is_valid_identifier(new_name) {
3675 return Err("Name: Invalid defined name".to_string());
3676 };
3677 let name_upper = name.to_uppercase();
3678 let new_name_upper = new_name.to_uppercase();
3679
3680 if name_upper != new_name_upper || scope != new_scope {
3681 for key in self.parsed_defined_names.keys() {
3682 if key.1.to_uppercase() == new_name_upper && key.0 == new_scope {
3683 return Err("Name: Defined name already exists".to_string());
3684 }
3685 }
3686 }
3687 let defined_names = &self.workbook.defined_names;
3688 let sheet_id = match scope {
3689 Some(index) => Some(
3690 self.workbook
3691 .worksheet(index)
3692 .map_err(|_| "Scope: Invalid sheet index")?
3693 .sheet_id,
3694 ),
3695 None => None,
3696 };
3697
3698 let new_sheet_id = match new_scope {
3699 Some(index) => Some(
3700 self.workbook
3701 .worksheet(index)
3702 .map_err(|_| "Scope: Invalid sheet index")?
3703 .sheet_id,
3704 ),
3705 None => None,
3706 };
3707
3708 let mut index = None;
3709 for (i, df) in defined_names.iter().enumerate() {
3710 if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3711 index = Some(i);
3712 }
3713 }
3714 let context = self.defined_name_context();
3716 let internal_formula = self.user_formula_to_internal(new_formula, &context)?;
3717 if let Some(i) = index {
3718 if let Some(df) = self.workbook.defined_names.get_mut(i) {
3719 if new_name != df.name {
3720 self.parser.set_lexer_mode(LexerMode::R1C1);
3725 let worksheets = &mut self.workbook.worksheets;
3726 for worksheet in worksheets {
3727 let cell_reference = CellReferenceRC {
3728 sheet: worksheet.get_name(),
3729 row: 1,
3730 column: 1,
3731 };
3732 let mut formulas = Vec::new();
3733 for formula in &worksheet.shared_formulas {
3734 let mut t = self.parser.parse(formula, &cell_reference);
3735 rename_defined_name_in_node(&mut t, name, scope, new_name);
3736 formulas.push(to_rc_format(&t));
3737 }
3738 worksheet.shared_formulas = formulas;
3739 }
3740 self.parser.set_lexer_mode(LexerMode::A1);
3742 }
3743 df.name = new_name.to_string();
3744 df.sheet_id = new_sheet_id;
3745 df.formula = internal_formula;
3746 self.reset_parsed_structures();
3747 }
3748 Ok(())
3749 } else {
3750 Err("Defined name not found".to_string())
3751 }
3752 }
3753 pub fn get_column_style(&self, sheet: u32, column: i32) -> Result<Option<Style>, String> {
3755 if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3756 let cols = &worksheet.cols;
3757 for col in cols {
3758 if column >= col.min && column <= col.max {
3759 if let Some(style_index) = col.style {
3760 let style = self.workbook.styles.get_style(style_index)?;
3761 return Ok(Some(style));
3762 }
3763 return Ok(None);
3764 }
3765 }
3766 Ok(None)
3767 } else {
3768 Err("Invalid sheet".to_string())
3769 }
3770 }
3771
3772 pub fn get_row_style(&self, sheet: u32, row: i32) -> Result<Option<Style>, String> {
3774 if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3775 let rows = &worksheet.rows;
3776 for r in rows {
3777 if row == r.r {
3778 let style = self.workbook.styles.get_style(r.s)?;
3779 return Ok(Some(style));
3780 }
3781 }
3782 Ok(None)
3783 } else {
3784 Err("Invalid sheet".to_string())
3785 }
3786 }
3787
3788 pub fn set_column_style(
3790 &mut self,
3791 sheet: u32,
3792 column: i32,
3793 style: &Style,
3794 ) -> Result<(), String> {
3795 let style_index = self.workbook.styles.get_style_index_or_create(style);
3796 self.workbook
3797 .worksheet_mut(sheet)?
3798 .set_column_style(column, style_index)
3799 }
3800
3801 pub fn set_row_style(&mut self, sheet: u32, row: i32, style: &Style) -> Result<(), String> {
3803 let style_index = self.workbook.styles.get_style_index_or_create(style);
3804 self.workbook
3805 .worksheet_mut(sheet)?
3806 .set_row_style(row, style_index)
3807 }
3808
3809 pub fn delete_column_style(&mut self, sheet: u32, column: i32) -> Result<(), String> {
3811 self.workbook
3812 .worksheet_mut(sheet)?
3813 .delete_column_style(column)
3814 }
3815
3816 pub fn delete_row_style(&mut self, sheet: u32, row: i32) -> Result<(), String> {
3818 self.workbook.worksheet_mut(sheet)?.delete_row_style(row)
3819 }
3820
3821 pub fn set_locale(&mut self, locale_id: &str) -> Result<(), String> {
3823 let locale = match get_locale(locale_id) {
3824 Ok(l) => l,
3825 Err(_) => return Err(format!("Invalid locale: {locale_id}")),
3826 };
3827 self.parser.set_locale(locale);
3828 self.locale = locale;
3829 self.workbook.settings.locale = locale_id.to_string();
3830 self.evaluate();
3831 Ok(())
3832 }
3833
3834 pub fn set_timezone(&mut self, timezone: &str) -> Result<(), String> {
3836 let tz = match Tz::parse(timezone) {
3837 Ok(tz) => tz,
3838 Err(_) => return Err(format!("Invalid timezone: {}", timezone)),
3839 };
3840 self.tz = tz;
3841 self.workbook.settings.tz = timezone.to_string();
3842 self.evaluate();
3843 Ok(())
3844 }
3845
3846 pub fn set_language(&mut self, language_id: &str) -> Result<(), String> {
3848 let language = match get_language(language_id) {
3849 Ok(l) => l,
3850 Err(_) => return Err(format!("Invalid language: {language_id}")),
3851 };
3852 self.parser.set_language(language);
3853 self.language = language;
3854 Ok(())
3855 }
3856
3857 pub fn get_language(&self) -> String {
3859 self.language.code.clone()
3860 }
3861
3862 pub fn get_timezone(&self) -> String {
3864 self.workbook.settings.tz.clone()
3865 }
3866
3867 pub fn get_locale(&self) -> String {
3869 self.workbook.settings.locale.clone()
3870 }
3871
3872 pub fn get_fmt_settings(&self) -> FmtSettings {
3874 let day_example = 46006.0; let currency = self.locale.currency.iso.clone();
3876 let currency_symbol = &self.locale.currency.symbol;
3877 let short_date = &self.locale.dates.date_formats.short;
3879 let long_date = &self.locale.dates.date_formats.long;
3881 let short_date_example = format_number(day_example, short_date, self.locale).text;
3882 let long_date_example = format_number(day_example, long_date, self.locale).text;
3883 let currency_format_template = &self.locale.numbers.currency_formats.standard;
3888 let currency_format = currency_format_template
3889 .replace("¤", &format!("\"{}\"", currency_symbol))
3890 .replace(" ", " ");
3891
3892 let number_fmt = "#,##0.00".to_string();
3893 let number_example = format_number(1234.567, &number_fmt, self.locale).text;
3894 FmtSettings {
3895 currency,
3896 currency_format,
3897 short_date: short_date.clone(),
3898 long_date: long_date.clone(),
3899 short_date_example,
3900 long_date_example,
3901 number_fmt,
3902 number_example,
3903 }
3904 }
3905
3906 pub fn cycle_reference(
3912 &self,
3913 value: &str,
3914 start: usize,
3915 end: usize,
3916 ) -> Result<(String, i32, i32), String> {
3917 crate::expressions::lexer::util::cycle_reference(
3918 value,
3919 start,
3920 end,
3921 self.locale,
3922 self.language,
3923 )
3924 }
3925}
3926
3927#[cfg(test)]
3928mod tests {
3929 #![allow(clippy::expect_used)]
3930 use super::CellReferenceIndex as CellReference;
3931 use crate::{test::util::new_empty_model, types::Cell};
3932
3933 #[test]
3934 fn test_cell_reference_to_string() {
3935 let model = new_empty_model();
3936 let reference = CellReference {
3937 sheet: 0,
3938 row: 32,
3939 column: 16,
3940 };
3941 assert_eq!(
3942 model.cell_reference_to_string(&reference),
3943 Ok("Sheet1!P32".to_string())
3944 )
3945 }
3946
3947 #[test]
3948 fn test_cell_reference_to_string_invalid_worksheet() {
3949 let model = new_empty_model();
3950 let reference = CellReference {
3951 sheet: 10,
3952 row: 1,
3953 column: 1,
3954 };
3955 assert_eq!(
3956 model.cell_reference_to_string(&reference),
3957 Err("Invalid sheet index".to_string())
3958 )
3959 }
3960
3961 #[test]
3962 fn test_cell_reference_to_string_invalid_column() {
3963 let model = new_empty_model();
3964 let reference = CellReference {
3965 sheet: 0,
3966 row: 1,
3967 column: 20_000,
3968 };
3969 assert_eq!(
3970 model.cell_reference_to_string(&reference),
3971 Err("Invalid column".to_string())
3972 )
3973 }
3974
3975 #[test]
3976 fn test_cell_reference_to_string_invalid_row() {
3977 let model = new_empty_model();
3978 let reference = CellReference {
3979 sheet: 0,
3980 row: 2_000_000,
3981 column: 1,
3982 };
3983 assert_eq!(
3984 model.cell_reference_to_string(&reference),
3985 Err("Invalid row".to_string())
3986 )
3987 }
3988
3989 #[test]
3990 fn test_get_cell() {
3991 let mut model = new_empty_model();
3992 model._set("A1", "35");
3993 model._set("A2", "");
3994 let worksheet = model.workbook.worksheet(0).expect("Invalid sheet");
3995
3996 assert_eq!(
3997 worksheet.cell(1, 1),
3998 Some(&Cell::NumberCell { v: 35.0, s: 0 })
3999 );
4000
4001 assert_eq!(worksheet.cell(2, 1), Some(&Cell::EmptyCell { s: 0 }));
4003 assert_eq!(worksheet.cell(3, 1), None)
4004 }
4005
4006 #[test]
4007 fn test_get_cell_invalid_sheet() {
4008 let model = new_empty_model();
4009 assert_eq!(
4010 model.workbook.worksheet(5),
4011 Err("Invalid sheet index".to_string()),
4012 )
4013 }
4014
4015 #[test]
4016 fn test_update_cell_with_sign_prefixed_formulas() {
4017 let mut model = new_empty_model();
4018
4019 let update_result = model.update_cell_with_formula(0, 1, 1, "-A2*2".to_string());
4020 model.evaluate();
4021 assert_eq!(update_result, Ok(()));
4022 assert_eq!(model._get_formula("A1"), *"=-A2*2");
4023 }
4024}