diff --git a/base/src/cast.rs b/base/src/cast.rs index 06cb2c9..6f8a528 100644 --- a/base/src/cast.rs +++ b/base/src/cast.rs @@ -5,6 +5,7 @@ use crate::{ token::Error, types::CellReferenceIndex, }, + formatter::format::parse_formatted_number, model::Model, }; @@ -89,20 +90,31 @@ impl Model { self.cast_to_number(result, cell) } - fn cast_to_number( + pub(crate) fn cast_to_number( &mut self, result: CalcResult, cell: CellReferenceIndex, ) -> Result { match result { CalcResult::Number(f) => Ok(f), - CalcResult::String(s) => match s.parse::() { + CalcResult::String(s) => match s.trim().parse::() { Ok(f) => Ok(f), - _ => Err(CalcResult::new_error( - Error::VALUE, - cell, - "Expecting number".to_string(), - )), + _ => { + let mut currencies = vec!["$", "€"]; + let currency = &self.locale.currency.symbol; + if !currencies.iter().any(|e| e == currency) { + currencies.push(currency); + } + // We try to parse as number + if let Ok((v, _number_format)) = parse_formatted_number(&s, ¤cies) { + return Ok(v); + } + Err(CalcResult::new_error( + Error::VALUE, + cell, + "Expecting number".to_string(), + )) + } }, CalcResult::Boolean(f) => { if f { diff --git a/base/src/formatter/format.rs b/base/src/formatter/format.rs index cfa7d01..1e0865a 100644 --- a/base/src/formatter/format.rs +++ b/base/src/formatter/format.rs @@ -744,10 +744,10 @@ fn parse_date(value: &str) -> Result<(i32, String), String> { /// "30.34%" => (0.3034, "0.00%") /// 100€ => (100, "100€") pub(crate) fn parse_formatted_number( - value: &str, + original: &str, currencies: &[&str], ) -> Result<(f64, Option), String> { - let value = value.trim(); + let value = original.trim(); let scientific_format = "0.00E+00"; // Check if it is a percentage @@ -799,7 +799,8 @@ pub(crate) fn parse_formatted_number( } } - if let Ok((serial_number, format)) = parse_date(value) { + // check if it is a date. NOTE: we don't trim the original here + if let Ok((serial_number, format)) = parse_date(original) { return Ok((serial_number as f64, Some(format))); } diff --git a/base/src/functions/mathematical.rs b/base/src/functions/mathematical.rs index a03dc0b..fa35669 100644 --- a/base/src/functions/mathematical.rs +++ b/base/src/functions/mathematical.rs @@ -1163,11 +1163,34 @@ impl Model { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } - let value = match self.get_number(&args[0], cell) { + + let value = self.evaluate_node_in_context(&args[0], cell); + + let multiple = self.evaluate_node_in_context(&args[1], cell); + + // if both are empty => #N/A + if matches!(value, CalcResult::EmptyArg) || matches!(multiple, CalcResult::EmptyArg) { + return CalcResult::Error { + error: Error::NA, + origin: cell, + message: "Bad argument for MROUND".to_string(), + }; + } + + // Booleans are not cast + if matches!(value, CalcResult::Boolean(_)) { + return CalcResult::new_error(Error::VALUE, cell, "Expecting number".to_string()); + } + + if matches!(multiple, CalcResult::Boolean(_)) { + return CalcResult::new_error(Error::VALUE, cell, "Expecting number".to_string()); + } + + let value = match self.cast_to_number(value, cell) { Ok(f) => f, Err(s) => return s, }; - let multiple = match self.get_number(&args[1], cell) { + let multiple = match self.cast_to_number(multiple, cell) { Ok(f) => f, Err(s) => return s, }; diff --git a/xlsx/tests/calc_tests/MROUND_edgecases.xlsx b/xlsx/tests/calc_tests/MROUND_edgecases.xlsx new file mode 100644 index 0000000..757ac8a Binary files /dev/null and b/xlsx/tests/calc_tests/MROUND_edgecases.xlsx differ