From 8adaca6a3ba53b0477bafd64cb749d6442979ae5 Mon Sep 17 00:00:00 2001 From: Zohar Zilberman Date: Sun, 10 Aug 2025 19:42:48 +0300 Subject: [PATCH] Move f32/f64 to be a type parameter of Value --- .github/workflows/pr.yaml | 9 - Cargo.lock | 3 +- Cargo.toml | 8 +- README.md | 223 ++-- src/calculate.rs | 672 ++++++++---- src/lib.rs | 301 +----- src/main.rs | 182 ++-- src/parse_formula.rs | 185 +++- src/types.rs | 46 +- tests/test.rs | 2135 ++++++++++++++++++++++--------------- 10 files changed, 2097 insertions(+), 1667 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index f8ae296..05ab7ab 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -9,10 +9,6 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - features: - - default - - f32 - - f64 phase: - debug - release @@ -30,11 +26,6 @@ jobs: run: | flags="" - if [ "${{ matrix.features}}" != "default" ] - then - flags="${flags} --no-default-features --features=${{ matrix.features }}" - fi - if [ "${{ matrix.phase }}" = "release" ] then flags="${flags} --release" diff --git a/Cargo.lock b/Cargo.lock index 068012c..c548f4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,10 +228,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "xlformula_engine" -version = "0.3.1" +version = "0.4.0" dependencies = [ "assert_approx_eq", "chrono", + "num-traits", "pest", "pest_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 099e5a9..cb60cc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,11 @@ license = "MIT OR Apache-2.0" name = "xlformula_engine" readme = "README.md" repository = "https://github.com/jiradaherbst/XLFormula-Engine" -version = "0.3.1" - -[features] -default = ["f64"] -f32 = [] -f64 = [] +version = "0.4.0" [dependencies] chrono = { version = "0.4.41", default-features = false } +num-traits = "0.2.19" pest = "2.8.1" pest_derive = "2.8.1" diff --git a/README.md b/README.md index 6bcfcdd..40176ee 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ XLFormula Engine is a Rust crate for parsing and evaluating Excel formulas. -It works with f64 by default (the `f64` feature) or with `f32` by enabling the `f32` feature and disabling default features. +Functions and structs get either `::` or `::` to indicate the number type used in the formulas. ## Features @@ -27,14 +27,7 @@ Add the corresponding entry to your Cargo.toml dependency list: ```toml [dependencies] -xlformula_engine = "0.3.0" -``` - -Or use the following for using `f32`: - -```toml -[dependencies] -xlformula_engine = { version = "0.3.0", default-features = false, features = ["f32"] } +xlformula_engine = "0.4.0" ``` ## Examples @@ -42,25 +35,22 @@ xlformula_engine = { version = "0.3.0", default-features = false, features = ["f Here are simple examples of parsing an Excel formula string and evaluating to a result: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"=1+2", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula::(&"=1+2", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=(1*(2+3))*2", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula::(&"=(1*(2+3))*2", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=1+3/0", None::); // error (#DIV/0!) -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula::(&"=1+3/0", None::>); // error (#DIV/0!) +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` The last string is evaluated to #DIV/0!. @@ -68,21 +58,18 @@ The last string is evaluated to #DIV/0!. Concatenating strings: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"=\"Hello \" & \" World!\"", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=\"Hello \" & \" World!\"", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=1 + \"Hello\"", None::); // error (#CAST!) -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=1 + \"Hello\"", None::>); // error (#CAST!) +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` Concatenating number and string results in a #CAST! error. @@ -90,137 +77,121 @@ Concatenating number and string results in a #CAST! error. Constants ( i.e. a string without '=' ): ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"1.2", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"1.2", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"Hello World", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"Hello World", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` Excel functions: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"=ABS(-1)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=ABS(-1)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=SUM(1,2,\"3\")", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=SUM(1,2,\"3\")", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=PRODUCT(ABS(1),2*1, 3,4*1)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=PRODUCT(ABS(1),2*1, 3,4*1)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=RIGHT(\"apple\", 3)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=RIGHT(\"apple\", 3)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\", 3)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\", 3)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\")", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\")", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=IF(TRUE,1,0)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=IF(TRUE,1,0)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` Logical expressions: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"=2>=1", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=2>=1", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=OR(1>1,1<>1)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=OR(1>1,1<>1)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=AND(\"test\",\"True\", 1, true) ", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=AND(\"test\",\"True\", 1, true) ", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` References: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::types; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { let data_function = |s: String| match s.as_str() { -"A" => types::Value::Text("=1+B".to_string()), -"B" => types::Value::Number(3.0), -_ => types::Value::Error(types::Error::Value), + "A" => types::Value::Text("=1+B".to_string()), + "B" => types::Value::Number(3.0), + _ => types::Value::Error(types::Error::Value), }; -let formula = parse_formula::parse_string_to_formula(&"=A+B", None::); +let formula = parse_formula::parse_string_to_formula(&"=A+B", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); -} ``` List: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() { -let formula = parse_formula::parse_string_to_formula(&"={1,2,3}+{1,2,3}", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"={1,2,3}+{1,2,3}", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=XOR({0,0,0})", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=XOR({0,0,0})", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=AVERAGE({1,2,3},1,2,3)", None::); -let result = calculate::calculate_formula(formula, None::); +let formula = parse_formula::parse_string_to_formula(&"=AVERAGE({1,2,3},1,2,3)", None::>); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` Date: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::types; @@ -230,69 +201,61 @@ use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() -> Result<(), ParseError> { -let start: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z")?; -let end: DateTime = DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z")?; +let start: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z").unwrap(); +let end: DateTime = DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z").unwrap(); let data_function = |s: String| match s.as_str() { -"start" => types::Value::Date(start), -"end" => types::Value::Date(end), -_ => types::Value::Error(types::Error::Value), + "start" => types::Value::Date(start), + "end" => types::Value::Date(end), + _ => types::Value::Error(types::Error::Value), }; -let formula = parse_formula::parse_string_to_formula(&"=DAYS(end, start)", None::); +let formula = parse_formula::parse_string_to_formula(&"=DAYS(end, start)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=start+1", None::); +let formula = parse_formula::parse_string_to_formula(&"=start+1", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); -let formula = parse_formula::parse_string_to_formula(&"=end-3", None::); +let formula = parse_formula::parse_string_to_formula(&"=end-3", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); -Ok(()) -} ``` Custom Function: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::types; -use xlformula_engine::types::XlNum; use xlformula_engine::NoReference; -fn main() { -let custom_functions = |s: String, params: Vec| match s.as_str() { -"Increase" => types::Value::Number(params[0] + 1.0), -"SimpleSum" => types::Value::Number(params[0] + params[1]), -"EqualFive" => types::Value::Number(5.0), -_ => types::Value::Error(types::Error::Value), +let custom_functions = |s: String, params: Vec| match s.as_str() { + "Increase" => types::Value::Number(params[0] + 1.0), + "SimpleSum" => types::Value::Number(params[0] + params[1]), + "EqualFive" => types::Value::Number(5.0), + _ => types::Value::Error(types::Error::Value), }; let formula = parse_formula::parse_string_to_formula(&"=Increase(1)+1", Some(&custom_functions)); -let result = calculate::calculate_formula(formula, None::); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula(&"=EqualFive()+1", Some(&custom_functions)); -let result = calculate::calculate_formula(formula, None::); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula(&"=SimpleSum(1,2)", Some(&custom_functions)); -let result = calculate::calculate_formula(formula, None::); +let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); -} ``` Handle blank in calculation: ```rust -extern crate xlformula_engine; use xlformula_engine::calculate; use xlformula_engine::parse_formula; use xlformula_engine::types; @@ -301,38 +264,36 @@ use chrono::{DateTime, FixedOffset}; use xlformula_engine::NoReference; use xlformula_engine::NoCustomFunction; -fn main() -> { - let data_function = |s: String| match s.as_str() { - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - - let custom_functions = |s: String, params: Vec| match s.as_str() { - "BLANK" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - - let formula = parse_formula::parse_string_to_formula(&"=SUM(B, 1)", None::); - let result = calculate::calculate_formula(formula, Some(&data_function)); - println!("Result is {}", calculate::result_to_string(result)); - - let formula = - parse_formula::parse_string_to_formula(&"=SUM(BLANK(), 1)", Some(&custom_functions)); - let result = calculate::calculate_formula(formula, None::); - println!("Result is {}", calculate::result_to_string(result)); - - //takes list as input - let formula = parse_formula::parse_string_to_formula(&"=SUM({B, 1})", None::); - let result = calculate::calculate_formula(formula, Some(&data_function)); - println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula(&"=XOR({F,B,T,B,F,{F,B,T,B,F}})", None::); - let result = calculate::calculate_formula(formula, Some(&data_function)); - println!("Result is {}", calculate::result_to_string(result)); - - let formula = parse_formula::parse_string_to_formula(&"=SUM(1, 2, , 3)", None::); - let result = calculate::calculate_formula(formula, None::); - println!("Result is {}", calculate::result_to_string(result)); -} +let data_function = |s: String| match s.as_str() { + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), +}; + +let custom_functions = |s: String, params: Vec| match s.as_str() { + "BLANK" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), +}; + +let formula = parse_formula::parse_string_to_formula(&"=SUM(B, 1)", None::>); +let result = calculate::calculate_formula(formula, Some(&data_function)); +println!("Result is {}", calculate::result_to_string(result)); + +let formula = + parse_formula::parse_string_to_formula(&"=SUM(BLANK(), 1)", Some(&custom_functions)); +let result = calculate::calculate_formula(formula, None::>); +println!("Result is {}", calculate::result_to_string(result)); + +//takes list as input +let formula = parse_formula::parse_string_to_formula(&"=SUM({B, 1})", None::>); +let result = calculate::calculate_formula(formula, Some(&data_function)); +println!("Result is {}", calculate::result_to_string(result)); +let formula = parse_formula::parse_string_to_formula(&"=XOR({F,B,T,B,F,{F,B,T,B,F}})", None::>); +let result = calculate::calculate_formula(formula, Some(&data_function)); +println!("Result is {}", calculate::result_to_string(result)); + +let formula = parse_formula::parse_string_to_formula(&"=SUM(1, 2, , 3)", None::>); +let result = calculate::calculate_formula(formula, None::>); +println!("Result is {}", calculate::result_to_string(result)); ``` ## License diff --git a/src/calculate.rs b/src/calculate.rs index e5c659e..abf300d 100644 --- a/src/calculate.rs +++ b/src/calculate.rs @@ -3,21 +3,33 @@ use crate::{ types::{self, XlNum}, }; use chrono::{DateTime, Duration, FixedOffset}; +use std::{fmt::Debug, str::FromStr}; -type NoCustomFunction<'a> = &'a fn(String, Vec) -> types::Value; +type NoCustomFunction<'a, N> = &'a fn(String, Vec) -> types::Value; -fn calculate_divide_operator(num1: XlNum, num2: XlNum) -> XlNum { +fn calculate_divide_operator(num1: N, num2: N) -> N +where + N: XlNum, + ::Err: Debug, +{ num1 / num2 } -fn is_float_int(num: XlNum) -> bool { - //((num as i32) as XlNum) == num - (((num as i32) as XlNum) - num).abs() == 0.0 +fn is_float_int(num: N) -> bool +where + N: XlNum, + ::Err: Debug, +{ + num.fract().is_zero() } -fn calculate_power_operator(num1: XlNum, num2: XlNum) -> XlNum { +fn calculate_power_operator(num1: N, num2: N) -> N +where + N: XlNum, + ::Err: Debug, +{ if is_float_int(num2) { - num1.powi(num2 as i32) + num1.powi(num2.as_()) } else { num1.powf(num2) } @@ -27,11 +39,15 @@ fn calculate_concat_operator(str1: &str, str2: &str) -> String { str1.to_owned() + str2 } -fn calculate_string_operation_rhs( +fn calculate_string_operation_rhs( l: &str, - rhs: types::Value, + rhs: types::Value, f: fn(str1: &str, str2: &str) -> String, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Boolean(_) => rhs, types::Value::Error(_) => rhs, @@ -43,11 +59,15 @@ fn calculate_string_operation_rhs( } } -fn calculate_string_operator( - lhs: types::Value, - rhs: types::Value, +fn calculate_string_operator( + lhs: types::Value, + rhs: types::Value, f: fn(str1: &str, str2: &str) -> String, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match lhs { types::Value::Boolean(_) => lhs, types::Value::Error(_) => lhs, @@ -59,16 +79,20 @@ fn calculate_string_operator( } } -fn calcualte_numeric_operator_rhs_text( +fn calcualte_numeric_operator_rhs_text( t: String, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { - match t.parse::() { + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ + match t.parse::() { Ok(nl) => match rhs { types::Value::Boolean(_) => rhs, types::Value::Error(_) => rhs, - types::Value::Text(t) => match t.parse::() { + types::Value::Text(t) => match t.parse::() { Ok(nr) => types::Value::Number(f(nl, nr)), Err(_) => types::Value::Error(types::Error::Cast), }, @@ -81,16 +105,20 @@ fn calcualte_numeric_operator_rhs_text( } } -fn calculate_numeric_operator_rhs_number( - l: XlNum, - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_numeric_operator_rhs_number( + l: N, + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Boolean(_) => rhs, types::Value::Error(_) => rhs, - types::Value::Text(t) => match t.parse::() { + types::Value::Text(t) => match t.parse::() { Ok(nr) => types::Value::Number(f(l, nr)), Err(_) => types::Value::Error(types::Error::Cast), }, @@ -106,20 +134,24 @@ fn calculate_numeric_operator_rhs_number( } } types::Value::Date(_) => types::Value::Error(types::Error::Value), - types::Value::Blank => types::Value::Number(f(l, 0.0)), + types::Value::Blank => types::Value::Number(f(l, N::zero())), } } -fn calculate_numeric_operator_product_rhs_number( - l: XlNum, - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_numeric_operator_product_rhs_number( + l: N, + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Boolean(_) => rhs, types::Value::Error(_) => rhs, - types::Value::Text(t) => match t.parse::() { + types::Value::Text(t) => match t.parse::() { Ok(nr) => types::Value::Number(f(l, nr)), Err(_) => types::Value::Error(types::Error::Cast), }, @@ -142,11 +174,15 @@ fn calculate_numeric_operator_product_rhs_number( } } -fn calculate_numeric_operator_rhs_iterator( - mut lhs_vec: Vec, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_numeric_operator_rhs_iterator( + mut lhs_vec: Vec>, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Number(_) => { if let Some(mut temp) = lhs_vec.pop() { @@ -176,25 +212,37 @@ fn calculate_numeric_operator_rhs_iterator( } } -fn add_days_to_date(d: DateTime, rhs: types::Value) -> types::Value { +fn add_days_to_date(d: DateTime, rhs: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { - types::Value::Number(x) => types::Value::Date(d + Duration::days(x as i64)), + types::Value::Number(x) => types::Value::Date(d + Duration::days(x.as_())), _ => types::Value::Error(types::Error::Value), } } -fn subtract_days_from_date(d: DateTime, rhs: types::Value) -> types::Value { +fn subtract_days_from_date(d: DateTime, rhs: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { - types::Value::Number(x) => types::Value::Date(d - Duration::days(x as i64)), + types::Value::Number(x) => types::Value::Date(d - Duration::days(x.as_())), _ => types::Value::Error(types::Error::Value), } } -fn calculate_numeric_operator( - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_numeric_operator( + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ //println!("{:?}::{:?}", lhs, rhs); match lhs { types::Value::Boolean(_) => lhs, @@ -203,15 +251,19 @@ fn calculate_numeric_operator( types::Value::Number(l) => calculate_numeric_operator_rhs_number(l, lhs, rhs, f), types::Value::Iterator(lhs_vec) => calculate_numeric_operator_rhs_iterator(lhs_vec, rhs, f), types::Value::Date(_) => types::Value::Error(types::Error::Value), - types::Value::Blank => calculate_numeric_operator_rhs_number(0.0, lhs, rhs, f), + types::Value::Blank => calculate_numeric_operator_rhs_number(N::zero(), lhs, rhs, f), } } -fn calculate_numeric_product_operator( - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_numeric_product_operator( + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ //println!("{:?}::{:?}", lhs, rhs); match lhs { types::Value::Boolean(_) => lhs, @@ -220,21 +272,25 @@ fn calculate_numeric_product_operator( types::Value::Number(l) => calculate_numeric_operator_product_rhs_number(l, lhs, rhs, f), types::Value::Iterator(lhs_vec) => calculate_numeric_operator_rhs_iterator(lhs_vec, rhs, f), types::Value::Date(_) => types::Value::Error(types::Error::Value), - types::Value::Blank => calculate_numeric_operator_product_rhs_number(1.0, lhs, rhs, f), + types::Value::Blank => calculate_numeric_operator_product_rhs_number(N::one(), lhs, rhs, f), } } -fn calculate_average_operator_rhs_number( - element_count: &mut i32, - l: XlNum, - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_average_operator_rhs_number( + element_count: &mut i64, + l: N, + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Boolean(_) => rhs, types::Value::Error(_) => rhs, - types::Value::Text(t) => match t.parse::() { + types::Value::Text(t) => match t.parse::() { Ok(nr) => types::Value::Number(f(l, nr)), Err(_) => types::Value::Error(types::Error::Cast), }, @@ -259,17 +315,21 @@ fn calculate_average_operator_rhs_number( types::Value::Date(_) => types::Value::Error(types::Error::Value), types::Value::Blank => { *element_count -= 1; - types::Value::Number(f(l, 0.0)) + types::Value::Number(f(l, N::zero())) } } } -fn calculate_average_operator_rhs_iterator( - element_count: &mut i32, - mut lhs_vec: Vec, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_average_operator_rhs_iterator( + element_count: &mut i64, + mut lhs_vec: Vec>, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rhs { types::Value::Number(_) => { if let Some(mut temp) = lhs_vec.pop() { @@ -286,12 +346,16 @@ fn calculate_average_operator_rhs_iterator( } } -fn calculate_average_operator( - element_count: &mut i32, - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_average_operator( + element_count: &mut i64, + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match lhs { types::Value::Boolean(_) => lhs, types::Value::Error(_) => lhs, @@ -305,16 +369,20 @@ fn calculate_average_operator( types::Value::Date(_) => types::Value::Error(types::Error::Value), types::Value::Blank => { *element_count -= 1; - calculate_average_operator_rhs_number(element_count, 0.0, lhs, rhs, f) + calculate_average_operator_rhs_number(element_count, N::zero(), lhs, rhs, f) } } } -fn calculate_comparison_operator( - lhs: types::Value, - rhs: types::Value, - f: fn(num1: XlNum, num2: XlNum) -> bool, -) -> types::Value { +fn calculate_comparison_operator( + lhs: types::Value, + rhs: types::Value, + f: fn(num1: N, num2: N) -> bool, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match lhs { types::Value::Text(l) => match rhs { types::Value::Text(r) => { @@ -370,11 +438,15 @@ fn to_bool(value: types::Boolean) -> bool { } } -fn calculate_boolean_operator_rhs_boolean( +fn calculate_boolean_operator_rhs_boolean( l: types::Boolean, - rh: types::Value, + rh: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rh { types::Value::Boolean(r) => { if f(to_bool(l), to_bool(r)) { @@ -414,7 +486,11 @@ fn calculate_boolean_operator_rhs_boolean( } } -fn calculate_boolean_operator_rhs_error(rh: types::Value) -> types::Value { +fn calculate_boolean_operator_rhs_error(rh: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rh { types::Value::Boolean(r) => { if to_bool(r) { @@ -428,11 +504,15 @@ fn calculate_boolean_operator_rhs_error(rh: types::Value) -> types::Value { } } -fn calculate_boolean_operator_rhs_iterator( - rh: types::Value, - mut lhs_vec: Vec, +fn calculate_boolean_operator_rhs_iterator( + rh: types::Value, + mut lhs_vec: Vec>, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match rh { types::Value::Boolean(r) => { if let Some(mut temp) = lhs_vec.pop() { @@ -459,11 +539,15 @@ fn calculate_boolean_operator_rhs_iterator( } } -fn calculate_boolean_operator( - lhs: types::Value, - rhs: types::Value, +fn calculate_boolean_operator( + lhs: types::Value, + rhs: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let lh = cast_value_to_boolean(lhs); match lh { types::Value::Boolean(l) => { @@ -481,11 +565,15 @@ fn calculate_boolean_operator( } } -fn calculate_boolean_operator_or( - lhs: types::Value, - rhs: types::Value, +fn calculate_boolean_operator_or( + lhs: types::Value, + rhs: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let lh = cast_value_to_boolean(lhs); match lh { types::Value::Boolean(l) => { @@ -504,11 +592,15 @@ fn calculate_boolean_operator_or( } } -fn calculate_boolean_operator_xor( - lhs: types::Value, - rhs: types::Value, +fn calculate_boolean_operator_xor( + lhs: types::Value, + rhs: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let lh = cast_value_to_boolean(lhs); match lh { types::Value::Boolean(l) => { @@ -527,7 +619,11 @@ fn calculate_boolean_operator_xor( } } -fn calculate_abs(value: types::Value) -> types::Value { +fn calculate_abs(value: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match value { types::Value::Boolean(_) => value, types::Value::Error(_) => value, @@ -535,11 +631,15 @@ fn calculate_abs(value: types::Value) -> types::Value { types::Value::Number(l) => types::Value::Number(l.abs()), types::Value::Iterator(_) => types::Value::Error(types::Error::Value), types::Value::Date(_) => types::Value::Error(types::Error::Value), - types::Value::Blank => types::Value::Number(0.0), + types::Value::Blank => types::Value::Number(N::zero()), } } -fn calculate_negation(value: types::Value) -> types::Value { +fn calculate_negation(value: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match value { types::Value::Boolean(l) => { if !(to_bool(l)) { @@ -563,7 +663,7 @@ fn calculate_negation(value: types::Value) -> types::Value { } } types::Value::Number(l) => { - if l == 0.0 { + if l.is_zero() { types::Value::Boolean(types::Boolean::True) } else { types::Value::Boolean(types::Boolean::False) @@ -575,9 +675,13 @@ fn calculate_negation(value: types::Value) -> types::Value { } } -fn calculate_negate(value: types::Value) -> types::Value { +fn calculate_negate(value: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match value { - types::Value::Number(l) => types::Value::Number(-l), + types::Value::Number(n) => types::Value::Number(-n), types::Value::Iterator(mut value_vec) => { let mut result_vec = Vec::new(); while let Some(top) = value_vec.pop() { @@ -600,7 +704,11 @@ fn cast_text_to_boolean(s: &str) -> Option { } } -fn cast_value_to_boolean(value: types::Value) -> types::Value { +fn cast_value_to_boolean(value: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match value { types::Value::Boolean(_) => value, types::Value::Error(_) => value, @@ -618,7 +726,7 @@ fn cast_value_to_boolean(value: types::Value) -> types::Value { } } types::Value::Number(l) => { - if l != 0.0 { + if !l.is_zero() { types::Value::Boolean(types::Boolean::True) } else { types::Value::Boolean(types::Boolean::False) @@ -637,10 +745,14 @@ fn cast_value_to_boolean(value: types::Value) -> types::Value { } } -fn convert_iterator_to_result( - result: types::Value, +fn convert_iterator_to_result( + result: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match result { types::Value::Iterator(mut value_vec) => { if let Some(mut temp) = value_vec.pop() { @@ -665,10 +777,14 @@ fn convert_iterator_to_result( } } -fn convert_iterator_to_result_or( - result: types::Value, +fn convert_iterator_to_result_or( + result: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match result { types::Value::Iterator(mut value_vec) => { if let Some(mut temp) = value_vec.pop() { @@ -693,10 +809,14 @@ fn convert_iterator_to_result_or( } } -fn convert_iterator_to_result_xor( - result: types::Value, +fn convert_iterator_to_result_xor( + result: types::Value, f: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match result { types::Value::Iterator(mut value_vec) => { if let Some(mut temp) = value_vec.pop() { @@ -721,10 +841,14 @@ fn convert_iterator_to_result_xor( } } -fn get_values( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> (types::Value, types::Value) { +fn get_values( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> (types::Value, types::Value) +where + N: XlNum, + ::Err: Debug, +{ ( match exp.values.pop() { Some(formula) => calculate_formula(formula, f), @@ -737,20 +861,28 @@ fn get_values( ) } -fn get_value( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { +fn get_value( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match exp.values.pop() { Some(formula) => calculate_formula(formula, f), None => types::Value::Error(types::Error::Argument), } } -fn get_date_values( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> (types::Value, types::Value) { +fn get_date_values( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> (types::Value, types::Value) +where + N: XlNum, + ::Err: Debug, +{ ( match exp.values.pop() { Some(formula) => calculate_formula(formula, f), @@ -763,13 +895,17 @@ fn get_date_values( ) } -fn get_number_and_string_values( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> (types::Value, types::Value) { +fn get_number_and_string_values( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> (types::Value, types::Value) +where + N: XlNum, + ::Err: Debug, +{ if exp.values.len() == 1 { ( - types::Value::Number(1.0), + types::Value::Number(N::one()), match exp.values.pop() { Some(formula) => calculate_formula(formula, f), None => types::Value::Error(types::Error::Argument), @@ -789,10 +925,14 @@ fn get_number_and_string_values( } } -fn get_iff_values( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> (types::Value, types::Value, types::Value) { +fn get_iff_values( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> (types::Value, types::Value, types::Value) +where + N: XlNum, + ::Err: Debug, +{ ( match exp.values.pop() { Some(formula) => calculate_formula(formula, f), @@ -809,10 +949,14 @@ fn get_iff_values( ) } -fn calculate_iterator( - mut vec: Vec, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { +fn calculate_iterator( + mut vec: Vec>, + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let mut value_vec = Vec::new(); while let Some(top) = vec.pop() { value_vec.push(calculate_formula(top, f)); @@ -820,15 +964,19 @@ fn calculate_iterator( types::Value::Iterator(value_vec) } -fn calculate_reference( +fn calculate_reference( string: String, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match f { Some(f) => match f(string) { types::Value::Number(x) => types::Value::Number(x), types::Value::Text(s) => calculate_formula( - parse_formula::parse_string_to_formula(&s, None::), + parse_formula::parse_string_to_formula(&s, None::>), Some(f), ), types::Value::Boolean(x) => types::Value::Boolean(x), @@ -842,11 +990,15 @@ fn calculate_reference( } } -fn calculate_bool( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, +fn calculate_bool( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, f_bool: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let mut result = match exp.values.pop() { Some(formula) => calculate_formula(formula, f), None => types::Value::Error(types::Error::Argument), @@ -858,11 +1010,15 @@ fn calculate_bool( convert_iterator_to_result(result, f_bool) } -fn calculate_or( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, +fn calculate_or( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, f_bool: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let mut result = match exp.values.pop() { Some(formula) => calculate_formula(formula, f), None => types::Value::Error(types::Error::Argument), @@ -874,11 +1030,15 @@ fn calculate_or( convert_iterator_to_result_or(result, f_bool) } -fn calculate_xor( - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, +fn calculate_xor( + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, f_bool: fn(bool1: bool, bool2: bool) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let mut result = match exp.values.pop() { Some(formula) => calculate_formula(formula, f), None => types::Value::Error(types::Error::Argument), @@ -890,12 +1050,16 @@ fn calculate_xor( convert_iterator_to_result_xor(result, f_bool) } -fn calculate_collective_operator( - mut collective_value: types::Value, - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, - f_collective: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_collective_operator( + mut collective_value: types::Value, + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, + f_collective: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ while let Some(top) = exp.values.pop() { collective_value = calculate_numeric_operator(collective_value, calculate_formula(top, f), f_collective); @@ -903,12 +1067,16 @@ fn calculate_collective_operator( collective_value } -fn calculate_collective_product_operator( - mut collective_value: types::Value, - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, - f_collective: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_collective_product_operator( + mut collective_value: types::Value, + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, + f_collective: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ while let Some(top) = exp.values.pop() { collective_value = calculate_numeric_product_operator( collective_value, @@ -917,17 +1085,21 @@ fn calculate_collective_product_operator( ); } match collective_value { - types::Value::Blank => types::Value::Number(0.0), + types::Value::Blank => types::Value::Number(N::zero()), _ => collective_value, } } -fn calculate_average( - mut collective_value: types::Value, - mut exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, - f_collective: fn(num1: XlNum, num2: XlNum) -> XlNum, -) -> types::Value { +fn calculate_average( + mut collective_value: types::Value, + mut exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, + f_collective: fn(num1: N, num2: N) -> N, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let mut element_count = 0; while let Some(top) = exp.values.pop() { element_count += 1; @@ -943,13 +1115,17 @@ fn calculate_average( } else { calculate_numeric_operator( collective_value, - types::Value::Number(element_count as XlNum), + types::Value::Number(N::from_i64(element_count).unwrap()), calculate_divide_operator, ) } } -fn calculate_days(date_pair: (types::Value, types::Value)) -> types::Value { +fn calculate_days(date_pair: (types::Value, types::Value)) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let begin_of_date: DateTime = DateTime::parse_from_rfc3339("1900-01-01T02:00:00.000Z") .ok() @@ -957,23 +1133,27 @@ fn calculate_days(date_pair: (types::Value, types::Value)) -> types::Value { let (start, end) = date_pair; match (start, end) { (types::Value::Date(start), types::Value::Date(end)) => { - types::Value::Number((end - start).num_days() as XlNum) + types::Value::Number(N::from_i64((end - start).num_days()).unwrap()) } (types::Value::Blank, types::Value::Date(end)) => { - types::Value::Number((end - begin_of_date).num_days() as XlNum) + types::Value::Number(N::from_i64((end - begin_of_date).num_days()).unwrap()) } (types::Value::Date(start), types::Value::Blank) => { - types::Value::Number((begin_of_date - start).num_days() as XlNum) + types::Value::Number(N::from_i64((begin_of_date - start).num_days()).unwrap()) } - (types::Value::Blank, types::Value::Blank) => types::Value::Number(0.0), + (types::Value::Blank, types::Value::Blank) => types::Value::Number(N::zero()), _ => types::Value::Error(types::Error::Value), } } -fn calculate_right(number_string: (types::Value, types::Value)) -> types::Value { +fn calculate_right(number_string: (types::Value, types::Value)) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let (number, string) = number_string; let trim_length = match number { - types::Value::Number(x) => x as usize, + types::Value::Number(x) => x.as_(), _ => 0, }; @@ -989,10 +1169,14 @@ fn calculate_right(number_string: (types::Value, types::Value)) -> types::Value types::Value::Text(trimmed_string.to_string()) } -fn calculate_left(number_string: (types::Value, types::Value)) -> types::Value { +fn calculate_left(number_string: (types::Value, types::Value)) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let (number, string) = number_string; let trim_length = match number { - types::Value::Number(x) => x as usize, + types::Value::Number(x) => x.as_(), _ => 0, }; @@ -1008,7 +1192,13 @@ fn calculate_left(number_string: (types::Value, types::Value)) -> types::Value { types::Value::Text(trimmed_string.to_string()) } -fn calculate_iff(iff_arguments: (types::Value, types::Value, types::Value)) -> types::Value { +fn calculate_iff( + iff_arguments: (types::Value, types::Value, types::Value), +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ let (false_value, true_value, bool_expression) = iff_arguments; match bool_expression { types::Value::Boolean(bool_value) => { @@ -1019,7 +1209,7 @@ fn calculate_iff(iff_arguments: (types::Value, types::Value, types::Value)) -> t } } types::Value::Number(number_value) => { - if number_value == 0.0 { + if number_value.is_zero() { false_value } else { true_value @@ -1032,7 +1222,11 @@ fn calculate_iff(iff_arguments: (types::Value, types::Value, types::Value)) -> t } } -fn calculate_isblank(value: types::Value) -> types::Value { +fn calculate_isblank(value: types::Value) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match value { types::Value::Blank => types::Value::Boolean(types::Boolean::True), types::Value::Text(s) => { @@ -1048,21 +1242,25 @@ fn calculate_isblank(value: types::Value) -> types::Value { } } -fn calculate_function( +fn calculate_function( func: types::Function, - exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { + exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match func { types::Function::Abs => calculate_abs(get_value(exp, f)), types::Function::Sum => { - calculate_collective_operator(types::Value::Number(0.0), exp, f, |n1, n2| n1 + n2) + calculate_collective_operator(types::Value::Number(N::zero()), exp, f, |n1, n2| n1 + n2) } types::Function::Product => { calculate_collective_product_operator(types::Value::Blank, exp, f, |n1, n2| n1 * n2) } types::Function::Average => { - calculate_average(types::Value::Number(0.00), exp, f, |n1, n2| n1 + n2) + calculate_average(types::Value::Number(N::zero()), exp, f, |n1, n2| n1 + n2) } types::Function::Or => calculate_or(exp, f, |n1, n2| n1 || n2), types::Function::And => calculate_bool(exp, f, |n1, n2| n1 && n2), @@ -1077,10 +1275,14 @@ fn calculate_function( } } -fn calculate_operation( - exp: types::Expression, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { +fn calculate_operation( + exp: types::Expression, + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match exp.op { types::Operator::Plus => { let (value2, value1) = get_values(exp, f); @@ -1105,7 +1307,7 @@ fn calculate_operation( types::Operator::Divide => { let (value2, value1) = get_values(exp, f); match value2 { - types::Value::Number(0.0) => types::Value::Error(types::Error::Div0), + types::Value::Number(n) if n.is_zero() => types::Value::Error(types::Error::Div0), _ => calculate_numeric_operator(value1, value2, calculate_divide_operator), } } @@ -1126,7 +1328,9 @@ fn calculate_operation( (types::Value::Text(l), types::Value::Text(r)) => { compare_strings(l, r, |s1, s2| s1 == s2) } - _ => calculate_comparison_operator(value1, value2, |n1, n2| (n1 - n2).abs() == 0.0), + _ => calculate_comparison_operator(value1, value2, |n1, n2| { + (n1 - n2).abs().is_zero() + }), } } types::Operator::NotEqual => { @@ -1138,7 +1342,9 @@ fn calculate_operation( (types::Value::Text(l), types::Value::Text(r)) => { compare_strings(l, r, |s1, s2| s1 != s2) } - _ => calculate_comparison_operator(value1, value2, |n1, n2| (n1 - n2).abs() > 0.0), + _ => calculate_comparison_operator(value1, value2, |n1, n2| { + (n1 - n2).abs() > N::zero() + }), } } types::Operator::Greater => { @@ -1181,11 +1387,15 @@ fn calculate_operation( } } -fn compare_dates( +fn compare_dates( date1: DateTime, date2: DateTime, f: fn(d1: DateTime, d2: DateTime) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ if f(date1, date2) { types::Value::Boolean(types::Boolean::True) } else { @@ -1193,11 +1403,15 @@ fn compare_dates( } } -fn compare_strings( +fn compare_strings( string1: String, string2: String, f: fn(s1: String, s2: String) -> bool, -) -> types::Value { +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ if f(string1, string2) { types::Value::Boolean(types::Boolean::True) } else { @@ -1207,10 +1421,14 @@ fn compare_strings( /// Evaluates a string that was parsed and stored in Expression Struct. /// Takes an optional closure with the trait bound Fn(String) -> types::Value. -pub fn calculate_formula( - formula: types::Formula, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { +pub fn calculate_formula( + formula: types::Formula, + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ match formula { types::Formula::Operation(exp) => calculate_operation(exp, f), types::Formula::Value(val) => val, @@ -1220,19 +1438,25 @@ pub fn calculate_formula( } /// Converts a result from Value Enum to a printable string. -pub fn result_to_string(_value: types::Value) -> String { - match _value { +pub fn result_to_string(value: types::Value) -> String +where + N: XlNum, +{ + match value { types::Value::Number(number) => show_number(number), types::Value::Text(text) => text, types::Value::Error(error) => show_error(error), types::Value::Boolean(boolean) => show_boolean(boolean), types::Value::Iterator(value_vec) => show_iterator(value_vec), types::Value::Date(date) => date.to_string(), - types::Value::Blank => show_blank(), + types::Value::Blank => show_blank::(), } } -fn show_number(number: XlNum) -> String { +fn show_number(number: N) -> String +where + N: XlNum, +{ if number.is_infinite() { String::from("#DIV/0!") } else { @@ -1258,7 +1482,10 @@ fn show_boolean(boolean: types::Boolean) -> String { } } -fn show_iterator(mut value_vec: Vec) -> String { +fn show_iterator(mut value_vec: Vec>) -> String +where + N: XlNum, +{ value_vec.reverse(); let mut result = '{'.to_string(); while let Some(top) = value_vec.pop() { @@ -1270,7 +1497,10 @@ fn show_iterator(mut value_vec: Vec) -> String { result } -fn show_blank() -> String { - show_number(0.0) +fn show_blank() -> String +where + N: XlNum, +{ + show_number(N::zero()) //String::from("BLANK") } diff --git a/src/lib.rs b/src/lib.rs index 5dfe8be..7fdb596 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,299 +1,4 @@ -//!# XLFormula Engine -//!XLFormula Engine is a Rust crate for parsing and evaluating Excel formulas. -//! -//!It works with f64 by default (the `f64` feature) or with `f32` by enabling the `f32` feature and disabling default features. -//! -//!## Features -//!It supports: -//! -//!* Any numbers, negative and positive, as float or integer -//!* Arithmetic operations +, -, /, *, ^ -//!* Logical operations AND(), OR(), NOT(), XOR() -//!* Comparison operations =, >, >=, <, <=, <> -//!* String operation & (concatenation) -//!* Build-in variables TRUE, FALSE -//!* Excel functions ABS(), SUM(), PRODUCT(), AVERAGE(), RIGHT(), LEFT(), IF(), ISBLANK() -//!* Operations on lists of values (one dimensional range) -//!* Add or subtract dates and excel funtion DAYS() -//!* Custom functions with number arguments -//!* Handle blank/null values in calculation -//!* Handle empty/missing parameters of function calls as blank values -//! -//!## Installation -//! -//!Add the corresponding entry to your Cargo.toml dependency list: -//!```toml -//![dependencies] -//!xlformula_engine = "0.3.0" -//!``` -//! -//!## Examples -//! -//!Here are simple examples of parsing an Excel formula string and evaluating to a result: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=1+2", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=(1*(2+3))*2", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=1+3/0", None::); // error (#DIV/0!) -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//!The last string is evaluated to #DIV/0!. -//! -//!Concatenating strings: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=\"Hello \" & \" World!\"", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=1 + \"Hello\"", None::); // error (#CAST!) -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//!Concatenating number and string results in a #CAST! error. -//! -//!Constants ( i.e. a string without '=' ): -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"1.2", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"Hello World", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!Excel functions: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=ABS(-1)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=SUM(1,2,\"3\")", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=PRODUCT(ABS(1),2*1, 3,4*1)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=RIGHT(\"apple\", 3)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\", 3)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=LEFT(\"apple\")", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=IF(TRUE,1,0)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!Logical expressions: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=2>=1", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=OR(1>1,1<>1)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=AND(\"test\",\"True\", 1, true) ", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!References: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::types; -//!use xlformula_engine::NoCustomFunction; -//! -//!let data_function = |s: String| match s.as_str() { -//!"A" => types::Value::Text("=1+B".to_string()), -//!"B" => types::Value::Number(3.0), -//!_ => types::Value::Error(types::Error::Value), -//!}; -//!let formula = parse_formula::parse_string_to_formula(&"=A+B", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!List: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let formula = parse_formula::parse_string_to_formula(&"={1,2,3}+{1,2,3}", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=XOR({0,0,0})", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=AVERAGE({1,2,3},1,2,3)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!Date: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::types; -//!use chrono::format::ParseError; -//!use chrono::{DateTime, FixedOffset}; -//!use xlformula_engine::NoCustomFunction; -//! -//!fn main() -> Result<(), ParseError> { -//!let start: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z")?; -//!let end: DateTime = DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z")?; -//!let data_function = |s: String| match s.as_str() { -//!"start" => types::Value::Date(start), -//!"end" => types::Value::Date(end), -//!_ => types::Value::Error(types::Error::Value), -//!}; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=DAYS(end, start)", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=start+1", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = parse_formula::parse_string_to_formula(&"=end-3", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//! Ok(()) -//!} -//!``` -//! -//!Custom Function: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::types; -//!use xlformula_engine::types::XlNum; -//!use xlformula_engine::NoReference; -//! -//!let custom_functions = |s: String, params: Vec| match s.as_str() { -//!"Increase" => types::Value::Number(params[0] + 1.0), -//!"SimpleSum" => types::Value::Number(params[0] + params[1]), -//!"EqualFive" => types::Value::Number(5.0), -//!_ => types::Value::Error(types::Error::Value), -//!}; -//! -//!let formula = -//!parse_formula::parse_string_to_formula(&"=Increase(1)+1", Some(&custom_functions)); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = -//!parse_formula::parse_string_to_formula(&"=EqualFive()+1", Some(&custom_functions)); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = -//!parse_formula::parse_string_to_formula(&"=SimpleSum(1,2)", Some(&custom_functions)); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` -//! -//!Handle blank in calculation: -//!```rust -//!extern crate xlformula_engine; -//!use xlformula_engine::calculate; -//!use xlformula_engine::parse_formula; -//!use xlformula_engine::types; -//!use xlformula_engine::types::XlNum; -//!use chrono::format::ParseError; -//!use chrono::{DateTime, FixedOffset}; -//!use xlformula_engine::NoReference; -//!use xlformula_engine::NoCustomFunction; -//! -//!let data_function = |s: String| match s.as_str() { -//!"B" => types::Value::Blank, -//!_ => types::Value::Error(types::Error::Value), -//!}; -//! -//!let custom_functions = |s: String, params: Vec| match s.as_str() { -//!"BLANK" => types::Value::Blank, -//!_ => types::Value::Error(types::Error::Value), -//!}; -//! -//!let formula = parse_formula::parse_string_to_formula(&"=SUM(B, 1)", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = -//!parse_formula::parse_string_to_formula(&"=SUM(BLANK(), 1)", Some(&custom_functions)); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!//takes list as input -//!let formula = parse_formula::parse_string_to_formula(&"=SUM({B, 1})", None::); -//!let result = calculate::calculate_formula(formula, Some(&data_function)); -//!println!("Result is {}", calculate::result_to_string(result)); -//! -//!let formula = -//!parse_formula::parse_string_to_formula(&"=SUM(1, 2, , 3)", None::); -//!let result = calculate::calculate_formula(formula, None::); -//!println!("Result is {}", calculate::result_to_string(result)); -//!``` - -#[macro_use] -extern crate pest_derive; - +#[doc = include_str!("../README.md")] /// Evaluates a formula. pub mod calculate; @@ -303,5 +8,5 @@ pub mod types; /// Parses a string using `pest` and `pest::prec_climber`. pub mod parse_formula; -pub type NoReference<'a> = &'a fn(String) -> types::Value; -pub type NoCustomFunction<'a> = &'a fn(String, Vec) -> types::Value; +pub type NoReference<'a, N> = &'a fn(String) -> types::Value; +pub type NoCustomFunction<'a, N> = &'a fn(String, Vec) -> types::Value; diff --git a/src/main.rs b/src/main.rs index 9858d6c..f5b8d0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +extern crate xlformula_engine; use chrono::{format::ParseError, DateTime, FixedOffset}; +use std::{fmt::Debug, str::FromStr}; use xlformula_engine::{ calculate, parse_formula, types::{self, XlNum}, @@ -6,110 +8,129 @@ use xlformula_engine::{ }; fn main() -> Result<(), ParseError> { - let formula = parse_formula::parse_string_to_formula("=1+2", None::); - let result = calculate::calculate_formula(formula, None::); + println!("========== f32 =========="); + demo::()?; + println!("========== f64 =========="); + demo::()?; + Ok(()) +} + +fn demo() -> Result<(), ParseError> +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula("=1+2", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let data_function = |s: String| match s.as_str() { "A" => types::Value::Text("=1+B".to_string()), - "B" => types::Value::Number(3.0), + "B" => types::Value::Number(N::from_f32(3.0).unwrap()), "C" => types::Value::Text("=1+A".to_string()), _ => types::Value::Error(types::Error::Value), }; - let formula = parse_formula::parse_string_to_formula("=A+B", None::); + let formula = parse_formula::parse_string_to_formula("=A+B", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=SUM(A,B,C)", None::); + let formula = + parse_formula::parse_string_to_formula("=SUM(A,B,C)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=1+2", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula("=1+2", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=(1*(2+3))*2", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = + parse_formula::parse_string_to_formula("=(1*(2+3))*2", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=1+3/0", None::); // error (#DIV/0!) - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula("=1+3/0", None::>); // error (#DIV/0!) + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula( r#"="Hello " & " World!""#, - None::, + None::>, ); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula(r#"=1 + "Hello""#, None::); // error (#CAST!) - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula(r#"=1 + "Hello""#, None::>); // error (#CAST!) + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("1.2", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula("1.2", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("Hello World", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = + parse_formula::parse_string_to_formula("Hello World", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=ABS(-1)", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula("=ABS(-1)", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula(r#"=SUM(1,2,"3")"#, None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula(r#"=SUM(1,2,"3")"#, None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula( "=PRODUCT(ABS(1),2*1, 3,4*1)", - None::, + None::>, ); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=2>=1", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula("=2>=1", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=OR(1>1,1<>1)", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = + parse_formula::parse_string_to_formula("=OR(1>1,1<>1)", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula( r#"=AND("test","True", 1, true) "#, - None::, + None::>, ); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula( "=SUM({1,2,3}, 4, {5,6,7})", - None::, + None::>, ); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = - parse_formula::parse_string_to_formula("=AVERAGE({1,2,3},1,2,3)", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula( + "=AVERAGE({1,2,3},1,2,3)", + None::>, + ); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=XOR({0,0,0})", None::); - let result = calculate::calculate_formula(formula, None::); + let formula = + parse_formula::parse_string_to_formula("=XOR({0,0,0})", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula("={1,2,3}+{1,2,3}", None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula("={1,2,3}+{1,2,3}", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula("={0,0}+{1,2,3}", None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula("={0,0}+{1,2,3}", None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); // error (#ARG!) let start: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z")?; @@ -121,73 +142,75 @@ fn main() -> Result<(), ParseError> { }; let formula = - parse_formula::parse_string_to_formula("=DAYS(end, start)", None::); + parse_formula::parse_string_to_formula("=DAYS(end, start)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=start+1", None::); + let formula = parse_formula::parse_string_to_formula("=start+1", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let formula = parse_formula::parse_string_to_formula("=end-3", None::); + let formula = parse_formula::parse_string_to_formula("=end-3", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let custom_functions = |s: String, params: Vec| match s.as_str() { - "Increase" => types::Value::Number(params[0] + 1.0), + let custom_functions = |s: String, params: Vec| match s.as_str() { + "Increase" => types::Value::Number(params[0] + N::one()), "SimpleSum" => types::Value::Number(params[0] + params[1]), - "EqualFive" => types::Value::Number(5.0), + "EqualFive" => types::Value::Number(N::from_f32(5.0).unwrap()), _ => types::Value::Error(types::Error::Value), }; let formula = parse_formula::parse_string_to_formula("=Increase(1)+1", Some(&custom_functions)); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula("=EqualFive()+1", Some(&custom_functions)); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula("=SimpleSum(1,2)", Some(&custom_functions)); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); - let custom_function = |s: String, _params: Vec| match s.as_str() { - "EqualFive" => types::Value::Number(5.0), + let custom_function = |s: String, _params: Vec| match s.as_str() { + "EqualFive" => types::Value::Number(N::from_f32(5.0).unwrap()), _ => types::Value::Error(types::Error::Value), }; let formula = parse_formula::parse_string_to_formula("=EqualFive()", Some(&custom_function)); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); ///////////// RIGHT function - let formula = - parse_formula::parse_string_to_formula(r#"=RIGHT("apple", 3)"#, None::); - let result = calculate::calculate_formula(formula, None::); + let formula = parse_formula::parse_string_to_formula( + r#"=RIGHT("apple", 3)"#, + None::>, + ); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula(r#"=RIGHT("apple")"#, None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula(r#"=RIGHT("apple")"#, None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula( r#"="P"&RIGHT("000"&1,3)"#, - None::, + None::>, ); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); ///////////// LEFT function let formula = - parse_formula::parse_string_to_formula(r#"=LEFT("apple", 3)"#, None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula(r#"=LEFT("apple", 3)"#, None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula(r#"=LEFT("apple")"#, None::); - let result = calculate::calculate_formula(formula, None::); + parse_formula::parse_string_to_formula(r#"=LEFT("apple")"#, None::>); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); ///////////// Handle blank in calculation @@ -196,19 +219,19 @@ fn main() -> Result<(), ParseError> { _ => types::Value::Error(types::Error::Value), }; - let custom_functions = |s: String, params: Vec| match s.as_str() { - "Increase" => types::Value::Number(params[0] + 1.0), + let custom_functions = |s: String, params: Vec| match s.as_str() { + "Increase" => types::Value::Number(params[0] + N::one()), "BLANK" => types::Value::Blank, _ => types::Value::Error(types::Error::Value), }; - let formula = parse_formula::parse_string_to_formula("=SUM(B, 1)", None::); + let formula = parse_formula::parse_string_to_formula("=SUM(B, 1)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); let formula = parse_formula::parse_string_to_formula("=SUM(BLANK(), 1)", Some(&custom_functions)); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); println!("Result is {}", calculate::result_to_string(result)); let data_function = |s: String| match s.as_str() { @@ -218,17 +241,18 @@ fn main() -> Result<(), ParseError> { _ => types::Value::Error(types::Error::Value), }; - let formula = parse_formula::parse_string_to_formula("=OR({F,B})", None::); + let formula = parse_formula::parse_string_to_formula("=OR({F,B})", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); let formula = - parse_formula::parse_string_to_formula("=SUM(1, 2, , 3)", None::); + parse_formula::parse_string_to_formula("=SUM(1, 2, , 3)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); ///////////// IF function - let formula = parse_formula::parse_string_to_formula("=IF(TRUE,1,0)", None::); + let formula = + parse_formula::parse_string_to_formula("=IF(TRUE,1,0)", None::>); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); @@ -240,7 +264,7 @@ fn main() -> Result<(), ParseError> { let formula = parse_formula::parse_string_to_formula( r#"=IF(ReferenceKey="10","",ReferenceKey&" - ")&ReferenceName"#, - None::, + None::>, ); let result = calculate::calculate_formula(formula, Some(&data_function)); @@ -252,13 +276,17 @@ fn main() -> Result<(), ParseError> { "ReferenceName" => types::Value::Text("Test".to_string()), _ => types::Value::Error(types::Error::Value), }; - let formula = - parse_formula::parse_string_to_formula("=ISBLANK(ReferenceKey)", None::); + let formula = parse_formula::parse_string_to_formula( + "=ISBLANK(ReferenceKey)", + None::>, + ); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); - let formula = - parse_formula::parse_string_to_formula("=ISBLANK(ReferenceName)", None::); + let formula = parse_formula::parse_string_to_formula( + "=ISBLANK(ReferenceName)", + None::>, + ); let result = calculate::calculate_formula(formula, Some(&data_function)); println!("Result is {}", calculate::result_to_string(result)); diff --git a/src/parse_formula.rs b/src/parse_formula.rs index 9e419a8..afd446e 100644 --- a/src/parse_formula.rs +++ b/src/parse_formula.rs @@ -3,13 +3,15 @@ use pest::{ pratt_parser::{Assoc, Op, PrattParser}, Parser, }; +use pest_derive::Parser; +use std::{fmt::Debug, str::FromStr}; #[derive(Parser)] #[grammar = "grammar.pest"] pub struct GrammarParser; /// Use this function to catch a parse error. -fn parse_string(s: &str) -> Option> { +fn parse_string(s: &'_ str) -> Option> { let parse_result = GrammarParser::parse(Rule::formula, s); //println!("{:#?}", parse_result); match parse_result { @@ -24,7 +26,11 @@ fn parse_string(s: &str) -> Option> { // .next() } -fn parse_string_constant(parse_result: pest::iterators::Pair) -> types::Formula { +fn parse_string_constant(parse_result: pest::iterators::Pair) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let string = parse_result .into_inner() .as_str() @@ -36,10 +42,14 @@ fn parse_string_constant(parse_result: pest::iterators::Pair) -> types::Fo } /// Parses a string and stores it in Formula Enum. -pub fn parse_string_to_formula( +pub fn parse_string_to_formula( s: &str, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ match parse_string(s) { Some(parse_result) => match parse_result.as_rule() { Rule::expr => build_formula_with_parser(parse_result.into_inner(), f), @@ -50,25 +60,42 @@ pub fn parse_string_to_formula( } } -fn build_formula_number(pair: pest::iterators::Pair) -> types::Formula { - let x = pair.as_str().parse::().unwrap(); +fn build_formula_number(pair: pest::iterators::Pair) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ + let s = pair.as_str(); + let x = s.parse::().unwrap(); let value = types::Value::Number(x); types::Formula::Value(value) } -fn build_formula_string_double_quote(pair: pest::iterators::Pair) -> types::Formula { +fn build_formula_string_double_quote(pair: pest::iterators::Pair) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let string = pair.into_inner().as_str().parse::().unwrap(); let value = types::Value::Text(string.replace("\"\"", "\"")); types::Formula::Value(value) } -fn build_formula_string_single_quote(pair: pest::iterators::Pair) -> types::Formula { +fn build_formula_string_single_quote(pair: pest::iterators::Pair) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let string = pair.into_inner().as_str().parse::().unwrap(); let value = types::Value::Text(string); types::Formula::Value(value) } -fn build_formula_boolean(boolean_value: bool) -> types::Formula { +fn build_formula_boolean(boolean_value: bool) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ if boolean_value { types::Formula::Value(types::Value::Boolean(types::Boolean::True)) } else { @@ -76,11 +103,15 @@ fn build_formula_boolean(boolean_value: bool) -> types::Formula { } } -fn build_formula_unary_operator( +fn build_formula_unary_operator( unary_operation: Rule, pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let op_type = match unary_operation { Rule::abs => types::Operator::Function(types::Function::Abs), Rule::not => types::Operator::Function(types::Function::Not), @@ -94,15 +125,23 @@ fn build_formula_unary_operator( types::Formula::Operation(operation) } -fn build_formula_reference(pair: pest::iterators::Pair) -> types::Formula { +fn build_formula_reference(pair: pest::iterators::Pair) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let string = pair.as_str().parse::().unwrap(); types::Formula::Reference(string) } -fn build_formula_iterator( +fn build_formula_iterator( pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for term in pair.into_inner() { vec.push(build_formula_with_parser(term.into_inner(), f)); @@ -110,11 +149,15 @@ fn build_formula_iterator( types::Formula::Iterator(vec) } -fn build_formula_collective_operator( +fn build_formula_collective_operator( collective_operation: Rule, pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for term in pair.into_inner() { if (term.as_str().parse::().unwrap() == "") @@ -152,11 +195,15 @@ fn rule_to_function_operator(collective_operation: Rule) -> types::Operator { } } -fn build_formula_collective_operator_average( +fn build_formula_collective_operator_average( collective_operation: Rule, pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for term in pair.into_inner() { if (term.as_str().parse::().unwrap() == "") @@ -164,7 +211,7 @@ fn build_formula_collective_operator_average( | (term.as_str().parse::().unwrap() == ", ") | (term.as_str().parse::().unwrap() == " ,") { - vec.push(types::Formula::Value(types::Value::Number(0.0))) + vec.push(types::Formula::Value(types::Value::Number(N::zero()))) } else { vec.push(build_formula_with_parser(term.into_inner(), f)) } @@ -177,11 +224,15 @@ fn build_formula_collective_operator_average( types::Formula::Operation(operation) } -fn build_formula_collective_operator_and( +fn build_formula_collective_operator_and( collective_operation: Rule, pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for term in pair.into_inner() { if (term.as_str().parse::().unwrap() == "") @@ -204,10 +255,14 @@ fn build_formula_collective_operator_and( types::Formula::Operation(operation) } -fn build_formula_iff( +fn build_formula_iff( pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for term in pair.into_inner() { if (term.as_str().parse::().unwrap() == "") @@ -227,15 +282,19 @@ fn build_formula_iff( types::Formula::Operation(operation) } -fn build_formula_custom_function( +fn build_formula_custom_function( pair: pest::iterators::Pair, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let mut vec = Vec::new(); for field in pair.clone().into_inner() { if field.as_rule() == Rule::expr { let x = field.into_inner().as_str(); - let y = x.parse::(); + let y = x.parse::(); if let Ok(y) = y { vec.push(y); } @@ -265,11 +324,15 @@ fn build_formula_custom_function( } } -fn build_formula_binary_operator( +fn build_formula_binary_operator( binary_operator: Rule, - lhs: types::Formula, - rhs: types::Formula, -) -> types::Formula { + lhs: types::Formula, + rhs: types::Formula, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let op_type = match binary_operator { Rule::add => types::Operator::Plus, Rule::subtract => types::Operator::Minus, @@ -292,10 +355,14 @@ fn build_formula_binary_operator( types::Formula::Operation(operation) } -fn build_formula_with_parser( +fn build_formula_with_parser( expression: pest::iterators::Pairs, - f: Option<&impl Fn(String, Vec) -> types::Value>, -) -> types::Formula { + f: Option<&impl Fn(String, Vec) -> types::Value>, +) -> types::Formula +where + N: XlNum, + ::Err: Debug, +{ let pratt = PrattParser::new() .op(Op::infix(Rule::concat, Assoc::Left)) .op(Op::infix(Rule::equal, Assoc::Left) | Op::infix(Rule::not_equal, Assoc::Left)) @@ -334,24 +401,26 @@ fn build_formula_with_parser( _ => unreachable!(), }) .map_infix( - |lhs: types::Formula, op: pest::iterators::Pair, rhs: types::Formula| match op - .as_rule() - { - Rule::add => build_formula_binary_operator(Rule::add, lhs, rhs), - Rule::subtract => build_formula_binary_operator(Rule::subtract, lhs, rhs), - Rule::multiply => build_formula_binary_operator(Rule::multiply, lhs, rhs), - Rule::divide => build_formula_binary_operator(Rule::divide, lhs, rhs), - Rule::power => build_formula_binary_operator(Rule::power, lhs, rhs), - Rule::concat => build_formula_binary_operator(Rule::concat, lhs, rhs), - Rule::equal => build_formula_binary_operator(Rule::equal, lhs, rhs), - Rule::not_equal => build_formula_binary_operator(Rule::not_equal, lhs, rhs), - Rule::greater => build_formula_binary_operator(Rule::greater, lhs, rhs), - Rule::less => build_formula_binary_operator(Rule::less, lhs, rhs), - Rule::greater_or_equal => { - build_formula_binary_operator(Rule::greater_or_equal, lhs, rhs) + |lhs: types::Formula, op: pest::iterators::Pair, rhs: types::Formula| { + match op.as_rule() { + Rule::add => build_formula_binary_operator(Rule::add, lhs, rhs), + Rule::subtract => build_formula_binary_operator(Rule::subtract, lhs, rhs), + Rule::multiply => build_formula_binary_operator(Rule::multiply, lhs, rhs), + Rule::divide => build_formula_binary_operator(Rule::divide, lhs, rhs), + Rule::power => build_formula_binary_operator(Rule::power, lhs, rhs), + Rule::concat => build_formula_binary_operator(Rule::concat, lhs, rhs), + Rule::equal => build_formula_binary_operator(Rule::equal, lhs, rhs), + Rule::not_equal => build_formula_binary_operator(Rule::not_equal, lhs, rhs), + Rule::greater => build_formula_binary_operator(Rule::greater, lhs, rhs), + Rule::less => build_formula_binary_operator(Rule::less, lhs, rhs), + Rule::greater_or_equal => { + build_formula_binary_operator(Rule::greater_or_equal, lhs, rhs) + } + Rule::less_or_equal => { + build_formula_binary_operator(Rule::less_or_equal, lhs, rhs) + } + _ => unreachable!(), } - Rule::less_or_equal => build_formula_binary_operator(Rule::less_or_equal, lhs, rhs), - _ => unreachable!(), }, ) .parse(expression) diff --git a/src/types.rs b/src/types.rs index a321fdf..74f8326 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,9 @@ use chrono::{DateTime, FixedOffset}; +use num_traits::{AsPrimitive, Float, FromPrimitive}; +use std::{ + fmt::{Debug, Display}, + str::FromStr, +}; /// Defines Excel Functions. #[derive(Debug, Copy, Clone)] pub enum Function { @@ -54,15 +59,28 @@ pub enum Boolean { False, } -#[cfg(feature = "f32")] -pub type XlNum = f32; -#[cfg(feature = "f64")] -pub type XlNum = f64; +pub trait XlNum: + Float + + AsPrimitive + + AsPrimitive + + AsPrimitive + + FromPrimitive + + FromStr + + Debug + + Display +{ +} + +impl XlNum for f32 {} +impl XlNum for f64 {} /// The result of an evaluation. #[derive(Debug, Clone, PartialEq)] -pub enum Value { - Number(XlNum), +pub enum Value +where + N: XlNum, +{ + Number(N), Text(String), Boolean(Boolean), Iterator(Vec), @@ -73,16 +91,22 @@ pub enum Value { /// Defines each term in Expression Struct. #[derive(Debug, Clone)] -pub enum Formula { - Operation(Expression), - Value(Value), +pub enum Formula +where + N: XlNum, +{ + Operation(Expression), + Value(Value), Reference(String), Iterator(Vec), } /// Struct that holds a parsed string. Formula enum and Expression Struct are defined recursively. #[derive(Debug, Clone)] -pub struct Expression { +pub struct Expression +where + N: XlNum, +{ pub op: Operator, - pub values: Vec, + pub values: Vec>, } diff --git a/tests/test.rs b/tests/test.rs index 3120e1d..0d0f27f 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,1572 +1,1997 @@ -use chrono::{format::ParseError, DateTime, Duration, FixedOffset}; +use chrono::{DateTime, Duration, FixedOffset}; +use std::{fmt::Debug, str::FromStr}; use xlformula_engine::{ calculate, parse_formula, types::{self, XlNum}, NoCustomFunction, NoReference, }; -fn evaluate_formula_number(s: &str) -> XlNum { - let formula = parse_formula::parse_string_to_formula(s, None::); - let result = calculate::calculate_formula(formula, None::); +fn evaluate_formula_number(s: &str) -> N +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); + let result = calculate::calculate_formula(formula, None::>); + calculate::result_to_string(result).parse::().unwrap() +} + +fn evaluate_formula_string(s: &str) -> String +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); + let result = calculate::calculate_formula(formula, None::>); calculate::result_to_string(result) - .parse::() - .unwrap() } -fn evaluate_formula_string(s: &str) -> String { - let formula = parse_formula::parse_string_to_formula(s, None::); - let result = calculate::calculate_formula(formula, None::); - calculate::result_to_string(result) -} - -fn evaluate_formula_string_with_reference( +fn evaluate_formula_string_with_reference( s: &str, - f: Option<&impl Fn(String) -> types::Value>, -) -> String { - let formula = parse_formula::parse_string_to_formula(s, None::); + f: Option<&impl Fn(String) -> types::Value>, +) -> String +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); let result = calculate::calculate_formula(formula, f); calculate::result_to_string(result) } -fn evaluate_formula_number_with_reference( +fn evaluate_formula_number_with_reference( s: &str, - f: Option<&impl Fn(String) -> types::Value>, -) -> XlNum { - let formula = parse_formula::parse_string_to_formula(s, None::); + f: Option<&impl Fn(String) -> types::Value>, +) -> N +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); let result = calculate::calculate_formula(formula, f); - calculate::result_to_string(result) - .parse::() - .unwrap() + calculate::result_to_string(result).parse::().unwrap() } -fn evaluate_formula_number_with_reference_no_conversion( +fn evaluate_formula_number_with_reference_no_conversion( s: &str, - f: Option<&impl Fn(String) -> types::Value>, -) -> types::Value { - let formula = parse_formula::parse_string_to_formula(s, None::); + f: Option<&impl Fn(String) -> types::Value>, +) -> types::Value +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); calculate::calculate_formula(formula, f) //calculate::result_to_string(result).parse::().unwrap() } -fn evaluate_formula_boolean_with_reference( +fn evaluate_formula_boolean_with_reference( s: &str, - f: Option<&impl Fn(String) -> types::Value>, -) -> String { - let formula = parse_formula::parse_string_to_formula(s, None::); + f: Option<&impl Fn(String) -> types::Value>, +) -> String +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); let result = calculate::calculate_formula(formula, f); calculate::result_to_string(result) //.parse::().unwrap() } -fn evaluate_formula_date_with_reference( +fn evaluate_formula_date_with_reference( s: &str, - f: Option<&impl Fn(String) -> types::Value>, -) -> String { - let formula = parse_formula::parse_string_to_formula(s, None::); + f: Option<&impl Fn(String) -> types::Value>, +) -> String +where + N: XlNum, + ::Err: Debug, +{ + let formula = parse_formula::parse_string_to_formula(s, None::>); let result = calculate::calculate_formula(formula, f); calculate::result_to_string(result) } -fn evaluate_formula_number_with_custom_function( +fn evaluate_formula_number_with_custom_function( s: &str, - custom_function: Option<&impl Fn(String, Vec) -> types::Value>, + custom_function: Option<&impl Fn(String, Vec) -> types::Value>, //reference: Option<&impl Fn(String) -> types::Value>, -) -> XlNum { +) -> N +where + N: XlNum, + ::Err: Debug, +{ let formula = parse_formula::parse_string_to_formula(s, custom_function); - let result = calculate::calculate_formula(formula, None::); - calculate::result_to_string(result) - .parse::() - .unwrap() + let result = calculate::calculate_formula(formula, None::>); + calculate::result_to_string(result).parse::().unwrap() } -fn evaluate_formula_string_with_custom_function( +fn evaluate_formula_string_with_custom_function( s: &str, - custom_function: Option<&impl Fn(String, Vec) -> types::Value>, + custom_function: Option<&impl Fn(String, Vec) -> types::Value>, //reference: Option<&impl Fn(String) -> types::Value>, -) -> String { +) -> String +where + N: XlNum, + ::Err: Debug, +{ let formula = parse_formula::parse_string_to_formula(s, custom_function); - let result = calculate::calculate_formula(formula, None::); + let result = calculate::calculate_formula(formula, None::>); calculate::result_to_string(result) } -fn _evaluate_formula_number_with_custom_function_and_reference( +fn _evaluate_formula_number_with_custom_function_and_reference( s: &str, - custom_function: Option<&impl Fn(String, Vec) -> types::Value>, - reference: Option<&impl Fn(String) -> types::Value>, -) -> XlNum { + custom_function: Option<&impl Fn(String, Vec) -> types::Value>, + reference: Option<&impl Fn(String) -> types::Value>, +) -> N +where + N: XlNum, + ::Err: Debug, +{ let formula = parse_formula::parse_string_to_formula(s, custom_function); let result = calculate::calculate_formula(formula, reference); - calculate::result_to_string(result) - .parse::() - .unwrap() + calculate::result_to_string(result).parse::().unwrap() +} + +macro_rules! test_all_types { + ($func:ident, $formula:expr, $result:expr) => { + assert_eq!($func::($formula), $result); + assert_eq!($func::($formula), $result); + }; +} + +macro_rules! test_all_types_with_data { + ($func:ident, $formula:expr, $data:expr, $result:expr) => { + assert_eq!($func::($formula, $data), $result); + assert_eq!($func::($formula, $data), $result); + }; } /////////////////// Simple math operators with floats and integer /////////////////// #[test] fn it_evaluate_add_operator_simple_addition() { - assert_eq!(evaluate_formula_number("=1+2"), 3.0,); + test_all_types!(evaluate_formula_number, "=1+2", 3.0); } #[test] fn it_evaluate_add_operator_spaces_between_operators() { - assert_eq!(evaluate_formula_number("=1 + 2"), 3.0,); + test_all_types!(evaluate_formula_number, "=1 + 2", 3.0); } #[test] fn it_evaluate_add_operator_spaces_before_number() { - assert_eq!(evaluate_formula_number("= 1+2"), 3.0,); + test_all_types!(evaluate_formula_number, "= 1+2", 3.0); } #[test] fn it_evaluate_add_operator_with_large_numbers() { - assert_eq!( - evaluate_formula_number("=1234567890 + 1234567890"), + test_all_types!( + evaluate_formula_number, + "=1234567890 + 1234567890", 2469135780.0 ); } #[test] fn it_evaluate_add_operator_with_negative_numbers() { - assert_eq!(evaluate_formula_number("=-1 + -2"), -3.0); + test_all_types!(evaluate_formula_number, "=-1 + -2", -3.0); } #[test] fn it_evaluate_minus_operator1() { - assert_eq!(evaluate_formula_number("=123 - 23"), 100.0,); + test_all_types!(evaluate_formula_number, "=123 - 23", 100.0); } #[test] fn it_evaluate_minus_operator_with_negative_numbers() { - assert_eq!(evaluate_formula_number("=-12--6"), -6.0,); + test_all_types!(evaluate_formula_number, "=-12--6", -6.0); } #[test] fn it_evaluate_multiply_operator() { - assert_eq!(evaluate_formula_number("=3 * 2"), 6.0,); + test_all_types!(evaluate_formula_number, "=3 * 2", 6.0); } #[test] fn it_evaluate_divide_operator() { - assert_eq!(evaluate_formula_number("=6 / 3"), 2.0,); + test_all_types!(evaluate_formula_number, "=6 / 3", 2.0); } #[test] fn it_evaluate_divide_operator_divsion_by_zero() { - assert_eq!(evaluate_formula_string("=6 / 0"), "#DIV/0!"); + test_all_types!(evaluate_formula_string, "=6 / 0", "#DIV/0!"); } #[test] fn it_evaluate_negative() { - assert_eq!(evaluate_formula_number("=-1 * -5"), 5.0,); + test_all_types!(evaluate_formula_number, "=-1 * -5", 5.0); } #[test] fn it_evaluate_power_int() { - assert_eq!(evaluate_formula_number("=2^3"), 8.0,); + test_all_types!(evaluate_formula_number, "=2^3", 8.0); } #[test] fn it_evaluate_float() { - assert_eq!(evaluate_formula_number("=1.2+0.5"), 1.7,); + test_all_types!(evaluate_formula_number, "=1.2+0.5", 1.7); } #[test] -#[cfg(feature = "f32")] -fn it_evaluate_negative_float_f32() { +fn it_evaluate_negative_float() { // left: `-0.70000005`, right: `-0.7`' - assert_approx_eq::assert_approx_eq!(evaluate_formula_number("=-1.2+0.5"), -0.7); -} - -#[test] -#[cfg(feature = "f64")] -fn it_evaluate_negative_float_f64() { - assert_eq!(evaluate_formula_number("=-1.2+0.5"), -0.7); + assert_approx_eq::assert_approx_eq!(evaluate_formula_number::("=-1.2+0.5"), -0.7); + assert_eq!(evaluate_formula_number::("=-1.2+0.5"), -0.7); } #[test] fn it_evaluate_power_float() { - assert_eq!(evaluate_formula_number("=4^0.5"), 2.0,); + test_all_types!(evaluate_formula_number, "=4^0.5", 2.0); } #[test] fn it_evaluate_multiple_operations() { - assert_eq!(evaluate_formula_number("=1+2+3"), 6.0,); + test_all_types!(evaluate_formula_number, "=1+2+3", 6.0); } #[test] fn it_evaluate_multiple_operations2() { - assert_eq!(evaluate_formula_number("=1+2-3"), 0.0,); + test_all_types!(evaluate_formula_number, "=1+2-3", 0.0); } #[test] fn it_evaluate_multiple_operations_in_right_order() { - assert_eq!(evaluate_formula_number("=1+2*3"), 7.0,); + test_all_types!(evaluate_formula_number, "=1+2*3", 7.0); } #[test] fn it_evaluate_multiple_operations_in_right_order2() { - assert_eq!(evaluate_formula_number("=1+3/3"), 2.0,); + test_all_types!(evaluate_formula_number, "=1+3/3", 2.0); } #[test] fn it_evaluate_multiple_operations_with_errors() { - assert_eq!(evaluate_formula_string("=1+3/0"), "#DIV/0!",); + test_all_types!(evaluate_formula_string, "=1+3/0", "#DIV/0!"); } #[test] fn it_evaluate_parens() { - assert_eq!(evaluate_formula_number("=(1+2)"), 3.0,); + test_all_types!(evaluate_formula_number, "=(1+2)", 3.0); } #[test] fn it_evaluate_multiple_parens() { - assert_eq!(evaluate_formula_number("=(1+2)+(3+4)"), 10.0,); + test_all_types!(evaluate_formula_number, "=(1+2)+(3+4)", 10.0); } #[test] fn it_evaluate_nested_parens() { - assert_eq!(evaluate_formula_number("=(1*(2+3))*2"), 10.0,); + test_all_types!(evaluate_formula_number, "=(1*(2+3))*2", 10.0); } /////////////////// Strings /////////////////// #[test] fn it_evaluate_strings() { - assert_eq!(evaluate_formula_string("=\"Hello! \""), "Hello! ",); + test_all_types!(evaluate_formula_string, "=\"Hello! \"", "Hello! "); } #[test] fn it_evaluate_strings_in_numeric_operator() { - assert_eq!(evaluate_formula_string("=\"Hello\"+1"), "#CAST!",); + test_all_types!(evaluate_formula_string, "=\"Hello\"+1", "#CAST!"); } #[test] fn it_evaluate_strings_in_numeric_operator2() { - assert_eq!(evaluate_formula_string("=1 + \"Hello\""), "#CAST!",); + test_all_types!(evaluate_formula_string, "=1 + \"Hello\"", "#CAST!"); } #[test] fn it_evaluate_concat_operator1() { - assert_eq!( - evaluate_formula_string("=\"Hello\" & \"World!\""), - "HelloWorld!", + test_all_types!( + evaluate_formula_string, + "=\"Hello\" & \"World!\"", + "HelloWorld!" ); } #[test] fn it_evaluate_concat_operator3() { - assert_eq!( - evaluate_formula_string("=\"Hello \" & \" World!\""), - "Hello World!", + test_all_types!( + evaluate_formula_string, + "=\"Hello \" & \" World!\"", + "Hello World!" ); } #[test] fn it_evaluate_concat_operator_with_casting() { - assert_eq!(evaluate_formula_string("=\"Hello\"&1"), "Hello1",); + test_all_types!(evaluate_formula_string, "=\"Hello\"&1", "Hello1"); } #[test] fn it_evaluate_concat_operator_with_casting2() { - assert_eq!(evaluate_formula_string("=\"Hello \"&1.2"), "Hello 1.2",); + test_all_types!(evaluate_formula_string, "=\"Hello \"&1.2", "Hello 1.2"); } #[test] fn it_evaluate_concat_operator_with_numberic() { - assert_eq!(evaluate_formula_string("=1 & 2"), "12",); + test_all_types!(evaluate_formula_string, "=1 & 2", "12"); } #[test] fn it_evaluate_strings_with_quoted_quotes1() { - assert_eq!( - evaluate_formula_string("=\"Hello 'World'\""), - "Hello 'World'", + test_all_types!( + evaluate_formula_string, + "=\"Hello 'World'\"", + "Hello 'World'" ); } #[test] fn it_evaluate_strings_with_quoted_quotes() { - assert_eq!( - evaluate_formula_string("=\"Hello \"\"World\"\"\""), - "Hello \"World\"", + test_all_types!( + evaluate_formula_string, + "=\"Hello \"\"World\"\"\"", + "Hello \"World\"" ); } #[test] fn it_evaluate_strings_with_single_quotes() { - assert_eq!( - evaluate_formula_string("=\"Hello \"&'World'"), - "Hello World", + test_all_types!( + evaluate_formula_string, + "=\"Hello \"&'World'", + "Hello World" ); } #[test] fn it_evaluate_strings_with_quotes() { - assert_eq!( - evaluate_formula_string("='Hello \"World\"'"), - "Hello \"World\"", + test_all_types!( + evaluate_formula_string, + "='Hello \"World\"'", + "Hello \"World\"" ); } #[test] fn it_evaluate_strings_with_quotes2() { - assert_eq!( - evaluate_formula_string("='Hello'"), // '& 'World' - "Hello", + test_all_types!( + evaluate_formula_string, + "='Hello'", // '& 'World' + "Hello" ); } /////////////////// Constants /////////////////// #[test] fn it_evaluate_constant_number() { - assert_eq!(evaluate_formula_number("1"), 1.0,); + test_all_types!(evaluate_formula_number, "1", 1.0); } #[test] fn it_evaluate_constant_number_float() { - assert_eq!(evaluate_formula_number("1.2"), 1.2,); + test_all_types!(evaluate_formula_number, "1.2", 1.2); } #[test] fn it_evaluate_constant_text() { - assert_eq!(evaluate_formula_string("Hello World"), "Hello World",); + test_all_types!(evaluate_formula_string, "Hello World", "Hello World"); } #[test] fn it_evaluate_constant_text_with_quotes() { - assert_eq!(evaluate_formula_string("Hello \"World'"), "Hello \"World'",); + test_all_types!(evaluate_formula_string, "Hello \"World'", "Hello \"World'"); } #[test] fn it_evaluate_constant_starting_with_equal() { - assert_eq!(evaluate_formula_string("'="), "=",); - assert_eq!(evaluate_formula_string("'=hello"), "=hello",); + test_all_types!(evaluate_formula_string, "'=", "="); + test_all_types!(evaluate_formula_string, "'=hello", "=hello"); } /////////////////// Formulas /////////////////// #[test] fn it_support_basic_math_function() { - assert_eq!(evaluate_formula_number("=ABS(-1)"), 1.0,); + test_all_types!(evaluate_formula_number, "=ABS(-1)", 1.0); } #[test] fn it_support_basic_math_function_with_nested_formulas() { - assert_eq!(evaluate_formula_number("=ABS(-1-4)"), 5.0,); + test_all_types!(evaluate_formula_number, "=ABS(-1-4)", 5.0); } #[test] fn it_support_basic_math_function_with_nested_functions() { - assert_eq!(evaluate_formula_number("=ABS(ABS(-1))"), 1.0,); + test_all_types!(evaluate_formula_number, "=ABS(ABS(-1))", 1.0); } #[test] fn it_evaluate_functions_sum() { - assert_eq!( - evaluate_formula_number("=SUM(1*1, ABS(2), ABS(2+1), 4)"), - 10.0, + test_all_types!( + evaluate_formula_number, + "=SUM(1*1, ABS(2), ABS(2+1), 4)", + 10.0 ); - assert_eq!(evaluate_formula_number("=SUM(1, 2, , 3)"), 6.0,); - assert_eq!(evaluate_formula_number("=SUM( 1 , 2,,3,)"), 6.0,); - assert_eq!(evaluate_formula_number("=SUM( 1 , 2,,,3,)"), 6.0,); - assert_eq!(evaluate_formula_number("=SUM(,)"), 0.0,); + test_all_types!(evaluate_formula_number, "=SUM(1, 2, , 3)", 6.0); + test_all_types!(evaluate_formula_number, "=SUM( 1 , 2,,3,)", 6.0); + test_all_types!(evaluate_formula_number, "=SUM( 1 , 2,,,3,)", 6.0); + test_all_types!(evaluate_formula_number, "=SUM(,)", 0.0); } #[test] fn it_evaluate_functions_avg() { - assert_eq!(evaluate_formula_number("=AVERAGE(1,2)"), 1.5,); - assert_eq!(evaluate_formula_number("=AVERAGE({1,2,3})"), 2.0,); - assert_eq!(evaluate_formula_number("=AVERAGE({1,2,3},1,2,3)"), 2.0,); - assert_eq!(evaluate_formula_number("=AVERAGE(3,1,2,3)"), 2.25,); - assert_eq!(evaluate_formula_number("=AVERAGE(1,2,3,4,5,1,2,3)"), 2.625,); - assert_eq!( - evaluate_formula_number("=AVERAGE({1,2,3,4,5},1,2,3)"), - 2.625, - ); - assert_eq!( - evaluate_formula_number("=AVERAGE(AVERAGE({1,2,3,4,5}),1,2,3)"), - 2.25, - ); - assert_eq!(evaluate_formula_number("=AVERAGE({100,200})"), 150.0,); - assert_eq!(evaluate_formula_number("=AVERAGE( 1 , 2,,3)"), 1.5,); - assert_eq!(evaluate_formula_number("=AVERAGE( 1 , )"), 0.5,); - assert_eq!(evaluate_formula_number("=AVERAGE(,)"), 0.0,); - assert_eq!(evaluate_formula_number("=AVERAGE(1,,2,3,)",), 1.2); - //assert_eq!(evaluate_formula_number(&"=AVERAGE({{100,200}})"), 150.0); + test_all_types!(evaluate_formula_number, "=AVERAGE(1,2)", 1.5); + test_all_types!(evaluate_formula_number, "=AVERAGE({1,2,3})", 2.0); + test_all_types!(evaluate_formula_number, "=AVERAGE({1,2,3},1,2,3)", 2.0); + test_all_types!(evaluate_formula_number, "=AVERAGE(3,1,2,3)", 2.25); + test_all_types!(evaluate_formula_number, "=AVERAGE(1,2,3,4,5,1,2,3)", 2.625); + test_all_types!( + evaluate_formula_number, + "=AVERAGE({1,2,3,4,5},1,2,3)", + 2.625 + ); + test_all_types!( + evaluate_formula_number, + "=AVERAGE(AVERAGE({1,2,3,4,5}),1,2,3)", + 2.25 + ); + test_all_types!(evaluate_formula_number, "=AVERAGE({100,200})", 150.0); + test_all_types!(evaluate_formula_number, "=AVERAGE( 1 , 2,,3)", 1.5); + test_all_types!(evaluate_formula_number, "=AVERAGE( 1 , )", 0.5); + test_all_types!(evaluate_formula_number, "=AVERAGE(,)", 0.0); + test_all_types!(evaluate_formula_number, "=AVERAGE(1,,2,3,)", 1.2); + // test_all_types!(evaluate_formula_number, "=AVERAGE({{100,200}})", 150.0); } #[test] fn it_evaluate_functions_product() { - assert_eq!(evaluate_formula_number("=PRODUCT(ABS(1),2*1, 3,4*1)"), 24.0,); - assert_eq!(evaluate_formula_number("=PRODUCT( 1 , 2,,3)"), 6.0,); - assert_eq!(evaluate_formula_number("=PRODUCT(,)"), 0.0,); + test_all_types!(evaluate_formula_number, "=PRODUCT(ABS(1),2*1, 3,4*1)", 24.0); + test_all_types!(evaluate_formula_number, "=PRODUCT( 1 , 2,,3)", 6.0); + test_all_types!(evaluate_formula_number, "=PRODUCT(,)", 0.0); } #[test] fn it_evaluate_operators_with_casting() { - assert_eq!(evaluate_formula_number("=\"1\"+2+\"3\""), 6.0,); + test_all_types!(evaluate_formula_number, "=\"1\"+2+\"3\"", 6.0); } #[test] fn it_evaluate_functions_with_casting() { - assert_eq!(evaluate_formula_number("=SUM(1,2,\"3\")"), 6.0,); + test_all_types!(evaluate_formula_number, "=SUM(1,2,\"3\")", 6.0); } /////////////////////// Parse error ////////////////////////////////// #[test] fn it_evaluate_wrong_parens1() { - assert_eq!(evaluate_formula_string("=(2+3"), "#PARSE!",); - assert_eq!(evaluate_formula_string("=\"Hello World"), "#PARSE!",); - assert_eq!(evaluate_formula_string("=Hello World"), "#PARSE!",); + test_all_types!(evaluate_formula_string, "=(2+3", "#PARSE!"); + test_all_types!(evaluate_formula_string, "=\"Hello World", "#PARSE!"); + test_all_types!(evaluate_formula_string, "=Hello World", "#PARSE!"); } //////////////////////////// Boolean ////////////////////////////////// #[test] fn it_evaluate_comparison_operators() { - assert_eq!(evaluate_formula_string("=1*1=1/1"), "TRUE",); - assert_eq!(evaluate_formula_string("=1^1<>1"), "FALSE",); - assert_eq!(evaluate_formula_string("=1*2>1"), "TRUE",); - assert_eq!(evaluate_formula_string("=1*1/1+2<1^1"), "FALSE",); - assert_eq!(evaluate_formula_string("=2>=1"), "TRUE",); - assert_eq!(evaluate_formula_string("=11<=3"), "FALSE",); - assert_eq!(evaluate_formula_string("=\"Joshu\"=\"Joshu\""), "TRUE",); - assert_eq!(evaluate_formula_string("=\"Joshu\"<>\"NotJoshu\""), "TRUE",); + test_all_types!(evaluate_formula_string, "=1*1=1/1", "TRUE"); + test_all_types!(evaluate_formula_string, "=1^1<>1", "FALSE"); + test_all_types!(evaluate_formula_string, "=1*2>1", "TRUE"); + test_all_types!(evaluate_formula_string, "=1*1/1+2<1^1", "FALSE"); + test_all_types!(evaluate_formula_string, "=2>=1", "TRUE"); + test_all_types!(evaluate_formula_string, "=11<=3", "FALSE"); + test_all_types!(evaluate_formula_string, "=\"Joshu\"=\"Joshu\"", "TRUE"); + test_all_types!(evaluate_formula_string, "=\"Joshu\"<>\"NotJoshu\"", "TRUE"); } #[test] fn it_evaluate_boolean_or() { - assert_eq!(evaluate_formula_string("=OR(1>1,1<>1)"), "FALSE",); - assert_eq!(evaluate_formula_string("=OR(1=1,2<=4)"), "TRUE",); - assert_eq!(evaluate_formula_string("=OR(\"True\")"), "TRUE",); - assert_eq!(evaluate_formula_string("=OR(True)"), "TRUE",); - assert_eq!(evaluate_formula_string("=OR(1)"), "TRUE",); - assert_eq!(evaluate_formula_string("=OR(\"test\")"), "#CAST!",); - assert_eq!( - evaluate_formula_string("=OR(\"false\",\"FALSE\", 1, FALSE)"), - "TRUE", + test_all_types!(evaluate_formula_string, "=OR(1>1,1<>1)", "FALSE"); + test_all_types!(evaluate_formula_string, "=OR(1=1,2<=4)", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(\"True\")", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(True)", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(1)", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(\"test\")", "#CAST!"); + test_all_types!( + evaluate_formula_string, + "=OR(\"false\",\"FALSE\", 1, FALSE)", + "TRUE" ); - assert_eq!( - evaluate_formula_string("=OR(\"True\",1,\"test\", true) "), - "TRUE", + test_all_types!( + evaluate_formula_string, + "=OR(\"True\",1,\"test\", true) ", + "TRUE" ); - assert_eq!(evaluate_formula_string("=OR(1, )"), "TRUE",); - assert_eq!(evaluate_formula_string("=OR(1,,, )"), "TRUE",); + test_all_types!(evaluate_formula_string, "=OR(1, )", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(1,,, )", "TRUE"); } #[test] fn it_evaluate_boolean_and() { - assert_eq!(evaluate_formula_string("=AND(1>1,1=1)"), "FALSE",); - assert_eq!(evaluate_formula_string("=AND(1=1,2<=4)"), "TRUE",); - assert_eq!(evaluate_formula_string("=AND(\"true\", 0)"), "FALSE",); - assert_eq!(evaluate_formula_string("=AND(\"True\")"), "TRUE",); - assert_eq!(evaluate_formula_string("=AND(false)"), "FALSE",); - assert_eq!(evaluate_formula_string("=AND(1)"), "TRUE",); - assert_eq!( - evaluate_formula_string("=AND(\"test\", \"test\")"), - "#VALUE!", + test_all_types!(evaluate_formula_string, "=AND(1>1,1=1)", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(1=1,2<=4)", "TRUE"); + test_all_types!(evaluate_formula_string, "=AND(\"true\", 0)", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(\"True\")", "TRUE"); + test_all_types!(evaluate_formula_string, "=AND(false)", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(1)", "TRUE"); + test_all_types!( + evaluate_formula_string, + "=AND(\"test\", \"test\")", + "#VALUE!" ); - assert_eq!( - evaluate_formula_string("=AND(\"test\",\"True\", 1, true) "), - "#VALUE!", + test_all_types!( + evaluate_formula_string, + "=AND(\"test\",\"True\", 1, true) ", + "#VALUE!" ); - assert_eq!( - evaluate_formula_string("=AND(\"True\",\"test\", 1, true) "), - "#VALUE!", + test_all_types!( + evaluate_formula_string, + "=AND(\"True\",\"test\", 1, true) ", + "#VALUE!" ); - assert_eq!( - evaluate_formula_string("=AND(\"True\", 1, true, \"test\")"), - "#VALUE!", + test_all_types!( + evaluate_formula_string, + "=AND(\"True\", 1, true, \"test\")", + "#VALUE!" ); - assert_eq!(evaluate_formula_string("=AND(1, )"), "FALSE",); - assert_eq!(evaluate_formula_string("=AND(1,,,1)"), "FALSE",); + test_all_types!(evaluate_formula_string, "=AND(1, )", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(1,,,1)", "FALSE"); } #[test] fn it_evaluate_boolean_xor() { - assert_eq!(evaluate_formula_string("=XOR(2=2,1=1)"), "FALSE",); - assert_eq!(evaluate_formula_string("=XOR(1=1,2>4)"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(\"True\")"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(False)"), "FALSE",); - assert_eq!(evaluate_formula_string("=XOR(1)"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(1=1,\"test\")"), "TRUE",); - assert_eq!( - evaluate_formula_string("=XOR(TRUE, TRUE, TRUE, TRUE)"), + test_all_types!(evaluate_formula_string, "=XOR(2=2,1=1)", "FALSE"); + test_all_types!(evaluate_formula_string, "=XOR(1=1,2>4)", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(\"True\")", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(False)", "FALSE"); + test_all_types!(evaluate_formula_string, "=XOR(1)", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(1=1,\"test\")", "TRUE"); + test_all_types!( + evaluate_formula_string, + "=XOR(TRUE, TRUE, TRUE, TRUE)", "FALSE" ); - assert_eq!(evaluate_formula_string("=XOR(TRUE, TRUE)"), "FALSE"); - assert_eq!( - evaluate_formula_string("=XOR(false, FALSE, \"false\", false)"), + test_all_types!(evaluate_formula_string, "=XOR(TRUE, TRUE)", "FALSE"); + test_all_types!( + evaluate_formula_string, + "=XOR(false, FALSE, \"false\", false)", "FALSE" ); - assert_eq!(evaluate_formula_string("=XOR(true)"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(false)"), "FALSE",); - assert_eq!(evaluate_formula_string("=XOR(1, )"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(1,, )"), "TRUE",); - assert_eq!(evaluate_formula_string("=XOR(1,,1 )"), "FALSE",); - assert_eq!(evaluate_formula_string("=XOR(1,,1, )"), "FALSE",); + test_all_types!(evaluate_formula_string, "=XOR(true)", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(false)", "FALSE"); + test_all_types!(evaluate_formula_string, "=XOR(1, )", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(1,, )", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR(1,,1 )", "FALSE"); + test_all_types!(evaluate_formula_string, "=XOR(1,,1, )", "FALSE"); } #[test] fn it_evaluate_boolean_not() { - assert_eq!(evaluate_formula_string("=NOT(11<=3)"), "TRUE",); - assert_eq!(evaluate_formula_string("=NOT(1=1)"), "FALSE",); - assert_eq!(evaluate_formula_string("=NOT(True)"), "FALSE",); - assert_eq!(evaluate_formula_string("=NOT(\"false\")"), "TRUE",); - assert_eq!(evaluate_formula_string("=NOT(\"test\")"), "#CAST!",); - assert_eq!(evaluate_formula_string("=NOT(0)"), "TRUE",); - assert_eq!(evaluate_formula_string("=not(11<=3)"), "TRUE",); - assert_eq!(evaluate_formula_string("=Not(11<=3)"), "TRUE",); - assert_eq!(evaluate_formula_string("=nOT(11<=3)"), "TRUE",); + test_all_types!(evaluate_formula_string, "=NOT(11<=3)", "TRUE"); + test_all_types!(evaluate_formula_string, "=NOT(1=1)", "FALSE"); + test_all_types!(evaluate_formula_string, "=NOT(True)", "FALSE"); + test_all_types!(evaluate_formula_string, "=NOT(\"false\")", "TRUE"); + test_all_types!(evaluate_formula_string, "=NOT(\"test\")", "#CAST!"); + test_all_types!(evaluate_formula_string, "=NOT(0)", "TRUE"); + test_all_types!(evaluate_formula_string, "=not(11<=3)", "TRUE"); + test_all_types!(evaluate_formula_string, "=Not(11<=3)", "TRUE"); + test_all_types!(evaluate_formula_string, "=nOT(11<=3)", "TRUE"); } //////////////////////////// References ////////////////////////////////// #[test] fn it_evaluate_references() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(1.0), - "B" => types::Value::Number(2.0), - "C" => types::Value::Number(3.0), - "fix_rate" => types::Value::Number(10.0), - "input." => types::Value::Number(2.0), - "D" => types::Value::Number(1.0), - "F" => types::Value::Text("=D+1".to_string()), - "G" => types::Value::Text("=F+1+D+1".to_string()), - "Test" => types::Value::Iterator(vec![ - types::Value::Number(100.0), - types::Value::Number(200.0), - types::Value::Number(300.0), - ]), - - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=A+B", Some(&data_function)), - 3.0, - ); - assert_eq!( - evaluate_formula_number_with_reference("=(A*(B+C))*B", Some(&data_function)), - 10.0, - ); - assert_eq!( - evaluate_formula_number_with_reference("=fix_rate*input.", Some(&data_function)), - 20.0, + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::from_f32(1.0).unwrap()), + "B" => types::Value::Number(N::from_f32(2.0).unwrap()), + "C" => types::Value::Number(N::from_f32(3.0).unwrap()), + "fix_rate" => types::Value::Number(N::from_f32(10.0).unwrap()), + "input." => types::Value::Number(N::from_f32(2.0).unwrap()), + "D" => types::Value::Number(N::from_f32(1.0).unwrap()), + "F" => types::Value::Text("=D+1".to_string()), + "G" => types::Value::Text("=F+1+D+1".to_string()), + "Test" => types::Value::Iterator(vec![ + types::Value::Number(N::from_f32(100.0).unwrap()), + types::Value::Number(N::from_f32(200.0).unwrap()), + types::Value::Number(N::from_f32(300.0).unwrap()), + ]), + + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A+B", + Some(&data_function), + 3.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=D+F", Some(&data_function)), - 3.0, + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=(A*(B+C))*B", + Some(&data_function), + 10.0 + ); + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=fix_rate*input.", + Some(&data_function), + 20.0 + ); + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=D+F", + Some(&data_function), + 3.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=G+F", Some(&data_function)), - 7.0, + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=G+F", + Some(&data_function), + 7.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM(A,B,C)", Some(&data_function)), - 6.0, + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(A,B,C)", + Some(&data_function), + 6.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(Test)", Some(&data_function)), - 200.0, + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(Test)", + Some(&data_function), + 200.0 ); } #[test] fn it_evaluate_references_other_formulas() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Text("=1+B".to_string()), - "B" => types::Value::Number(3.0), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=A+B", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Text("=1+B".to_string()), + "B" => types::Value::Number(N::from_f32(3.0).unwrap()), + _ => types::Value::Error(types::Error::Value), + } + } + + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A+B", + Some(&data_function), 7.0 ); } #[test] fn it_evaluate_references_boolean_formulas() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Boolean(types::Boolean::True), - "B" => types::Value::Boolean(types::Boolean::False), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(A,B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Boolean(types::Boolean::True), + "B" => types::Value::Boolean(types::Boolean::False), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(A,B)", + Some(&data_function), "FALSE" ); } #[test] fn it_evaluate_references_error_value_formulas() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Boolean(types::Boolean::True), - "B" => types::Value::Error(types::Error::Value), //types::Value::Boolean(types::Boolean::False), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(A,B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Boolean(types::Boolean::True), + "B" => types::Value::Error(types::Error::Value), //types::Value::Boolean(types::Boolean::False), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(A,B)", + Some(&data_function), "#VALUE!" ); } #[test] fn it_evaluate_references_with_dot() { - let data_function = |s: String| match s.as_str() { - "A.B" => types::Value::Number(1.0), - "B.C" => types::Value::Number(2.0), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=A.B+B.C", Some(&data_function)), - 3.0, + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A.B" => types::Value::Number(N::from_f32(1.0).unwrap()), + "B.C" => types::Value::Number(N::from_f32(2.0).unwrap()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A.B+B.C", + Some(&data_function), + 3.0 ); } #[test] fn it_evaluate_iterators() { - assert_eq!(evaluate_formula_number("=SUM({1,2,3})"), 6.0,); - assert_eq!(evaluate_formula_number("=PRODUCT({1,2,3})"), 6.0,); - assert_eq!(evaluate_formula_number("=SUM({1,2,3}, {5,6})"), 17.0,); + test_all_types!(evaluate_formula_number, "=SUM({1,2,3})", 6.0); + test_all_types!(evaluate_formula_number, "=PRODUCT({1,2,3})", 6.0); + test_all_types!(evaluate_formula_number, "=SUM({1,2,3}, {5,6})", 17.0); } #[test] fn it_evaluate_iterators_and_scalars() { - assert_eq!(evaluate_formula_number("=SUM({1,2,3}, 4)"), 10.0,); - assert_eq!(evaluate_formula_number("=PRODUCT({1,2,3}, 4)"), 24.0,); + test_all_types!(evaluate_formula_number, "=SUM({1,2,3}, 4)", 10.0); + test_all_types!(evaluate_formula_number, "=PRODUCT({1,2,3}, 4)", 24.0); } #[test] fn it_evaluate_multiple_iterators_and_scalars() { - assert_eq!(evaluate_formula_number("=SUM({1,2,3})"), 6.0,); - assert_eq!(evaluate_formula_number("=SUM({ 1,2,3}, 4, {5, 6})"), 21.0,); - assert_eq!(evaluate_formula_number("=SUM({1,2,3},4,{5,6})"), 21.0,); - assert_eq!(evaluate_formula_number("=SUM({1+1,2,3-3},4,{5,6*1})"), 19.0,); - assert_eq!( - evaluate_formula_number("=PRODUCT({1+1,2,3-2},4, {5,6*1})"), - 480.0, + test_all_types!(evaluate_formula_number, "=SUM({1,2,3})", 6.0); + test_all_types!(evaluate_formula_number, "=SUM({ 1,2,3}, 4, {5, 6})", 21.0); + test_all_types!(evaluate_formula_number, "=SUM({1,2,3},4,{5,6})", 21.0); + test_all_types!(evaluate_formula_number, "=SUM({1+1,2,3-3},4,{5,6*1})", 19.0); + test_all_types!( + evaluate_formula_number, + "=PRODUCT({1+1,2,3-2},4, {5,6*1})", + 480.0 ); } #[test] fn it_evaluate_references_iterator() { - let data_function = |s: String| match s.as_str() { - "A.B" => types::Value::Number(1.0), - "B.C" => types::Value::Number(2.0), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=SUM({A.B,B.C})", Some(&data_function)), - 3.0, + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A.B" => types::Value::Number(N::from_f32(1.0).unwrap()), + "B.C" => types::Value::Number(N::from_f32(2.0).unwrap()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM({A.B,B.C})", + Some(&data_function), + 3.0 ); } #[test] fn it_evaluate_iterator_operators() { - assert_eq!(evaluate_formula_string("={1,2,3}+{1,2,3}"), "{2,4,6}"); - assert_eq!(evaluate_formula_string("={3,2,1}-{1,2,3}"), "{2,0,-2}"); - assert_eq!(evaluate_formula_string("={1,2,3}*{1,2,3}"), "{1,4,9}"); - assert_eq!(evaluate_formula_string("={1,2,3}/{1,2,3}"), "{1,1,1}"); - assert_eq!( - evaluate_formula_string("={1,2,3}/{0,0,0}"), + test_all_types!(evaluate_formula_string, "={1,2,3}+{1,2,3}", "{2,4,6}"); + test_all_types!(evaluate_formula_string, "={3,2,1}-{1,2,3}", "{2,0,-2}"); + test_all_types!(evaluate_formula_string, "={1,2,3}*{1,2,3}", "{1,4,9}"); + test_all_types!(evaluate_formula_string, "={1,2,3}/{1,2,3}", "{1,1,1}"); + test_all_types!( + evaluate_formula_string, + "={1,2,3}/{0,0,0}", "{#DIV/0!,#DIV/0!,#DIV/0!}" ); - assert_eq!(evaluate_formula_string("=-{1,2,3}"), "{-1,-2,-3}"); - assert_eq!(evaluate_formula_string("=-({1,2,3})"), "{-1,-2,-3}"); + test_all_types!(evaluate_formula_string, "=-{1,2,3}", "{-1,-2,-3}"); + test_all_types!(evaluate_formula_string, "=-({1,2,3})", "{-1,-2,-3}"); } #[test] fn it_evaluate_iterator_in_logic_functions() { - assert_eq!(evaluate_formula_string("=AND(0,{0,0,0},0)"), "FALSE"); - assert_eq!(evaluate_formula_string("=AND(0,{0,0,0})"), "FALSE"); - assert_eq!(evaluate_formula_string("=AND({0,0,0},0)"), "FALSE"); - assert_eq!(evaluate_formula_string("=OR(1,{1,0,0},0,1)"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR(0,{0,1,0})"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR({0,0,0},1)"), "TRUE"); - assert_eq!(evaluate_formula_string("=AND({0,0,0})"), "FALSE"); - assert_eq!(evaluate_formula_string("=AND({1,0,0})"), "FALSE"); - assert_eq!(evaluate_formula_string("=AND({1,1,1})"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR({0,0,0})"), "FALSE"); - assert_eq!(evaluate_formula_string("=OR({1,0,0})"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR({0,1,1})"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR({1,0,1})"), "TRUE"); - assert_eq!(evaluate_formula_string("=OR({1,1,1})"), "TRUE"); - assert_eq!(evaluate_formula_string("=XOR({1,0,1})"), "FALSE"); - assert_eq!(evaluate_formula_string("=XOR({0,1,0})"), "TRUE"); - assert_eq!(evaluate_formula_string("=XOR({0,0,0})"), "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(0,{0,0,0},0)", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND(0,{0,0,0})", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND({0,0,0},0)", "FALSE"); + test_all_types!(evaluate_formula_string, "=OR(1,{1,0,0},0,1)", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR(0,{0,1,0})", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR({0,0,0},1)", "TRUE"); + test_all_types!(evaluate_formula_string, "=AND({0,0,0})", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND({1,0,0})", "FALSE"); + test_all_types!(evaluate_formula_string, "=AND({1,1,1})", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR({0,0,0})", "FALSE"); + test_all_types!(evaluate_formula_string, "=OR({1,0,0})", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR({0,1,1})", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR({1,0,1})", "TRUE"); + test_all_types!(evaluate_formula_string, "=OR({1,1,1})", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR({1,0,1})", "FALSE"); + test_all_types!(evaluate_formula_string, "=XOR({0,1,0})", "TRUE"); + test_all_types!(evaluate_formula_string, "=XOR({0,0,0})", "FALSE"); } #[test] fn it_evaluate_iterator_with_diffrent_number_of_entries() { - assert_eq!(evaluate_formula_string("={0,0}+{1,2,3}"), "{1,2,#ARG!}"); - assert_eq!(evaluate_formula_string("={0,0}*{1,2,3}"), "{0,0,#ARG!}"); - assert_eq!( - evaluate_formula_string("={1,2,3}/{0,0}"), + test_all_types!(evaluate_formula_string, "={0,0}+{1,2,3}", "{1,2,#ARG!}"); + test_all_types!(evaluate_formula_string, "={0,0}*{1,2,3}", "{0,0,#ARG!}"); + test_all_types!( + evaluate_formula_string, + "={1,2,3}/{0,0}", "{#DIV/0!,#DIV/0!,#ARG!}" ); - assert_eq!(evaluate_formula_string("={0,0}+{1,\"Hi\"}"), "{1,#CAST!}"); -} - -#[test] -fn it_evaluate_date() -> Result<(), ParseError> { - let start: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z")?; - let end: DateTime = DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z")?; - let data_function = |s: String| match s.as_str() { - "start" => types::Value::Date(start), - "end" => types::Value::Date(end), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=DAYS(end, start)", Some(&data_function)), + test_all_types!(evaluate_formula_string, "={0,0}+{1,\"Hi\"}", "{1,#CAST!}"); +} + +#[test] +fn it_evaluate_date() { + fn start_date() -> DateTime { + DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z").unwrap() + } + fn end_date() -> DateTime { + DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z").unwrap() + } + fn data_function(s: String) -> types::Value { + match s.as_str() { + "start" => types::Value::Date(start_date()), + "end" => types::Value::Date(end_date()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=DAYS(end, start)", + Some(&data_function), 182.00 ); - assert_eq!( - evaluate_formula_number_with_reference("=days(end, start)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=days(end, start)", + Some(&data_function), 182.00 ); - assert_eq!( - evaluate_formula_date_with_reference("=start + 1", Some(&data_function)), - (start + Duration::days(1)).to_string() + test_all_types_with_data!( + evaluate_formula_date_with_reference, + "=start + 1", + Some(&data_function), + (start_date() + Duration::days(1)).to_string() ); - assert_eq!( - evaluate_formula_date_with_reference("=end-3", Some(&data_function)), - (end - Duration::days(3)).to_string() + test_all_types_with_data!( + evaluate_formula_date_with_reference, + "=end-3", + Some(&data_function), + (end_date() - Duration::days(3)).to_string() ); - Ok(()) } #[test] fn it_evaluate_custom_functions_() { - let custom_functions = |s: String, params: Vec| match s.as_str() { - "Increase" => types::Value::Number(params[0] + 1.0), - "SimpleSum" => types::Value::Number(params[0] + params[1]), - "CustomSum" => types::Value::Number(params[0] + params[1] + params[2]), - "EqualFive" => types::Value::Number(5.0), - "CountText" => types::Value::Text(10.0.to_string()), - "CountNumber" => types::Value::Number(20.0), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_custom_function("=Increase(1)", Some(&custom_functions)), + fn custom_functions(s: String, params: Vec) -> types::Value { + match s.as_str() { + "Increase" => types::Value::Number(params[0] + N::one()), + "SimpleSum" => types::Value::Number(params[0] + params[1]), + "CustomSum" => types::Value::Number(params[0] + params[1] + params[2]), + "EqualFive" => types::Value::Number(N::from_f32(5.0).unwrap()), + "CountText" => types::Value::Text(10.0.to_string()), + "CountNumber" => types::Value::Number(N::from_f32(20.0).unwrap()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=Increase(1)", + Some(&custom_functions), 2.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=SimpleSum(1,2)", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=SimpleSum(1,2)", + Some(&custom_functions), 3.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=CustomSum(1,2,3)", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=CustomSum(1,2,3)", + Some(&custom_functions), 6.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=EqualFive()+1", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=EqualFive()+1", + Some(&custom_functions), 6.0 ); - assert_eq!( - evaluate_formula_string_with_custom_function("=\"P\"&CountText()", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_string_with_custom_function, + "=\"P\"&CountText()", + Some(&custom_functions), "P10" ); - assert_eq!( - evaluate_formula_string_with_custom_function( - "=\"P\"&CountNumber()", - Some(&custom_functions) - ), + test_all_types_with_data!( + evaluate_formula_string_with_custom_function, + "=\"P\"&CountNumber()", + Some(&custom_functions), "P20" ); } #[test] fn it_evaluate_left_and_right_functions() { - assert_eq!(evaluate_formula_string("=RIGHT(\"apple\", 3)"), "ple",); - assert_eq!(evaluate_formula_string("=RIGHT(\"apple\")"), "e",); + test_all_types!(evaluate_formula_string, "=RIGHT(\"apple\", 3)", "ple"); + test_all_types!(evaluate_formula_string, "=RIGHT(\"apple\")", "e"); - assert_eq!(evaluate_formula_string("=\"P\"&RIGHT(\"000\"&1,3)"), "P001",); - assert_eq!(evaluate_formula_string("=LEFT(\"apple\", 3)"), "app",); - assert_eq!(evaluate_formula_string("=LEFT(\"apple\")"), "a",); + test_all_types!(evaluate_formula_string, "=\"P\"&RIGHT(\"000\"&1,3)", "P001"); + test_all_types!(evaluate_formula_string, "=LEFT(\"apple\", 3)", "app"); + test_all_types!(evaluate_formula_string, "=LEFT(\"apple\")", "a"); - assert_eq!(evaluate_formula_string("=\"P\"&LEFT(\"000\"&1,3)"), "P000",); + test_all_types!(evaluate_formula_string, "=\"P\"&LEFT(\"000\"&1,3)", "P000"); - assert_eq!(evaluate_formula_string("=LEFT(\"apple\", 10)"), "apple",); - assert_eq!(evaluate_formula_string("=RIGHT(\"apple\", 10)"), "apple",); + test_all_types!(evaluate_formula_string, "=LEFT(\"apple\", 10)", "apple"); + test_all_types!(evaluate_formula_string, "=RIGHT(\"apple\", 10)", "apple"); } #[test] fn it_evaluates_blanks() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(1.0), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=SUM(A,B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::one()), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(A,B)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=A+B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A+B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference_no_conversion("=SUM(A,C)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference_no_conversion, + "=SUM(A,C)", + Some(&data_function), types::Value::Error(types::Error::Value) ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(A,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(A,B)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM(A,B,2,3,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(A,B,2,3,B)", + Some(&data_function), 6.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(A,B,2,3,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(A,B,2,3,B)", + Some(&data_function), 2.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(A,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(A,B)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(1,B,B,B,A,2)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(1,B,B,B,A,2)", + Some(&data_function), 2.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=1+B+B+B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=1+B+B+B", + Some(&data_function), 1.0 ); } #[test] fn it_evaluates_blanks_only() { - let data_function = |s: String| match s.as_str() { - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=SUM(B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(B)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_string_with_reference("=AVERAGE(B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=AVERAGE(B)", + Some(&data_function), "#DIV/0!" ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(B)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=-B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=-B", + Some(&data_function), 0.0 ); } #[test] fn it_evaluates_blanks_when_blank_in_first_position() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(1.0), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=SUM(B,A)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::one()), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(B,A)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B+A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B+A", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(B,A)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(B,A)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM(B,2,3,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(B,2,3,B)", + Some(&data_function), 5.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(B,2,3,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(B,2,3,B)", + Some(&data_function), 2.5 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(B,A)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(B,A)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(B,A,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(B,A,B)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(B,B,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(B,B,B)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(B,B,B,A,2)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(B,B,B,A,2)", + Some(&data_function), 2.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B+B+B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B+B+B", + Some(&data_function), 0.0 ); } #[test] fn it_evaluates_blanks_in_abs_function() { - let data_function = |s: String| match s.as_str() { - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=ABS(B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=ABS(B)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=ABS(-B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=ABS(-B)", + Some(&data_function), 0.0 ); } #[test] fn it_evaluates_blanks_in_days_function() { - let start: DateTime = DateTime::parse_from_rfc3339("2019-02-01T02:00:00.000Z") - .ok() - .unwrap(); - let data_function = |s: String| match s.as_str() { - "start" => types::Value::Date(start), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=DAYS(B, B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + let start: DateTime = + DateTime::parse_from_rfc3339("2019-02-01T02:00:00.000Z").unwrap(); + match s.as_str() { + "start" => types::Value::Date(start), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=DAYS(B, B)", + Some(&data_function), 0.00 ); - assert_eq!( - evaluate_formula_number_with_reference("=DAYS(start, B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=DAYS(start, B)", + Some(&data_function), 43495.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=DAYS(B, start)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=DAYS(B, start)", + Some(&data_function), -43495.0 ); } #[test] fn it_evaluates_blanks_with_operators() { - let data_function = |s: String| match s.as_str() { - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=1-B", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=1-B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B-1", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B-1", + Some(&data_function), -1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=1+B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=1+B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B+1", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B+1", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_string_with_reference("=1/B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=1/B", + Some(&data_function), "#DIV/0!" ); - assert_eq!( - evaluate_formula_number_with_reference("=B/1", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B/1", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=1*B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=1*B", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B*1", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B*1", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=1^B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=1^B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B^1", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B^1", + Some(&data_function), 0.0 ); } #[test] fn it_evaluates_blanks_with_operators_and_reference() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(1.0), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=A-B", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::one()), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A-B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B-A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B-A", + Some(&data_function), -1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=A+B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A+B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B+A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B+A", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_string_with_reference("=A/B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=A/B", + Some(&data_function), "#DIV/0!" ); - assert_eq!( - evaluate_formula_number_with_reference("=B/A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B/A", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=A*B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A*B", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B*A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B*A", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=A^B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=A^B", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=B^A", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=B^A", + Some(&data_function), 0.0 ); } #[test] fn it_evaluates_blanks_in_boolean_operations() { - let data_function = |s: String| match s.as_str() { - "T" => types::Value::Boolean(types::Boolean::True), - "B" => types::Value::Blank, - "F" => types::Value::Boolean(types::Boolean::False), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(T,B)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "T" => types::Value::Boolean(types::Boolean::True), + "B" => types::Value::Blank, + "F" => types::Value::Boolean(types::Boolean::False), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(T,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR(T,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR(T,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=NOT(B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=NOT(B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(F,B)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR(F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR(F,B)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR(T,B,F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR(T,B,F,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR(F,B,T,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR(F,B,T,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(T,B,F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(T,B,F,B)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND(F,B,T,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(F,B,T,B)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(T,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(T,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(F,B)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(T,B,F,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(T,B,F,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(F,B,T,B)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(F,B,T,B)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(T,B,F,B,T)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(T,B,F,B,T)", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR(F,B,T,B,F)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR(F,B,T,B,F)", + Some(&data_function), "TRUE" ); } #[test] fn evaluate_invalid_reference_in_functions() { - let data_function = |_s: String| types::Value::Error(types::Error::Value); + fn data_function(_s: String) -> types::Value { + types::Value::Error(types::Error::Value) + } + // test with a non existing reference - let formula = - parse_formula::parse_string_to_formula("=AND(T, NO_REFERENCE)", None::); assert_eq!( - calculate::calculate_formula(formula, Some(&data_function)), + calculate::calculate_formula::( + parse_formula::parse_string_to_formula( + "=AND(T, NO_REFERENCE)", + None::>, + ), + Some(&data_function) + ), types::Value::Error(types::Error::Value) ); assert_eq!( - evaluate_formula_boolean_with_reference("=AND(T, NO_REFERENCE)", Some(&data_function)), + calculate::calculate_formula::( + parse_formula::parse_string_to_formula( + "=AND(T, NO_REFERENCE)", + None::>, + ), + Some(&data_function) + ), + types::Value::Error(types::Error::Value) + ); + + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(T, NO_REFERENCE)", + Some(&data_function), "#VALUE!" ); } #[test] fn it_evaluates_blanks_in_comparison_operators() { - let data_function = |s: String| match s.as_str() { - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_string_with_reference("=B=B", Some(&data_function)), - "TRUE", + fn data_function(s: String) -> types::Value { + match s.as_str() { + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B=B", + Some(&data_function), + "TRUE" ); - assert_eq!( - evaluate_formula_string_with_reference("=1=B", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=1=B", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=B=1", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B=1", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=1>B", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=1>B", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=B>1", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B>1", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=0=B", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=0=B", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=B=0", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B=0", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=B=\"\"", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=B=\"\"", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=\"\"=B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=\"\"=B", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=\"something\"=B", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=\"something\"=B", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=B='test'", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B='test'", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("='test'=B", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "='test'=B", + Some(&data_function), + "FALSE" ); } #[test] fn it_evaluates_blanks_in_comparison_operators_with_references() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(-2.0), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_string_with_reference("=A>=B", Some(&data_function)), - "FALSE", + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::from_f32(-2.0).unwrap()), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=A>=B", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=B>=A", Some(&data_function)), - "FALSE", + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B>=A", + Some(&data_function), + "FALSE" ); - assert_eq!( - evaluate_formula_string_with_reference("=A types::Value::Number(-2.0), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_string_with_reference("=\"Hello\"&B", Some(&data_function)), - "Hello", - ); - assert_eq!( - evaluate_formula_string_with_reference("=B&\"Hello\"", Some(&data_function)), - "Hello", - ); - assert_eq!( - evaluate_formula_string_with_reference("=B&B", Some(&data_function)), - "", - ); - assert_eq!( - evaluate_formula_string_with_reference("=RIGHT(B)", Some(&data_function)), - "", - ); - assert_eq!( - evaluate_formula_string_with_reference("=LEFT(B)", Some(&data_function)), - "", + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::from_f32(-2.0).unwrap()), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=\"Hello\"&B", + Some(&data_function), + "Hello" + ); + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B&\"Hello\"", + Some(&data_function), + "Hello" + ); + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=B&B", + Some(&data_function), + "" + ); + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=RIGHT(B)", + Some(&data_function), + "" + ); + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=LEFT(B)", + Some(&data_function), + "" ); } #[test] fn it_evaluates_blank_constructors() { - let custom_functions = |s: String, _params: Vec| match s.as_str() { - "BLANK" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_custom_function("=SUM(BLANK())", Some(&custom_functions)), + fn custom_functions(s: String, _params: Vec) -> types::Value { + match s.as_str() { + "BLANK" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=SUM(BLANK())", + Some(&custom_functions), 0.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=SUM(BLANK(), 1)", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=SUM(BLANK(), 1)", + Some(&custom_functions), 1.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function( - "=PRODUCT(BLANK(), 1)", - Some(&custom_functions) - ), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=PRODUCT(BLANK(), 1)", + Some(&custom_functions), 1.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function( - "=AVERAGE(BLANK(), 1)", - Some(&custom_functions) - ), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=AVERAGE(BLANK(), 1)", + Some(&custom_functions), 1.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=BLANK()+1", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=BLANK()+1", + Some(&custom_functions), 1.0 ); - assert_eq!( - evaluate_formula_number_with_custom_function("=BLANK()*1", Some(&custom_functions)), + test_all_types_with_data!( + evaluate_formula_number_with_custom_function, + "=BLANK()*1", + Some(&custom_functions), 0.0 ); } #[test] fn it_evaluates_blank_in_iterators() { - let data_function = |s: String| match s.as_str() { - "A" => types::Value::Number(100.0), - "Array" => types::Value::Iterator(vec![ - types::Value::Number(100.0), - types::Value::Blank, - types::Value::Blank, - ]), - "B" => types::Value::Blank, - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE({A, B, B})", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "A" => types::Value::Number(N::from_f32(100.0).unwrap()), + "Array" => types::Value::Iterator(vec![ + types::Value::Number(N::from_f32(100.0).unwrap()), + types::Value::Blank, + types::Value::Blank, + ]), + "B" => types::Value::Blank, + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE({A, B, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE({A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE({A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE({B, A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE({B, A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=AVERAGE(Array)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=AVERAGE(Array)", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM({A, B, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM({A, B, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM({A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM({A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM({B, A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM({B, A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=SUM(Array)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=SUM(Array)", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT({A, B, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT({A, B, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT({A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT({A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT({B, A, B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT({B, A, B})", + Some(&data_function), 100.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=PRODUCT(Array)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=PRODUCT(Array)", + Some(&data_function), 100.0 ); } #[test] fn it_evaluates_blank_with_iterators_in_boolean_operations() { - let data_function = |s: String| match s.as_str() { - "T" => types::Value::Boolean(types::Boolean::True), - "B" => types::Value::Blank, - "F" => types::Value::Boolean(types::Boolean::False), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference("=AND({T,B})", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "T" => types::Value::Boolean(types::Boolean::True), + "B" => types::Value::Blank, + "F" => types::Value::Boolean(types::Boolean::False), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND({T,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR({T,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR({T,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND({F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND({F,B})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR({F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR({F,B})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR({T,B,F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR({T,B,F,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=OR({F,B,T,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=OR({F,B,T,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND({T,B,F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND({T,B,F,B})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=AND({F,B,T,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND({F,B,T,B})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({T,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({T,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({F,B})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({T,B,F,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({T,B,F,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({F,B,T,B})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({F,B,T,B})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({T,B,F,B,T})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({T,B,F,B,T})", + Some(&data_function), "FALSE" ); - assert_eq!( - evaluate_formula_boolean_with_reference("=XOR({F,B,T,B,F})", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({F,B,T,B,F})", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_boolean_with_reference( - "=XOR({F,B,T,B,F,{F,B,T,B,F}})", - Some(&data_function) - ), + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=XOR({F,B,T,B,F,{F,B,T,B,F}})", + Some(&data_function), "FALSE" ); } #[test] fn it_evaluates_formulas_with_3_params() { - assert_eq!(evaluate_formula_number("=IF()"), 0.0); - assert_eq!(evaluate_formula_number("=IF( )"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF( ,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(, )"), 0.0); - assert_eq!(evaluate_formula_number("=IF( , )"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(, ,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,, )"), 0.0); - assert_eq!(evaluate_formula_number("=IF(1,,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(1,2,)"), 2.0); - assert_eq!(evaluate_formula_number("=IF(1,2)"), 2.0); - assert_eq!(evaluate_formula_number("=IF(0,2,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(0,2)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(0,,3)"), 3.0); - assert_eq!(evaluate_formula_number("=IF(1,,3)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(0,2,3)"), 3.0); - assert_eq!(evaluate_formula_number("=IF(,2,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,2)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(,2,3)"), 3.0); - assert_eq!(evaluate_formula_number("=IF(,,3)"), 3.0); -} - -#[test] -fn it_evaluates_if_formulas() -> Result<(), ParseError> { - assert_eq!(evaluate_formula_number("=IF(TRUE,1,0)"), 1.0); - assert_eq!(evaluate_formula_number("=IF(FALSE,1,0)"), 0.0); - assert_eq!(evaluate_formula_string("=IF(TRUE,\"a\",0)"), "a"); - assert_eq!(evaluate_formula_number("=IF(FALSE,\"a\",0)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(1=1,1,0)"), 1.0); - assert_eq!(evaluate_formula_number("=IF(1=2,1,0)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(AND(TRUE,FALSE),1,0)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(TRUE,IF(FALSE,1,2),0)"), 2.0); - assert_eq!(evaluate_formula_number("=IF(2,1,0)"), 1.0); - assert_eq!(evaluate_formula_number("=IF(-1,1,0)"), 1.0); - assert_eq!(evaluate_formula_number("=IF(0,1,0)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(TRUE,1+2+3,0)"), 6.0); - assert_eq!(evaluate_formula_number("=IF(FALSE,1)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(FALSE,1,)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(TRUE,,1)"), 0.0); - assert_eq!(evaluate_formula_number("=IF(TRUE, ,1)"), 0.0); - assert_eq!(evaluate_formula_string("=IF(TRUE,TRUE,FALSE)"), "TRUE"); - assert_eq!( - evaluate_formula_string("=IF( TRUE , TRUE , FALSE )"), + test_all_types!(evaluate_formula_number, "=IF()", 0.0); + test_all_types!(evaluate_formula_number, "=IF( )", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF( ,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(, )", 0.0); + test_all_types!(evaluate_formula_number, "=IF( , )", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(, ,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,, )", 0.0); + test_all_types!(evaluate_formula_number, "=IF(1,,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(1,2,)", 2.0); + test_all_types!(evaluate_formula_number, "=IF(1,2)", 2.0); + test_all_types!(evaluate_formula_number, "=IF(0,2,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(0,2)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(0,,3)", 3.0); + test_all_types!(evaluate_formula_number, "=IF(1,,3)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(0,2,3)", 3.0); + test_all_types!(evaluate_formula_number, "=IF(,2,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,2)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(,2,3)", 3.0); + test_all_types!(evaluate_formula_number, "=IF(,,3)", 3.0); +} + +#[test] +fn it_evaluates_if_formulas() { + test_all_types!(evaluate_formula_number, "=IF(TRUE,1,0)", 1.0); + test_all_types!(evaluate_formula_number, "=IF(FALSE,1,0)", 0.0); + test_all_types!(evaluate_formula_string, "=IF(TRUE,\"a\",0)", "a"); + test_all_types!(evaluate_formula_number, "=IF(FALSE,\"a\",0)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(1=1,1,0)", 1.0); + test_all_types!(evaluate_formula_number, "=IF(1=2,1,0)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(AND(TRUE,FALSE),1,0)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(TRUE,IF(FALSE,1,2),0)", 2.0); + test_all_types!(evaluate_formula_number, "=IF(2,1,0)", 1.0); + test_all_types!(evaluate_formula_number, "=IF(-1,1,0)", 1.0); + test_all_types!(evaluate_formula_number, "=IF(0,1,0)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(TRUE,1+2+3,0)", 6.0); + test_all_types!(evaluate_formula_number, "=IF(FALSE,1)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(FALSE,1,)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(TRUE,,1)", 0.0); + test_all_types!(evaluate_formula_number, "=IF(TRUE, ,1)", 0.0); + test_all_types!(evaluate_formula_string, "=IF(TRUE,TRUE,FALSE)", "TRUE"); + test_all_types!( + evaluate_formula_string, + "=IF( TRUE , TRUE , FALSE )", "TRUE" ); - assert_eq!(evaluate_formula_string("=IF(TRUE , TRUE, FALSE )"), "TRUE"); - assert_eq!(evaluate_formula_number("=IF(1,IF(FALSE,1,2),0)"), 2.0); - assert_eq!( - evaluate_formula_number("=IF(1=0,IF(FALSE,1,2),IF(TRUE,1,2))"), + test_all_types!(evaluate_formula_string, "=IF(TRUE , TRUE, FALSE )", "TRUE"); + test_all_types!(evaluate_formula_number, "=IF(1,IF(FALSE,1,2),0)", 2.0); + test_all_types!( + evaluate_formula_number, + "=IF(1=0,IF(FALSE,1,2),IF(TRUE,1,2))", 1.0 ); - assert_eq!( - evaluate_formula_string("=IF(1/0,IF(FALSE,1,2),0)"), - "#DIV/0!", + test_all_types!( + evaluate_formula_string, + "=IF(1/0,IF(FALSE,1,2),0)", + "#DIV/0!" ); - assert_eq!(evaluate_formula_string("=IF(\"text\",1,0)"), "#VALUE!",); - - let date1: DateTime = DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z")?; - let date2: DateTime = DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z")?; - let data_function = |s: String| match s.as_str() { - "date1" => types::Value::Date(date1), - "date2" => types::Value::Date(date2), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_number_with_reference("=IF(date1=date2,1,0)", Some(&data_function)), + test_all_types!(evaluate_formula_string, "=IF(\"text\",1,0)", "#VALUE!"); + + fn data_function(s: String) -> types::Value { + let date1: DateTime = + DateTime::parse_from_rfc3339("2019-03-01T02:00:00.000Z").unwrap(); + let date2: DateTime = + DateTime::parse_from_rfc3339("2019-08-30T02:00:00.000Z").unwrap(); + match s.as_str() { + "date1" => types::Value::Date(date1), + "date2" => types::Value::Date(date2), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=IF(date1=date2,1,0)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=IF(date1<>date2,1,0)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=IF(date1<>date2,1,0)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=IF(date1date2,1,0)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=IF(date1>date2,1,0)", + Some(&data_function), 0.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=IF(date1<=date2,1,0)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=IF(date1<=date2,1,0)", + Some(&data_function), 1.0 ); - assert_eq!( - evaluate_formula_number_with_reference("=IF(date1>=date2,1,0)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_number_with_reference, + "=IF(date1>=date2,1,0)", + Some(&data_function), 0.0 ); - Ok(()) } #[test] fn it_evaluates_if_formulas_with_text() { - let data_function = |s: String| match s.as_str() { - "ReferenceKey" => types::Value::Text("100".to_string()), - "ReferenceName" => types::Value::Text("Test".to_string()), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_string_with_reference( - "=IF(ReferenceKey=\"10\",\"\",ReferenceKey&\" - \")&ReferenceName", - Some(&data_function) - ), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "ReferenceKey" => types::Value::Text("100".to_string()), + "ReferenceName" => types::Value::Text("Test".to_string()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=IF(ReferenceKey=\"10\",\"\",ReferenceKey&\" - \")&ReferenceName", + Some(&data_function), "100 - Test" ); } #[test] fn it_evaluates_isblank_function() { - let data_function = |s: String| match s.as_str() { - // "ReferenceKey" => types::Value::Text("100".to_string()), - "ReferenceName" => types::Value::Text("Test".to_string()), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_string_with_reference("=ISBLANK(ReferenceKey)", Some(&data_function)), + fn data_function(s: String) -> types::Value { + match s.as_str() { + // "ReferenceKey" => types::Value::Text("100".to_string()), + "ReferenceName" => types::Value::Text("Test".to_string()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=ISBLANK(ReferenceKey)", + Some(&data_function), "TRUE" ); - assert_eq!( - evaluate_formula_string_with_reference("=ISBLANK(ReferenceName)", Some(&data_function)), + test_all_types_with_data!( + evaluate_formula_string_with_reference, + "=ISBLANK(ReferenceName)", + Some(&data_function), "FALSE" ); } #[test] fn test_inner_function_with_whitespace() { - let data_function = |s: String| match s.as_str() { - "any_dropdowns" => types::Value::Number(1.0), - "primary_carrier" => types::Value::Text("Allianz*".to_owned()), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference( - "=AND(RIGHT(primary_carrier, 1)=\"*\", 1 > 0)", - Some(&data_function) - ), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "any_dropdowns" => types::Value::Number(N::one()), + "primary_carrier" => types::Value::Text("Allianz*".to_owned()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=AND(RIGHT(primary_carrier, 1)=\"*\", 1 > 0)", + Some(&data_function), "TRUE" ); } #[test] fn test_zero_padding() { - let data_function = |s: String| match s.as_str() { - "num" => types::Value::Number(91.0), - _ => types::Value::Error(types::Error::Value), - }; - assert_eq!( - evaluate_formula_boolean_with_reference( - "=RIGHT(\"00000000\" & num, 8)", - Some(&data_function) - ), + fn data_function(s: String) -> types::Value { + match s.as_str() { + "num" => types::Value::Number(N::from_f32(91.0).unwrap()), + _ => types::Value::Error(types::Error::Value), + } + } + test_all_types_with_data!( + evaluate_formula_boolean_with_reference, + "=RIGHT(\"00000000\" & num, 8)", + Some(&data_function), "00000091" ); }