Skip to main content

ironcalc_base/
model.rs

1#![deny(missing_docs)]
2
3use std::borrow::Cow;
4use std::collections::HashMap;
5use std::sync::Arc;
6use std::vec::Vec;
7
8use crate::arithmetic::power;
9use crate::expressions::parser::static_analysis::run_static_analysis_on_node;
10use crate::{
11    calc_result::{CalcResult, Range},
12    cell::CellValue,
13    constants::{self, LAST_COLUMN, LAST_ROW},
14    expressions::{
15        lexer::LexerMode,
16        parser::{
17            move_formula::{move_formula, MoveContext},
18            new_parser_english,
19            static_analysis::StaticResult,
20            stringify::{
21                rename_defined_name_in_node, to_english_string, to_localized_string, to_rc_format,
22            },
23            ArrayNode, CompletionContext, NamedVariable, Node, Parser,
24        },
25        token::{get_error_by_name, Error, OpProduct, OpSum, OpUnary},
26        types::*,
27        utils::{self, is_valid_column_number, is_valid_identifier, is_valid_row},
28    },
29    formatter::{
30        format::{format_number, parse_formatted_number},
31        lexer::is_likely_date_number_format,
32    },
33    implicit_intersection::implicit_intersection,
34    language::{get_default_language, get_language, Language},
35    locale::{get_default_locale, get_locale, Locale},
36    types::*,
37    utils as common,
38};
39
40use crate::evaluation::{CellKey, Evaluation, FormulaCell, Seen};
41use crate::{cf_types::CfCellResult, tz::Tz};
42
43#[cfg(any(test, feature = "mock_time"))]
44pub use crate::mock_time::get_milliseconds_since_epoch;
45
46/// Number of milliseconds since January 1, 1970
47/// Used by time and date functions. It takes the value from the environment:
48/// * The Operative System
49/// * The JavaScript environment
50/// * Or mocked for tests
51#[cfg(not(any(test, feature = "mock_time")))]
52#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
53#[allow(clippy::expect_used)]
54pub fn get_milliseconds_since_epoch() -> i64 {
55    use std::time::{SystemTime, UNIX_EPOCH};
56    SystemTime::now()
57        .duration_since(UNIX_EPOCH)
58        .expect("problem with system time")
59        .as_millis() as i64
60}
61
62/// Number of milliseconds since January 1, 1970
63/// Used by time and date functions. It takes the value from the environment:
64/// * The Operative System
65/// * The JavaScript environment
66/// * Or mocked for tests
67#[cfg(not(any(test, feature = "mock_time")))]
68#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
69pub fn get_milliseconds_since_epoch() -> i64 {
70    use js_sys::Date;
71    Date::now() as i64
72}
73
74// The structure of a cell.
75// It can be:
76// * A single cell
77// * The anchor of an array formula
78// * The anchor of a dynamic formula
79// * A part of an array formula spill
80// * A part of a dynamic formula spill
81pub(crate) enum CellStructure {
82    SingleCell,
83    ArrayFormula {
84        range: (i32, i32),
85    },
86    DynamicFormula {
87        range: (i32, i32),
88    },
89    SpillArray {
90        anchor: (i32, i32),
91        range: (i32, i32),
92    },
93    SpillDynamic {
94        anchor: (i32, i32),
95        range: (i32, i32),
96    },
97}
98
99/// A parsed formula for a defined name
100#[derive(Clone)]
101pub(crate) enum ParsedDefinedName {
102    /// CellReference (`=C4`)
103    CellReference(CellReferenceIndex),
104    /// A Range (`=C4:D6`)
105    RangeReference(Range),
106    /// `=LAMBDA(params..., body)`
107    LambdaDefinition(Vec<NamedVariable>, Node),
108    /// `=SomethingElse`
109    InvalidDefinedNameFormula,
110}
111
112/// Formatting settings for a locale
113pub struct FmtSettings {
114    /// Currency format
115    pub currency: String,
116    /// Currency format with symbol
117    pub currency_format: String,
118    /// Short date format
119    pub short_date: String,
120    /// Example of short date format
121    pub short_date_example: String,
122    /// Long date format
123    pub long_date: String,
124    /// Example of long date format
125    pub long_date_example: String,
126    /// Number format
127    pub number_fmt: String,
128    /// Example of number format
129    pub number_example: String,
130}
131
132/// The top left element of an array, which is what the implicit intersection
133/// operator makes of it.
134fn top_left_of_array(array: &[Vec<ArrayNode>], cell: CellReferenceIndex) -> CalcResult {
135    match array.first().and_then(|row| row.first()) {
136        Some(ArrayNode::Number(n)) => CalcResult::Number(*n),
137        Some(ArrayNode::Boolean(b)) => CalcResult::Boolean(*b),
138        Some(ArrayNode::String(s)) => CalcResult::String(s.clone()),
139        Some(ArrayNode::Error(error)) => CalcResult::new_error(error.clone(), cell, String::new()),
140        Some(ArrayNode::Empty) => CalcResult::EmptyCell,
141        None => CalcResult::new_error(Error::CALC, cell, "Empty array".to_string()),
142    }
143}
144
145fn array_node_to_formula_value(node: ArrayNode) -> FormulaValue {
146    match node {
147        ArrayNode::Boolean(b) => FormulaValue::Boolean(b),
148        // No cell holds a number that is not finite: a calculation that
149        // overflows is an error, as it is for the result of a single cell.
150        ArrayNode::Number(n) if !n.is_finite() => {
151            FormulaValue::new_error(Error::NUM, String::new(), String::new())
152        }
153        ArrayNode::Number(n) => FormulaValue::Number(n),
154        ArrayNode::String(s) => FormulaValue::Text(s),
155        ArrayNode::Error(ei) => FormulaValue::new_error(ei, String::new(), String::new()),
156        ArrayNode::Empty => FormulaValue::Number(0.0),
157    }
158}
159
160fn array_node_to_spill_value(node: ArrayNode) -> SpillValue {
161    match node {
162        ArrayNode::Boolean(b) => SpillValue::Boolean(b),
163        // See `array_node_to_formula_value`
164        ArrayNode::Number(n) if !n.is_finite() => SpillValue::Error(Error::NUM),
165        ArrayNode::Number(n) => SpillValue::Number(n),
166        ArrayNode::String(s) => SpillValue::Text(s),
167        ArrayNode::Error(ei) => SpillValue::Error(ei),
168        ArrayNode::Empty => SpillValue::Number(0.0),
169    }
170}
171
172fn formula_value_to_spill_value(v: &FormulaValue) -> SpillValue {
173    match v {
174        FormulaValue::Unevaluated => SpillValue::Error(Error::ERROR),
175        FormulaValue::Boolean(b) => SpillValue::Boolean(*b),
176        FormulaValue::Number(n) => SpillValue::Number(*n),
177        FormulaValue::Text(s) => SpillValue::Text(s.clone()),
178        FormulaValue::Error { ei, .. } => SpillValue::Error(ei.clone()),
179    }
180}
181
182/// A dynamical IronCalc model.
183///
184/// Its is composed of a `Workbook`. Everything else are dynamical quantities:
185///
186/// * The Locale: a parsed version of the Workbook's locale
187/// * The Timezone: an object representing the Workbook's timezone
188/// * The language. Note that the timezone and the locale belong to the workbook while
189///   the language can be different for different users looking _at the same_ workbook.
190/// * Parsed Formulas: All the formulas in the workbook are parsed here (runtime only)
191/// * A list of cells with its status (evaluating, evaluated, not evaluated)
192/// * A dictionary with the shared strings and their indices.
193///   This is an optimization for large files (~1 million rows)
194pub struct Model<'a> {
195    /// A Rust internal representation of an Excel workbook
196    pub workbook: Workbook,
197    /// A list of parsed formulas. They are shared, not owned, so that the
198    /// evaluation can hold on to the formula it is running, while the model is
199    /// borrowed mutably, without copying it.
200    pub parsed_formulas: Vec<Vec<(Arc<Node>, StaticResult)>>,
201    /// A list of parsed defined names
202    pub(crate) parsed_defined_names: HashMap<(Option<u32>, String), ParsedDefinedName>,
203    /// An optimization to lookup strings faster
204    pub(crate) shared_strings: HashMap<String, usize>,
205    /// An instance of the parser
206    pub(crate) parser: Parser<'a>,
207    /// The locale of the model
208    pub(crate) locale: &'a Locale,
209    /// The language used
210    pub(crate) language: &'a Language,
211    /// The timezone used to evaluate the model
212    pub(crate) tz: Tz,
213    /// The view id. A view consists of a selected sheet and ranges.
214    pub(crate) view_id: u32,
215    /// A stack of variables used for LET function evaluation. The key is the variable id, and the value is the variable value.
216    pub(crate) variable_stack: HashMap<usize, CalcResult>,
217    /// Last variable id used. It is incremented every time a new variable is created (for example, when evaluating a LET function).
218    pub(crate) last_variable_id: usize,
219    /// Lambdas
220    pub(crate) lambdas: HashMap<usize, (Vec<NamedVariable>, Node)>,
221    /// Last lambda id used. It is incremented every time a new lambda is created.
222    pub(crate) last_lambda_id: usize,
223    /// Everything the evaluation algorithm needs besides the workbook. See `evaluation.rs`.
224    pub(crate) evaluation: Evaluation,
225    /// Evaluated CF results per cell, keyed by (sheet_index, row, column).
226    /// Rebuilt from scratch on every call to evaluate_conditional_formatting().
227    pub(crate) cf_cache: HashMap<(u32, i32, i32), Vec<CfCellResult>>,
228    /// Dynamic links: links created by formulas like HYPERLINK
229    pub(crate) links: HashMap<(u32, i32, i32), Link>,
230    /// For each sheet, where each of its shared formulas is. Only an aid to
231    /// find a formula without comparing it with all the others; the list is
232    /// what counts and what is saved. Rebuilt with the parsed formulas
233    /// (`parse_formulas`), extended when a formula is added. See
234    /// `shared_formula_index`.
235    pub(crate) shared_formula_lookup: Vec<SharedFormulaLookup>,
236}
237
238/// Formula text to index in `Worksheet::shared_formulas`, for one sheet.
239#[derive(Default)]
240pub(crate) struct SharedFormulaLookup {
241    pub(crate) index: HashMap<String, i32>,
242    /// How many formulas of the list the index was built from. A formula the
243    /// index does not know is new only if this is the length of the list. The
244    /// size of the index says nothing: a formula can be in the list twice,
245    /// which renaming a sheet or a defined name can cause.
246    pub(crate) built_from: usize,
247}
248
249/// The lookup for a list of shared formulas. If a formula is in the list twice
250/// the first one stays, as it would for someone looking through the list from
251/// the start.
252pub(crate) fn build_shared_formula_lookup(shared_formulas: &[String]) -> SharedFormulaLookup {
253    let mut index = HashMap::with_capacity(shared_formulas.len());
254    for (i, formula) in shared_formulas.iter().enumerate() {
255        index.entry(formula.clone()).or_insert(i as i32);
256    }
257    SharedFormulaLookup {
258        index,
259        built_from: shared_formulas.len(),
260    }
261}
262
263// FIXME: Maybe this should be the same as CellReference
264/// A struct pointing to a cell
265pub struct CellIndex {
266    /// Sheet index (0-indexed)
267    pub index: u32,
268    /// Row index
269    pub row: i32,
270    /// Column index
271    pub column: i32,
272}
273
274impl<'a> Model<'a> {
275    pub(crate) fn get_next_variable_id(&mut self) -> usize {
276        let id = self.last_variable_id;
277        self.last_variable_id += 1;
278        id
279    }
280    pub(crate) fn clear_variable_stack(&mut self) {
281        self.variable_stack.clear();
282        self.last_variable_id = 0;
283    }
284    pub(crate) fn get_next_lambda_id(&mut self) -> usize {
285        let id = self.last_lambda_id;
286        self.last_lambda_id += 1;
287        id
288    }
289    pub(crate) fn clear_lambdas(&mut self) {
290        self.lambdas.clear();
291        self.last_lambda_id = 0;
292    }
293    pub(crate) fn evaluate_node_with_reference(
294        &mut self,
295        node: &Node,
296        cell: CellReferenceIndex,
297    ) -> CalcResult {
298        match node {
299            Node::ReferenceKind {
300                sheet_name: _,
301                sheet_index,
302                absolute_row,
303                absolute_column,
304                row,
305                column,
306            } => {
307                let mut row1 = *row;
308                let mut column1 = *column;
309                if !absolute_row {
310                    row1 += cell.row;
311                }
312                if !absolute_column {
313                    column1 += cell.column;
314                }
315                CalcResult::Range {
316                    left: CellReferenceIndex {
317                        sheet: *sheet_index,
318                        row: row1,
319                        column: column1,
320                    },
321                    right: CellReferenceIndex {
322                        sheet: *sheet_index,
323                        row: row1,
324                        column: column1,
325                    },
326                }
327            }
328            Node::RangeKind {
329                sheet_name: _,
330                sheet_index,
331                absolute_row1,
332                absolute_column1,
333                row1,
334                column1,
335                absolute_row2,
336                absolute_column2,
337                row2,
338                column2,
339            } => {
340                let mut row_left = *row1;
341                let mut column_left = *column1;
342                if !absolute_row1 {
343                    row_left += cell.row;
344                }
345                if !absolute_column1 {
346                    column_left += cell.column;
347                }
348                let mut row_right = *row2;
349                let mut column_right = *column2;
350                if !absolute_row2 {
351                    row_right += cell.row;
352                }
353                if !absolute_column2 {
354                    column_right += cell.column;
355                }
356                // FIXME: HACK. The parser is currently parsing Sheet3!A1:A10 as Sheet3!A1:(present sheet)!A10
357                CalcResult::Range {
358                    left: CellReferenceIndex {
359                        sheet: *sheet_index,
360                        row: row_left,
361                        column: column_left,
362                    },
363                    right: CellReferenceIndex {
364                        sheet: *sheet_index,
365                        row: row_right,
366                        column: column_right,
367                    },
368                }
369            }
370            Node::ImplicitIntersection {
371                automatic: _,
372                child,
373            } => match self.evaluate_node_with_reference(child, cell) {
374                CalcResult::Range { left, right } => {
375                    match implicit_intersection(&cell, &Range { left, right }) {
376                        Some(r) => CalcResult::Range { left: r, right: r },
377                        None => CalcResult::new_error(
378                            Error::VALUE,
379                            cell,
380                            format!("Error with Implicit Intersection in cell {cell:?}"),
381                        ),
382                    }
383                }
384                // The implicit intersection of an array is its top left element.
385                CalcResult::Array(array) => top_left_of_array(&array, cell),
386                // The implicit intersection of a scalar is the scalar itself.
387                other => other,
388            },
389            // A variable holds what it was bound to, a reference included
390            Node::NamedVariableKind { .. } => self.variable_value(node, cell),
391            // A defined name that stands for a reference is that reference,
392            // to a single cell as well
393            Node::DefinedNameKind((name, scope, _)) => {
394                if let Ok(Some(parsed_defined_name)) = self.get_parsed_defined_name(name, *scope) {
395                    match parsed_defined_name {
396                        ParsedDefinedName::CellReference(reference) => {
397                            return CalcResult::Range {
398                                left: reference,
399                                right: reference,
400                            };
401                        }
402                        ParsedDefinedName::RangeReference(range) => {
403                            return CalcResult::Range {
404                                left: range.left,
405                                right: range.right,
406                            };
407                        }
408                        ParsedDefinedName::LambdaDefinition(..)
409                        | ParsedDefinedName::InvalidDefinedNameFormula => {}
410                    }
411                }
412                self.evaluate_node_in_context(node, cell)
413            }
414            _ => self.evaluate_node_in_context(node, cell),
415        }
416    }
417
418    /// What a variable of a LET or a parameter of a LAMBDA is bound to.
419    fn variable_value(&mut self, node: &Node, cell: CellReferenceIndex) -> CalcResult {
420        match node {
421            Node::NamedVariableKind { name, id: Some(id) } => {
422                match self.variable_stack.get(&(*id as usize)) {
423                    Some(v) => v.clone(),
424                    None => CalcResult::new_error(
425                        Error::NAME,
426                        cell,
427                        format!("Variable \"{name}\" not found in scope."),
428                    ),
429                }
430            }
431            Node::NamedVariableKind { name, id: None } => CalcResult::new_error(
432                Error::NAME,
433                cell,
434                format!("Variable name \"{name}\" not found."),
435            ),
436            _ => CalcResult::new_error(Error::ERROR, cell, "Not a variable".to_string()),
437        }
438    }
439
440    fn get_range(&mut self, left: &Node, right: &Node, cell: CellReferenceIndex) -> CalcResult {
441        let left_result = self.evaluate_node_with_reference(left, cell);
442        let right_result = self.evaluate_node_with_reference(right, cell);
443        match (left_result, right_result) {
444            (
445                CalcResult::Range {
446                    left: left1,
447                    right: right1,
448                },
449                CalcResult::Range {
450                    left: left2,
451                    right: right2,
452                },
453            ) => {
454                if left1.row == right1.row
455                    && left1.column == right1.column
456                    && left2.row == right2.row
457                    && left2.column == right2.column
458                {
459                    return CalcResult::Range {
460                        left: left1,
461                        right: right2,
462                    };
463                }
464                CalcResult::Error {
465                    error: Error::VALUE,
466                    origin: cell,
467                    message: "Invalid range".to_string(),
468                }
469            }
470            _ => CalcResult::Error {
471                error: Error::VALUE,
472                origin: cell,
473                message: "Invalid range".to_string(),
474            },
475        }
476    }
477
478    /// Collapses a reference-context result to a value: a Range is implicitly
479    /// intersected at `cell` and the intersected cell evaluated; anything else
480    /// (scalar, array, error) passes through unchanged.
481    pub(crate) fn implicit_intersection_to_value(
482        &mut self,
483        result: CalcResult,
484        cell: CellReferenceIndex,
485    ) -> CalcResult {
486        match result {
487            CalcResult::Range { left, right } => {
488                match implicit_intersection(&cell, &Range { left, right }) {
489                    Some(cell_reference) => self.evaluate_cell(cell_reference),
490                    None => CalcResult::new_error(
491                        Error::VALUE,
492                        cell,
493                        format!("Error with Implicit Intersection in cell {cell:?}"),
494                    ),
495                }
496            }
497            other => other,
498        }
499    }
500
501    pub(crate) fn formula_without_prefix<'b>(&self, value: &'b str) -> Option<&'b str> {
502        if let Some(stripped) = value.strip_prefix('=') {
503            if stripped.is_empty() {
504                None
505            } else {
506                Some(stripped)
507            }
508        } else if let Some(stripped) = value.strip_prefix(['+', '-']) {
509            if stripped.is_empty() || self.cast_number(stripped).is_some() {
510                None
511            } else {
512                Some(value)
513            }
514        } else {
515            None
516        }
517    }
518
519    /// Parses a formula that is stored internally (always in English) and
520    /// returns the resulting node.
521    ///
522    /// Formula strings kept outside of cells (defined names and conditional
523    /// formatting rules) are always stored in English — see
524    /// [Model::user_formula_to_internal]. They must therefore be parsed with
525    /// the English language and locale regardless of the user's active
526    /// language. This temporarily switches the parser, parses, and restores it.
527    pub(crate) fn parse_internal_formula(&mut self, body: &str, context: &CellReferenceRC) -> Node {
528        let locale = self.locale;
529        let language = self.language;
530        self.parser.set_locale(get_default_locale());
531        self.parser.set_language(get_default_language());
532        let node = self.parser.parse(body, context);
533        self.parser.set_locale(locale);
534        self.parser.set_language(language);
535        node
536    }
537
538    /// Translates a formula the user typed (in the active language and locale)
539    /// into the canonical English representation that is stored internally.
540    ///
541    /// The formula is first parsed in the active language/locale. If that fails
542    /// it is parsed as English — this lets internally generated formulas (which
543    /// are already English, e.g. produced by undo/redo or cut & paste) round
544    /// trip unchanged regardless of the active language. Returns an error if the
545    /// formula parses in neither. Any leading `=` is preserved.
546    pub(crate) fn user_formula_to_internal(
547        &mut self,
548        formula: &str,
549        context: &CellReferenceRC,
550    ) -> Result<String, String> {
551        let trimmed = formula.trim();
552        let had_equals = trimmed.starts_with('=');
553        let body = trimmed.strip_prefix('=').unwrap_or(trimmed);
554        let mut node = self.parser.parse(body, context);
555        if let Node::ParseErrorKind { .. } = node {
556            // The user's language could not parse it: it might already be in the
557            // internal English form.
558            node = self.parse_internal_formula(body, context);
559        }
560        if let Node::ParseErrorKind { .. } = node {
561            return Err(format!("Invalid formula: '{formula}'"));
562        }
563        let english = to_english_string(&node, context);
564        Ok(if had_equals {
565            format!("={english}")
566        } else {
567            english
568        })
569    }
570
571    /// Returns completion information for a formula being edited in a cell.
572    ///
573    /// `formula` is the raw cell input (it may start with `=`) and `cursor` is a
574    /// char offset into it. The references in the formula are resolved relative
575    /// to the cell at (`sheet`, `row`, `column`). See
576    /// [`CompletionContext`](crate::expressions::parser::CompletionContext).
577    pub fn formula_completion(
578        &mut self,
579        sheet: u32,
580        row: i32,
581        column: i32,
582        formula: &str,
583        cursor: usize,
584    ) -> Result<CompletionContext, String> {
585        let sheet_name = self.workbook.worksheet(sheet)?.get_name();
586        let cell_reference = CellReferenceRC {
587            sheet: sheet_name,
588            row,
589            column,
590        };
591        // The parser works on the formula body, without the leading `=`. Drop it
592        // and shift the cursor so it keeps pointing at the same character.
593        let (body, cursor) = match formula.strip_prefix('=') {
594            Some(rest) => (rest, cursor.saturating_sub(1)),
595            None => (formula, cursor),
596        };
597        Ok(self.parser.parse_at_cursor(body, cursor, &cell_reference))
598    }
599
600    /// Translates an internally-stored (English) formula into the active
601    /// language and locale for display to the user. Any leading `=` is
602    /// preserved. If the formula fails to parse it is returned unchanged.
603    pub(crate) fn internal_formula_to_display(
604        &self,
605        formula: &str,
606        context: &CellReferenceRC,
607    ) -> String {
608        let trimmed = formula.trim();
609        let had_equals = trimmed.starts_with('=');
610        let body = trimmed.strip_prefix('=').unwrap_or(trimmed);
611        if body.is_empty() {
612            return formula.to_string();
613        }
614        // Stored formulas are in English, so parse with an English parser.
615        let worksheet_names = self
616            .workbook
617            .worksheets
618            .iter()
619            .map(|s| s.get_name())
620            .collect();
621        let defined_names = self.workbook.get_defined_names_with_scope();
622        let mut parser =
623            new_parser_english(worksheet_names, defined_names, self.workbook.tables.clone());
624        let node = parser.parse(body, context);
625        if let Node::ParseErrorKind { .. } = node {
626            return formula.to_string();
627        }
628        let local = to_localized_string(&node, context, self.locale, self.language);
629        if had_equals {
630            format!("={local}")
631        } else {
632            local
633        }
634    }
635
636    /// Evaluates a formula string on a sheet, returning the numeric result.
637    /// Assumes the workbook has already been evaluated (cell values are up-to-date).
638    /// Returns `None` if the formula is invalid or does not produce a number.
639    pub(crate) fn evaluate_formula(&mut self, formula: &str, sheet: u32) -> Option<f64> {
640        let body = formula.trim().strip_prefix('=').unwrap_or(formula.trim());
641        if body.is_empty() {
642            return None;
643        }
644        let sheet_name = self.workbook.worksheets.get(sheet as usize)?.get_name();
645        let context_rc = CellReferenceRC {
646            sheet: sheet_name,
647            row: 1,
648            column: 1,
649        };
650        let node = self.parse_internal_formula(body, &context_rc);
651        let context_index = CellReferenceIndex {
652            sheet,
653            row: 1,
654            column: 1,
655        };
656        match self.evaluate_node_in_context(&node, context_index) {
657            CalcResult::Number(n) => Some(n),
658            _ => None,
659        }
660    }
661
662    pub(crate) fn evaluate_node_in_context(
663        &mut self,
664        node: &Node,
665        cell: CellReferenceIndex,
666    ) -> CalcResult {
667        use Node::*;
668        match node {
669            OpSumKind { kind, left, right } => match kind {
670                OpSum::Add => self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 + f2)),
671                OpSum::Minus => self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 - f2)),
672            },
673            NumberKind(value) => CalcResult::Number(*value),
674            StringKind(value) => CalcResult::String(value.replace(r#""""#, r#"""#)),
675            BooleanKind(value) => CalcResult::Boolean(*value),
676            ReferenceKind {
677                sheet_name: _,
678                sheet_index,
679                absolute_row,
680                absolute_column,
681                row,
682                column,
683            } => {
684                let mut row1 = *row;
685                let mut column1 = *column;
686                if !absolute_row {
687                    row1 += cell.row;
688                }
689                if !absolute_column {
690                    column1 += cell.column;
691                }
692                self.evaluate_cell(CellReferenceIndex {
693                    sheet: *sheet_index,
694                    row: row1,
695                    column: column1,
696                })
697            }
698            WrongReferenceKind { .. } => {
699                CalcResult::new_error(Error::REF, cell, "Wrong reference".to_string())
700            }
701            OpRangeKind { left, right } => self.get_range(left, right, cell),
702            WrongRangeKind { .. } => {
703                CalcResult::new_error(Error::REF, cell, "Wrong range".to_string())
704            }
705            RangeKind {
706                sheet_index,
707                row1,
708                column1,
709                row2,
710                column2,
711                absolute_column1,
712                absolute_row2,
713                absolute_row1,
714                absolute_column2,
715                sheet_name: _,
716            } => {
717                let r1 = if *absolute_row1 {
718                    *row1
719                } else {
720                    *row1 + cell.row
721                };
722                let r2 = if *absolute_row2 {
723                    *row2
724                } else {
725                    *row2 + cell.row
726                };
727                let c1 = if *absolute_column1 {
728                    *column1
729                } else {
730                    *column1 + cell.column
731                };
732                let c2 = if *absolute_column2 {
733                    *column2
734                } else {
735                    *column2 + cell.column
736                };
737                CalcResult::Range {
738                    left: CellReferenceIndex {
739                        sheet: *sheet_index,
740                        row: r1.min(r2),
741                        column: c1.min(c2),
742                    },
743                    right: CellReferenceIndex {
744                        sheet: *sheet_index,
745                        row: r1.max(r2),
746                        column: c1.max(c2),
747                    },
748                }
749            }
750            OpConcatenateKind { left, right } => self.handle_concatenate(left, right, cell),
751            OpProductKind { kind, left, right } => match kind {
752                OpProduct::Times => {
753                    self.handle_arithmetic(left, right, cell, &|f1, f2| Ok(f1 * f2))
754                }
755                OpProduct::Divide => self.handle_arithmetic(left, right, cell, &|f1, f2| {
756                    if f2 == 0.0 {
757                        Err(Error::DIV)
758                    } else {
759                        Ok(f1 / f2)
760                    }
761                }),
762            },
763            OpPowerKind { left, right } => self.handle_arithmetic(left, right, cell, &power),
764            FunctionKind { kind, args } => self.evaluate_function(kind, args, cell),
765            NamedFunctionKind { name, args, id } => {
766                let lambda_result = if let Some(var_id) = id {
767                    // Bound by LET — look up the variable, which should be a Lambda.
768                    match self.variable_stack.get(&(*var_id as usize)) {
769                        Some(v) => v.clone(),
770                        None => {
771                            return CalcResult::new_error(
772                                Error::NAME,
773                                cell,
774                                format!("Variable \"{name}\" not found in scope."),
775                            )
776                        }
777                    }
778                } else {
779                    // Not bound by LET — look up as a defined-name Lambda.
780                    // Prefer sheet-local (current sheet) over global (scope = None),
781                    // matching Excel's name resolution order.
782                    let name_lower = name.to_lowercase();
783                    let found = self
784                        .parsed_defined_names
785                        .get(&(Some(cell.sheet), name_lower.clone()))
786                        .or_else(|| self.parsed_defined_names.get(&(None, name_lower)))
787                        .cloned();
788                    match found {
789                        Some(ParsedDefinedName::LambdaDefinition(param_names, body)) => {
790                            let lambda_id = self.get_next_lambda_id();
791                            self.lambdas.insert(lambda_id, (param_names, body));
792                            CalcResult::Lambda(lambda_id)
793                        }
794                        _ => {
795                            return CalcResult::new_error(
796                                Error::NAME,
797                                cell,
798                                format!("Invalid function: {name}"),
799                            )
800                        }
801                    }
802                };
803                self.call_lambda(lambda_result, args, cell)
804            }
805            ArrayKind(s) => CalcResult::Array(s.to_owned()),
806            DefinedNameKind((name, scope, _)) => {
807                if let Ok(Some(parsed_defined_name)) = self.get_parsed_defined_name(name, *scope) {
808                    match parsed_defined_name {
809                        ParsedDefinedName::CellReference(reference) => {
810                            self.evaluate_cell(reference)
811                        }
812                        ParsedDefinedName::RangeReference(range) => CalcResult::Range {
813                            left: range.left,
814                            right: range.right,
815                        },
816                        ParsedDefinedName::LambdaDefinition(param_names, body) => {
817                            let lambda_id = self.get_next_lambda_id();
818                            self.lambdas.insert(lambda_id, (param_names, body));
819                            CalcResult::Lambda(lambda_id)
820                        }
821                        ParsedDefinedName::InvalidDefinedNameFormula => CalcResult::new_error(
822                            Error::NAME,
823                            cell,
824                            format!("Defined name \"{name}\" is not a reference."),
825                        ),
826                    }
827                } else {
828                    CalcResult::new_error(
829                        Error::NAME,
830                        cell,
831                        format!("Defined name \"{name}\" not found."),
832                    )
833                }
834            }
835            TableNameKind(s) => CalcResult::new_error(
836                Error::NAME,
837                cell,
838                format!("table name \"{s}\" not supported."),
839            ),
840            // A variable bound to a single cell is that cell's value here,
841            // where a value is wanted; it is the reference itself where a
842            // reference is wanted (see `evaluate_node_with_reference`).
843            NamedVariableKind { .. } => match self.variable_value(node, cell) {
844                CalcResult::Range { left, right } if left == right => self.evaluate_cell(left),
845                value => value,
846            },
847            CompareKind { kind, left, right } => self.handle_comparison(left, right, cell, kind),
848            // What they are applied to is turned into a number, and they work
849            // element by element on a range or an array
850            UnaryKind { kind, right } => match kind {
851                OpUnary::Minus => self.apply_number_unary(right, cell, |f| Ok(-f)),
852                OpUnary::Percentage => self.apply_number_unary(right, cell, |f| Ok(f / 100.0)),
853            },
854            ErrorKind(kind) => CalcResult::new_error(kind.clone(), cell, "".to_string()),
855            ParseErrorKind {
856                formula, message, ..
857            } => CalcResult::new_error(
858                Error::ERROR,
859                cell,
860                format!("Error parsing {formula}: {message}"),
861            ),
862            EmptyArgKind => CalcResult::EmptyArg,
863            SpillRangeOperator { child } => match self.evaluate_node_with_reference(child, cell) {
864                CalcResult::Range { left, right } => {
865                    if left != right {
866                        return CalcResult::new_error(
867                            Error::ERROR,
868                            cell,
869                            format!("Error with Spill Range Operator in cell {cell:?}"),
870                        );
871                    }
872                    // The spill range depends on the *shape* of the anchor, which is
873                    // only known once the anchor has been evaluated. Evaluating it here
874                    // also records the dependency in `readers`.
875                    if let CalcResult::Error {
876                        error: Error::CIRC, ..
877                    } = self.evaluate_cell(left)
878                    {
879                        return CalcResult::new_error(
880                            Error::CIRC,
881                            cell,
882                            "Circular reference detected".to_string(),
883                        );
884                    }
885                    let sheet = left.sheet;
886                    let row = left.row;
887                    let column = left.column;
888                    let worksheet = match self.workbook.worksheet(sheet) {
889                        Ok(s) => s,
890                        Err(e) => {
891                            return CalcResult::new_error(
892                                Error::REF,
893                                cell,
894                                format!("Sheet index {sheet} not found: {e}"),
895                            );
896                        }
897                    };
898                    match worksheet.get_cell_spill(row, column) {
899                        Ok((width, height)) => CalcResult::Range {
900                            left: CellReferenceIndex { sheet, row, column },
901                            right: CellReferenceIndex {
902                                sheet,
903                                row: row + height - 1,
904                                column: column + width - 1,
905                            },
906                        },
907                        Err(e) => CalcResult::new_error(
908                            Error::REF,
909                            cell,
910                            format!("Cell {sheet}!{row},{column} not found: {e}"),
911                        ),
912                    }
913                }
914                _ => CalcResult::new_error(
915                    Error::ERROR,
916                    cell,
917                    format!("Error with Spill Range Operator in cell {cell:?}"),
918                ),
919            },
920            ImplicitIntersection {
921                automatic: _,
922                child,
923            } => match self.evaluate_node_with_reference(child, cell) {
924                CalcResult::Array(array) => top_left_of_array(&array, cell),
925                result => self.implicit_intersection_to_value(result, cell),
926            },
927            LambdaDefKind { parameters, body } => {
928                let id = self.get_next_lambda_id();
929                self.lambdas.insert(id, (parameters.clone(), *body.clone()));
930                CalcResult::Lambda(id)
931            }
932            LambdaCallKind { lambda, args } => {
933                let lambda_result = self.evaluate_node_in_context(lambda, cell);
934                self.call_lambda(lambda_result, args, cell)
935            }
936        }
937    }
938
939    fn cell_reference_to_string(
940        &self,
941        cell_reference: &CellReferenceIndex,
942    ) -> Result<String, String> {
943        let sheet = self.workbook.worksheet(cell_reference.sheet)?;
944        let column = utils::number_to_column(cell_reference.column)
945            .ok_or_else(|| "Invalid column".to_string())?;
946        if !is_valid_row(cell_reference.row) {
947            return Err("Invalid row".to_string());
948        }
949        Ok(format!("{}!{}{}", sheet.name, column, cell_reference.row))
950    }
951
952    fn get_value_from_array(
953        &self,
954        array: &[Vec<ArrayNode>],
955        row: i32,
956        column: i32,
957    ) -> Option<ArrayNode> {
958        let width = array[0].len() as i32;
959        let height = array.len() as i32;
960        if row < 1 || row > height || column < 1 || column > width {
961            return None;
962        }
963        let value = &array[(row - 1) as usize][(column - 1) as usize];
964        Some(value.clone())
965    }
966
967    /// Sets `result` in the formula cell at `cell_reference`, which `cell`
968    /// describes (see `FormulaCell`)
969    /// If the result is an array it will spill over other cells
970    /// If the formula is an array formula it will update the spill area.
971    ///    If the array is smaller than the spill area it will fill the remaining cells with #N/A error
972    ///    If the array is just one element it will fill the original range with that element
973    pub(crate) fn set_cells_with_result(
974        &mut self,
975        cell_reference: CellReferenceIndex,
976        cell: FormulaCell,
977        result: &CalcResult,
978    ) -> Result<(), String> {
979        let CellReferenceIndex { sheet, column, row } = cell_reference;
980        let original_range = cell.array;
981        let s = cell.s;
982        let formula = cell.f;
983        // Handle array results separately: they always return early, writing all cells
984        // themselves. By dispatching here we avoid needing an unreachable arm in the
985        // `new_cell` match below.
986        if let CalcResult::Array(array) = result {
987            if array.is_empty() || array[0].is_empty() {
988                return self.set_cells_with_result(
989                    cell_reference,
990                    cell,
991                    &CalcResult::new_error(
992                        Error::CALC,
993                        cell_reference,
994                        "Formula produced a zero-size array".to_string(),
995                    ),
996                );
997            }
998            let array_width = array[0].len() as i32;
999            let array_height = array.len() as i32;
1000
1001            match original_range {
1002                Some((true, old_area)) => {
1003                    return match self.spill_dynamic_array(
1004                        cell_reference,
1005                        old_area,
1006                        formula,
1007                        s,
1008                        array,
1009                    )? {
1010                        Some(error) => self.set_cells_with_result(cell_reference, cell, &error),
1011                        None => Ok(()),
1012                    };
1013                }
1014                Some((false, (original_width, original_height))) => {
1015                    // CSE array formula: fill the declared range with the array values.
1016                    // Use relative indices for get_value_from_array (1-based).
1017                    for r in row..row + original_height {
1018                        for c in column..column + original_width {
1019                            let rel_row = r - row + 1;
1020                            let rel_col = c - column + 1;
1021                            let value = self.get_value_from_array(array, rel_row, rel_col);
1022                            let new_cell = if r == row && c == column {
1023                                let fv = match value {
1024                                    Some(node) => array_node_to_formula_value(node),
1025                                    None => FormulaValue::new_error(
1026                                        Error::NIMPL,
1027                                        "".to_string(),
1028                                        "Unexpected array result".to_string(),
1029                                    ),
1030                                };
1031                                Cell::ArrayFormula {
1032                                    f: formula,
1033                                    s,
1034                                    r: (original_width, original_height),
1035                                    kind: ArrayKind::Cse,
1036                                    v: fv,
1037                                }
1038                            } else {
1039                                let sv = match value {
1040                                    Some(node) => array_node_to_spill_value(node),
1041                                    None => SpillValue::Error(Error::VALUE),
1042                                };
1043                                let existing_style =
1044                                    self.workbook.worksheets[sheet as usize].get_style(r, c);
1045                                Cell::SpillCell {
1046                                    s: existing_style,
1047                                    a: (row, column),
1048                                    v: sv,
1049                                }
1050                            };
1051                            // The cells are created on demand: a structural
1052                            // operation may have moved the array without them.
1053                            self.workbook.worksheets[sheet as usize].update_cell(r, c, new_cell)?;
1054                        }
1055                    }
1056                    // All cells (anchor + spills) have been written above.
1057                    return Ok(());
1058                }
1059                None => {
1060                    // Scalar formula produced an array at runtime. We only coerce safely
1061                    // when the array is 1x1 (the result is genuinely a single value just
1062                    // wrapped in an array). For larger arrays, Excel would apply implicit
1063                    // intersection (legacy) or, for formulas identified as dynamic/array
1064                    // at parse time, auto-spill. In this `original_range == None` path we
1065                    // do not have that array/dynamic context, so neither behavior is
1066                    // available here; picking [0][0] could silently produce wrong results.
1067                    // Emit #VALUE! instead so the divergence is visible.
1068                    let coerced = if array_width == 1 && array_height == 1 {
1069                        match self.get_value_from_array(array, 1, 1) {
1070                            Some(node) => array_node_to_formula_value(node),
1071                            None => FormulaValue::new_error(
1072                                Error::VALUE,
1073                                "".to_string(),
1074                                "Unexpected array result".to_string(),
1075                            ),
1076                        }
1077                    } else {
1078                        // Currently unreachable from normal user formulas: static
1079                        // analysis wraps array-returning subexpressions in scalar
1080                        // contexts in implicit intersection (`@`), which collapses
1081                        // them to a single value before they reach the cell. If we
1082                        // ever get here, static analysis or implicit-intersection
1083                        // insertion has regressed.
1084                        debug_assert!(
1085                            false,
1086                            "Larger-than-1x1 array reached scalar-context cell \
1087                             (sheet={sheet}, row={row}, column={column}, \
1088                             {array_width}x{array_height}); implicit intersection \
1089                             was expected to collapse it.",
1090                        );
1091                        FormulaValue::new_error(
1092                            Error::VALUE,
1093                            "".to_string(),
1094                            "Array result in scalar context".to_string(),
1095                        )
1096                    };
1097                    *self.workbook.worksheets[sheet as usize]
1098                        .cell_mut(row, column)
1099                        .ok_or("expected a cell")? = Cell::CellFormula {
1100                        f: formula,
1101                        s,
1102                        v: coerced,
1103                    };
1104                    return Ok(());
1105                }
1106            }
1107        }
1108
1109        let formula_value = match result {
1110            CalcResult::Number(value) => {
1111                // safety belt
1112                if value.is_nan() || value.is_infinite() {
1113                    // This should never happen, is there a way we can log this events?
1114                    return self.set_cells_with_result(
1115                        cell_reference,
1116                        cell,
1117                        &CalcResult::Error {
1118                            error: Error::NUM,
1119                            origin: cell_reference,
1120                            message: "".to_string(),
1121                        },
1122                    );
1123                }
1124                FormulaValue::Number(*value)
1125            }
1126            CalcResult::String(value) => FormulaValue::Text(value.clone()),
1127            CalcResult::Boolean(value) => FormulaValue::Boolean(*value),
1128            CalcResult::Error {
1129                error,
1130                origin,
1131                message,
1132            } => {
1133                let o = match self.cell_reference_to_string(origin) {
1134                    Ok(s) => s,
1135                    Err(_) => "".to_string(),
1136                };
1137                FormulaValue::new_error(error.clone(), o, message.to_string())
1138            }
1139            CalcResult::Range { .. } => {
1140                // This should never happen
1141                debug_assert!(false, "Unexpected range result in non-array formula");
1142                return Err("Cannot set a range as cell value".to_string());
1143            }
1144            CalcResult::EmptyCell | CalcResult::EmptyArg => {
1145                // We treat empty cells as number 0.
1146                return self.set_cells_with_result(cell_reference, cell, &CalcResult::Number(0.0));
1147            }
1148            // CalcResult::Array is handled before this match (see above); it always returns early.
1149            CalcResult::Array(_) | CalcResult::Lambda(_) => {
1150                debug_assert!(false, "Unexpected array result in non-array formula");
1151                return Err("Unexpected array result in non-array formula".to_string());
1152            }
1153        };
1154
1155        if let Some((true, old_area)) = original_range {
1156            // A dynamic anchor without an array to spill keeps no spill cells.
1157            if !self.retire_own_spill_cells(cell_reference, old_area, (1, 1))? {
1158                return Ok(());
1159            }
1160        }
1161
1162        // If the cell is the anchor of a CSE array formula, fill all spill cells
1163        if let Some((false, (width, height))) = original_range {
1164            let spill_value = formula_value_to_spill_value(&formula_value);
1165            let ws = &mut self.workbook.worksheets[sheet as usize];
1166            for r in row..row + height {
1167                for c in column..column + width {
1168                    if r == row && c == column {
1169                        continue;
1170                    }
1171                    let existing_style = ws.get_style(r, c);
1172                    ws.update_cell(
1173                        r,
1174                        c,
1175                        Cell::SpillCell {
1176                            a: (row, column),
1177                            s: existing_style,
1178                            v: spill_value.clone(),
1179                        },
1180                    )?;
1181                }
1182            }
1183        }
1184
1185        // The formula cell is on the sheet already and only its value changes:
1186        // write into it rather than build a cell to replace it. This runs once
1187        // for every formula of every evaluation.
1188        let worksheet = &mut self.workbook.worksheets[sheet as usize];
1189        match (worksheet.cell_mut(row, column), original_range) {
1190            (Some(Cell::CellFormula { f, s: style, v }), None) => {
1191                *f = formula;
1192                *style = s;
1193                *v = formula_value;
1194            }
1195            (
1196                Some(Cell::ArrayFormula {
1197                    f,
1198                    s: style,
1199                    r,
1200                    kind,
1201                    v,
1202                }),
1203                Some((is_dynamic, area)),
1204            ) => {
1205                *f = formula;
1206                *style = s;
1207                // A dynamic anchor that stores a single value has no area.
1208                (*kind, *r) = if is_dynamic {
1209                    (ArrayKind::Dynamic, (1, 1))
1210                } else {
1211                    (ArrayKind::Cse, area)
1212                };
1213                *v = formula_value;
1214            }
1215            // Not expected: the cell is not the formula cell it was when its
1216            // formula started to run. Replace whatever is there.
1217            (_, original_range) => {
1218                let new_cell = match original_range {
1219                    Some((is_dynamic, area)) => {
1220                        let (kind, r) = if is_dynamic {
1221                            (ArrayKind::Dynamic, (1, 1))
1222                        } else {
1223                            (ArrayKind::Cse, area)
1224                        };
1225                        Cell::ArrayFormula {
1226                            f: formula,
1227                            s,
1228                            r,
1229                            kind,
1230                            v: formula_value,
1231                        }
1232                    }
1233                    None => Cell::CellFormula {
1234                        f: formula,
1235                        s,
1236                        v: formula_value,
1237                    },
1238                };
1239                worksheet.update_cell(row, column, new_cell)?;
1240            }
1241        }
1242        Ok(())
1243    }
1244
1245    /// Spills `array` from the dynamic anchor at `anchor`, which currently
1246    /// occupies `old_area` (width, height). Returns the error the anchor must
1247    /// store instead when the array cannot be spilled: it would leave the sheet,
1248    /// or a cell of the area is occupied. Own spill cells never block: they are
1249    /// rewritten or, outside the new area, removed.
1250    ///
1251    /// Before writing, the evaluation is asked whether the write contradicts
1252    /// what a formula already read in this pass; if so nothing is written and
1253    /// the pass restarts (see `evaluation.rs`).
1254    fn spill_dynamic_array(
1255        &mut self,
1256        anchor: CellReferenceIndex,
1257        old_area: (i32, i32),
1258        formula: i32,
1259        style: i32,
1260        array: &[Vec<ArrayNode>],
1261    ) -> Result<Option<CalcResult>, String> {
1262        let CellReferenceIndex { sheet, row, column } = anchor;
1263        let height = array.len() as i32;
1264        let width = array[0].len() as i32;
1265        let spill_error = |message: &str| {
1266            Some(CalcResult::new_error(
1267                Error::SPILL,
1268                anchor,
1269                message.to_string(),
1270            ))
1271        };
1272        if row + height - 1 > LAST_ROW || column + width - 1 > LAST_COLUMN {
1273            return Ok(spill_error("Spill would exceed worksheet bounds"));
1274        }
1275
1276        // Is the area free? Another array's spill cell blocks, whichever anchor
1277        // came first in the sheet: the spill that exists keeps its cells, as in
1278        // Excel, and the newcomer gets #SPILL! until it goes away. Such cells
1279        // are recorded as seen occupied, since removing them later in the pass
1280        // would change this outcome.
1281        let mut blocked = false;
1282        let mut occupied_by_other_spills = Vec::new();
1283        {
1284            let worksheet = self.workbook.worksheet(sheet)?;
1285            for r in row..row + height {
1286                for c in column..column + width {
1287                    if (r, c) == (row, column) {
1288                        continue;
1289                    }
1290                    if worksheet.merged_cell_containing(r, c).is_some() {
1291                        blocked = true;
1292                        continue;
1293                    }
1294                    match worksheet.cell(r, c).as_deref() {
1295                        None | Some(Cell::EmptyCell { .. }) => {}
1296                        Some(Cell::SpillCell { a, .. }) if *a == (row, column) => {}
1297                        Some(Cell::SpillCell { .. }) => {
1298                            blocked = true;
1299                            occupied_by_other_spills.push(CellReferenceIndex {
1300                                sheet,
1301                                row: r,
1302                                column: c,
1303                            });
1304                        }
1305                        Some(_) => blocked = true,
1306                    }
1307                }
1308            }
1309        }
1310        for position in occupied_by_other_spills {
1311            self.record_seen(position, Seen::Occupied);
1312        }
1313        if blocked {
1314            return Ok(spill_error("Cannot spill array result"));
1315        }
1316
1317        let writes: Vec<CellKey> = (row..row + height)
1318            .flat_map(|r| (column..column + width).map(move |c| (sheet, r, c)))
1319            .filter(|&(_, r, c)| (r, c) != (row, column))
1320            .collect();
1321        let clears = self.own_spill_cells_outside(anchor, old_area, (width, height))?;
1322        if self.spill_contradicts_a_read(anchor, &writes, &clears) {
1323            return Ok(None);
1324        }
1325
1326        let worksheet = self.workbook.worksheet_mut(sheet)?;
1327        for r in row..row + height {
1328            for c in column..column + width {
1329                let value = array[(r - row) as usize][(c - column) as usize].clone();
1330                let cell = if (r, c) == (row, column) {
1331                    Cell::ArrayFormula {
1332                        f: formula,
1333                        s: style,
1334                        r: (width, height),
1335                        kind: ArrayKind::Dynamic,
1336                        v: array_node_to_formula_value(value),
1337                    }
1338                } else {
1339                    Cell::SpillCell {
1340                        a: (row, column),
1341                        s: worksheet.get_style(r, c),
1342                        v: array_node_to_spill_value(value),
1343                    }
1344                };
1345                // Cells are created on demand: rows and columns may not exist yet.
1346                worksheet.update_cell(r, c, cell)?;
1347            }
1348        }
1349        for (_, r, c) in clears {
1350            worksheet.cell_clear_contents(r, c)?;
1351        }
1352        Ok(None)
1353    }
1354
1355    /// Removes the spill cells the dynamic anchor at `anchor` still has from a
1356    /// previous evaluation outside `keep_area` (width, height). Returns `false`
1357    /// without touching the sheet when the evaluation decides the pass must
1358    /// restart instead (a formula was blocked by one of those cells).
1359    fn retire_own_spill_cells(
1360        &mut self,
1361        anchor: CellReferenceIndex,
1362        old_area: (i32, i32),
1363        keep_area: (i32, i32),
1364    ) -> Result<bool, String> {
1365        let clears = self.own_spill_cells_outside(anchor, old_area, keep_area)?;
1366        if self.spill_contradicts_a_read(anchor, &[], &clears) {
1367            return Ok(false);
1368        }
1369        let worksheet = self.workbook.worksheet_mut(anchor.sheet)?;
1370        for (_, r, c) in clears {
1371            worksheet.cell_clear_contents(r, c)?;
1372        }
1373        Ok(true)
1374    }
1375
1376    /// The spill cells of `anchor` inside `old_area` but outside `keep_area`
1377    /// (both width, height). Cells with any other content are not the anchor's
1378    /// to touch.
1379    fn own_spill_cells_outside(
1380        &self,
1381        anchor: CellReferenceIndex,
1382        old_area: (i32, i32),
1383        keep_area: (i32, i32),
1384    ) -> Result<Vec<CellKey>, String> {
1385        let CellReferenceIndex { sheet, row, column } = anchor;
1386        let worksheet = self.workbook.worksheet(sheet)?;
1387        let mut cells = Vec::new();
1388        for r in row..row + old_area.1 {
1389            for c in column..column + old_area.0 {
1390                let kept = r < row + keep_area.1 && c < column + keep_area.0;
1391                if kept {
1392                    continue;
1393                }
1394                if matches!(
1395                    worksheet.cell(r, c).as_deref(),
1396                    Some(Cell::SpillCell { a, .. }) if *a == (row, column)
1397                ) {
1398                    cells.push((sheet, r, c));
1399                }
1400            }
1401        }
1402        Ok(cells)
1403    }
1404
1405    /// Sets the color of the sheet tab.
1406    ///
1407    /// # Examples
1408    ///
1409    /// ```rust
1410    /// # use ironcalc_base::{Model, types::Color};
1411    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1412    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1413    /// assert_eq!(model.workbook.worksheet(0)?.color, Color::None);
1414    /// model.set_sheet_color(0, &Color::Rgb("#DBBE29".to_string()))?;
1415    /// assert_eq!(model.workbook.worksheet(0)?.color, Color::Rgb("#DBBE29".to_string()));
1416    /// # Ok(())
1417    /// # }
1418    /// ```
1419    pub fn set_sheet_color(&mut self, sheet: u32, color: &Color) -> Result<(), String> {
1420        let worksheet = self.workbook.worksheet_mut(sheet)?;
1421        worksheet.color = color.clone();
1422        Ok(())
1423    }
1424
1425    /// Changes the visibility of a sheet
1426    pub fn set_sheet_state(&mut self, sheet: u32, state: SheetState) -> Result<(), String> {
1427        let worksheet = self.workbook.worksheet_mut(sheet)?;
1428        worksheet.state = state;
1429        Ok(())
1430    }
1431
1432    /// Sets the workbook theme.
1433    pub fn set_theme(&mut self, theme: crate::types::Theme) {
1434        self.workbook.theme = theme;
1435        self.evaluate_conditional_formatting();
1436    }
1437
1438    /// Returns the Theme
1439    pub fn get_theme(&self) -> Theme {
1440        self.workbook.theme.clone()
1441    }
1442
1443    /// Makes the grid lines in the sheet visible (`true`) or hidden (`false`)
1444    pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<(), String> {
1445        let worksheet = self.workbook.worksheet_mut(sheet)?;
1446        worksheet.show_grid_lines = show_grid_lines;
1447        Ok(())
1448    }
1449
1450    // Returns the 'single' value of a cell. Not arrays or ranges.
1451    pub(crate) fn get_cell_value(
1452        &self,
1453        cell: &Cell,
1454        cell_reference: CellReferenceIndex,
1455    ) -> CalcResult {
1456        use Cell::*;
1457        match cell {
1458            EmptyCell { .. } => CalcResult::EmptyCell,
1459            BooleanCell { v, .. } => CalcResult::Boolean(*v),
1460            NumberCell { v, .. } => CalcResult::Number(*v),
1461            ErrorCell { ei, .. } => {
1462                let message = ei.to_localized_error_string(self.language);
1463                CalcResult::new_error(ei.clone(), cell_reference, message)
1464            }
1465            SharedString { si, .. } => {
1466                if let Some(s) = self.workbook.shared_strings.get(*si as usize) {
1467                    CalcResult::String(s.clone())
1468                } else {
1469                    let message = "Invalid shared string".to_string();
1470                    CalcResult::new_error(Error::ERROR, cell_reference, message)
1471                }
1472            }
1473            CellFormula {
1474                v: FormulaValue::Unevaluated,
1475                ..
1476            }
1477            | ArrayFormula {
1478                v: FormulaValue::Unevaluated,
1479                ..
1480            } => CalcResult::Error {
1481                error: Error::ERROR,
1482                origin: cell_reference,
1483                message: "Unevaluated formula".to_string(),
1484            },
1485            CellFormula {
1486                v: FormulaValue::Boolean(v),
1487                ..
1488            }
1489            | ArrayFormula {
1490                v: FormulaValue::Boolean(v),
1491                ..
1492            } => CalcResult::Boolean(*v),
1493            CellFormula {
1494                v: FormulaValue::Number(v),
1495                ..
1496            }
1497            | ArrayFormula {
1498                v: FormulaValue::Number(v),
1499                ..
1500            } => CalcResult::Number(*v),
1501            CellFormula {
1502                v: FormulaValue::Text(v),
1503                ..
1504            }
1505            | ArrayFormula {
1506                v: FormulaValue::Text(v),
1507                ..
1508            } => CalcResult::String(v.clone()),
1509            CellFormula {
1510                v: FormulaValue::Error { ei, d },
1511                ..
1512            }
1513            | ArrayFormula {
1514                v: FormulaValue::Error { ei, d },
1515                ..
1516            } => {
1517                if let Some(cell_reference) = self.parse_reference(&d.o) {
1518                    CalcResult::new_error(ei.clone(), cell_reference, d.m.clone())
1519                } else {
1520                    CalcResult::Error {
1521                        error: ei.clone(),
1522                        origin: cell_reference,
1523                        message: ei.to_localized_error_string(self.language),
1524                    }
1525                }
1526            }
1527            SpillCell {
1528                v: SpillValue::Number(v),
1529                ..
1530            } => CalcResult::Number(*v),
1531            SpillCell {
1532                v: SpillValue::Boolean(v),
1533                ..
1534            } => CalcResult::Boolean(*v),
1535            SpillCell {
1536                v: SpillValue::Text(v),
1537                ..
1538            } => CalcResult::String(v.clone()),
1539            SpillCell {
1540                v: SpillValue::Error(ei),
1541                ..
1542            } => {
1543                let message = ei.to_localized_error_string(self.language);
1544                CalcResult::new_error(ei.clone(), cell_reference, message)
1545            }
1546        }
1547    }
1548
1549    /// Returns `true` if the cell is completely empty.
1550    ///
1551    /// # Examples
1552    ///
1553    /// ```rust
1554    /// # use ironcalc_base::Model;
1555    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1556    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1557    /// assert_eq!(model.is_empty_cell(0, 1, 1)?, true);
1558    /// model.set_user_input(0, 1, 1, "Attention is all you need".to_string());
1559    /// assert_eq!(model.is_empty_cell(0, 1, 1)?, false);
1560    /// # Ok(())
1561    /// # }
1562    /// ```
1563    pub fn is_empty_cell(&self, sheet: u32, row: i32, column: i32) -> Result<bool, String> {
1564        self.workbook.worksheet(sheet)?.is_empty_cell(row, column)
1565    }
1566
1567    /// Evaluates all cells in a given range and returns the results in a 2D vector.
1568    pub(crate) fn evaluate_range(
1569        &mut self,
1570        left: CellReferenceIndex,
1571        right: CellReferenceIndex,
1572    ) -> Vec<Vec<ArrayNode>> {
1573        let width = (right.column - left.column + 1).max(0) as usize;
1574        let height = (right.row - left.row + 1).max(0) as usize;
1575        let mut result = Vec::with_capacity(height);
1576        for row in left.row..=right.row {
1577            let mut row_result = Vec::with_capacity(width);
1578            self.read_row(left.sheet, row, left.column, right.column, &mut |value| {
1579                row_result.push(value)
1580            });
1581            result.push(row_result);
1582        }
1583        result
1584    }
1585
1586    /// The numbers of a range, row by row in one vector, if every cell of it
1587    /// holds a number; `None` as soon as a row has anything else in it. For
1588    /// the functions that work on matrices of numbers: a large range of data
1589    /// is read into the numbers alone, an eighth of what `evaluate_range`
1590    /// needs for it.
1591    ///
1592    /// `None` says nothing about what the range holds: `evaluate_range` then
1593    /// reads it again, from the start. What this read of it evaluated stays
1594    /// evaluated.
1595    pub(crate) fn evaluate_range_of_numbers(
1596        &mut self,
1597        left: CellReferenceIndex,
1598        right: CellReferenceIndex,
1599    ) -> Option<Vec<f64>> {
1600        let width = (right.column - left.column + 1).max(0) as usize;
1601        let height = (right.row - left.row + 1).max(0) as usize;
1602        let mut numbers = Vec::with_capacity(width * height);
1603        let mut only_numbers = true;
1604        for row in left.row..=right.row {
1605            // A row the sheet holds as numbers is copied as it is. Not while
1606            // the pass is being abandoned: nothing read then is a value.
1607            if self.evaluation.restart.is_none() && !self.evaluation.unwinding {
1608                let sheet_data = &self.workbook.worksheets[left.sheet as usize].sheet_data;
1609                if let Some(row_numbers) =
1610                    sheet_data.numbers_in_row_between(row, left.column, right.column)
1611                {
1612                    numbers.extend_from_slice(row_numbers);
1613                    continue;
1614                }
1615            }
1616            self.read_row(
1617                left.sheet,
1618                row,
1619                left.column,
1620                right.column,
1621                &mut |value| match value {
1622                    ArrayNode::Number(number) => numbers.push(number),
1623                    _ => only_numbers = false,
1624                },
1625            );
1626            if !only_numbers {
1627                return None;
1628            }
1629        }
1630        Some(numbers)
1631    }
1632
1633    /// Evaluates the cells of a row from `first_column` to `last_column`, in
1634    /// order, and hands the value of each to `push`.
1635    fn read_row(
1636        &mut self,
1637        sheet: u32,
1638        row: i32,
1639        first_column: i32,
1640        last_column: i32,
1641        push: &mut impl FnMut(ArrayNode),
1642    ) {
1643        let mut column = first_column;
1644        while column <= last_column {
1645            // Constants are read straight from the sheet; what is left,
1646            // formulas and the cells they write, is evaluated cell by cell.
1647            let (stop, resume) = self.read_constants(sheet, row, column, last_column, push);
1648            for c in stop..resume {
1649                let cell_reference = CellReferenceIndex {
1650                    sheet,
1651                    row,
1652                    column: c,
1653                };
1654                let value = match self.evaluate_cell(cell_reference) {
1655                    CalcResult::Number(n) => ArrayNode::Number(n),
1656                    CalcResult::Boolean(b) => ArrayNode::Boolean(b),
1657                    CalcResult::String(s) => ArrayNode::String(s),
1658                    CalcResult::Error { error, .. } => ArrayNode::Error(error),
1659                    CalcResult::EmptyCell | CalcResult::EmptyArg => ArrayNode::Empty,
1660                    CalcResult::Range { .. } | CalcResult::Array(_) | CalcResult::Lambda(_) => {
1661                        // This should never happen, but we need to handle it anyway
1662                        debug_assert!(false, "Unexpected array result in non-array formula");
1663                        ArrayNode::Error(Error::NIMPL)
1664                    }
1665                };
1666                push(value);
1667            }
1668            column = resume;
1669        }
1670    }
1671
1672    /// Reads the cells of a row from `column` on, up to `last_column`, for as
1673    /// long as they are constants or hold nothing, and hands their values to
1674    /// `push`: exactly what `evaluate_cell` would give for each of them,
1675    /// with the row found once instead of once per cell. A range of data is
1676    /// read whole this way.
1677    ///
1678    /// It stops at the first formula or spill cell, which only `evaluate_cell`
1679    /// can read, and returns `(stop, resume)`: the cells from `stop` up to
1680    /// `resume`, not included, are left to the caller, and reading goes on
1681    /// from `resume`. Both are past `last_column` once the row is read. Cells
1682    /// are read in order and nothing is read beyond the first formula:
1683    /// running it can change what the cells after it hold.
1684    fn read_constants(
1685        &mut self,
1686        sheet: u32,
1687        row: i32,
1688        column: i32,
1689        last_column: i32,
1690        push: &mut impl FnMut(ArrayNode),
1691    ) -> (i32, i32) {
1692        let evaluation = &mut self.evaluation;
1693        if evaluation.restart.is_some() || evaluation.unwinding {
1694            // Nothing read now counts, and nothing is put on record: leave
1695            // the rest of the row to `evaluate_cell`, which knows.
1696            return (column, last_column + 1);
1697        }
1698        let shared_strings = &self.workbook.shared_strings;
1699        let mut cells = self.workbook.worksheets[sheet as usize]
1700            .sheet_data
1701            .cells_in_row_between(row, column, last_column);
1702        let mut next = column;
1703        // A position without a cell holds nothing, like an empty cell.
1704        let mut read_empty = |from: i32, to: i32, push: &mut dyn FnMut(ArrayNode)| {
1705            for column in from..to {
1706                evaluation.record_seen(CellReferenceIndex { sheet, row, column }, Seen::Empty);
1707                push(ArrayNode::Empty);
1708            }
1709        };
1710        while let Some((c, cell)) = cells.next() {
1711            let value = match &*cell {
1712                Cell::EmptyCell { .. } => {
1713                    read_empty(next, c + 1, push);
1714                    next = c + 1;
1715                    continue;
1716                }
1717                Cell::NumberCell { v, .. } => ArrayNode::Number(*v),
1718                Cell::BooleanCell { v, .. } => ArrayNode::Boolean(*v),
1719                Cell::ErrorCell { ei, .. } => ArrayNode::Error(ei.clone()),
1720                Cell::SharedString { si, .. } => match shared_strings.get(*si as usize) {
1721                    Some(s) => ArrayNode::String(s.clone()),
1722                    None => ArrayNode::Error(Error::ERROR),
1723                },
1724                Cell::CellFormula { .. } | Cell::ArrayFormula { .. } | Cell::SpillCell { .. } => {
1725                    read_empty(next, c, push);
1726                    // The formulas that follow it without a gap go with it.
1727                    let mut resume = c + 1;
1728                    for (following, cell) in cells.by_ref() {
1729                        let is_constant = !matches!(
1730                            *cell,
1731                            Cell::CellFormula { .. }
1732                                | Cell::ArrayFormula { .. }
1733                                | Cell::SpillCell { .. }
1734                        );
1735                        if following != resume || is_constant {
1736                            break;
1737                        }
1738                        resume += 1;
1739                    }
1740                    return (c, resume);
1741                }
1742            };
1743            read_empty(next, c, push);
1744            push(value);
1745            next = c + 1;
1746        }
1747        read_empty(next, last_column + 1, push);
1748        (last_column + 1, last_column + 1)
1749    }
1750
1751    #[inline(always)]
1752    pub(crate) fn fetch_cell(&self, cell_reference: CellReferenceIndex) -> Option<Cow<'_, Cell>> {
1753        self.workbook.worksheets[cell_reference.sheet as usize]
1754            .sheet_data
1755            .cell(cell_reference.row, cell_reference.column)
1756    }
1757
1758    pub(crate) fn get_sheet_index_by_name(&self, name: &str) -> Option<u32> {
1759        let worksheets = &self.workbook.worksheets;
1760        for (index, worksheet) in worksheets.iter().enumerate() {
1761            if worksheet.get_name().to_uppercase() == name.to_uppercase() {
1762                return Some(index as u32);
1763            }
1764        }
1765        None
1766    }
1767
1768    /// Returns a model from an internal binary representation of a workbook
1769    ///
1770    /// # Examples
1771    ///
1772    /// ```rust
1773    /// # use ironcalc_base::Model;
1774    /// # use ironcalc_base::cell::CellValue;
1775    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1776    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1777    /// model.set_user_input(0, 1, 1, "Stella!".to_string());
1778    /// let model2 = Model::from_bytes(&model.to_bytes(), "en")?;
1779    /// assert_eq!(
1780    ///     model2.get_cell_value_by_index(0, 1, 1),
1781    ///     Ok(CellValue::String("Stella!".to_string()))
1782    /// );
1783    /// # Ok(())
1784    /// # }
1785    /// ```
1786    ///
1787    /// See also:
1788    /// * [Model::to_bytes]
1789    pub fn from_bytes(s: &[u8], language_id: &'a str) -> Result<Model<'a>, String> {
1790        let workbook: Workbook =
1791            bitcode::decode(s).map_err(|e| format!("Error parsing workbook: {e}"))?;
1792        Model::from_workbook(workbook, language_id)
1793    }
1794
1795    /// Returns a model from a Workbook object
1796    ///
1797    /// # Examples
1798    ///
1799    /// ```rust
1800    /// # use ironcalc_base::Model;
1801    /// # use ironcalc_base::cell::CellValue;
1802    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1803    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1804    /// model.set_user_input(0, 1, 1, "Stella!".to_string());
1805    /// let model2 = Model::from_workbook(model.workbook, "en")?;
1806    /// assert_eq!(
1807    ///     model2.get_cell_value_by_index(0, 1, 1),
1808    ///     Ok(CellValue::String("Stella!".to_string()))
1809    /// );
1810    /// # Ok(())
1811    /// # }
1812    /// ```
1813    pub fn from_workbook(workbook: Workbook, language_id: &str) -> Result<Model<'_>, String> {
1814        let parsed_formulas = Vec::new();
1815        let worksheets = &workbook.worksheets;
1816
1817        let worksheet_names = worksheets.iter().map(|s| s.get_name()).collect();
1818
1819        let defined_names = workbook.get_defined_names_with_scope();
1820        // add all tables
1821        // let mut tables = Vec::new();
1822        // for worksheet in worksheets {
1823        //     let mut tables_in_sheet = HashMap::new();
1824        //     for table in &worksheet.tables {
1825        //         tables_in_sheet.insert(table.name.clone(), table.clone());
1826        //     }
1827        //     tables.push(tables_in_sheet);
1828        // }
1829
1830        let locale =
1831            get_locale(&workbook.settings.locale).map_err(|_| "Invalid locale".to_string())?;
1832        let tz = Tz::parse(&workbook.settings.tz)?;
1833
1834        let language = match get_language(language_id) {
1835            Ok(lang) => lang,
1836            Err(_) => return Err("Invalid language".to_string()),
1837        };
1838        let parser = Parser::new(
1839            worksheet_names,
1840            defined_names,
1841            workbook.tables.clone(),
1842            locale,
1843            language,
1844        );
1845        let mut shared_strings = HashMap::new();
1846        for (index, s) in workbook.shared_strings.iter().enumerate() {
1847            shared_strings.insert(s.to_string(), index);
1848        }
1849
1850        let mut model = Model {
1851            workbook,
1852            parsed_formulas,
1853            shared_formula_lookup: Vec::new(),
1854            shared_strings,
1855            parsed_defined_names: HashMap::new(),
1856            parser,
1857            language,
1858            locale,
1859            tz,
1860            view_id: 0,
1861            variable_stack: HashMap::new(),
1862            last_variable_id: 0,
1863            lambdas: HashMap::new(),
1864            last_lambda_id: 0,
1865            evaluation: Evaluation::default(),
1866            cf_cache: HashMap::new(),
1867            links: HashMap::new(),
1868        };
1869
1870        model.parse_formulas();
1871        model.parse_defined_names();
1872        model.evaluate_conditional_formatting();
1873
1874        Ok(model)
1875    }
1876
1877    /// Parses a reference like "Sheet1!B4" into {0, 2, 4}
1878    ///
1879    /// # Examples
1880    ///
1881    /// ```rust
1882    /// # use ironcalc_base::Model;
1883    /// # use ironcalc_base::expressions::types::CellReferenceIndex;
1884    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1885    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1886    /// model.set_user_input(0, 1, 1, "Stella!".to_string());
1887    /// let reference = model.parse_reference("Sheet1!D40");
1888    /// assert_eq!(reference, Some(CellReferenceIndex {sheet: 0, row: 40, column: 4}));
1889    /// # Ok(())
1890    /// # }
1891    /// ```
1892    pub fn parse_reference(&self, s: &str) -> Option<CellReferenceIndex> {
1893        let bytes = s.as_bytes();
1894        let mut sheet_name = "".to_string();
1895        let mut column = "".to_string();
1896        let mut row = "".to_string();
1897        let mut state = "sheet"; // "sheet", "col", "row"
1898        for &byte in bytes {
1899            match state {
1900                "sheet" => {
1901                    if byte == b'!' {
1902                        state = "col"
1903                    } else {
1904                        sheet_name.push(byte as char);
1905                    }
1906                }
1907                "col" => {
1908                    if byte.is_ascii_alphabetic() {
1909                        column.push(byte as char);
1910                    } else {
1911                        state = "row";
1912                        row.push(byte as char);
1913                    }
1914                }
1915                _ => {
1916                    row.push(byte as char);
1917                }
1918            }
1919        }
1920        let sheet = self.get_sheet_index_by_name(&sheet_name)?;
1921        let row = match row.parse::<i32>() {
1922            Ok(r) => r,
1923            Err(_) => return None,
1924        };
1925        if !(1..=constants::LAST_ROW).contains(&row) {
1926            return None;
1927        }
1928
1929        let column = match utils::column_to_number(&column) {
1930            Ok(column) => {
1931                if is_valid_column_number(column) {
1932                    column
1933                } else {
1934                    return None;
1935                }
1936            }
1937            Err(_) => return None,
1938        };
1939
1940        Some(CellReferenceIndex { sheet, row, column })
1941    }
1942
1943    /// Moves the formula `value` from `source` (in `area`) to `target`.
1944    ///
1945    /// # Examples
1946    ///
1947    /// ```rust
1948    /// # use ironcalc_base::Model;
1949    /// # use ironcalc_base::expressions::types::{Area, CellReferenceIndex};
1950    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1951    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
1952    /// let source = CellReferenceIndex { sheet: 0, row: 3, column: 1};
1953    /// let target = CellReferenceIndex { sheet: 0, row: 50, column: 1};
1954    /// let area = Area { sheet: 0, row: 1, column: 1, width: 5, height: 4};
1955    /// let result = model.move_cell_value_to_area("=B1", &source, &target, &area)?;
1956    /// assert_eq!(&result, "=B48");
1957    /// # Ok(())
1958    /// # }
1959    /// ```
1960    ///
1961    /// See also:
1962    /// * [Model::extend_to()]
1963    /// * [Model::extend_copied_value()]
1964    pub fn move_cell_value_to_area(
1965        &mut self,
1966        value: &str,
1967        source: &CellReferenceIndex,
1968        target: &CellReferenceIndex,
1969        area: &Area,
1970    ) -> Result<String, String> {
1971        let source_sheet_name = self
1972            .workbook
1973            .worksheet(source.sheet)
1974            .map_err(|e| format!("Could not find source worksheet: {e}"))?
1975            .get_name();
1976        if source.sheet != area.sheet {
1977            return Err("Source and area are in different sheets".to_string());
1978        }
1979        if source.row < area.row || source.row >= area.row + area.height {
1980            return Err("Source is outside the area".to_string());
1981        }
1982        if source.column < area.column || source.column >= area.column + area.width {
1983            return Err("Source is outside the area".to_string());
1984        }
1985        let target_sheet_name = self
1986            .workbook
1987            .worksheet(target.sheet)
1988            .map_err(|e| format!("Could not find target worksheet: {e}"))?
1989            .get_name();
1990        if let Some(formula) = self.formula_without_prefix(value) {
1991            let cell_reference = CellReferenceRC {
1992                sheet: source_sheet_name.to_owned(),
1993                row: source.row,
1994                column: source.column,
1995            };
1996            let formula_str = move_formula(
1997                &self.parser.parse(formula, &cell_reference),
1998                &MoveContext {
1999                    source_sheet_name: &source_sheet_name,
2000                    row: source.row,
2001                    column: source.column,
2002                    area,
2003                    target_sheet_name: &target_sheet_name,
2004                    row_delta: target.row - source.row,
2005                    column_delta: target.column - source.column,
2006                },
2007                self.locale,
2008                self.language,
2009            );
2010            Ok(format!("={formula_str}"))
2011        } else {
2012            Ok(value.to_string())
2013        }
2014    }
2015
2016    /// 'Extends' the value from cell (`sheet`, `row`, `column`) to (`target_row`, `target_column`) in the same sheet
2017    ///
2018    /// # Examples
2019    ///
2020    /// ```rust
2021    /// # use ironcalc_base::Model;
2022    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2023    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2024    /// let (sheet, row, column) = (0, 1, 1);
2025    /// model.set_user_input(sheet, row, column, "=B1*D4".to_string());
2026    /// let (target_row, target_column) = (30, 1);
2027    /// let result = model.extend_to(sheet, row, column, target_row, target_column)?;
2028    /// assert_eq!(&result, "=B30*D33");
2029    /// # Ok(())
2030    /// # }
2031    /// ```
2032    ///
2033    /// See also:
2034    /// * [Model::extend_copied_value()]
2035    /// * [Model::move_cell_value_to_area()]
2036    pub fn extend_to(
2037        &self,
2038        sheet: u32,
2039        row: i32,
2040        column: i32,
2041        target_row: i32,
2042        target_column: i32,
2043    ) -> Result<String, String> {
2044        let cell = self.workbook.worksheet(sheet)?.cell(row, column);
2045        let result = match cell {
2046            Some(cell) => match cell.get_formula() {
2047                None => cell.get_localized_text(
2048                    &self.workbook.shared_strings,
2049                    self.locale,
2050                    self.language,
2051                ),
2052                Some(i) => {
2053                    let (formula, _static_result) =
2054                        &self.parsed_formulas[sheet as usize][i as usize];
2055                    let cell_ref = CellReferenceRC {
2056                        sheet: self.workbook.worksheets[sheet as usize].get_name(),
2057                        row: target_row,
2058                        column: target_column,
2059                    };
2060                    format!(
2061                        "={}",
2062                        to_localized_string(formula, &cell_ref, self.locale, self.language)
2063                    )
2064                }
2065            },
2066            None => "".to_string(),
2067        };
2068        Ok(result)
2069    }
2070
2071    /// 'Extends' the formula `value` from `source` to `target`
2072    ///
2073    /// # Examples
2074    ///
2075    /// ```rust
2076    /// # use ironcalc_base::Model;
2077    /// # use ironcalc_base::expressions::types::CellReferenceIndex;
2078    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2079    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2080    /// let source = CellReferenceIndex {sheet: 0, row: 1, column: 1};
2081    /// let target = CellReferenceIndex {sheet: 0, row: 30, column: 1};
2082    /// let result = model.extend_copied_value("=B1*D4", &source, &target)?;
2083    /// assert_eq!(&result, "=B30*D33");
2084    /// # Ok(())
2085    /// # }
2086    /// ```
2087    ///
2088    /// See also:
2089    /// * [Model::extend_to()]
2090    /// * [Model::move_cell_value_to_area()]
2091    pub fn extend_copied_value(
2092        &mut self,
2093        value: &str,
2094        source: &CellReferenceIndex,
2095        target: &CellReferenceIndex,
2096    ) -> Result<String, String> {
2097        let source_sheet_name = match self.workbook.worksheets.get(source.sheet as usize) {
2098            Some(ws) => ws.get_name(),
2099            None => {
2100                return Err("Invalid worksheet index".to_owned());
2101            }
2102        };
2103        let target_sheet_name = match self.workbook.worksheets.get(target.sheet as usize) {
2104            Some(ws) => ws.get_name(),
2105            None => {
2106                return Err("Invalid worksheet index".to_owned());
2107            }
2108        };
2109
2110        if let Some(formula_str) = self.formula_without_prefix(value) {
2111            let cell_reference = CellReferenceRC {
2112                sheet: source_sheet_name.to_string(),
2113                row: source.row,
2114                column: source.column,
2115            };
2116            let formula = &self.parser.parse(formula_str, &cell_reference);
2117            let cell_reference = CellReferenceRC {
2118                sheet: target_sheet_name,
2119                row: target.row,
2120                column: target.column,
2121            };
2122            return Ok(format!(
2123                "={}",
2124                to_localized_string(formula, &cell_reference, self.locale, self.language)
2125            ));
2126        }
2127        Ok(value.to_string())
2128    }
2129
2130    /// Returns the formula in (`sheet`, `row`, `column`) if any
2131    ///
2132    /// # Examples
2133    ///
2134    /// ```rust
2135    /// # use ironcalc_base::Model;
2136    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2137    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2138    /// let (sheet, row, column) = (0, 1, 1);
2139    /// model.set_user_input(sheet, row, column, "=SIN(B1*C3)+1".to_string());
2140    /// model.evaluate();
2141    /// let result = model.get_cell_formula(sheet, row, column)?;
2142    /// assert_eq!(result, Some("=SIN(B1*C3)+1".to_string()));
2143    /// # Ok(())
2144    /// # }
2145    /// ```
2146    ///
2147    /// See also:
2148    /// * [Model::get_localized_cell_content()]
2149    pub fn get_cell_formula(
2150        &self,
2151        sheet: u32,
2152        row: i32,
2153        column: i32,
2154    ) -> Result<Option<String>, String> {
2155        let worksheet = self.workbook.worksheet(sheet)?;
2156        match worksheet.cell(row, column) {
2157            Some(cell) => match cell.get_formula() {
2158                Some(formula_index) => {
2159                    let (formula, _static_result) = &self
2160                        .parsed_formulas
2161                        .get(sheet as usize)
2162                        .ok_or("missing sheet")?
2163                        .get(formula_index as usize)
2164                        .ok_or("missing formula")?;
2165                    let cell_ref = CellReferenceRC {
2166                        sheet: worksheet.get_name(),
2167                        row,
2168                        column,
2169                    };
2170                    Ok(Some(format!(
2171                        "={}",
2172                        to_localized_string(formula, &cell_ref, self.locale, self.language)
2173                    )))
2174                }
2175                None => Ok(None),
2176            },
2177            None => Ok(None),
2178        }
2179    }
2180
2181    /// Returns the text for the formula in (`sheet`, `row`, `column`) in English if any
2182    ///
2183    /// See also:
2184    /// * [Model::get_localized_cell_content()]
2185    pub(crate) fn get_english_cell_formula(
2186        &self,
2187        sheet: u32,
2188        row: i32,
2189        column: i32,
2190    ) -> Result<Option<String>, String> {
2191        let worksheet = self.workbook.worksheet(sheet)?;
2192        match worksheet.cell(row, column) {
2193            Some(cell) => match cell.get_formula() {
2194                Some(formula_index) => {
2195                    let (formula, _static_result) = &self
2196                        .parsed_formulas
2197                        .get(sheet as usize)
2198                        .ok_or("missing sheet")?
2199                        .get(formula_index as usize)
2200                        .ok_or("missing formula")?;
2201                    let cell_ref = CellReferenceRC {
2202                        sheet: worksheet.get_name(),
2203                        row,
2204                        column,
2205                    };
2206                    let language_en = get_default_language();
2207                    Ok(Some(format!(
2208                        "={}",
2209                        to_localized_string(formula, &cell_ref, self.locale, language_en)
2210                    )))
2211                }
2212                None => Ok(None),
2213            },
2214            None => Ok(None),
2215        }
2216    }
2217
2218    /// Updates the value of a cell with some text
2219    /// It does not change the style unless needs to add "quoting"
2220    ///
2221    /// # Examples
2222    ///
2223    /// ```rust
2224    /// # use ironcalc_base::Model;
2225    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2226    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2227    /// let (sheet, row, column) = (0, 1, 1);
2228    /// model.set_user_input(sheet, row, column, "Hello!".to_string())?;
2229    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "Hello!".to_string());
2230    ///
2231    /// model.update_cell_with_text(sheet, row, column, "Goodbye!")?;
2232    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "Goodbye!".to_string());
2233    /// # Ok(())
2234    /// # }
2235    /// ```
2236    ///
2237    /// See also:
2238    /// * [Model::set_user_input()]
2239    /// * [Model::update_cell_with_number()]
2240    /// * [Model::update_cell_with_bool()]
2241    /// * [Model::update_cell_with_formula()]
2242    pub fn update_cell_with_text(
2243        &mut self,
2244        sheet: u32,
2245        row: i32,
2246        column: i32,
2247        value: &str,
2248    ) -> Result<(), String> {
2249        let style_index = self.get_cell_style_index(sheet, row, column)?;
2250
2251        let new_style_index = if common::value_needs_quoting(value, self.language) {
2252            self.workbook
2253                .styles
2254                .get_style_with_quote_prefix(style_index)?
2255        } else if self.workbook.styles.style_is_quote_prefix(style_index) {
2256            self.workbook
2257                .styles
2258                .get_style_without_quote_prefix(style_index)?
2259        } else {
2260            style_index
2261        };
2262
2263        self.set_cell_with_string(sheet, row, column, value, new_style_index)
2264    }
2265
2266    /// Updates the value of a cell with a boolean value
2267    /// It does not change the style
2268    ///
2269    /// # Examples
2270    ///
2271    /// ```rust
2272    /// # use ironcalc_base::Model;
2273    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2274    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2275    /// let (sheet, row, column) = (0, 1, 1);
2276    /// model.set_user_input(sheet, row, column, "TRUE".to_string())?;
2277    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "TRUE".to_string());
2278    ///
2279    /// model.update_cell_with_bool(sheet, row, column, false)?;
2280    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "FALSE".to_string());
2281    /// # Ok(())
2282    /// # }
2283    /// ```
2284    ///
2285    /// See also:
2286    /// * [Model::set_user_input()]
2287    /// * [Model::update_cell_with_number()]
2288    /// * [Model::update_cell_with_text()]
2289    /// * [Model::update_cell_with_formula()]
2290    pub fn update_cell_with_bool(
2291        &mut self,
2292        sheet: u32,
2293        row: i32,
2294        column: i32,
2295        value: bool,
2296    ) -> Result<(), String> {
2297        let style_index = self.get_cell_style_index(sheet, row, column)?;
2298        let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
2299            self.workbook
2300                .styles
2301                .get_style_without_quote_prefix(style_index)?
2302        } else {
2303            style_index
2304        };
2305        self.set_cell_with_boolean(sheet, row, column, value, new_style_index)
2306    }
2307
2308    /// Updates the value of a cell with a number
2309    /// It does not change the style
2310    ///
2311    /// # Examples
2312    ///
2313    /// ```rust
2314    /// # use ironcalc_base::Model;
2315    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2316    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2317    /// let (sheet, row, column) = (0, 1, 1);
2318    /// model.set_user_input(sheet, row, column, "42".to_string())?;
2319    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "42".to_string());
2320    ///
2321    /// model.update_cell_with_number(sheet, row, column, 23.0)?;
2322    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "23".to_string());
2323    /// # Ok(())
2324    /// # }
2325    /// ```
2326    ///
2327    /// See also:
2328    /// * [Model::set_user_input()]
2329    /// * [Model::update_cell_with_text()]
2330    /// * [Model::update_cell_with_bool()]
2331    /// * [Model::update_cell_with_formula()]
2332    pub fn update_cell_with_number(
2333        &mut self,
2334        sheet: u32,
2335        row: i32,
2336        column: i32,
2337        value: f64,
2338    ) -> Result<(), String> {
2339        let style_index = self.get_cell_style_index(sheet, row, column)?;
2340        let new_style_index = if self.workbook.styles.style_is_quote_prefix(style_index) {
2341            self.workbook
2342                .styles
2343                .get_style_without_quote_prefix(style_index)?
2344        } else {
2345            style_index
2346        };
2347        self.set_cell_with_number(sheet, row, column, value, new_style_index)
2348    }
2349
2350    /// Updates the formula of given cell
2351    /// It does not change the style unless needs to add "quoting"
2352    /// Expects the formula to start with "="
2353    ///
2354    /// # Examples
2355    ///
2356    /// ```rust
2357    /// # use ironcalc_base::Model;
2358    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2359    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2360    /// let (sheet, row, column) = (0, 1, 1);
2361    /// model.set_user_input(sheet, row, column, "=A2*2".to_string())?;
2362    /// model.evaluate();
2363    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "=A2*2".to_string());
2364    ///
2365    /// model.update_cell_with_formula(sheet, row, column, "=A3*2".to_string())?;
2366    /// model.evaluate();
2367    /// assert_eq!(model.get_localized_cell_content(sheet, row, column)?, "=A3*2".to_string());
2368    /// # Ok(())
2369    /// # }
2370    /// ```
2371    ///
2372    /// See also:
2373    /// * [Model::set_user_input()]
2374    /// * [Model::update_cell_with_number()]
2375    /// * [Model::update_cell_with_bool()]
2376    /// * [Model::update_cell_with_text()]
2377    pub fn update_cell_with_formula(
2378        &mut self,
2379        sheet: u32,
2380        row: i32,
2381        column: i32,
2382        formula: String,
2383    ) -> Result<(), String> {
2384        let mut style_index = self.get_cell_style_index(sheet, row, column)?;
2385        if self.workbook.styles.style_is_quote_prefix(style_index) {
2386            style_index = self
2387                .workbook
2388                .styles
2389                .get_style_without_quote_prefix(style_index)?;
2390        }
2391
2392        if let Some(new_formula) = self.formula_without_prefix(&formula) {
2393            self.set_cell_with_formula(sheet, row, column, new_formula, style_index)?;
2394            Ok(())
2395        } else {
2396            Err(format!("\"{formula}\" is not a valid formula"))
2397        }
2398    }
2399
2400    // If we are writing in (sheet, row, column). If it is:
2401    // - A single cell => do nothing
2402    // - Part of an array formula => we bail
2403    // - Anchor of an array formula => we delete the formula and we clear the spill
2404    // - Part of a dynamic array formula => we delete the formula and we clear the spill
2405    // - Anchor of a dynamic array formula
2406    //     => we clear the spill and we set an unevaluated dynamic formula.
2407    pub(crate) fn prepare_cell_for_user_input(
2408        &mut self,
2409        sheet: u32,
2410        row: i32,
2411        column: i32,
2412    ) -> Result<(), String> {
2413        if self.workbook.worksheet(sheet)?.is_covered_cell(row, column) {
2414            return Err("Cannot edit a cell that is part of a merged cell".to_string());
2415        }
2416        match self.get_cell_structure(sheet, row, column)? {
2417            CellStructure::SingleCell => {
2418                // noop
2419            }
2420            CellStructure::ArrayFormula { range } => {
2421                // We cannot write in a cell that is part of an array formula
2422                let (width, height) = range;
2423                if width > 1 || height > 1 {
2424                    return Err(
2425                        "Cannot write in a cell that is part of an array formula".to_string()
2426                    );
2427                }
2428            }
2429            CellStructure::DynamicFormula { range } => {
2430                // clear the spill of the dynamic formula
2431                let (width, height) = range;
2432                let ws = self.workbook.worksheet_mut(sheet)?;
2433                for r in row..row + height {
2434                    for c in column..column + width {
2435                        // We ignore errors here
2436                        let _ = ws.cell_clear_contents(r, c);
2437                    }
2438                }
2439            }
2440            CellStructure::SpillArray { .. } => {
2441                return Err("Cannot write in a cell that is part of an array formula".to_string());
2442            }
2443            CellStructure::SpillDynamic { anchor, range } => {
2444                // It is part of a dynamic array formula, but it is not the anchor.
2445                // We can write in it but we need to clear the spill and reset the anchor
2446                // to an unevaluated dynamic formula so it will re-spill on next evaluate().
2447                let (anchor_row, anchor_column) = anchor;
2448                let (width, height) = range;
2449                let ws = self.workbook.worksheet_mut(sheet)?;
2450                // Extract formula index and style from the anchor before mutating
2451                let (formula_index, anchor_style) = {
2452                    let anchor_cell = ws
2453                        .cell(anchor_row, anchor_column)
2454                        .ok_or_else(|| "Dynamic formula anchor not found".to_string())?;
2455                    let fi = anchor_cell
2456                        .get_formula()
2457                        .ok_or_else(|| "Dynamic formula anchor has no formula".to_string())?;
2458                    let s = anchor_cell.get_style();
2459                    (fi, s)
2460                };
2461                ws.set_cell_with_dynamic_formula(
2462                    anchor_row,
2463                    anchor_column,
2464                    formula_index,
2465                    anchor_style,
2466                    1,
2467                    1,
2468                )?;
2469                for r in anchor_row..anchor_row + height {
2470                    for c in anchor_column..anchor_column + width {
2471                        if r == anchor_row && c == anchor_column {
2472                            continue;
2473                        }
2474                        // We ignore errors here
2475                        let _ = ws.cell_clear_contents(r, c);
2476                    }
2477                }
2478            }
2479        };
2480        Ok(())
2481    }
2482
2483    /// Sets a cell parametrized by (`sheet`, `row`, `column`) with `value`.
2484    ///
2485    /// This mimics a user entering a value on a cell.
2486    ///
2487    /// If you enter a currency `$100` it will set as a number and update the style
2488    ///  Note that for currencies/percentage there is only one possible style
2489    ///  The value is always a string, so we need to try to cast it into numbers/booleans/errors
2490    ///
2491    /// # Examples
2492    ///
2493    /// ```rust
2494    /// # use ironcalc_base::Model;
2495    /// # use ironcalc_base::cell::CellValue;
2496    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2497    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
2498    /// model.set_user_input(0, 1, 1, "100$".to_string());
2499    /// model.set_user_input(0, 2, 1, "125$".to_string());
2500    /// model.set_user_input(0, 3, 1, "-10$".to_string());
2501    /// model.set_user_input(0, 1, 2, "=SUM(A:A)".to_string());
2502    /// model.evaluate();
2503    /// assert_eq!(model.get_cell_value_by_index(0, 1, 2), Ok(CellValue::Number(215.0)));
2504    /// assert_eq!(model.get_formatted_cell_value(0, 1, 2), Ok("215$".to_string()));
2505    /// # Ok(())
2506    /// # }
2507    /// ```
2508    ///
2509    /// See also:
2510    /// * [Model::update_cell_with_formula()]
2511    /// * [Model::update_cell_with_number()]
2512    /// * [Model::update_cell_with_bool()]
2513    /// * [Model::update_cell_with_text()]
2514    pub fn set_user_input(
2515        &mut self,
2516        sheet: u32,
2517        row: i32,
2518        column: i32,
2519        value: String,
2520    ) -> Result<(), String> {
2521        // first we make sure we can write in the cell and clear the spills.
2522        self.prepare_cell_for_user_input(sheet, row, column)?;
2523        if value.is_empty() {
2524            // If the value is empty we just clear the cell.
2525            // Deleting the contents of a cell also removes its link.
2526            let ws = self.workbook.worksheet_mut(sheet)?;
2527            ws.cell_clear_contents(row, column)?;
2528            ws.links.remove(&(row, column));
2529            return Ok(());
2530        }
2531
2532        // If value starts with "'" then we force the style to be quote_prefix
2533        let style_index = self.get_cell_style_index(sheet, row, column)?;
2534        if let Some(new_value) = value.strip_prefix('\'') {
2535            let new_style = self
2536                .workbook
2537                .styles
2538                .get_style_with_quote_prefix(style_index)?;
2539            self.set_cell_with_string(sheet, row, column, new_value, new_style)?;
2540        } else {
2541            let mut new_style_index = style_index;
2542            if self.workbook.styles.style_is_quote_prefix(style_index) {
2543                new_style_index = self
2544                    .workbook
2545                    .styles
2546                    .get_style_without_quote_prefix(style_index)?;
2547            }
2548            if let Some(formula) = self.formula_without_prefix(&value) {
2549                let formula_index =
2550                    self.set_cell_with_formula(sheet, row, column, formula, new_style_index)?;
2551                // Update the style if needed
2552                let cell = CellReferenceIndex { sheet, row, column };
2553                let (parsed_formula, _static_result) =
2554                    &self.parsed_formulas[sheet as usize][formula_index as usize];
2555                if let Some(units) = self.compute_node_units(parsed_formula, &cell) {
2556                    let new_style_index = self
2557                        .workbook
2558                        .styles
2559                        .get_style_with_format(new_style_index, &units.get_num_fmt())?;
2560                    let style = self.workbook.styles.get_style(new_style_index)?;
2561                    self.set_cell_style(sheet, row, column, &style)?;
2562                }
2563            } else {
2564                // The list of currencies is '$', '€' and the local currency
2565                let mut currencies = vec!["$", "€"];
2566                let currency = &self.locale.currency.symbol;
2567                if !currencies.iter().any(|e| e == currency) {
2568                    currencies.push(currency);
2569                }
2570
2571                //  We try to parse as number
2572                if let Ok((v, number_format)) =
2573                    parse_formatted_number(&value, &currencies, self.locale)
2574                {
2575                    if let Some(num_fmt) = number_format {
2576                        // Should not apply the format in the following cases:
2577                        // - we assign a date to already date-formatted cell
2578                        let should_apply_format = !(is_likely_date_number_format(
2579                            &self.workbook.styles.get_style(new_style_index)?.num_fmt,
2580                        ) && is_likely_date_number_format(&num_fmt));
2581                        if should_apply_format {
2582                            new_style_index = self
2583                                .workbook
2584                                .styles
2585                                .get_style_with_format(new_style_index, &num_fmt)?;
2586                        }
2587                    }
2588                    let worksheet = self.workbook.worksheet_mut(sheet)?;
2589                    worksheet.set_cell_with_number(row, column, v, new_style_index)?;
2590                    return Ok(());
2591                }
2592                // We try to parse as boolean
2593                if let Ok(v) = value.to_lowercase().parse::<bool>() {
2594                    let worksheet = self.workbook.worksheet_mut(sheet)?;
2595                    worksheet.set_cell_with_boolean(row, column, v, new_style_index)?;
2596                    return Ok(());
2597                }
2598                // Check is it is error value
2599                let upper = value.to_uppercase();
2600                let worksheet = self.workbook.worksheet_mut(sheet)?;
2601                match get_error_by_name(&upper, self.language) {
2602                    Some(error) => {
2603                        worksheet.set_cell_with_error(row, column, error, new_style_index)?;
2604                    }
2605                    None => {
2606                        self.set_cell_with_string(sheet, row, column, &value, new_style_index)?;
2607                        // If the input looks like an URL or an email address a link is
2608                        // attached to the cell, the same way other inputs change the
2609                        // number format. Note that a quote prefix prevents this.
2610                        self.auto_link_cell(sheet, row, column, &value)?;
2611                    }
2612                }
2613            }
2614        }
2615        Ok(())
2616    }
2617
2618    /// Sets an array formula in an area (CSE formula)
2619    pub fn set_user_array_formula(
2620        &mut self,
2621        sheet: u32,
2622        row: i32,
2623        column: i32,
2624        width: i32,
2625        height: i32,
2626        value: &str,
2627    ) -> Result<(), String> {
2628        if self
2629            .workbook
2630            .worksheet(sheet)?
2631            .merged_cells
2632            .iter()
2633            .any(|m| m.intersects(row, column, width, height))
2634        {
2635            return Err("Cannot set an array formula over merged cells".to_string());
2636        }
2637        // Every cell of the area is about to be overwritten, so each one gets the
2638        // same treatment as a cell the user types into: refuse if it belongs to
2639        // another array formula, clear the dynamic spill it is part of. The check
2640        // runs over the whole area before anything is modified.
2641        for r in row..row + height {
2642            for c in column..column + width {
2643                if (r, c) == (row, column) {
2644                    continue;
2645                }
2646                match self.get_cell_structure(sheet, r, c)? {
2647                    CellStructure::ArrayFormula { range: (w, h) } if w > 1 || h > 1 => {
2648                        return Err(
2649                            "Cannot write in a cell that is part of an array formula".to_string()
2650                        );
2651                    }
2652                    CellStructure::SpillArray { .. } => {
2653                        return Err(
2654                            "Cannot write in a cell that is part of an array formula".to_string()
2655                        );
2656                    }
2657                    _ => {}
2658                }
2659            }
2660        }
2661        for r in row..row + height {
2662            for c in column..column + width {
2663                self.prepare_cell_for_user_input(sheet, r, c)?;
2664            }
2665        }
2666        // If value starts with "'" then we force the style to be quote_prefix
2667        let style_index = self.get_cell_style_index(sheet, row, column)?;
2668        if value.strip_prefix('\'').is_none() {
2669            let mut new_style_index = style_index;
2670            if self.workbook.styles.style_is_quote_prefix(style_index) {
2671                new_style_index = self
2672                    .workbook
2673                    .styles
2674                    .get_style_without_quote_prefix(style_index)?;
2675            }
2676            if let Some(formula) = value.strip_prefix('=') {
2677                // It is a formula, we mark it as an array formulas and fill the "spills" with placeholders
2678                let formula_index = self.set_cell_with_array_formula(
2679                    sheet,
2680                    row,
2681                    column,
2682                    formula,
2683                    new_style_index,
2684                    width,
2685                    height,
2686                )?;
2687
2688                // Update the style if needed
2689                let cell = CellReferenceIndex { sheet, row, column };
2690                let (parsed_formula, _static_result) =
2691                    &self.parsed_formulas[sheet as usize][formula_index as usize];
2692
2693                if let Some(units) = self.compute_node_units(parsed_formula, &cell) {
2694                    let new_style_index = self
2695                        .workbook
2696                        .styles
2697                        .get_style_with_format(new_style_index, &units.get_num_fmt())?;
2698                    let style = self.workbook.styles.get_style(new_style_index)?;
2699                    self.set_cell_style(sheet, row, column, &style)?;
2700                }
2701                // Fill the area with spill cells pointing at the anchor. Their
2702                // placeholder value is never observed: reading a spill cell
2703                // evaluates its anchor first, which rewrites them. (Plain
2704                // placeholder cells would be constants, so a reader that ran
2705                // before the anchor would keep a stale value.)
2706                for r in row..row + height {
2707                    for c in column..column + width {
2708                        if r == row && c == column {
2709                            continue;
2710                        }
2711                        let mut new_style_index_spill = self.get_cell_style_index(sheet, r, c)?;
2712                        if self
2713                            .workbook
2714                            .styles
2715                            .style_is_quote_prefix(new_style_index_spill)
2716                        {
2717                            new_style_index_spill = self
2718                                .workbook
2719                                .styles
2720                                .get_style_without_quote_prefix(new_style_index_spill)?;
2721                        }
2722                        self.workbook.worksheet_mut(sheet)?.update_cell(
2723                            r,
2724                            c,
2725                            Cell::SpillCell {
2726                                s: new_style_index_spill,
2727                                a: (row, column),
2728                                v: SpillValue::Text(String::new()),
2729                            },
2730                        )?;
2731                    }
2732                }
2733                return Ok(());
2734            }
2735        }
2736        // just use set user input on every cell
2737        for r in row..row + height {
2738            for c in column..column + width {
2739                self.set_user_input(sheet, r, c, value.to_string())?;
2740            }
2741        }
2742
2743        Ok(())
2744    }
2745
2746    pub(crate) fn get_cell_structure(
2747        &self,
2748        sheet: u32,
2749        row: i32,
2750        column: i32,
2751    ) -> Result<CellStructure, String> {
2752        let worksheet = self.workbook.worksheet(sheet)?;
2753        worksheet.get_cell_structure(row, column)
2754    }
2755
2756    /// The index of a formula among the shared formulas of the sheet, adding
2757    /// it to them if it is new.
2758    ///
2759    /// It tries to use the 'cache' lookup first to avoid quadratic time complexity when adding new formulas.
2760    fn shared_formula_index(
2761        &mut self,
2762        sheet: u32,
2763        parsed_formula: Node,
2764        static_result: StaticResult,
2765    ) -> Result<i32, String> {
2766        let text = to_rc_format(&parsed_formula);
2767        let sheet_index = sheet as usize;
2768        let shared_formulas = &mut self.workbook.worksheet_mut(sheet)?.shared_formulas;
2769        if self.shared_formula_lookup.len() <= sheet_index {
2770            self.shared_formula_lookup
2771                .resize_with(sheet_index + 1, SharedFormulaLookup::default);
2772        }
2773        let lookup = &mut self.shared_formula_lookup[sheet_index];
2774        // The lookup is believed only if it was built from the whole list and,
2775        // for the formula it knows, the list agrees with it.
2776        let agrees = |lookup: &SharedFormulaLookup, shared_formulas: &Vec<String>| {
2777            lookup.built_from == shared_formulas.len()
2778                && match lookup.index.get(&text) {
2779                    Some(index) => shared_formulas.get(*index as usize) == Some(&text),
2780                    None => true,
2781                }
2782        };
2783        if !agrees(lookup, shared_formulas) {
2784            *lookup = build_shared_formula_lookup(shared_formulas);
2785        }
2786        if let Some(index) = lookup.index.get(&text) {
2787            return Ok(*index);
2788        }
2789        let index = shared_formulas.len() as i32;
2790        lookup.index.insert(text.clone(), index);
2791        shared_formulas.push(text);
2792        lookup.built_from = shared_formulas.len();
2793        self.parsed_formulas[sheet_index].push((Arc::new(parsed_formula), static_result));
2794        Ok(index)
2795    }
2796
2797    fn set_cell_with_formula(
2798        &mut self,
2799        sheet: u32,
2800        row: i32,
2801        column: i32,
2802        formula: &str,
2803        style: i32,
2804    ) -> Result<i32, String> {
2805        let worksheet = self.workbook.worksheet_mut(sheet)?;
2806        let cell_reference = CellReferenceRC {
2807            sheet: worksheet.get_name(),
2808            row,
2809            column,
2810        };
2811        let mut parsed_formula = self.parser.parse(formula, &cell_reference);
2812        // If the formula fails to parse try adding a parenthesis
2813        // SUM(A1:A3  => SUM(A1:A3)
2814        if let Node::ParseErrorKind { .. } = parsed_formula {
2815            let new_parsed_formula = self.parser.parse(&format!("{formula})"), &cell_reference);
2816            match new_parsed_formula {
2817                Node::ParseErrorKind { .. } => {}
2818                _ => parsed_formula = new_parsed_formula,
2819            }
2820        }
2821        let static_result = run_static_analysis_on_node(&parsed_formula);
2822        let is_dynamic = !matches!(static_result, StaticResult::Scalar);
2823
2824        let formula_index = self.shared_formula_index(sheet, parsed_formula, static_result)?;
2825        let worksheet = self.workbook.worksheet_mut(sheet)?;
2826        if is_dynamic {
2827            worksheet.set_cell_with_dynamic_formula(row, column, formula_index, style, 1, 1)?;
2828        } else {
2829            worksheet.set_cell_with_formula(row, column, formula_index, style)?;
2830        }
2831        Ok(formula_index)
2832    }
2833
2834    // FIXME
2835    #[allow(clippy::too_many_arguments)]
2836    pub(crate) fn set_cell_with_array_formula(
2837        &mut self,
2838        sheet: u32,
2839        row: i32,
2840        column: i32,
2841        formula: &str,
2842        style: i32,
2843        width: i32,
2844        height: i32,
2845    ) -> Result<i32, String> {
2846        let worksheet = self.workbook.worksheet_mut(sheet)?;
2847        let cell_reference = CellReferenceRC {
2848            sheet: worksheet.get_name(),
2849            row,
2850            column,
2851        };
2852        let mut parsed_formula = self.parser.parse(formula, &cell_reference);
2853        // If the formula fails to parse try adding a parenthesis
2854        // SUM(A1:A3  => SUM(A1:A3)
2855        if let Node::ParseErrorKind { .. } = parsed_formula {
2856            let new_parsed_formula = self.parser.parse(&format!("{formula})"), &cell_reference);
2857            match new_parsed_formula {
2858                Node::ParseErrorKind { .. } => {}
2859                _ => parsed_formula = new_parsed_formula,
2860            }
2861        }
2862        let static_result = run_static_analysis_on_node(&parsed_formula);
2863
2864        let formula_index = self.shared_formula_index(sheet, parsed_formula, static_result)?;
2865        let worksheet = self.workbook.worksheet_mut(sheet)?;
2866        worksheet.set_cell_with_array_formula(row, column, formula_index, style, width, height)?;
2867        Ok(formula_index)
2868    }
2869
2870    pub(crate) fn set_cell_with_string(
2871        &mut self,
2872        sheet: u32,
2873        row: i32,
2874        column: i32,
2875        value: &str,
2876        style: i32,
2877    ) -> Result<(), String> {
2878        match self.shared_strings.get(value) {
2879            Some(string_index) => {
2880                self.workbook.worksheet_mut(sheet)?.set_cell_with_string(
2881                    row,
2882                    column,
2883                    *string_index as i32,
2884                    style,
2885                )?;
2886            }
2887            None => {
2888                let string_index = self.workbook.shared_strings.len();
2889                self.workbook.shared_strings.push(value.to_string());
2890                self.shared_strings.insert(value.to_string(), string_index);
2891                self.workbook.worksheet_mut(sheet)?.set_cell_with_string(
2892                    row,
2893                    column,
2894                    string_index as i32,
2895                    style,
2896                )?;
2897            }
2898        }
2899        Ok(())
2900    }
2901
2902    fn set_cell_with_boolean(
2903        &mut self,
2904        sheet: u32,
2905        row: i32,
2906        column: i32,
2907        value: bool,
2908        style: i32,
2909    ) -> Result<(), String> {
2910        self.workbook
2911            .worksheet_mut(sheet)?
2912            .set_cell_with_boolean(row, column, value, style)
2913    }
2914
2915    fn set_cell_with_number(
2916        &mut self,
2917        sheet: u32,
2918        row: i32,
2919        column: i32,
2920        value: f64,
2921        style: i32,
2922    ) -> Result<(), String> {
2923        self.workbook
2924            .worksheet_mut(sheet)?
2925            .set_cell_with_number(row, column, value, style)
2926    }
2927
2928    // Helper function that returns a defined name given the name and scope
2929    fn get_parsed_defined_name(
2930        &self,
2931        name: &str,
2932        scope: Option<u32>,
2933    ) -> Result<Option<ParsedDefinedName>, String> {
2934        let name_upper = name.to_uppercase();
2935
2936        for (key, df) in &self.parsed_defined_names {
2937            if key.1.to_uppercase() == name_upper && key.0 == scope {
2938                return Ok(Some(df.clone()));
2939            }
2940        }
2941        Ok(None)
2942    }
2943
2944    // Returns the formula for a defined name
2945    pub(crate) fn get_defined_name_formula(
2946        &self,
2947        name: &str,
2948        scope: Option<u32>,
2949    ) -> Result<String, String> {
2950        let name_upper = name.to_uppercase();
2951        let defined_names = &self.workbook.defined_names;
2952        let sheet_id = match scope {
2953            Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
2954            None => None,
2955        };
2956        for df in defined_names {
2957            if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
2958                return Ok(df.formula.clone());
2959            }
2960        }
2961        Err("Defined name not found".to_string())
2962    }
2963
2964    /// Returns the list of defined names as `(name, scope, formula)`.
2965    ///
2966    /// Formulas are stored internally in English; they are translated into the
2967    /// active language/locale for display.
2968    pub fn get_defined_name_list(&self) -> Vec<(String, Option<u32>, String)> {
2969        let context = self.defined_name_context();
2970        self.workbook
2971            .get_defined_names_with_scope()
2972            .into_iter()
2973            .map(|(name, scope, formula)| {
2974                let formula = self.internal_formula_to_display(&formula, &context);
2975                (name, scope, formula)
2976            })
2977            .collect()
2978    }
2979
2980    /// Gets the Excel Value (Bool, Number, String) of a cell
2981    ///
2982    /// See also:
2983    /// * [Model::get_cell_value_by_index()]
2984    pub fn get_cell_value_by_ref(&self, cell_ref: &str) -> Result<CellValue, String> {
2985        let cell_reference = match self.parse_reference(cell_ref) {
2986            Some(c) => c,
2987            None => return Err(format!("Error parsing reference: '{cell_ref}'")),
2988        };
2989        let sheet_index = cell_reference.sheet;
2990        let column = cell_reference.column;
2991        let row = cell_reference.row;
2992
2993        self.get_cell_value_by_index(sheet_index, row, column)
2994    }
2995
2996    /// Returns the cell value for (`sheet`, `row`, `column`)
2997    ///
2998    /// See also:
2999    /// * [Model::get_formatted_cell_value()]
3000    pub fn get_cell_value_by_index(
3001        &self,
3002        sheet_index: u32,
3003        row: i32,
3004        column: i32,
3005    ) -> Result<CellValue, String> {
3006        let cell = self
3007            .workbook
3008            .worksheet(sheet_index)?
3009            .cell(row, column)
3010            .map(Cow::into_owned)
3011            .unwrap_or_default();
3012        let cell_value = cell.value(&self.workbook.shared_strings, self.language);
3013        Ok(cell_value)
3014    }
3015
3016    /// Returns the formatted cell value for (`sheet`, `row`, `column`)
3017    ///
3018    /// See also:
3019    /// * [Model::get_cell_value_by_index()]
3020    /// * [Model::get_cell_value_by_ref]
3021    ///
3022    /// # Examples
3023    ///
3024    /// ```rust
3025    /// # use ironcalc_base::Model;
3026    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3027    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
3028    /// let (sheet, row, column) = (0, 1, 1);
3029    /// model.set_user_input(sheet, row, column, "=1/3".to_string());
3030    /// model.evaluate();
3031    /// let result = model.get_formatted_cell_value(sheet, row, column)?;
3032    /// assert_eq!(result, "0.333333333".to_string());
3033    /// # Ok(())
3034    /// # }
3035    /// ```
3036    pub fn get_formatted_cell_value(
3037        &self,
3038        sheet_index: u32,
3039        row: i32,
3040        column: i32,
3041    ) -> Result<String, String> {
3042        match self.workbook.worksheet(sheet_index)?.cell(row, column) {
3043            Some(cell) => {
3044                let format = self.get_style_for_cell(sheet_index, row, column)?.num_fmt;
3045                let formatted_value =
3046                    cell.formatted_value(&self.workbook.shared_strings, self.language, |value| {
3047                        format_number(value, &format, self.locale).text
3048                    });
3049                Ok(formatted_value)
3050            }
3051            None => Ok("".to_string()),
3052        }
3053    }
3054
3055    /// Return the typeof a cell
3056    pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<CellType, String> {
3057        Ok(match self.workbook.worksheet(sheet)?.cell(row, column) {
3058            Some(c) => c.get_type(),
3059            None => CellType::Number,
3060        })
3061    }
3062
3063    /// Returns a string with the cell content in the given language and locale.
3064    /// If there is a formula returns the formula
3065    /// If the cell is empty returns the empty string
3066    /// Returns an error if there is no worksheet
3067    /// If the cell has quote prefix style it adds a ' at the beginning of the value
3068    /// If the cell is date formatted it tries to format it as date
3069    pub fn get_localized_cell_content(
3070        &self,
3071        sheet: u32,
3072        row: i32,
3073        column: i32,
3074    ) -> Result<String, String> {
3075        let worksheet = self.workbook.worksheet(sheet)?;
3076        let cell = match worksheet.cell(row, column) {
3077            Some(c) => c,
3078            None => return Ok("".to_string()),
3079        };
3080        match cell.get_formula() {
3081            Some(formula_index) => {
3082                let formula = &self.parsed_formulas[sheet as usize][formula_index as usize].0;
3083                let cell_ref = CellReferenceRC {
3084                    sheet: worksheet.get_name(),
3085                    row,
3086                    column,
3087                };
3088                Ok(format!(
3089                    "={}",
3090                    to_localized_string(formula, &cell_ref, self.locale, self.language)
3091                ))
3092            }
3093            None => {
3094                let style_index = cell.get_style();
3095                let style = self.workbook.styles.get_style(style_index)?;
3096                if style.quote_prefix {
3097                    Ok(format!(
3098                        "'{}",
3099                        cell.get_localized_text(
3100                            &self.workbook.shared_strings,
3101                            self.locale,
3102                            self.language,
3103                        )
3104                    ))
3105                } else {
3106                    // If it is a date formatted cell we try to format it as date, if it fails we return the raw value
3107                    if is_likely_date_number_format(&style.num_fmt) {
3108                        let value = cell.value(&self.workbook.shared_strings, self.language);
3109                        if let CellValue::Number(n) = value {
3110                            let formatted = format_number(n, &style.num_fmt, self.locale);
3111                            if formatted.error.is_none() {
3112                                return Ok(formatted.text);
3113                            }
3114                        }
3115                    }
3116                    Ok(cell.get_localized_text(
3117                        &self.workbook.shared_strings,
3118                        self.locale,
3119                        self.language,
3120                    ))
3121                }
3122            }
3123        }
3124    }
3125
3126    /// Returns a list of all cells, in natural order: by sheet, then by row,
3127    /// then by column.
3128    pub fn get_all_cells(&self) -> Vec<CellIndex> {
3129        let mut cells = Vec::new();
3130        for (index, sheet) in self.workbook.worksheets.iter().enumerate() {
3131            for (row, column, _) in sheet.sheet_data.cells() {
3132                cells.push(CellIndex {
3133                    index: index as u32,
3134                    row,
3135                    column,
3136                });
3137            }
3138        }
3139        cells
3140    }
3141
3142    /// Removes the content of every cell in the range but leaves the style.
3143    ///
3144    /// See also:
3145    /// * [Model::range_clear_all()]
3146    ///
3147    /// # Examples
3148    ///
3149    /// ```rust
3150    /// # use ironcalc_base::Model;
3151    /// # use ironcalc_base::expressions::types::Area;
3152    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3153    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
3154    /// let (sheet, row, column) = (0, 1, 1);
3155    /// model.set_user_input(sheet, row, column, "100$".to_string());
3156    /// let area = Area {
3157    ///     sheet,
3158    ///     row,
3159    ///     column,
3160    ///     width: 1,
3161    ///     height: 1,
3162    /// };
3163    /// model.range_clear_contents(&area)?;
3164    /// model.set_user_input(sheet, row, column, "10".to_string());
3165    /// let result = model.get_formatted_cell_value(sheet, row, column)?;
3166    /// assert_eq!(result, "10$".to_string());
3167    /// # Ok(())
3168    /// # }
3169    /// ```
3170    pub fn range_clear_contents(&mut self, range: &Area) -> Result<(), String> {
3171        if !self.can_clear_range(range)? {
3172            return Err("Cannot clear the range because it contains array formulas".to_string());
3173        }
3174        let sheet = range.sheet;
3175        let ws = self.workbook.worksheet_mut(sheet)?;
3176        for row in range.row..range.row + range.height {
3177            for column in range.column..range.column + range.width {
3178                let structure = ws.get_cell_structure(row, column)?;
3179                match structure {
3180                    CellStructure::DynamicFormula { range }
3181                    | CellStructure::ArrayFormula { range, .. } => {
3182                        let (width, height) = range;
3183                        for r in row..row + height {
3184                            for c in column..column + width {
3185                                let _ = ws.cell_clear_contents(r, c);
3186                            }
3187                        }
3188                    }
3189                    _ => {
3190                        let _ = ws.cell_clear_contents(row, column);
3191                    }
3192                }
3193            }
3194        }
3195        // Deleting the contents of a cell also removes its link
3196        ws.links.retain(|&(row, column), _| {
3197            row < range.row
3198                || row >= range.row + range.height
3199                || column < range.column
3200                || column >= range.column + range.width
3201        });
3202        Ok(())
3203    }
3204
3205    // Returns true if for every array formula in the range, the whole spill is included in the range,
3206    // false otherwise.
3207    pub(crate) fn can_clear_range(&self, range: &Area) -> Result<bool, String> {
3208        let sheet = range.sheet;
3209        for row in range.row..range.row + range.height {
3210            for column in range.column..range.column + range.width {
3211                match self.get_cell_structure(sheet, row, column)? {
3212                    CellStructure::ArrayFormula { range: r } => {
3213                        let (width, height) = r;
3214                        if column + width > range.column + range.width
3215                            || row + height > range.row + range.height
3216                        {
3217                            return Ok(false);
3218                        }
3219                    }
3220                    CellStructure::SpillArray {
3221                        anchor: a,
3222                        range: r,
3223                    } => {
3224                        let (anchor_row, anchor_column) = a;
3225                        let (width, height) = r;
3226                        if anchor_column < range.column
3227                            || anchor_row < range.row
3228                            || anchor_column + width > range.column + range.width
3229                            || anchor_row + height > range.row + range.height
3230                        {
3231                            return Ok(false);
3232                        }
3233                    }
3234                    _ => {
3235                        // noop
3236                    }
3237                }
3238            }
3239        }
3240        Ok(true)
3241    }
3242
3243    /// Deletes a range by removing it from worksheet data. All content and style is removed.
3244    /// It fails if it deletes part of an array formula.
3245    /// Deletes the whole spill if it is part of a dynamic array formula.
3246    ///
3247    /// See also:
3248    /// * [Model::range_clear_contents()]
3249    ///
3250    /// # Examples
3251    ///
3252    /// ```rust
3253    /// # use ironcalc_base::Model;
3254    /// # use ironcalc_base::expressions::types::Area;
3255    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3256    /// let mut model = Model::new_empty("model", "en", "UTC", "en")?;
3257    /// let (sheet, row, column) = (0, 1, 1);
3258    /// model.set_user_input(sheet, row, column, "100$".to_string());
3259    /// let area = Area {
3260    ///     sheet,
3261    ///     row,
3262    ///     column,
3263    ///     width: 1,
3264    ///     height: 1,
3265    /// };
3266    /// model.range_clear_all(&area)?;
3267    /// model.set_user_input(sheet, row, column, "10".to_string());
3268    /// let result = model.get_formatted_cell_value(sheet, row, column)?;
3269    /// assert_eq!(result, "10".to_string());
3270    /// # Ok(())
3271    /// # }
3272    pub fn range_clear_all(&mut self, area: &Area) -> Result<(), String> {
3273        if !self.can_clear_range(area)? {
3274            return Err("Cannot clear the range because it contains array formulas".to_string());
3275        }
3276        let worksheet = self.workbook.worksheet_mut(area.sheet)?;
3277
3278        let sheet_data = &mut worksheet.sheet_data;
3279        let mut cells_to_clear = Vec::new();
3280        for row in area.row..area.row + area.height {
3281            // Only the cells that exist: the area can be a whole row.
3282            for column in sheet_data.columns_in_row(row) {
3283                if column < area.column || column >= area.column + area.width {
3284                    continue;
3285                }
3286                // If it is part of a dynamic array we need to clear the spill
3287                if let Some(Cell::ArrayFormula {
3288                    r,
3289                    kind: ArrayKind::Dynamic,
3290                    ..
3291                }) = sheet_data.cell(row, column).as_deref()
3292                {
3293                    // clear the spill of the dynamic formula
3294                    let (width, height) = *r;
3295                    for r in row..row + height {
3296                        for c in column..column + width {
3297                            cells_to_clear.push((r, c));
3298                        }
3299                    }
3300                }
3301                sheet_data.remove_cell(row, column);
3302            }
3303        }
3304        for (row, column) in cells_to_clear {
3305            // we ignore errors here because the cell might have already been cleared as part of an array formula
3306            let _ = worksheet.cell_clear_contents(row, column);
3307        }
3308        // Deleting the cells also removes their links
3309        worksheet.links.retain(|&(row, column), _| {
3310            row < area.row
3311                || row >= area.row + area.height
3312                || column < area.column
3313                || column >= area.column + area.width
3314        });
3315        Ok(())
3316    }
3317
3318    // Finds all the dynamic array formulas that spills:
3319    // * Delete the spilled cells
3320    // * Update the formula cell to be DynamicFormula with r = (1,1)
3321    pub(crate) fn reset_dynamic_array_spills(&mut self, sheet: u32) -> Result<(), String> {
3322        // Collect anchor info first — can't mutate sheet_data while iterating over it.
3323        let anchors: Vec<(i32, i32, i32, i32, i32, i32)> = {
3324            let ws = self.workbook.worksheet(sheet)?;
3325            let mut result = Vec::new();
3326            for (row, column, cell) in ws.sheet_data.cells() {
3327                if let Cell::ArrayFormula {
3328                    r,
3329                    f,
3330                    s,
3331                    kind: ArrayKind::Dynamic,
3332                    ..
3333                } = &*cell
3334                {
3335                    let (width, height) = *r;
3336                    result.push((row, column, *f, *s, width, height));
3337                }
3338            }
3339            result
3340        };
3341
3342        for (row, column, f, s, width, height) in anchors {
3343            let ws = self.workbook.worksheet_mut(sheet)?;
3344            // Reset the anchor cell to DynamicFormula with r = (1, 1)
3345            if let Some(cell) = ws.cell_mut(row, column) {
3346                *cell = Cell::ArrayFormula {
3347                    f,
3348                    s,
3349                    r: (1, 1),
3350                    kind: ArrayKind::Dynamic,
3351                    v: FormulaValue::Unevaluated,
3352                };
3353            }
3354            // Delete all spill cells
3355            for r in row..row + height {
3356                for c in column..column + width {
3357                    if r == row && c == column {
3358                        continue;
3359                    }
3360                    let _ = ws.cell_clear_contents(r, c);
3361                }
3362            }
3363        }
3364        Ok(())
3365    }
3366
3367    /// Returns the style index for cell (`sheet`, `row`, `column`)
3368    pub fn get_cell_style_index(&self, sheet: u32, row: i32, column: i32) -> Result<i32, String> {
3369        // First check the cell, then row, the column
3370        let cell = self.workbook.worksheet(sheet)?.cell(row, column);
3371
3372        match cell {
3373            Some(cell) => Ok(cell.get_style()),
3374            None => {
3375                let rows = &self.workbook.worksheet(sheet)?.rows;
3376                for r in rows {
3377                    if r.r == row {
3378                        if r.custom_format {
3379                            return Ok(r.s);
3380                        }
3381                        break;
3382                    }
3383                }
3384                let cols = &self.workbook.worksheet(sheet)?.cols;
3385                for c in cols.iter() {
3386                    let min = c.min;
3387                    let max = c.max;
3388                    if column >= min && column <= max {
3389                        return Ok(c.style.unwrap_or(0));
3390                    }
3391                }
3392                Ok(0)
3393            }
3394        }
3395    }
3396
3397    /// Returns the style for cell (`sheet`, `row`, `column`)
3398    /// If the cell does not have a style defined we check the row, otherwise the column and finally a default
3399    pub fn get_style_for_cell(&self, sheet: u32, row: i32, column: i32) -> Result<Style, String> {
3400        let style_index = self.get_cell_style_index(sheet, row, column)?;
3401        let style = self.workbook.styles.get_style(style_index)?;
3402        Ok(style)
3403    }
3404
3405    /// Returns the style defined in a cell if any.
3406    pub fn get_cell_style_or_none(
3407        &self,
3408        sheet: u32,
3409        row: i32,
3410        column: i32,
3411    ) -> Result<Option<Style>, String> {
3412        let style = self
3413            .workbook
3414            .worksheet(sheet)?
3415            .cell(row, column)
3416            .map(|c| self.workbook.styles.get_style(c.get_style()))
3417            .transpose();
3418        style
3419    }
3420
3421    /// Returns an internal binary representation of the workbook
3422    ///
3423    /// See also:
3424    /// * [Model::from_bytes]
3425    pub fn to_bytes(&self) -> Vec<u8> {
3426        bitcode::encode(&self.workbook)
3427    }
3428
3429    /// Returns data about the worksheets
3430    pub fn get_worksheets_properties(&self) -> Vec<SheetProperties> {
3431        self.workbook
3432            .worksheets
3433            .iter()
3434            .map(|worksheet| SheetProperties {
3435                name: worksheet.get_name(),
3436                state: worksheet.state.to_string(),
3437                color: worksheet.color.clone(),
3438                sheet_id: worksheet.sheet_id,
3439            })
3440            .collect()
3441    }
3442
3443    /// Returns markup representation of the given `sheet`.
3444    pub fn get_sheet_markup(&self, sheet: u32) -> Result<String, String> {
3445        let worksheet = self.workbook.worksheet(sheet)?;
3446        let dimension = worksheet.dimension();
3447
3448        let mut rows = Vec::new();
3449
3450        for row in 1..(dimension.max_row + 1) {
3451            let mut row_markup: Vec<String> = Vec::new();
3452
3453            for column in 1..(dimension.max_column + 1) {
3454                let mut cell_markup = match self.get_cell_formula(sheet, row, column)? {
3455                    Some(formula) => formula,
3456                    None => self.get_formatted_cell_value(sheet, row, column)?,
3457                };
3458                let style = self.get_style_for_cell(sheet, row, column)?;
3459                if style.font.b {
3460                    cell_markup = format!("**{cell_markup}**")
3461                }
3462                row_markup.push(cell_markup);
3463            }
3464
3465            rows.push(row_markup.join("|"));
3466        }
3467
3468        Ok(rows.join("\n"))
3469    }
3470
3471    /// Returns the number of frozen rows in `sheet`
3472    pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32, String> {
3473        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3474            Ok(worksheet.frozen_rows)
3475        } else {
3476            Err("Invalid sheet".to_string())
3477        }
3478    }
3479
3480    /// Return the number of frozen columns in `sheet`
3481    pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32, String> {
3482        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3483            Ok(worksheet.frozen_columns)
3484        } else {
3485            Err("Invalid sheet".to_string())
3486        }
3487    }
3488
3489    /// Sets the number of frozen rows to `frozen_rows` in the workbook.
3490    /// Fails if `frozen`_rows` is either too small (<0) or too large (>LAST_ROW)`
3491    pub fn set_frozen_rows(&mut self, sheet: u32, frozen_rows: i32) -> Result<(), String> {
3492        if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
3493            if frozen_rows < 0 {
3494                return Err("Frozen rows cannot be negative".to_string());
3495            }
3496            if frozen_rows >= LAST_ROW {
3497                return Err("Too many rows".to_string());
3498            }
3499            worksheet.frozen_rows = frozen_rows;
3500            Ok(())
3501        } else {
3502            Err("Invalid sheet".to_string())
3503        }
3504    }
3505
3506    /// Sets the number of frozen columns to `frozen_column` in the workbook.
3507    /// Fails if `frozen`_columns` is either too small (<0) or too large (>LAST_COLUMN)`
3508    pub fn set_frozen_columns(&mut self, sheet: u32, frozen_columns: i32) -> Result<(), String> {
3509        if let Some(worksheet) = self.workbook.worksheets.get_mut(sheet as usize) {
3510            if frozen_columns < 0 {
3511                return Err("Frozen columns cannot be negative".to_string());
3512            }
3513            if frozen_columns >= LAST_COLUMN {
3514                return Err("Too many columns".to_string());
3515            }
3516            worksheet.frozen_columns = frozen_columns;
3517            Ok(())
3518        } else {
3519            Err("Invalid sheet".to_string())
3520        }
3521    }
3522
3523    /// Returns the width of a column
3524    #[inline]
3525    pub fn get_column_width(&self, sheet: u32, column: i32) -> Result<f64, String> {
3526        self.workbook.worksheet(sheet)?.get_column_width(column)
3527    }
3528
3529    /// Sets the width of a column
3530    #[inline]
3531    pub fn set_column_width(&mut self, sheet: u32, column: i32, width: f64) -> Result<(), String> {
3532        self.workbook
3533            .worksheet_mut(sheet)?
3534            .set_column_width(column, width)
3535    }
3536
3537    /// Sets whether a column is hidden
3538    #[inline]
3539    pub fn set_column_hidden(
3540        &mut self,
3541        sheet: u32,
3542        column: i32,
3543        hidden: bool,
3544    ) -> Result<(), String> {
3545        self.workbook
3546            .worksheet_mut(sheet)?
3547            .set_column_hidden(column, hidden)
3548    }
3549
3550    /// Sets whether a row is hidden
3551    #[inline]
3552    pub fn set_row_hidden(&mut self, sheet: u32, row: i32, hidden: bool) -> Result<(), String> {
3553        self.workbook
3554            .worksheet_mut(sheet)?
3555            .set_row_hidden(row, hidden)
3556    }
3557
3558    /// Returns whether a column is hidden
3559    #[inline]
3560    pub fn is_column_hidden(&self, sheet: u32, column: i32) -> Result<bool, String> {
3561        self.workbook.worksheet(sheet)?.is_column_hidden(column)
3562    }
3563
3564    /// Returns whether a row is hidden
3565    #[inline]
3566    pub fn is_row_hidden(&self, sheet: u32, row: i32) -> Result<bool, String> {
3567        self.workbook.worksheet(sheet)?.is_row_hidden(row)
3568    }
3569
3570    /// Returns the height of a row
3571    #[inline]
3572    pub fn get_row_height(&self, sheet: u32, row: i32) -> Result<f64, String> {
3573        self.workbook.worksheet(sheet)?.row_height(row)
3574    }
3575
3576    /// Sets the height of a row
3577    #[inline]
3578    pub fn set_row_height(&mut self, sheet: u32, column: i32, height: f64) -> Result<(), String> {
3579        self.workbook
3580            .worksheet_mut(sheet)?
3581            .set_row_height(column, height)
3582    }
3583
3584    /// Adds a new defined name.
3585    /// If scope is None it is a global defined name, otherwise it is local to the sheet with index scope.
3586    pub fn new_defined_name(
3587        &mut self,
3588        name: &str,
3589        scope: Option<u32>,
3590        formula: &str,
3591    ) -> Result<(), String> {
3592        let sheet_id = self.is_valid_defined_name(name, scope, formula)?;
3593        // Defined-name formulas are stored internally in English so they keep
3594        // working when the user switches language/locale.
3595        let context = self.defined_name_context();
3596        let internal_formula = self.user_formula_to_internal(formula, &context)?;
3597        self.workbook.defined_names.push(DefinedName {
3598            name: name.to_string(),
3599            formula: internal_formula,
3600            sheet_id,
3601        });
3602        self.reset_parsed_structures();
3603
3604        Ok(())
3605    }
3606
3607    /// The context used to parse/stringify defined-name formulas. Defined names
3608    /// have no natural anchor cell, so we use the first worksheet's A1.
3609    pub(crate) fn defined_name_context(&self) -> CellReferenceRC {
3610        CellReferenceRC {
3611            sheet: self
3612                .workbook
3613                .worksheets
3614                .first()
3615                .map(|ws| ws.get_name())
3616                .unwrap_or_else(|| "Sheet1".to_string()),
3617            row: 1,
3618            column: 1,
3619        }
3620    }
3621
3622    /// Validates if a defined name can be created
3623    pub fn is_valid_defined_name(
3624        &mut self,
3625        name: &str,
3626        scope: Option<u32>,
3627        formula: &str,
3628    ) -> Result<Option<u32>, String> {
3629        if !is_valid_identifier(name) {
3630            return Err("Name: Invalid defined name".to_string());
3631        }
3632        let name_upper = name.to_uppercase();
3633        let defined_names = &self.workbook.defined_names;
3634        let sheet_id = match scope {
3635            Some(index) => match self.workbook.worksheet(index) {
3636                Ok(ws) => Some(ws.sheet_id),
3637                Err(_) => return Err("Scope: Invalid sheet index".to_string()),
3638            },
3639            None => None,
3640        };
3641        // if the defined name already exist return error
3642        for df in defined_names {
3643            if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3644                return Err("Name: Defined name already exists".to_string());
3645            }
3646        }
3647
3648        // Make sure the formula is valid — accept cell/range references OR a LAMBDA definition.
3649        let is_reference =
3650            common::ParsedReference::parse_reference_formula(None, formula, self.locale, |name| {
3651                self.get_sheet_index_by_name(name)
3652            })
3653            .is_ok();
3654
3655        if !is_reference {
3656            // Try the full parser to see if it is a LAMBDA definition.
3657            // Defined-name formulas may carry a leading '='; strip it before parsing.
3658            use crate::expressions::types::CellReferenceRC;
3659            let formula_body = formula.strip_prefix('=').unwrap_or(formula);
3660            let dummy_ref = CellReferenceRC {
3661                sheet: self
3662                    .workbook
3663                    .worksheets
3664                    .first()
3665                    .map(|ws| ws.get_name())
3666                    .unwrap_or_else(|| "Sheet1".to_string()),
3667                row: 1,
3668                column: 1,
3669            };
3670            // Accept the formula whether it is written in the active language
3671            // or already in the internal English form (e.g. generated by
3672            // undo/redo or cut & paste).
3673            let mut node = self.parser.parse(formula_body, &dummy_ref);
3674            if let Node::ParseErrorKind { .. } = node {
3675                node = self.parse_internal_formula(formula_body, &dummy_ref);
3676            }
3677            if !matches!(node, Node::LambdaDefKind { .. }) {
3678                return Err("Formula: Invalid defined name formula".to_string());
3679            }
3680        }
3681
3682        Ok(sheet_id)
3683    }
3684
3685    /// Delete defined name of name and scope
3686    pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
3687        let name_upper = name.to_uppercase();
3688        let defined_names = &self.workbook.defined_names;
3689        let sheet_id = match scope {
3690            Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
3691            None => None,
3692        };
3693        let mut index = None;
3694        for (i, df) in defined_names.iter().enumerate() {
3695            if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3696                index = Some(i);
3697            }
3698        }
3699        if let Some(i) = index {
3700            self.workbook.defined_names.remove(i);
3701            self.reset_parsed_structures();
3702            Ok(())
3703        } else {
3704            Err("Defined name not found".to_string())
3705        }
3706    }
3707
3708    /// Update defined name
3709    pub fn update_defined_name(
3710        &mut self,
3711        name: &str,
3712        scope: Option<u32>,
3713        new_name: &str,
3714        new_scope: Option<u32>,
3715        new_formula: &str,
3716    ) -> Result<(), String> {
3717        if !is_valid_identifier(new_name) {
3718            return Err("Name: Invalid defined name".to_string());
3719        };
3720        let name_upper = name.to_uppercase();
3721        let new_name_upper = new_name.to_uppercase();
3722
3723        if name_upper != new_name_upper || scope != new_scope {
3724            for key in self.parsed_defined_names.keys() {
3725                if key.1.to_uppercase() == new_name_upper && key.0 == new_scope {
3726                    return Err("Name: Defined name already exists".to_string());
3727                }
3728            }
3729        }
3730        let defined_names = &self.workbook.defined_names;
3731        let sheet_id = match scope {
3732            Some(index) => Some(
3733                self.workbook
3734                    .worksheet(index)
3735                    .map_err(|_| "Scope: Invalid sheet index")?
3736                    .sheet_id,
3737            ),
3738            None => None,
3739        };
3740
3741        let new_sheet_id = match new_scope {
3742            Some(index) => Some(
3743                self.workbook
3744                    .worksheet(index)
3745                    .map_err(|_| "Scope: Invalid sheet index")?
3746                    .sheet_id,
3747            ),
3748            None => None,
3749        };
3750
3751        let mut index = None;
3752        for (i, df) in defined_names.iter().enumerate() {
3753            if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
3754                index = Some(i);
3755            }
3756        }
3757        // Defined-name formulas are stored internally in English.
3758        let context = self.defined_name_context();
3759        let internal_formula = self.user_formula_to_internal(new_formula, &context)?;
3760        if let Some(i) = index {
3761            if let Some(df) = self.workbook.defined_names.get_mut(i) {
3762                if new_name != df.name {
3763                    // We need to rename the name in every formula:
3764
3765                    // Parse all formulas with the old name
3766                    // All internal formulas are R1C1
3767                    self.parser.set_lexer_mode(LexerMode::R1C1);
3768                    let worksheets = &mut self.workbook.worksheets;
3769                    for worksheet in worksheets {
3770                        let cell_reference = CellReferenceRC {
3771                            sheet: worksheet.get_name(),
3772                            row: 1,
3773                            column: 1,
3774                        };
3775                        let mut formulas = Vec::new();
3776                        for formula in &worksheet.shared_formulas {
3777                            let mut t = self.parser.parse(formula, &cell_reference);
3778                            rename_defined_name_in_node(&mut t, name, scope, new_name);
3779                            formulas.push(to_rc_format(&t));
3780                        }
3781                        worksheet.shared_formulas = formulas;
3782                    }
3783                    // Se the mode back to A1
3784                    self.parser.set_lexer_mode(LexerMode::A1);
3785                }
3786                df.name = new_name.to_string();
3787                df.sheet_id = new_sheet_id;
3788                df.formula = internal_formula;
3789                self.reset_parsed_structures();
3790            }
3791            Ok(())
3792        } else {
3793            Err("Defined name not found".to_string())
3794        }
3795    }
3796    /// Returns the style object of a column, if any
3797    pub fn get_column_style(&self, sheet: u32, column: i32) -> Result<Option<Style>, String> {
3798        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3799            let cols = &worksheet.cols;
3800            for col in cols {
3801                if column >= col.min && column <= col.max {
3802                    if let Some(style_index) = col.style {
3803                        let style = self.workbook.styles.get_style(style_index)?;
3804                        return Ok(Some(style));
3805                    }
3806                    return Ok(None);
3807                }
3808            }
3809            Ok(None)
3810        } else {
3811            Err("Invalid sheet".to_string())
3812        }
3813    }
3814
3815    /// Returns the style object of a row, if any
3816    pub fn get_row_style(&self, sheet: u32, row: i32) -> Result<Option<Style>, String> {
3817        if let Some(worksheet) = self.workbook.worksheets.get(sheet as usize) {
3818            let rows = &worksheet.rows;
3819            for r in rows {
3820                if row == r.r {
3821                    let style = self.workbook.styles.get_style(r.s)?;
3822                    return Ok(Some(style));
3823                }
3824            }
3825            Ok(None)
3826        } else {
3827            Err("Invalid sheet".to_string())
3828        }
3829    }
3830
3831    /// Sets a column with style
3832    pub fn set_column_style(
3833        &mut self,
3834        sheet: u32,
3835        column: i32,
3836        style: &Style,
3837    ) -> Result<(), String> {
3838        let style_index = self.workbook.styles.get_style_index_or_create(style);
3839        self.workbook
3840            .worksheet_mut(sheet)?
3841            .set_column_style(column, style_index)
3842    }
3843
3844    /// Sets a row with style
3845    pub fn set_row_style(&mut self, sheet: u32, row: i32, style: &Style) -> Result<(), String> {
3846        let style_index = self.workbook.styles.get_style_index_or_create(style);
3847        self.workbook
3848            .worksheet_mut(sheet)?
3849            .set_row_style(row, style_index)
3850    }
3851
3852    /// Deletes the style of a column if the is any
3853    pub fn delete_column_style(&mut self, sheet: u32, column: i32) -> Result<(), String> {
3854        self.workbook
3855            .worksheet_mut(sheet)?
3856            .delete_column_style(column)
3857    }
3858
3859    /// Deletes the style of a row if there is any
3860    pub fn delete_row_style(&mut self, sheet: u32, row: i32) -> Result<(), String> {
3861        self.workbook.worksheet_mut(sheet)?.delete_row_style(row)
3862    }
3863
3864    /// Sets the locale of the model
3865    pub fn set_locale(&mut self, locale_id: &str) -> Result<(), String> {
3866        let locale = match get_locale(locale_id) {
3867            Ok(l) => l,
3868            Err(_) => return Err(format!("Invalid locale: {locale_id}")),
3869        };
3870        self.parser.set_locale(locale);
3871        self.locale = locale;
3872        self.workbook.settings.locale = locale_id.to_string();
3873        self.evaluate();
3874        Ok(())
3875    }
3876
3877    /// Sets the timezone of the model
3878    pub fn set_timezone(&mut self, timezone: &str) -> Result<(), String> {
3879        let tz = match Tz::parse(timezone) {
3880            Ok(tz) => tz,
3881            Err(_) => return Err(format!("Invalid timezone: {}", timezone)),
3882        };
3883        self.tz = tz;
3884        self.workbook.settings.tz = timezone.to_string();
3885        self.evaluate();
3886        Ok(())
3887    }
3888
3889    /// Sets the language
3890    pub fn set_language(&mut self, language_id: &str) -> Result<(), String> {
3891        let language = match get_language(language_id) {
3892            Ok(l) => l,
3893            Err(_) => return Err(format!("Invalid language: {language_id}")),
3894        };
3895        self.parser.set_language(language);
3896        self.language = language;
3897        Ok(())
3898    }
3899
3900    /// Gets the current language
3901    pub fn get_language(&self) -> String {
3902        self.language.code.clone()
3903    }
3904
3905    /// Gets the timezone of the model
3906    pub fn get_timezone(&self) -> String {
3907        self.workbook.settings.tz.clone()
3908    }
3909
3910    /// Gets the locale of the model
3911    pub fn get_locale(&self) -> String {
3912        self.workbook.settings.locale.clone()
3913    }
3914
3915    /// Gets the formatting settings based on the locale
3916    pub fn get_fmt_settings(&self) -> FmtSettings {
3917        let day_example = 46006.0; // December 15, 2025
3918        let currency = self.locale.currency.iso.clone();
3919        let currency_symbol = &self.locale.currency.symbol;
3920        // "M/d/yy"
3921        let short_date = &self.locale.dates.date_formats.short;
3922        // "M/d/yyyy"
3923        let long_date = &self.locale.dates.date_formats.long;
3924        let short_date_example = format_number(day_example, short_date, self.locale).text;
3925        let long_date_example = format_number(day_example, long_date, self.locale).text;
3926        // Number format ("#,##0.###")
3927        // The CLDR formats are a bit different than Excel's
3928        // let number_fmt = self.locale.numbers.decimal_formats.standard.clone();
3929        // "#,##0.00 ¤" Currency format might have weird spaces
3930        let currency_format_template = &self.locale.numbers.currency_formats.standard;
3931        let currency_format = currency_format_template
3932            .replace("¤", &format!("\"{}\"", currency_symbol))
3933            .replace(" ", " ");
3934
3935        let number_fmt = "#,##0.00".to_string();
3936        let number_example = format_number(1234.567, &number_fmt, self.locale).text;
3937        FmtSettings {
3938            currency,
3939            currency_format,
3940            short_date: short_date.clone(),
3941            long_date: long_date.clone(),
3942            short_date_example,
3943            long_date_example,
3944            number_fmt,
3945            number_example,
3946        }
3947    }
3948
3949    /// Cycles the references touched by the cursor through the four
3950    /// absolute/relative states, Excel F4 style: A1 -> $A$1 -> A$1 -> $A1 -> A1.
3951    /// Returns the new text together with the new cursor start and end.
3952    ///
3953    /// Given cycle_reference("=A1", 3, 3) returns ("=$A$1", 5, 5)
3954    pub fn cycle_reference(
3955        &self,
3956        value: &str,
3957        start: usize,
3958        end: usize,
3959    ) -> Result<(String, i32, i32), String> {
3960        crate::expressions::lexer::util::cycle_reference(
3961            value,
3962            start,
3963            end,
3964            self.locale,
3965            self.language,
3966        )
3967    }
3968}
3969
3970#[cfg(test)]
3971mod tests {
3972    #![allow(clippy::expect_used)]
3973    use super::CellReferenceIndex as CellReference;
3974    use crate::{test::util::new_empty_model, types::Cell};
3975
3976    #[test]
3977    fn test_cell_reference_to_string() {
3978        let model = new_empty_model();
3979        let reference = CellReference {
3980            sheet: 0,
3981            row: 32,
3982            column: 16,
3983        };
3984        assert_eq!(
3985            model.cell_reference_to_string(&reference),
3986            Ok("Sheet1!P32".to_string())
3987        )
3988    }
3989
3990    #[test]
3991    fn test_cell_reference_to_string_invalid_worksheet() {
3992        let model = new_empty_model();
3993        let reference = CellReference {
3994            sheet: 10,
3995            row: 1,
3996            column: 1,
3997        };
3998        assert_eq!(
3999            model.cell_reference_to_string(&reference),
4000            Err("Invalid sheet index".to_string())
4001        )
4002    }
4003
4004    #[test]
4005    fn test_cell_reference_to_string_invalid_column() {
4006        let model = new_empty_model();
4007        let reference = CellReference {
4008            sheet: 0,
4009            row: 1,
4010            column: 20_000,
4011        };
4012        assert_eq!(
4013            model.cell_reference_to_string(&reference),
4014            Err("Invalid column".to_string())
4015        )
4016    }
4017
4018    #[test]
4019    fn test_cell_reference_to_string_invalid_row() {
4020        let model = new_empty_model();
4021        let reference = CellReference {
4022            sheet: 0,
4023            row: 2_000_000,
4024            column: 1,
4025        };
4026        assert_eq!(
4027            model.cell_reference_to_string(&reference),
4028            Err("Invalid row".to_string())
4029        )
4030    }
4031
4032    #[test]
4033    fn test_get_cell() {
4034        let mut model = new_empty_model();
4035        model._set("A1", "35");
4036        model._set("A2", "");
4037        let worksheet = model.workbook.worksheet(0).expect("Invalid sheet");
4038
4039        assert_eq!(
4040            worksheet.cell(1, 1).as_deref(),
4041            Some(&Cell::NumberCell { v: 35.0, s: 0 })
4042        );
4043
4044        // Clears the content of A2 but not the style
4045        assert_eq!(
4046            worksheet.cell(2, 1).as_deref(),
4047            Some(&Cell::EmptyCell { s: 0 })
4048        );
4049        assert_eq!(worksheet.cell(3, 1), None)
4050    }
4051
4052    #[test]
4053    fn test_get_cell_invalid_sheet() {
4054        let model = new_empty_model();
4055        assert_eq!(
4056            model.workbook.worksheet(5),
4057            Err("Invalid sheet index".to_string()),
4058        )
4059    }
4060
4061    #[test]
4062    fn test_update_cell_with_sign_prefixed_formulas() {
4063        let mut model = new_empty_model();
4064
4065        let update_result = model.update_cell_with_formula(0, 1, 1, "-A2*2".to_string());
4066        model.evaluate();
4067        assert_eq!(update_result, Ok(()));
4068        assert_eq!(model._get_formula("A1"), *"=-A2*2");
4069    }
4070}