1use std::cell::RefCell;
9use std::collections::HashMap;
10use std::sync::OnceLock;
11
12use crate::calc::arg::*;
13use crate::cell::read_cell_value;
14use crate::errors::Result;
15use crate::file::File;
16use crate::lib_util::{
17 column_name_to_number, column_number_to_name, coordinates_to_cell_name, split_cell_name,
18};
19use crate::options::Options;
20use crate::xml::shared_strings::XlsxSi;
21use crate::xml::worksheet::{XlsxC, XlsxWorksheet};
22
23pub mod arg;
24pub mod common;
25pub mod database;
26pub mod date;
27pub mod engineering;
28pub mod financial;
29pub mod info;
30pub mod logical;
31pub mod lookup;
32pub mod math;
33pub mod statistical;
34pub mod text;
35pub mod web;
36
37#[derive(Debug, Clone)]
43pub struct CellRef {
44 pub sheet: Option<String>,
46 pub col: i32,
48 pub row: i32,
50 pub col_abs: bool,
52 pub row_abs: bool,
54 pub whole_col: bool,
56 pub whole_row: bool,
58}
59
60impl CellRef {
61 pub fn to_cell_name(&self) -> String {
63 coordinates_to_cell_name(self.col, self.row, false).unwrap_or_default()
64 }
65}
66
67#[derive(Debug)]
70pub struct CalcContext<'a> {
71 pub file: &'a File,
72 pub sheet: &'a str,
73 pub cell: String,
74 pub entry: String,
75 pub stack: RefCell<Vec<(String, String)>>,
76 pub max_calc_iterations: u32,
77 pub iterations: RefCell<HashMap<String, u32>>,
78 pub iterations_cache: RefCell<HashMap<String, FormulaArg>>,
79 pub sheet_bounds: RefCell<HashMap<String, (i32, i32)>>,
80}
81
82impl<'a> CalcContext<'a> {
83 pub fn new(file: &'a File, sheet: &'a str) -> Self {
84 Self {
85 file,
86 sheet,
87 cell: String::new(),
88 entry: String::new(),
89 stack: RefCell::new(Vec::new()),
90 max_calc_iterations: 0,
91 iterations: RefCell::new(HashMap::new()),
92 iterations_cache: RefCell::new(HashMap::new()),
93 sheet_bounds: RefCell::new(HashMap::new()),
94 }
95 }
96
97 pub fn new_with_cell(file: &'a File, sheet: &'a str, cell: impl Into<String>) -> Self {
98 Self {
99 file,
100 sheet,
101 cell: cell.into(),
102 entry: String::new(),
103 stack: RefCell::new(Vec::new()),
104 max_calc_iterations: 0,
105 iterations: RefCell::new(HashMap::new()),
106 iterations_cache: RefCell::new(HashMap::new()),
107 sheet_bounds: RefCell::new(HashMap::new()),
108 }
109 }
110
111 pub fn worksheet_bounds(&self, sheet: &str) -> (i32, i32) {
113 if let Some(bounds) = self.sheet_bounds.borrow().get(sheet).copied() {
114 return bounds;
115 }
116 let mut max_row = 0;
117 let mut max_col = 0;
118 if let Ok(ws) = self.file.work_sheet_reader(sheet) {
119 for row in &ws.sheet_data.row {
120 if let Some(r) = row.r {
121 max_row = max_row.max(r as i32);
122 }
123 for c in &row.c {
124 if let Some(ref name) = c.r {
125 if let Ok((col_name, row_num)) = split_cell_name(name) {
126 max_row = max_row.max(row_num);
127 if let Ok(col_num) = column_name_to_number(&col_name) {
128 max_col = max_col.max(col_num);
129 }
130 }
131 }
132 }
133 }
134 }
135 let bounds = (max_row.max(1), max_col.max(1));
136 self.sheet_bounds
137 .borrow_mut()
138 .insert(sheet.to_string(), bounds);
139 bounds
140 }
141}
142
143#[derive(Debug, Clone)]
148pub(crate) enum Expr {
149 Number(f64),
150 String(String),
151 Bool(bool),
152 Cell(CellRef),
153 Range(CellRef, CellRef),
154 Range3D(String, String, CellRef, CellRef),
155 Name(String),
156 Array(Vec<Vec<Expr>>),
157 Call(String, Vec<Expr>),
158 Unary(String, Box<Expr>),
159 Binary(String, Box<Expr>, Box<Expr>),
160}
161
162pub(crate) fn parse_formula(formula: &str) -> Result<Expr> {
167 let formula = formula.trim_start_matches('=').trim();
168 if formula.is_empty() {
169 return Ok(Expr::String(String::new()));
170 }
171 let mut parser = Parser::new(formula)?;
172 let expr = parser.parse_expr()?;
173 Ok(expr)
176}
177
178#[derive(Debug, Clone, PartialEq)]
183enum Token {
184 Number(f64),
185 Text(String),
186 Bool(bool),
187 Ident(String),
188 Op(String),
189 LParen,
190 RParen,
191 Comma,
192 Semicolon,
193 LBrace,
194 RBrace,
195 Eof,
196}
197
198struct Lexer<'a> {
199 input: &'a str,
200 pos: usize,
201}
202
203impl<'a> Lexer<'a> {
204 fn new(input: &'a str) -> Self {
205 Self { input, pos: 0 }
206 }
207
208 fn peek(&self) -> char {
209 self.input[self.pos..].chars().next().unwrap_or('\0')
210 }
211
212 fn peek_next(&self) -> char {
213 self.input[self.pos..].chars().nth(1).unwrap_or('\0')
214 }
215
216 fn advance(&mut self) -> char {
217 let c = self.peek();
218 self.pos += c.len_utf8();
219 c
220 }
221
222 fn skip_ws(&mut self) {
223 while self.peek().is_ascii_whitespace() {
224 self.advance();
225 }
226 }
227
228 fn peek_tokens(&self, n: usize) -> Result<Vec<Token>> {
230 let mut tmp = Lexer::new(self.input);
231 tmp.pos = self.pos;
232 let mut tokens = Vec::with_capacity(n);
233 for _ in 0..n {
234 tokens.push(tmp.next_token()?);
235 }
236 Ok(tokens)
237 }
238
239 fn next_token(&mut self) -> Result<Token> {
240 self.skip_ws();
241 let c = self.peek();
242 if c == '\0' {
243 return Ok(Token::Eof);
244 }
245
246 if c == '"' {
247 return self.read_string();
248 }
249
250 if c.is_ascii_digit() || (c == '.' && self.peek_next().is_ascii_digit()) {
251 return self.read_number();
252 }
253
254 if c.is_ascii_alphabetic() || c == '_' || c == '$' {
255 let ident = self.read_ident();
256 let upper = ident.to_uppercase();
257 if upper == "TRUE" {
258 return Ok(Token::Bool(true));
259 }
260 if upper == "FALSE" {
261 return Ok(Token::Bool(false));
262 }
263 return Ok(Token::Ident(ident));
264 }
265
266 self.advance();
267 match c {
268 '(' => Ok(Token::LParen),
269 ')' => Ok(Token::RParen),
270 '{' => Ok(Token::LBrace),
271 '}' => Ok(Token::RBrace),
272 ',' => Ok(Token::Comma),
273 ';' => Ok(Token::Semicolon),
274 '+' => Ok(Token::Op("+".to_string())),
275 '-' => Ok(Token::Op("-".to_string())),
276 '*' => Ok(Token::Op("*".to_string())),
277 '/' => Ok(Token::Op("/".to_string())),
278 '^' => Ok(Token::Op("^".to_string())),
279 '&' => Ok(Token::Op("&".to_string())),
280 '!' => Ok(Token::Op("!".to_string())),
281 ':' => Ok(Token::Op(":".to_string())),
282 '=' => Ok(Token::Op("=".to_string())),
283 '<' => {
284 if self.peek() == '=' {
285 self.advance();
286 Ok(Token::Op("<=".to_string()))
287 } else if self.peek() == '>' {
288 self.advance();
289 Ok(Token::Op("<>".to_string()))
290 } else {
291 Ok(Token::Op("<".to_string()))
292 }
293 }
294 '>' => {
295 if self.peek() == '=' {
296 self.advance();
297 Ok(Token::Op(">=".to_string()))
298 } else {
299 Ok(Token::Op(">".to_string()))
300 }
301 }
302 _ => Err(format!("unexpected character {:?} in formula", c).into()),
303 }
304 }
305
306 fn read_number(&mut self) -> Result<Token> {
307 let start = self.pos;
308 let mut has_dot = false;
309 let mut has_exp = false;
310 while !self.input[self.pos..].is_empty() {
311 let c = self.peek();
312 if c.is_ascii_digit() {
313 self.advance();
314 } else if c == '.' && !has_dot && !has_exp {
315 has_dot = true;
316 self.advance();
317 } else if (c == 'e' || c == 'E') && !has_exp {
318 has_exp = true;
319 self.advance();
320 if self.peek() == '+' || self.peek() == '-' {
321 self.advance();
322 }
323 } else {
324 break;
325 }
326 }
327 let s = &self.input[start..self.pos];
328 match s.parse::<f64>() {
329 Ok(n) => Ok(Token::Number(n)),
330 Err(_) => Err(format!("invalid numeric literal {}", s).into()),
331 }
332 }
333
334 fn read_string(&mut self) -> Result<Token> {
335 self.advance(); let mut s = String::new();
337 while !self.input[self.pos..].is_empty() {
338 let c = self.advance();
339 if c == '"' {
340 if self.peek() == '"' {
341 self.advance();
342 s.push('"');
343 } else {
344 break;
345 }
346 } else {
347 s.push(c);
348 }
349 }
350 Ok(Token::Text(s))
351 }
352
353 fn read_ident(&mut self) -> String {
354 let start = self.pos;
355 while !self.input[self.pos..].is_empty() {
356 let c = self.peek();
357 if c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '?' || c == '$' {
358 self.advance();
359 } else {
360 break;
361 }
362 }
363 self.input[start..self.pos].to_string()
364 }
365}
366
367struct Parser<'a> {
372 lexer: Lexer<'a>,
373 current: Token,
374}
375
376impl<'a> Parser<'a> {
377 fn new(input: &'a str) -> Result<Self> {
378 let mut lexer = Lexer::new(input);
379 let current = lexer.next_token()?;
380 Ok(Self { lexer, current })
381 }
382
383 fn bump(&mut self) -> Result<()> {
384 self.current = self.lexer.next_token()?;
385 Ok(())
386 }
387
388 fn expect_rparen(&mut self) -> Result<()> {
389 if !matches!(self.current, Token::RParen) {
390 return Err(format!("expected ')' got {:?}", self.current).into());
391 }
392 self.bump()
393 }
394
395 fn parse_expr(&mut self) -> Result<Expr> {
396 self.parse_compare()
397 }
398
399 fn parse_compare(&mut self) -> Result<Expr> {
400 let mut left = self.parse_concat()?;
401 if let Token::Op(ref op) = self.current {
402 if is_comparison(op) {
403 let op = op.clone();
404 self.bump()?;
405 let right = self.parse_concat()?;
406 left = Expr::Binary(op, Box::new(left), Box::new(right));
407 }
408 }
409 Ok(left)
410 }
411
412 fn parse_concat(&mut self) -> Result<Expr> {
413 let mut left = self.parse_add_sub()?;
414 while let Token::Op(ref op) = self.current {
415 if op == "&" {
416 self.bump()?;
417 let right = self.parse_add_sub()?;
418 left = Expr::Binary("&".to_string(), Box::new(left), Box::new(right));
419 } else {
420 break;
421 }
422 }
423 Ok(left)
424 }
425
426 fn parse_add_sub(&mut self) -> Result<Expr> {
427 let mut left = self.parse_mul_div()?;
428 while let Token::Op(ref op) = self.current {
429 if op == "+" || op == "-" {
430 let op = op.clone();
431 self.bump()?;
432 let right = self.parse_mul_div()?;
433 left = Expr::Binary(op, Box::new(left), Box::new(right));
434 } else {
435 break;
436 }
437 }
438 Ok(left)
439 }
440
441 fn parse_mul_div(&mut self) -> Result<Expr> {
442 let mut left = self.parse_power()?;
443 while let Token::Op(ref op) = self.current {
444 if op == "*" || op == "/" {
445 let op = op.clone();
446 self.bump()?;
447 let right = self.parse_power()?;
448 left = Expr::Binary(op, Box::new(left), Box::new(right));
449 } else {
450 break;
451 }
452 }
453 Ok(left)
454 }
455
456 fn parse_power(&mut self) -> Result<Expr> {
457 let left = self.parse_unary()?;
458 if let Token::Op(ref op) = self.current {
459 if op == "^" {
460 let op = op.clone();
461 self.bump()?;
462 let right = self.parse_power()?;
463 return Ok(Expr::Binary(op, Box::new(left), Box::new(right)));
464 }
465 }
466 Ok(left)
467 }
468
469 fn parse_unary(&mut self) -> Result<Expr> {
470 if let Token::Op(ref op) = self.current {
471 if op == "+" || op == "-" {
472 let op = op.clone();
473 self.bump()?;
474 let expr = self.parse_unary()?;
475 return Ok(Expr::Unary(op, Box::new(expr)));
476 }
477 }
478 self.parse_primary()
479 }
480
481 fn parse_primary(&mut self) -> Result<Expr> {
482 match self.current.clone() {
483 Token::Number(n) => {
484 self.bump()?;
485 if n.fract() == 0.0 && n >= 1.0 {
487 if let Token::Op(ref op) = self.current {
488 if op == ":" {
489 let first = CellRef {
490 sheet: None,
491 col: 0,
492 row: n as i32,
493 col_abs: false,
494 row_abs: false,
495 whole_col: false,
496 whole_row: true,
497 };
498 return self.parse_multi_area_range(first);
499 }
500 }
501 }
502 Ok(Expr::Number(n))
503 }
504 Token::Text(s) => {
505 self.bump()?;
506 Ok(Expr::String(s))
507 }
508 Token::Bool(b) => {
509 self.bump()?;
510 if let Token::LParen = self.current {
512 self.bump()?;
513 self.expect_rparen()?;
514 let name = if b { "TRUE" } else { "FALSE" }.to_string();
515 return Ok(Expr::Call(name, Vec::new()));
516 }
517 Ok(Expr::Bool(b))
518 }
519 Token::LParen => {
520 self.bump()?;
521 let expr = self.parse_expr()?;
522 self.expect_rparen()?;
523 Ok(expr)
524 }
525 Token::LBrace => self.parse_array(),
526 Token::Ident(name) => self.parse_ident_expr(name),
527 ref t => Err(format!("unexpected token {:?}", t).into()),
528 }
529 }
530
531 fn parse_array(&mut self) -> Result<Expr> {
532 self.bump()?; let mut rows: Vec<Vec<Expr>> = vec![Vec::new()];
534 loop {
535 if matches!(self.current, Token::RBrace) {
536 self.bump()?;
537 break;
538 }
539 let expr = self.parse_expr()?;
540 rows.last_mut().unwrap().push(expr);
541 match self.current {
542 Token::Comma => self.bump()?,
543 Token::Semicolon => {
544 self.bump()?;
545 rows.push(Vec::new());
546 }
547 Token::RBrace => {
548 self.bump()?;
549 break;
550 }
551 _ => return Err("expected ',', ';' or '}' in array literal".into()),
552 }
553 }
554 Ok(Expr::Array(rows))
555 }
556
557 fn parse_ident_expr(&mut self, name: String) -> Result<Expr> {
558 self.bump()?; if let Token::LParen = self.current {
562 self.bump()?;
563 let mut args = Vec::new();
564 if !matches!(self.current, Token::RParen) {
565 loop {
566 args.push(self.parse_expr()?);
567 if let Token::Comma = self.current {
568 self.bump()?;
569 continue;
570 }
571 break;
572 }
573 }
574 self.expect_rparen()?;
575 return Ok(Expr::Call(normalize_function_name(&name), args));
576 }
577
578 if let Token::Op(ref op) = self.current {
580 if op == ":" {
581 let peek = self.lexer.peek_tokens(2)?;
582 if let [Token::Ident(last_sheet), Token::Op(bang)] = peek.as_slice() {
583 if bang == "!" {
584 let last = last_sheet.clone();
585 self.bump()?; self.bump()?; self.bump()?; let expr = self.parse_expr()?;
589 let (start, end) = match expr {
590 Expr::Cell(r) => (r.clone(), r),
591 Expr::Range(s, e) => (s, e),
592 _ => {
593 return Err(
594 "expected cell or range reference in 3D reference".into()
595 );
596 }
597 };
598 return Ok(Expr::Range3D(name, last, start, end));
599 }
600 }
601 }
602 }
603
604 let first_ref = if let Token::Op(ref op) = self.current {
606 if op == "!" {
607 self.bump()?;
608 let sheet = Some(name.clone());
609 match self.current.clone() {
610 Token::Ident(n) => {
611 self.bump()?;
612 parse_cell_ref(&n, sheet)?
613 }
614 Token::Number(n) if n.fract() == 0.0 && n >= 1.0 => {
615 self.bump()?;
616 CellRef {
617 sheet,
618 col: 0,
619 row: n as i32,
620 col_abs: false,
621 row_abs: false,
622 whole_col: false,
623 whole_row: true,
624 }
625 }
626 t => {
627 return Err(
628 format!("expected cell reference after '!', got {:?}", t).into()
629 );
630 }
631 }
632 } else {
633 match parse_cell_ref(&name, None) {
634 Ok(r) => r,
635 Err(_) => {
636 if op == ":" {
637 return Err(format!("invalid cell reference {}", name).into());
638 }
639 return Ok(Expr::Name(name));
640 }
641 }
642 }
643 } else {
644 match parse_cell_ref(&name, None) {
645 Ok(r) => r,
646 Err(_) => return Ok(Expr::Name(name)),
647 }
648 };
649
650 if let Token::Op(ref op) = self.current {
652 if op == ":" {
653 return self.parse_multi_area_range(first_ref);
654 }
655 }
656
657 Ok(Expr::Cell(first_ref))
658 }
659
660 fn parse_multi_area_range(&mut self, first: CellRef) -> Result<Expr> {
664 let mut refs = vec![first];
665 while let Token::Op(ref op) = self.current {
666 if op != ":" {
667 break;
668 }
669 self.bump()?;
670 let (next_sheet, cell_name, is_row_number) = if let Token::Ident(n) =
671 self.current.clone()
672 {
673 self.bump()?;
674 if let Token::Op(ref op) = self.current {
675 if op == "!" {
676 self.bump()?;
677 let cell = match self.current.clone() {
678 Token::Ident(c) => c,
679 t => {
680 return Err(format!(
681 "expected cell reference after '!', got {:?}",
682 t
683 )
684 .into());
685 }
686 };
687 self.bump()?;
688 (Some(n), cell, false)
689 } else {
690 (None, n, false)
691 }
692 } else {
693 (None, n, false)
694 }
695 } else if let Token::Number(n) = self.current {
696 self.bump()?;
697 if n.fract() == 0.0 && n >= 1.0 {
698 (None, (n as i32).to_string(), true)
699 } else {
700 return Err("expected integer row reference".into());
701 }
702 } else {
703 return Err(
704 format!("expected cell reference after ':', got {:?}", self.current).into(),
705 );
706 };
707 let inherited = next_sheet.or_else(|| refs[0].sheet.clone());
708 let cell_ref = if is_row_number {
709 CellRef {
710 sheet: inherited,
711 col: 0,
712 row: cell_name.parse::<i32>().unwrap(),
713 col_abs: false,
714 row_abs: false,
715 whole_col: false,
716 whole_row: true,
717 }
718 } else {
719 parse_cell_ref(&cell_name, inherited)?
720 };
721 refs.push(cell_ref);
722 }
723 let (start, end) = build_bounding_range(&refs)?;
724 Ok(Expr::Range(start, end))
725 }
726}
727
728fn is_comparison(op: &str) -> bool {
729 matches!(op, "=" | "<>" | "<" | ">" | "<=" | ">=")
730}
731
732fn build_bounding_range(refs: &[CellRef]) -> Result<(CellRef, CellRef)> {
737 use crate::constants::{MAX_COLUMNS, TOTAL_ROWS};
738 if refs.is_empty() {
739 return Err("empty reference list".into());
740 }
741 let sheet = refs[0].sheet.clone();
742 if refs.iter().any(|r| r.sheet != sheet) {
743 return Err("multi-sheet reference is not supported".into());
744 }
745
746 let mut all_whole_col = true;
747 let mut all_whole_row = true;
748 let mut any_ref = false;
749 let mut min_col = i32::MAX;
750 let mut max_col = 0;
751 let mut min_row = i32::MAX;
752 let mut max_row = 0;
753
754 for r in refs {
755 if r.whole_col {
756 all_whole_row = false;
757 any_ref = true;
758 min_col = min_col.min(r.col);
759 max_col = max_col.max(r.col);
760 min_row = min_row.min(1);
761 max_row = max_row.max(TOTAL_ROWS as i32);
762 } else if r.whole_row {
763 all_whole_col = false;
764 any_ref = true;
765 min_row = min_row.min(r.row);
766 max_row = max_row.max(r.row);
767 min_col = min_col.min(1);
768 max_col = max_col.max(MAX_COLUMNS as i32);
769 } else {
770 all_whole_col = false;
771 all_whole_row = false;
772 any_ref = true;
773 min_col = min_col.min(r.col);
774 max_col = max_col.max(r.col);
775 min_row = min_row.min(r.row);
776 max_row = max_row.max(r.row);
777 }
778 }
779
780 if !any_ref {
781 return Err("no valid references".into());
782 }
783
784 let start = CellRef {
785 sheet: sheet.clone(),
786 col: min_col,
787 row: min_row,
788 col_abs: false,
789 row_abs: false,
790 whole_col: all_whole_col,
791 whole_row: all_whole_row,
792 };
793 let end = CellRef {
794 sheet,
795 col: max_col,
796 row: max_row,
797 col_abs: false,
798 row_abs: false,
799 whole_col: all_whole_col,
800 whole_row: all_whole_row,
801 };
802 Ok((start, end))
803}
804
805fn parse_cell_ref(name: &str, sheet: Option<String>) -> Result<CellRef> {
806 let without_dollar = name.replace('$', "");
807
808 if without_dollar.chars().all(|c| c.is_ascii_alphabetic()) {
810 let col = column_name_to_number(&without_dollar)
811 .map_err(|e| format!("invalid column {}: {}", name, e))?;
812 return Ok(CellRef {
813 sheet,
814 col,
815 row: 0,
816 col_abs: name.starts_with('$'),
817 row_abs: false,
818 whole_col: true,
819 whole_row: false,
820 });
821 }
822
823 if without_dollar.chars().all(|c| c.is_ascii_digit()) {
825 let row = without_dollar
826 .parse::<i32>()
827 .map_err(|_| format!("invalid row {}: {}", name, without_dollar))?;
828 return Ok(CellRef {
829 sheet,
830 col: 0,
831 row,
832 col_abs: false,
833 row_abs: name.starts_with('$'),
834 whole_col: false,
835 whole_row: true,
836 });
837 }
838
839 let (col_name, row) = split_cell_name(&without_dollar)
840 .map_err(|e| format!("invalid cell reference {}: {}", name, e))?;
841 let col = column_name_to_number(&col_name)
842 .map_err(|e| format!("invalid column {}: {}", col_name, e))?;
843
844 let col_abs = name.starts_with('$');
845 let row_abs = {
846 let without_leading = name.strip_prefix('$').unwrap_or(name);
847 without_leading.contains('$')
848 };
849
850 Ok(CellRef {
851 sheet,
852 col,
853 row,
854 col_abs,
855 row_abs,
856 whole_col: false,
857 whole_row: false,
858 })
859}
860
861pub(crate) fn normalize_function_name(name: &str) -> String {
865 name.to_uppercase()
866 .replace("_XLFN.", "")
867 .replace('.', "dot")
868}
869
870pub(crate) type FormulaFn = fn(&CalcContext, &[FormulaArg]) -> FormulaArg;
875
876fn build_registry() -> HashMap<&'static str, FormulaFn> {
877 let mut m: HashMap<&'static str, FormulaFn> = HashMap::with_capacity(512);
878
879 m.insert("IF", calc_if);
881
882 math::register(&mut m);
884 statistical::register(&mut m);
886 engineering::register(&mut m);
888 logical::register(&mut m);
890 info::register(&mut m);
892 lookup::register(&mut m);
894 text::register(&mut m);
896 date::register(&mut m);
898 financial::register(&mut m);
900 database::register(&mut m);
902 web::register(&mut m);
904
905 m
906}
907
908fn formula_registry() -> &'static HashMap<&'static str, FormulaFn> {
909 static REGISTRY: OnceLock<HashMap<&'static str, FormulaFn>> = OnceLock::new();
910 REGISTRY.get_or_init(build_registry)
911}
912
913fn eval(ctx: &CalcContext, expr: &Expr) -> FormulaArg {
918 match expr {
919 Expr::Number(n) => new_number_formula_arg(*n),
920 Expr::String(s) => new_string_formula_arg(s.clone()),
921 Expr::Bool(b) => new_bool_formula_arg(*b),
922 Expr::Cell(r) => eval_cell_ref(ctx, r),
923 Expr::Range(start, end) => eval_range(ctx, start, end),
924 Expr::Range3D(s1, s2, start, end) => eval_range_3d(ctx, s1, s2, start, end),
925 Expr::Name(name) => eval_name(ctx, name),
926 Expr::Array(rows) => eval_array(ctx, rows),
927 Expr::Call(name, args) => eval_call(ctx, name, args),
928 Expr::Unary(op, e) => eval_unary(op, eval(ctx, e)),
929 Expr::Binary(op, l, r) => eval_binary(op, eval(ctx, l), eval(ctx, r)),
930 }
931}
932
933fn eval_call(ctx: &CalcContext, name: &str, args: &[Expr]) -> FormulaArg {
934 let evaluated: Vec<FormulaArg> = args.iter().map(|e| eval_arg(ctx, e)).collect();
935 dispatch_function(ctx, name, &evaluated)
936}
937
938fn eval_arg(ctx: &CalcContext, expr: &Expr) -> FormulaArg {
943 let mut arg = eval(ctx, expr);
944 match expr {
945 Expr::Cell(r) => arg.cell_refs.push(r.clone()),
946 Expr::Range(start, end) => arg.cell_ranges.push((start.clone(), end.clone())),
947 _ => {}
948 }
949 arg
950}
951
952fn eval_array(ctx: &CalcContext, rows: &[Vec<Expr>]) -> FormulaArg {
953 let mut matrix = Vec::new();
954 for row in rows {
955 let mut line = Vec::new();
956 for e in row {
957 let v = eval(ctx, e);
958 if v.is_error() {
959 return v;
960 }
961 line.push(v);
962 }
963 matrix.push(line);
964 }
965 new_matrix_formula_arg(matrix)
966}
967
968fn eval_range_3d(
969 ctx: &CalcContext,
970 first_sheet: &str,
971 last_sheet: &str,
972 start: &CellRef,
973 end: &CellRef,
974) -> FormulaArg {
975 let sheets = match ctx.file.expand_3d_sheet_range(first_sheet, last_sheet) {
976 Ok(v) => v,
977 Err(_) => return new_error_formula_arg(FORMULA_ERROR_REF),
978 };
979 let mut matrix = Vec::new();
980 for sheet in sheets {
981 let mut start_ref = start.clone();
982 let mut end_ref = end.clone();
983 start_ref.sheet = Some(sheet.clone());
984 end_ref.sheet = Some(sheet.clone());
985 let arg = eval_range(ctx, &start_ref, &end_ref);
986 if arg.is_error() {
987 return arg;
988 }
989 match arg.typ {
990 ArgType::Matrix => matrix.extend(arg.matrix),
991 _ => matrix.push(vec![arg]),
992 }
993 }
994 new_matrix_formula_arg(matrix)
995}
996
997fn eval_name(ctx: &CalcContext, name: &str) -> FormulaArg {
998 let ref_to = ctx.file.get_defined_name_ref_to(name, ctx.sheet);
999 if ref_to.is_empty() {
1000 return new_error_formula_arg(FORMULA_ERROR_NAME);
1001 }
1002 let formula = ref_to.trim_start_matches('=').trim();
1003 if formula.is_empty() {
1004 return new_error_formula_arg(FORMULA_ERROR_NAME);
1005 }
1006 match parse_formula(formula) {
1007 Ok(expr) => eval(ctx, &expr),
1008 Err(_) => new_error_formula_arg(FORMULA_ERROR_NAME),
1009 }
1010}
1011
1012fn dispatch_function(ctx: &CalcContext, name: &str, args: &[FormulaArg]) -> FormulaArg {
1013 let registry = formula_registry();
1014 let key = name.replace('.', "dot");
1017 match registry.get(key.as_str()) {
1018 Some(f) => f(ctx, args),
1019 None => new_error_formula_arg(FORMULA_ERROR_NAME),
1020 }
1021}
1022
1023fn calc_if(_ctx: &CalcContext, args: &[FormulaArg]) -> FormulaArg {
1024 if args.is_empty() || args.len() > 3 {
1025 return new_error_formula_arg(FORMULA_ERROR_VALUE);
1026 }
1027 let cond = match args[0].typ {
1028 ArgType::String => match args[0].string.parse::<bool>() {
1029 Ok(b) => b,
1030 Err(_) => return new_error_formula_arg(FORMULA_ERROR_VALUE),
1031 },
1032 ArgType::Number => args[0].number == 1.0,
1033 _ => args[0].as_bool(),
1034 };
1035 if args.len() == 1 {
1036 return new_bool_formula_arg(cond);
1037 }
1038 if cond {
1039 if args[1].typ == ArgType::Number {
1040 args[1].to_number()
1041 } else {
1042 new_string_formula_arg(args[1].value())
1043 }
1044 } else if args.len() == 2 {
1045 new_bool_formula_arg(false)
1046 } else if args[2].typ == ArgType::Number {
1047 args[2].to_number()
1048 } else {
1049 new_string_formula_arg(args[2].value())
1050 }
1051}
1052
1053fn eval_unary(op: &str, arg: FormulaArg) -> FormulaArg {
1054 if arg.is_error() {
1055 return arg;
1056 }
1057 match op {
1058 "+" => arg,
1059 "-" => match arg.to_number().as_number() {
1060 Some(n) => new_number_formula_arg(-n),
1061 None => new_error_formula_arg(FORMULA_ERROR_VALUE),
1062 },
1063 _ => new_error_formula_arg(FORMULA_ERROR_VALUE),
1064 }
1065}
1066
1067fn eval_binary(op: &str, left: FormulaArg, right: FormulaArg) -> FormulaArg {
1068 if left.is_error() {
1069 return left;
1070 }
1071 if right.is_error() {
1072 return right;
1073 }
1074 match op {
1075 "+" => numeric_op(left, right, |a, b| a + b),
1076 "-" => numeric_op(left, right, |a, b| a - b),
1077 "*" => numeric_op(left, right, |a, b| a * b),
1078 "/" => {
1079 let a = left.to_number().as_number();
1080 let b = right.to_number().as_number();
1081 match (a, b) {
1082 (Some(_), Some(0.0)) => new_error_formula_arg(FORMULA_ERROR_DIV),
1083 (Some(x), Some(y)) => new_number_formula_arg(x / y),
1084 _ => new_error_formula_arg(FORMULA_ERROR_VALUE),
1085 }
1086 }
1087 "^" => numeric_op(left, right, |a, b| a.powf(b)),
1088 "&" => new_string_formula_arg(format!("{}{}", left.value(), right.value())),
1089 "=" => new_bool_formula_arg(compare_equal(&left, &right)),
1090 "<>" => new_bool_formula_arg(!compare_equal(&left, &right)),
1091 "<" | ">" | "<=" | ">=" => comparison_op(op, left, right),
1092 _ => new_error_formula_arg(FORMULA_ERROR_VALUE),
1093 }
1094}
1095
1096fn numeric_op<F: FnOnce(f64, f64) -> f64>(left: FormulaArg, right: FormulaArg, f: F) -> FormulaArg {
1097 match (left.to_number().as_number(), right.to_number().as_number()) {
1098 (Some(a), Some(b)) => new_number_formula_arg(f(a, b)),
1099 _ => new_error_formula_arg(FORMULA_ERROR_VALUE),
1100 }
1101}
1102
1103fn comparison_op(op: &str, left: FormulaArg, right: FormulaArg) -> FormulaArg {
1104 fn as_number(arg: &FormulaArg) -> Option<f64> {
1105 match arg.typ {
1106 ArgType::Number => Some(arg.number),
1107 ArgType::Empty => Some(0.0),
1108 _ => None,
1109 }
1110 }
1111
1112 if let (Some(a), Some(b)) = (as_number(&left), as_number(&right)) {
1115 let res = match op {
1116 "<" => a < b,
1117 ">" => a > b,
1118 "<=" => a <= b,
1119 ">=" => a >= b,
1120 _ => false,
1121 };
1122 return new_bool_formula_arg(res);
1123 }
1124
1125 let number_vs_text = |number_on_left: bool| match op {
1127 "<" | "<=" => number_on_left,
1128 ">" | ">=" => !number_on_left,
1129 _ => false,
1130 };
1131 if left.typ == ArgType::Number && right.typ == ArgType::String {
1132 return new_bool_formula_arg(number_vs_text(true));
1133 }
1134 if left.typ == ArgType::String && right.typ == ArgType::Number {
1135 return new_bool_formula_arg(number_vs_text(false));
1136 }
1137
1138 let a = left.value().to_uppercase();
1140 let b = right.value().to_uppercase();
1141 let res = match op {
1142 "<" => a < b,
1143 ">" => a > b,
1144 "<=" => a <= b,
1145 ">=" => a >= b,
1146 _ => false,
1147 };
1148 new_bool_formula_arg(res)
1149}
1150
1151fn eval_cell_ref(ctx: &CalcContext, reference: &CellRef) -> FormulaArg {
1152 let cell_name = reference.to_cell_name();
1153 let sheet = reference.sheet.as_deref().unwrap_or(ctx.sheet);
1154 let ref_key = format!("{}!{}", sheet, cell_name);
1155
1156 if reference.sheet.is_none() && (reference.whole_col || reference.whole_row) {
1159 let name = if reference.whole_col {
1160 column_number_to_name(reference.col).unwrap_or_default()
1161 } else {
1162 reference.row.to_string()
1163 };
1164 let ref_to = ctx.file.get_defined_name_ref_to(&name, ctx.sheet);
1165 if !ref_to.is_empty() {
1166 return eval_name(ctx, &ref_to);
1167 }
1168 }
1169
1170 if sheet == ctx.sheet && cell_name == ctx.cell {
1175 let ws = match ctx.file.work_sheet_reader(sheet) {
1176 Ok(ws) => ws,
1177 Err(_) => return new_error_formula_arg(FORMULA_ERROR_REF),
1178 };
1179 return if let Some(c) = find_cell(&ws, &cell_name) {
1180 cell_to_arg(ctx.file, &c)
1181 } else {
1182 new_empty_formula_arg()
1183 };
1184 }
1185
1186 if ctx
1187 .stack
1188 .borrow()
1189 .contains(&(sheet.to_string(), cell_name.clone()))
1190 {
1191 return new_error_formula_arg(FORMULA_ERROR_REF);
1192 }
1193
1194 let ws = match ctx.file.work_sheet_reader(sheet) {
1195 Ok(ws) => ws,
1196 Err(_) => return new_error_formula_arg(FORMULA_ERROR_REF),
1197 };
1198
1199 let cell = find_cell(&ws, &cell_name);
1200 let result = if let Some(ref c) = cell {
1201 if let Some(f) = &c.f {
1202 let formula = f.content.trim_start_matches('=').trim();
1203 if !formula.is_empty() {
1204 if ref_key != ctx.entry {
1206 if let Some(cached) = ctx
1207 .file
1208 .formula_arg_cache
1209 .lock()
1210 .unwrap()
1211 .get(&ref_key)
1212 .cloned()
1213 {
1214 return cached;
1215 }
1216 }
1217
1218 if ctx.max_calc_iterations > 0 && ref_key != ctx.entry {
1220 let mut iterations = ctx.iterations.borrow_mut();
1221 let count = iterations.entry(ref_key.clone()).or_insert(0);
1222 if *count <= ctx.max_calc_iterations {
1223 *count += 1;
1224 drop(iterations);
1225 ctx.stack
1226 .borrow_mut()
1227 .push((sheet.to_string(), cell_name.clone()));
1228 let res = match parse_formula(formula) {
1229 Ok(expr) => eval(ctx, &expr),
1230 Err(_) => new_error_formula_arg(FORMULA_ERROR_VALUE),
1231 };
1232 ctx.stack.borrow_mut().pop();
1233 ctx.iterations_cache
1234 .borrow_mut()
1235 .insert(ref_key.clone(), res.clone());
1236 ctx.file
1237 .formula_arg_cache
1238 .lock()
1239 .unwrap()
1240 .insert(ref_key.clone(), res.clone());
1241 return res;
1242 }
1243 return ctx
1244 .iterations_cache
1245 .borrow()
1246 .get(&ref_key)
1247 .cloned()
1248 .unwrap_or_else(new_empty_formula_arg);
1249 }
1250
1251 ctx.stack
1252 .borrow_mut()
1253 .push((sheet.to_string(), cell_name.clone()));
1254 let res = match parse_formula(formula) {
1255 Ok(expr) => eval(ctx, &expr),
1256 Err(_) => new_error_formula_arg(FORMULA_ERROR_VALUE),
1257 };
1258 ctx.stack.borrow_mut().pop();
1259 if ref_key != ctx.entry {
1260 ctx.file
1261 .formula_arg_cache
1262 .lock()
1263 .unwrap()
1264 .insert(ref_key.clone(), res.clone());
1265 }
1266 res
1267 } else {
1268 cell_to_arg(ctx.file, c)
1269 }
1270 } else {
1271 cell_to_arg(ctx.file, c)
1272 }
1273 } else {
1274 new_empty_formula_arg()
1275 };
1276
1277 result
1278}
1279
1280fn eval_range(ctx: &CalcContext, start: &CellRef, end: &CellRef) -> FormulaArg {
1281 let sheet = start.sheet.as_deref().unwrap_or(ctx.sheet);
1282 let (max_row, max_col) = ctx.worksheet_bounds(sheet);
1283 let (col1, col2, row1, row2) = if start.whole_col && end.whole_col {
1284 let c1 = start.col.min(end.col);
1285 let c2 = start.col.max(end.col);
1286 (c1, c2, 1, max_row)
1287 } else if start.whole_row && end.whole_row {
1288 let r1 = start.row.min(end.row);
1289 let r2 = start.row.max(end.row);
1290 (1, max_col, r1, r2)
1291 } else {
1292 let c1 = start.col.min(end.col);
1293 let c2 = start.col.max(end.col);
1294 let r1 = start.row.min(end.row);
1295 let r2 = start.row.max(end.row);
1296 (c1, c2, r1, r2)
1297 };
1298
1299 let mut matrix = Vec::new();
1300 for row in row1..=row2 {
1301 let mut line = Vec::new();
1302 for col in col1..=col2 {
1303 let reference = CellRef {
1304 sheet: Some(sheet.to_string()),
1305 col,
1306 row,
1307 col_abs: false,
1308 row_abs: false,
1309 whole_col: false,
1310 whole_row: false,
1311 };
1312 let value = eval_cell_ref(ctx, &reference);
1313 if value.is_error() {
1314 return value;
1315 }
1316 line.push(value);
1317 }
1318 matrix.push(line);
1319 }
1320 new_matrix_formula_arg(matrix)
1321}
1322
1323fn find_cell(ws: &XlsxWorksheet, cell: &str) -> Option<XlsxC> {
1324 for row in &ws.sheet_data.row {
1325 for c in &row.c {
1326 if c.r
1327 .as_deref()
1328 .map(|r| r.eq_ignore_ascii_case(cell))
1329 .unwrap_or(false)
1330 {
1331 return Some(c.clone());
1332 }
1333 }
1334 }
1335 None
1336}
1337
1338fn cell_to_arg(file: &File, c: &XlsxC) -> FormulaArg {
1339 match c.t.as_deref() {
1340 Some("s") => {
1341 if let Some(v) = &c.v {
1342 if let Ok(idx) = v.parse::<i32>() {
1343 return new_string_formula_arg(read_shared_string(file, idx));
1344 }
1345 }
1346 new_empty_formula_arg()
1347 }
1348 Some("inlineStr") => new_string_formula_arg(inline_string_text(c.is.as_ref())),
1349 Some("b") => new_bool_formula_arg(c.v.as_deref() == Some("1")),
1350 Some("str") => new_string_formula_arg(c.v.clone().unwrap_or_default()),
1351 _ => {
1352 if let Some(v) = &c.v {
1353 if let Ok(n) = v.parse::<f64>() {
1354 new_number_formula_arg(n)
1355 } else {
1356 new_string_formula_arg(v.clone())
1357 }
1358 } else {
1359 new_empty_formula_arg()
1360 }
1361 }
1362 }
1363}
1364
1365fn read_shared_string(file: &File, idx: i32) -> String {
1366 let sst = file.shared_strings_reader().unwrap_or_default();
1367 sst.si
1368 .get(idx as usize)
1369 .map(|si| string_from_si(si))
1370 .unwrap_or_default()
1371}
1372
1373fn inline_string_text(si: Option<&XlsxSi>) -> String {
1374 si.map(string_from_si).unwrap_or_default()
1375}
1376
1377fn string_from_si(si: &XlsxSi) -> String {
1378 if let Some(t) = &si.t {
1379 return t.val.clone();
1380 }
1381 si.r.iter()
1382 .filter_map(|r| r.t.as_ref().map(|t| t.val.clone()))
1383 .collect()
1384}
1385
1386impl File {
1391 pub fn calc_cell_value(&self, sheet: &str, cell: &str) -> Result<String> {
1395 let opts = self.options.lock().unwrap().clone();
1396 self.calc_cell_value_with_options(sheet, cell, &opts)
1397 }
1398
1399 pub fn calc_cell_value_with_options(
1401 &self,
1402 sheet: &str,
1403 cell: &str,
1404 opts: &Options,
1405 ) -> Result<String> {
1406 let cell_upper = cell.to_uppercase();
1407 let entry = format!("{}!{}", sheet, cell_upper);
1408 if opts.raw_cell_value {
1409 if let Some(v) = self.calc_raw_cache.lock().unwrap().get(&entry).cloned() {
1410 return Ok(v);
1411 }
1412 } else {
1413 if let Some(v) = self.calc_cache.lock().unwrap().get(&entry).cloned() {
1414 return Ok(v);
1415 }
1416 }
1417
1418 let ws = self.work_sheet_reader(sheet)?;
1419 let c = find_cell(&ws, &cell_upper);
1420
1421 let token = if let Some(c) = c {
1422 if let Some(f) = &c.f {
1423 let formula = f.content.trim_start_matches('=').trim();
1424 if !formula.is_empty() {
1425 let mut ctx = CalcContext::new_with_cell(self, sheet, cell_upper.clone());
1426 ctx.entry = entry.clone();
1427 ctx.max_calc_iterations = opts.max_calc_iterations;
1428 ctx.stack
1429 .borrow_mut()
1430 .push((sheet.to_string(), cell_upper.clone()));
1431 let expr = parse_formula(formula)?;
1432 let result = eval(&ctx, &expr);
1433 ctx.stack.borrow_mut().pop();
1434 result
1435 } else {
1436 cell_to_arg(self, &c)
1437 }
1438 } else {
1439 cell_to_arg(self, &c)
1440 }
1441 } else {
1442 new_empty_formula_arg()
1443 };
1444
1445 let style_idx = if opts.raw_cell_value {
1446 0
1447 } else {
1448 self.get_cell_style(sheet, &cell_upper).unwrap_or(0)
1449 };
1450
1451 let result = format_calc_result(self, &token, style_idx, opts.raw_cell_value)?;
1452 if opts.raw_cell_value {
1453 self.calc_raw_cache
1454 .lock()
1455 .unwrap()
1456 .insert(entry, result.clone());
1457 } else {
1458 self.calc_cache
1459 .lock()
1460 .unwrap()
1461 .insert(entry, result.clone());
1462 }
1463 Ok(result)
1464 }
1465}
1466
1467fn format_calc_result(
1471 file: &File,
1472 token: &FormulaArg,
1473 style_idx: i32,
1474 raw: bool,
1475) -> Result<String> {
1476 if token.typ == ArgType::Number && !token.boolean {
1477 let mut c = XlsxC::default();
1478 c.s = Some(style_idx as i64);
1479 c.v = Some(format_number_for_calc(token.number));
1480 Ok(read_cell_value(file, &c, raw))
1481 } else {
1482 let mut c = XlsxC::default();
1483 c.t = Some("str".to_string());
1484 c.v = Some(token.value());
1485 Ok(read_cell_value(file, &c, raw))
1486 }
1487}
1488
1489fn format_number_for_calc(n: f64) -> String {
1494 if n.is_nan() || n.is_infinite() {
1495 return n.to_string();
1496 }
1497 if n.fract() == 0.0 && n >= i64::MIN as f64 && n <= i64::MAX as f64 {
1498 return format!("{}", n as i64);
1499 }
1500 let abs = n.abs();
1501 if abs >= 1e15 || (abs > 0.0 && abs < 1e-15) {
1502 format!("{:.14e}", n).to_uppercase()
1503 } else {
1504 format!("{}", n)
1505 }
1506}
1507
1508#[cfg(test)]
1513mod tests {
1514 use super::*;
1515
1516 fn new_file() -> File {
1517 File::new_with_options(crate::options::Options::default())
1518 }
1519
1520 #[test]
1521 fn parse_numeric_literal() {
1522 let expr = parse_formula("123.45").unwrap();
1523 assert!(matches!(expr, Expr::Number(n) if (n - 123.45).abs() < 1e-9));
1524 }
1525
1526 #[test]
1527 fn parse_string_literal() {
1528 let expr = parse_formula("\"hello\"").unwrap();
1529 assert!(matches!(expr, Expr::String(s) if s == "hello"));
1530 }
1531
1532 #[test]
1533 fn parse_boolean_literals() {
1534 let expr = parse_formula("TRUE").unwrap();
1535 assert!(matches!(expr, Expr::Bool(true)));
1536 let expr = parse_formula("FALSE").unwrap();
1537 assert!(matches!(expr, Expr::Bool(false)));
1538 }
1539
1540 #[test]
1541 fn parse_cell_reference() {
1542 let expr = parse_formula("$A$1").unwrap();
1543 match expr {
1544 Expr::Cell(r) => {
1545 assert_eq!(r.col, 1);
1546 assert_eq!(r.row, 1);
1547 assert!(r.col_abs);
1548 assert!(r.row_abs);
1549 }
1550 _ => panic!("expected cell reference"),
1551 }
1552 }
1553
1554 #[test]
1555 fn parse_sheet_qualified_reference() {
1556 let expr = parse_formula("Sheet1!B2").unwrap();
1557 match expr {
1558 Expr::Cell(r) => {
1559 assert_eq!(r.sheet.as_deref(), Some("Sheet1"));
1560 assert_eq!(r.col, 2);
1561 assert_eq!(r.row, 2);
1562 }
1563 _ => panic!("expected qualified reference"),
1564 }
1565 }
1566
1567 #[test]
1568 fn parse_range_reference() {
1569 let expr = parse_formula("A1:B3").unwrap();
1570 match expr {
1571 Expr::Range(start, end) => {
1572 assert_eq!(start.col, 1);
1573 assert_eq!(start.row, 1);
1574 assert_eq!(end.col, 2);
1575 assert_eq!(end.row, 3);
1576 }
1577 _ => panic!("expected range"),
1578 }
1579 }
1580
1581 #[test]
1582 fn parse_function_call() {
1583 let expr = parse_formula("SUM(A1:A3, 5)").unwrap();
1584 match expr {
1585 Expr::Call(name, args) => {
1586 assert_eq!(name, "SUM");
1587 assert_eq!(args.len(), 2);
1588 }
1589 _ => panic!("expected function call"),
1590 }
1591 }
1592
1593 #[test]
1594 fn parse_binary_operators() {
1595 let expr = parse_formula("1+2*3^2").unwrap();
1596 match expr {
1597 Expr::Binary(op, _, _) => assert_eq!(op, "+"),
1598 _ => panic!("expected binary expression"),
1599 }
1600 }
1601
1602 #[test]
1603 fn calc_sum_range() {
1604 let f = new_file();
1605 f.set_cell_int("Sheet1", "A1", 1).unwrap();
1606 f.set_cell_int("Sheet1", "A2", 2).unwrap();
1607 f.set_cell_int("Sheet1", "A3", 3).unwrap();
1608 f.set_cell_formula("Sheet1", "A4", "SUM(A1:A3)").unwrap();
1609 assert_eq!(f.calc_cell_value("Sheet1", "A4").unwrap(), "6");
1610 }
1611
1612 #[test]
1613 fn calc_average_and_count() {
1614 let f = new_file();
1615 f.set_cell_int("Sheet1", "B1", 10).unwrap();
1616 f.set_cell_int("Sheet1", "B2", 20).unwrap();
1617 f.set_cell_int("Sheet1", "B3", 30).unwrap();
1618 f.set_cell_formula("Sheet1", "B4", "AVERAGE(B1:B3)")
1619 .unwrap();
1620 f.set_cell_formula("Sheet1", "B5", "COUNT(B1:B3)").unwrap();
1621 assert_eq!(f.calc_cell_value("Sheet1", "B4").unwrap(), "20");
1622 assert_eq!(f.calc_cell_value("Sheet1", "B5").unwrap(), "3");
1623 }
1624
1625 #[test]
1626 fn calc_max_min() {
1627 let f = new_file();
1628 f.set_cell_int("Sheet1", "C1", 5).unwrap();
1629 f.set_cell_int("Sheet1", "C2", 9).unwrap();
1630 f.set_cell_int("Sheet1", "C3", 2).unwrap();
1631 f.set_cell_formula("Sheet1", "C4", "MAX(C1:C3)").unwrap();
1632 f.set_cell_formula("Sheet1", "C5", "MIN(C1:C3)").unwrap();
1633 assert_eq!(f.calc_cell_value("Sheet1", "C4").unwrap(), "9");
1634 assert_eq!(f.calc_cell_value("Sheet1", "C5").unwrap(), "2");
1635 }
1636
1637 #[test]
1638 fn calc_if() {
1639 let f = new_file();
1640 f.set_cell_int("Sheet1", "D1", 5).unwrap();
1641 f.set_cell_formula("Sheet1", "D2", "IF(D1>3,\"yes\",\"no\")")
1642 .unwrap();
1643 f.set_cell_formula("Sheet1", "D3", "IF(D1<3,\"yes\",\"no\")")
1644 .unwrap();
1645 assert_eq!(f.calc_cell_value("Sheet1", "D2").unwrap(), "yes");
1646 assert_eq!(f.calc_cell_value("Sheet1", "D3").unwrap(), "no");
1647 }
1648
1649 #[test]
1650 fn calc_vlookup_exact() {
1651 let f = new_file();
1652 f.set_cell_int("Sheet1", "A1", 1).unwrap();
1653 f.set_cell_str("Sheet1", "B1", "one").unwrap();
1654 f.set_cell_int("Sheet1", "A2", 2).unwrap();
1655 f.set_cell_str("Sheet1", "B2", "two").unwrap();
1656 f.set_cell_int("Sheet1", "A3", 3).unwrap();
1657 f.set_cell_str("Sheet1", "B3", "three").unwrap();
1658 f.set_cell_formula("Sheet1", "C1", "VLOOKUP(2,A1:B3,2,FALSE)")
1659 .unwrap();
1660 assert_eq!(f.calc_cell_value("Sheet1", "C1").unwrap(), "two");
1661 }
1662
1663 #[test]
1664 fn calc_concatenate_and_ampersand() {
1665 let f = new_file();
1666 f.set_cell_str("Sheet1", "E1", "Hello").unwrap();
1667 f.set_cell_str("Sheet1", "E2", "World").unwrap();
1668 f.set_cell_formula("Sheet1", "E3", "CONCATENATE(E1,\" \",E2)")
1669 .unwrap();
1670 f.set_cell_formula("Sheet1", "E4", "E1&\" \"&E2").unwrap();
1671 assert_eq!(f.calc_cell_value("Sheet1", "E3").unwrap(), "Hello World");
1672 assert_eq!(f.calc_cell_value("Sheet1", "E4").unwrap(), "Hello World");
1673 }
1674
1675 #[test]
1676 fn calc_abs_and_round() {
1677 let f = new_file();
1678 f.set_cell_formula("Sheet1", "F1", "ABS(-12.5)").unwrap();
1679 f.set_cell_formula("Sheet1", "F2", "ROUND(3.14159,2)")
1680 .unwrap();
1681 assert_eq!(f.calc_cell_value("Sheet1", "F1").unwrap(), "12.5");
1682 assert_eq!(f.calc_cell_value("Sheet1", "F2").unwrap(), "3.14");
1683 }
1684
1685 #[test]
1686 fn calc_cross_sheet_reference() {
1687 let f = new_file();
1688 f.set_cell_int("Sheet1", "G1", 42).unwrap();
1689 f.set_cell_formula("Sheet1", "G2", "Sheet1!G1+8").unwrap();
1690 assert_eq!(f.calc_cell_value("Sheet1", "G2").unwrap(), "50");
1691 }
1692
1693 #[test]
1694 fn calc_today_and_now_are_serials() {
1695 let f = new_file();
1696 f.set_cell_formula("Sheet1", "H1", "TODAY()").unwrap();
1697 f.set_cell_formula("Sheet1", "H2", "NOW()").unwrap();
1698 let today = f.calc_cell_value("Sheet1", "H1").unwrap();
1699 assert!(today.parse::<f64>().is_ok(), "TODAY() returned {:?}", today);
1700 assert!(today.parse::<f64>().unwrap() > 0.0);
1701
1702 let now = f.calc_cell_value("Sheet1", "H2").unwrap();
1703 assert!(now.parse::<f64>().is_ok(), "NOW() returned {:?}", now);
1704 assert!(now.parse::<f64>().unwrap() > 0.0);
1705 }
1706
1707 #[test]
1708 fn calc_unsupported_function_returns_name_error() {
1709 let f = new_file();
1710 f.set_cell_formula("Sheet1", "I1", "NOTSUPPORTED(1)")
1711 .unwrap();
1712 assert_eq!(f.calc_cell_value("Sheet1", "I1").unwrap(), "#NAME?");
1713 }
1714
1715 #[test]
1716 fn calc_non_formula_cell_falls_back() {
1717 let f = new_file();
1718 f.set_cell_str("Sheet1", "J1", "plain").unwrap();
1719 assert_eq!(f.calc_cell_value("Sheet1", "J1").unwrap(), "plain");
1720 }
1721
1722 #[test]
1723 fn calc_indirect() {
1724 let f = new_file();
1725 f.set_cell_int("Sheet1", "A1", 42).unwrap();
1726 f.set_cell_formula("Sheet1", "B1", "INDIRECT(\"A1\")")
1727 .unwrap();
1728 assert_eq!(f.calc_cell_value("Sheet1", "B1").unwrap(), "42");
1729
1730 f.set_cell_formula("Sheet1", "C1", "INDIRECT(\"R1C1\",FALSE)")
1731 .unwrap();
1732 assert_eq!(f.calc_cell_value("Sheet1", "C1").unwrap(), "42");
1733 }
1734
1735 #[test]
1736 fn calc_formulatext_and_isref() {
1737 let f = new_file();
1738 f.set_cell_formula("Sheet1", "A1", "1+2").unwrap();
1739 f.set_cell_formula("Sheet1", "B1", "FORMULATEXT(A1)")
1740 .unwrap();
1741 assert_eq!(f.calc_cell_value("Sheet1", "B1").unwrap(), "=1+2");
1742
1743 f.set_cell_formula("Sheet1", "C1", "ISREF(A1)").unwrap();
1744 f.set_cell_formula("Sheet1", "D1", "ISREF(\"A1\")").unwrap();
1745 assert_eq!(f.calc_cell_value("Sheet1", "C1").unwrap(), "TRUE");
1746 assert_eq!(f.calc_cell_value("Sheet1", "D1").unwrap(), "FALSE");
1747 }
1748
1749 #[test]
1750 fn calc_column_row_sheet_sheets() {
1751 let f = new_file();
1752 f.new_sheet("Sheet2").unwrap();
1753 f.set_cell_formula("Sheet1", "A1", "COLUMN(B3)").unwrap();
1754 f.set_cell_formula("Sheet1", "A2", "ROW(B3)").unwrap();
1755 f.set_cell_formula("Sheet1", "A3", "SHEETS()").unwrap();
1756 f.set_cell_formula("Sheet1", "A4", "SHEET(Sheet1!A1)")
1757 .unwrap();
1758 f.set_cell_formula("Sheet1", "A5", "SHEET(Sheet2!A1)")
1759 .unwrap();
1760 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "2");
1761 assert_eq!(f.calc_cell_value("Sheet1", "A2").unwrap(), "3");
1762 assert_eq!(f.calc_cell_value("Sheet1", "A3").unwrap(), "2");
1763 let sheet1: i32 = f.calc_cell_value("Sheet1", "A4").unwrap().parse().unwrap();
1764 let sheet2: i32 = f.calc_cell_value("Sheet1", "A5").unwrap().parse().unwrap();
1765 assert!(sheet1 < sheet2);
1766 }
1767
1768 #[test]
1769 fn calc_norm_dist_and_sortby() {
1770 let f = new_file();
1771 f.set_cell_formula("Sheet1", "A1", "NORM.DIST(0,0,1,FALSE)")
1772 .unwrap();
1773 let v: f64 = f.calc_cell_value("Sheet1", "A1").unwrap().parse().unwrap();
1774 assert!((v - 0.398_942_280_4).abs() < 1e-6);
1775
1776 f.set_cell_int("Sheet1", "C1", 3).unwrap();
1777 f.set_cell_int("Sheet1", "C2", 1).unwrap();
1778 f.set_cell_int("Sheet1", "C3", 2).unwrap();
1779 f.set_cell_int("Sheet1", "D1", 30).unwrap();
1780 f.set_cell_int("Sheet1", "D2", 10).unwrap();
1781 f.set_cell_int("Sheet1", "D3", 20).unwrap();
1782 f.set_cell_formula("Sheet1", "E1", "SORTBY(C1:C3,D1:D3)")
1783 .unwrap();
1784 assert_eq!(f.calc_cell_value("Sheet1", "E1").unwrap(), "1");
1785 }
1786
1787 #[test]
1788 fn calc_array_literal() {
1789 let f = new_file();
1790 f.set_cell_formula("Sheet1", "A1", "SUM({1,2;3,4})")
1791 .unwrap();
1792 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "10");
1793 }
1794
1795 #[test]
1796 fn calc_3d_reference() {
1797 let f = new_file();
1798 f.new_sheet("Sheet2").unwrap();
1799 f.new_sheet("Sheet3").unwrap();
1800 f.set_cell_int("Sheet1", "A1", 1).unwrap();
1801 f.set_cell_int("Sheet2", "A1", 10).unwrap();
1802 f.set_cell_int("Sheet3", "A1", 100).unwrap();
1803 f.set_cell_formula("Sheet1", "B1", "SUM(Sheet1:Sheet3!A1)")
1804 .unwrap();
1805 assert_eq!(f.calc_cell_value("Sheet1", "B1").unwrap(), "111");
1806 }
1807
1808 #[test]
1809 fn calc_defined_name() {
1810 let f = new_file();
1811 f.set_cell_int("Sheet1", "A1", 5).unwrap();
1812 f.set_cell_int("Sheet1", "A2", 7).unwrap();
1813 f.set_defined_name(&crate::xml::workbook::DefinedName {
1814 name: "MyRange".to_string(),
1815 refers_to: "A1:A2".to_string(),
1816 ..Default::default()
1817 })
1818 .unwrap();
1819 f.set_cell_formula("Sheet1", "B1", "SUM(MyRange)").unwrap();
1820 assert_eq!(f.calc_cell_value("Sheet1", "B1").unwrap(), "12");
1821 }
1822
1823 #[test]
1824 fn calc_iterative_circular() {
1825 let mut opts = crate::options::Options::default();
1826 opts.max_calc_iterations = 5;
1827 let f = File::new_with_options(opts);
1828 f.set_cell_formula("Sheet1", "A1", "A2+1").unwrap();
1829 f.set_cell_formula("Sheet1", "A2", "A1+1").unwrap();
1830 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "2");
1831 }
1832
1833 #[test]
1834 fn calc_raw_cell_value_cache() {
1835 let mut opts = crate::options::Options::default();
1836 opts.raw_cell_value = true;
1837 let f = File::new_with_options(opts);
1838 f.set_cell_formula("Sheet1", "A1", "1+1").unwrap();
1839 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "2");
1840 f.set_cell_int("Sheet1", "A1", 99).unwrap();
1843 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "2");
1844 f.clear_calc_cache();
1845 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "99");
1846 }
1847
1848 #[test]
1849 fn calc_iferror_arity() {
1850 let f = new_file();
1851 f.set_cell_formula("Sheet1", "A1", "IFERROR(1,2,3)")
1852 .unwrap();
1853 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "#VALUE!");
1854 }
1855
1856 #[test]
1857 fn calc_sumif_text_numbers() {
1858 let f = new_file();
1859 f.set_cell_str("Sheet1", "A1", "1").unwrap();
1860 f.set_cell_str("Sheet1", "A2", "2").unwrap();
1861 f.set_cell_int("Sheet1", "A3", 3).unwrap();
1862 f.set_cell_formula("Sheet1", "A4", "SUMIF(A1:A3,\">0\")")
1863 .unwrap();
1864 assert_eq!(f.calc_cell_value("Sheet1", "A4").unwrap(), "6");
1865 }
1866
1867 #[test]
1868 fn calc_text_formats() {
1869 let f = new_file();
1870
1871 f.set_cell_formula("Sheet1", "A1", "TEXT(1234.5,\"#,##0.00\")")
1873 .unwrap();
1874 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "1,234.50");
1875
1876 f.set_cell_formula("Sheet1", "A2", "TEXT(1234.5,\"0.00_);(0.00)\")")
1878 .unwrap();
1879 assert_eq!(f.calc_cell_value("Sheet1", "A2").unwrap(), "1234.50 ");
1880
1881 f.set_cell_formula("Sheet1", "A3", "TEXT(1234.5,\"0.00;[Red](0.00);zero\")")
1883 .unwrap();
1884 assert_eq!(f.calc_cell_value("Sheet1", "A3").unwrap(), "1234.50");
1885
1886 f.set_cell_formula("Sheet1", "A4", "TEXT(0,\"0.00;[Red](0.00);zero\")")
1888 .unwrap();
1889 assert_eq!(f.calc_cell_value("Sheet1", "A4").unwrap(), "zero");
1890
1891 f.set_cell_formula("Sheet1", "A5", "TEXT(1234,\"0000\")")
1893 .unwrap();
1894 assert_eq!(f.calc_cell_value("Sheet1", "A5").unwrap(), "1234");
1895
1896 f.set_cell_formula("Sheet1", "A6", "TEXT(\"abc\",\"0.00;[Red](0.00);zero;@\")")
1898 .unwrap();
1899 assert_eq!(f.calc_cell_value("Sheet1", "A6").unwrap(), "abc");
1900
1901 f.set_cell_formula("Sheet1", "A7", "TEXT(\"07/07/2015\",\"mm/dd/yyyy\")")
1903 .unwrap();
1904 assert_eq!(f.calc_cell_value("Sheet1", "A7").unwrap(), "07/07/2015");
1905 f.set_cell_formula("Sheet1", "A8", "TEXT(42192,\"mm/dd/yyyy\")")
1906 .unwrap();
1907 assert_eq!(f.calc_cell_value("Sheet1", "A8").unwrap(), "07/07/2015");
1908 f.set_cell_formula("Sheet1", "A9", "TEXT(42192,\"mmm dd yyyy\")")
1909 .unwrap();
1910 assert_eq!(f.calc_cell_value("Sheet1", "A9").unwrap(), "Jul 07 2015");
1911 f.set_cell_formula("Sheet1", "A10", "TEXT(0.75,\"hh:mm\")")
1912 .unwrap();
1913 assert_eq!(f.calc_cell_value("Sheet1", "A10").unwrap(), "18:00");
1914 f.set_cell_formula("Sheet1", "A11", "TEXT(36.363636,\"0.00\")")
1915 .unwrap();
1916 assert_eq!(f.calc_cell_value("Sheet1", "A11").unwrap(), "36.36");
1917 f.set_cell_formula("Sheet1", "A12", "TEXT(567.9,\"$#,##0.00\")")
1918 .unwrap();
1919 assert_eq!(f.calc_cell_value("Sheet1", "A12").unwrap(), "$567.90");
1920 f.set_cell_formula(
1921 "Sheet1",
1922 "A13",
1923 "TEXT(-5,\"+ $#,##0.00;- $#,##0.00;$0.00\")",
1924 )
1925 .unwrap();
1926 assert_eq!(f.calc_cell_value("Sheet1", "A13").unwrap(), "- $5.00");
1927 f.set_cell_formula("Sheet1", "A14", "TEXT(5,\"+ $#,##0.00;- $#,##0.00;$0.00\")")
1928 .unwrap();
1929 assert_eq!(f.calc_cell_value("Sheet1", "A14").unwrap(), "+ $5.00");
1930 }
1931
1932 #[test]
1933 fn calc_text_conditional_formats() {
1934 let f = new_file();
1935
1936 f.set_cell_formula("Sheet1", "A1", "TEXT(150,\"[>100]0.00;0.0\")")
1938 .unwrap();
1939 assert_eq!(f.calc_cell_value("Sheet1", "A1").unwrap(), "150.00");
1940
1941 f.set_cell_formula("Sheet1", "A2", "TEXT(50,\"[>100]0.00;0.0\")")
1943 .unwrap();
1944 assert_eq!(f.calc_cell_value("Sheet1", "A2").unwrap(), "50.0");
1945
1946 f.set_cell_formula(
1948 "Sheet1",
1949 "A3",
1950 "TEXT(85,\"[>=90]\"\"A\"\";[>=60]\"\"B\"\";\"\"C\"\"\")",
1951 )
1952 .unwrap();
1953 assert_eq!(f.calc_cell_value("Sheet1", "A3").unwrap(), "B");
1954
1955 f.set_cell_formula("Sheet1", "A4", "TEXT(-10,\"[>0]0.00;[Red](0.00)\")")
1957 .unwrap();
1958 assert_eq!(f.calc_cell_value("Sheet1", "A4").unwrap(), "(10.00)");
1959 }
1960}