#![allow(clippy::panic)] use std::collections::HashMap; use crate::expressions::lexer::LexerMode; use crate::expressions::parser::stringify::{ to_rc_format, to_string, to_string_displaced, DisplaceData, }; use crate::expressions::parser::{Node, Parser}; use crate::expressions::types::CellReferenceRC; struct Formula<'a> { initial: &'a str, expected: &'a str, } #[test] fn test_parser_reference() { let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("A2", &cell_reference); assert_eq!(to_rc_format(&t), "R[1]C[0]"); } #[test] fn test_parser_absolute_column() { let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("$A1", &cell_reference); assert_eq!(to_rc_format(&t), "R[0]C1"); } #[test] fn test_parser_absolute_row_col() { let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("$C$5", &cell_reference); assert_eq!(to_rc_format(&t), "R5C3"); } #[test] fn test_parser_absolute_row_col_1() { let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("$A$1", &cell_reference); assert_eq!(to_rc_format(&t), "R1C1"); } #[test] fn test_parser_simple_formula() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("C3+Sheet2!D4", &cell_reference); assert_eq!(to_rc_format(&t), "R[2]C[2]+Sheet2!R[3]C[3]"); } #[test] fn test_parser_boolean() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("true", &cell_reference); assert_eq!(to_rc_format(&t), "TRUE"); } #[test] fn test_parser_bad_formula() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("#Value", &cell_reference); match &t { Node::ParseErrorKind { formula, message, position, } => { assert_eq!(formula, "#Value"); assert_eq!(message, "Invalid error."); assert_eq!(*position, 1); } _ => { panic!("Expected error in formula"); } } assert_eq!(to_rc_format(&t), "#Value"); } #[test] fn test_parser_bad_formula_1() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("<5", &cell_reference); match &t { Node::ParseErrorKind { formula, message, position, } => { assert_eq!(formula, "<5"); assert_eq!(message, "Unexpected token: 'COMPARE'"); assert_eq!(*position, 0); } _ => { panic!("Expected error in formula"); } } assert_eq!(to_rc_format(&t), "<5"); } #[test] fn test_parser_bad_formula_2() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("*5", &cell_reference); match &t { Node::ParseErrorKind { formula, message, position, } => { assert_eq!(formula, "*5"); assert_eq!(message, "Unexpected token: 'PRODUCT'"); assert_eq!(*position, 0); } _ => { panic!("Expected error in formula"); } } assert_eq!(to_rc_format(&t), "*5"); } #[test] fn test_parser_bad_formula_3() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("SUM(#VALVE!)", &cell_reference); match &t { Node::ParseErrorKind { formula, message, position, } => { assert_eq!(formula, "SUM(#VALVE!)"); assert_eq!(message, "Invalid error."); assert_eq!(*position, 5); } _ => { panic!("Expected error in formula"); } } assert_eq!(to_rc_format(&t), "SUM(#VALVE!)"); } #[test] fn test_parser_formulas() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); let formulas = vec![ Formula { initial: "IF(C3:D4>2,B5,SUM(D1:D7))", expected: "IF(R[2]C[2]:R[3]C[3]>2,R[4]C[1],SUM(R[0]C[3]:R[6]C[3]))", }, Formula { initial: "-A1", expected: "-R[0]C[0]", }, Formula { initial: "#VALUE!", expected: "#VALUE!", }, Formula { initial: "SUM(C3:D4)", expected: "SUM(R[2]C[2]:R[3]C[3])", }, Formula { initial: "A1/(B1-C1)", expected: "R[0]C[0]/(R[0]C[1]-R[0]C[2])", }, ]; // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; for formula in formulas { let t = parser.parse( formula.initial, &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }, ); assert_eq!(to_rc_format(&t), formula.expected); assert_eq!(to_string(&t, &cell_reference), formula.initial); } } #[test] fn test_parser_r1c1_formulas() { let worksheets = vec!["Sheet1".to_string(), "Sheet2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); parser.set_lexer_mode(LexerMode::R1C1); let formulas = vec![ Formula { initial: "IF(R[2]C[2]:R[3]C[3]>2,R[4]C[1],SUM(R[0]C[3]:R[6]C[3]))", expected: "IF(E5:F6>2,D7,SUM(F3:F9))", }, Formula { initial: "-R[0]C[0]", expected: "-C3", }, Formula { initial: "R[1]C[-1]+1", expected: "B4+1", }, Formula { initial: "#VALUE!", expected: "#VALUE!", }, Formula { initial: "SUM(R[2]C[2]:R[3]C[3])", expected: "SUM(E5:F6)", }, Formula { initial: "R[-3]C[0]", expected: "#REF!", }, Formula { initial: "R[0]C[-3]", expected: "#REF!", }, Formula { initial: "R[-2]C[-2]", expected: "A1", }, Formula { initial: "SIN(R[-3]C[-3])", expected: "SIN(#REF!)", }, ]; // Reference cell is Sheet1!C3 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 3, column: 3, }; for formula in formulas { let t = parser.parse( formula.initial, &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }, ); assert_eq!(to_string(&t, &cell_reference), formula.expected); assert_eq!(to_rc_format(&t), formula.initial); } } #[test] fn test_parser_quotes() { let worksheets = vec!["Sheet1".to_string(), "Second Sheet".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("C3+'Second Sheet'!D4", &cell_reference); assert_eq!(to_rc_format(&t), "R[2]C[2]+'Second Sheet'!R[3]C[3]"); } #[test] fn test_parser_escape_quotes() { let worksheets = vec!["Sheet1".to_string(), "Second '2' Sheet".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("C3+'Second ''2'' Sheet'!D4", &cell_reference); assert_eq!(to_rc_format(&t), "R[2]C[2]+'Second ''2'' Sheet'!R[3]C[3]"); } #[test] fn test_parser_parenthesis() { let worksheets = vec!["Sheet1".to_string(), "Second2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("(C3=\"Yes\")*5", &cell_reference); assert_eq!(to_rc_format(&t), "(R[2]C[2]=\"Yes\")*5"); } #[test] fn test_parser_excel_xlfn() { let worksheets = vec!["Sheet1".to_string(), "Second2".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); // Reference cell is Sheet1!A1 let cell_reference = CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let t = parser.parse("_xlfn.CONCAT(C3)", &cell_reference); assert_eq!(to_rc_format(&t), "CONCAT(R[2]C[2])"); } #[test] fn test_to_string_displaced() { let context = &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); let node = parser.parse("C3", context); let displace_data = DisplaceData::Column { sheet: 0, column: 1, delta: 4, }; let t = to_string_displaced(&node, context, &displace_data); assert_eq!(t, "G3".to_string()); } #[test] fn test_to_string_displaced_full_ranges() { let context = &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); let node = parser.parse("SUM(3:3)", context); let displace_data = DisplaceData::Column { sheet: 0, column: 1, delta: 4, }; assert_eq!( to_string_displaced(&node, context, &displace_data), "SUM(3:3)".to_string() ); let node = parser.parse("SUM(D:D)", context); let displace_data = DisplaceData::Row { sheet: 0, row: 3, delta: 4, }; assert_eq!( to_string_displaced(&node, context, &displace_data), "SUM(D:D)".to_string() ); } #[test] fn test_to_string_displaced_too_low() { let context = &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); let node = parser.parse("C3", context); let displace_data = DisplaceData::Column { sheet: 0, column: 1, delta: -40, }; let t = to_string_displaced(&node, context, &displace_data); assert_eq!(t, "#REF!".to_string()); } #[test] fn test_to_string_displaced_too_high() { let context = &CellReferenceRC { sheet: "Sheet1".to_string(), row: 1, column: 1, }; let worksheets = vec!["Sheet1".to_string()]; let mut parser = Parser::new(worksheets, vec![], HashMap::new()); let node = parser.parse("C3", context); let displace_data = DisplaceData::Column { sheet: 0, column: 1, delta: 4000000, }; let t = to_string_displaced(&node, context, &displace_data); assert_eq!(t, "#REF!".to_string()); }