FIX: Cast to string now checks for dates, currencies or percentages
Fixes part of #535
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
7e966baa0d
commit
5ff4774c5a
@@ -5,6 +5,7 @@ use crate::{
|
|||||||
token::Error,
|
token::Error,
|
||||||
types::CellReferenceIndex,
|
types::CellReferenceIndex,
|
||||||
},
|
},
|
||||||
|
formatter::format::parse_formatted_number,
|
||||||
model::Model,
|
model::Model,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,20 +90,31 @@ impl Model {
|
|||||||
self.cast_to_number(result, cell)
|
self.cast_to_number(result, cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_to_number(
|
pub(crate) fn cast_to_number(
|
||||||
&mut self,
|
&mut self,
|
||||||
result: CalcResult,
|
result: CalcResult,
|
||||||
cell: CellReferenceIndex,
|
cell: CellReferenceIndex,
|
||||||
) -> Result<f64, CalcResult> {
|
) -> Result<f64, CalcResult> {
|
||||||
match result {
|
match result {
|
||||||
CalcResult::Number(f) => Ok(f),
|
CalcResult::Number(f) => Ok(f),
|
||||||
CalcResult::String(s) => match s.parse::<f64>() {
|
CalcResult::String(s) => match s.trim().parse::<f64>() {
|
||||||
Ok(f) => Ok(f),
|
Ok(f) => Ok(f),
|
||||||
_ => Err(CalcResult::new_error(
|
_ => {
|
||||||
|
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,
|
Error::VALUE,
|
||||||
cell,
|
cell,
|
||||||
"Expecting number".to_string(),
|
"Expecting number".to_string(),
|
||||||
)),
|
))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
CalcResult::Boolean(f) => {
|
CalcResult::Boolean(f) => {
|
||||||
if f {
|
if f {
|
||||||
|
|||||||
@@ -744,10 +744,10 @@ fn parse_date(value: &str) -> Result<(i32, String), String> {
|
|||||||
/// "30.34%" => (0.3034, "0.00%")
|
/// "30.34%" => (0.3034, "0.00%")
|
||||||
/// 100€ => (100, "100€")
|
/// 100€ => (100, "100€")
|
||||||
pub(crate) fn parse_formatted_number(
|
pub(crate) fn parse_formatted_number(
|
||||||
value: &str,
|
original: &str,
|
||||||
currencies: &[&str],
|
currencies: &[&str],
|
||||||
) -> Result<(f64, Option<String>), String> {
|
) -> Result<(f64, Option<String>), String> {
|
||||||
let value = value.trim();
|
let value = original.trim();
|
||||||
let scientific_format = "0.00E+00";
|
let scientific_format = "0.00E+00";
|
||||||
|
|
||||||
// Check if it is a percentage
|
// 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)));
|
return Ok((serial_number as f64, Some(format)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1163,11 +1163,34 @@ impl Model {
|
|||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
return CalcResult::new_args_number_error(cell);
|
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,
|
Ok(f) => f,
|
||||||
Err(s) => return s,
|
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,
|
Ok(f) => f,
|
||||||
Err(s) => return s,
|
Err(s) => return s,
|
||||||
};
|
};
|
||||||
|
|||||||
BIN
xlsx/tests/calc_tests/MROUND_edgecases.xlsx
Normal file
BIN
xlsx/tests/calc_tests/MROUND_edgecases.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user