use crate::display::display_number;
use crate::eval::functions::date::serial::text_to_date_serial;
use crate::types::{ErrorKind, Value};
pub(crate) fn parse_number_text(text: &str) -> Option<f64> {
let trimmed = text.trim();
if trimmed.is_empty() {
return Some(0.0);
}
if let Ok(n) = trimmed.parse::<f64>() {
return Some(n);
}
if let Some(pct) = trimmed.strip_suffix('%') {
if let Ok(n) = pct.trim().replace(',', "").parse::<f64>() {
return Some(n / 100.0);
}
}
if let Some(rest) = trimmed.strip_prefix('$') {
if let Ok(n) = rest.trim().replace(',', "").parse::<f64>() {
return Some(n);
}
}
let no_commas = trimmed.replace(',', "");
no_commas.parse::<f64>().ok()
}
pub fn to_number(v: Value) -> Result<f64, Value> {
match v {
Value::Number(n) | Value::Date(n) => Ok(n),
Value::Bool(b) => Ok(if b { 1.0 } else { 0.0 }),
Value::Empty => Ok(0.0),
Value::Text(s) => parse_number_text(&s)
.or_else(|| text_to_date_serial(&s))
.ok_or(Value::Error(ErrorKind::Value)),
Value::Error(_) | Value::ErrorMsg(_, _) => Err(v),
Value::Array(_) => Err(Value::Error(ErrorKind::Value)),
Value::Zoned(_) => Err(Value::Error(ErrorKind::Value)),
Value::Sparkline(_) => Err(Value::Error(ErrorKind::Value)),
}
}
pub fn to_string_val(v: Value) -> Result<String, Value> {
match v {
Value::Text(s) => Ok(s),
Value::Number(n) | Value::Date(n) => Ok(display_number(n)),
Value::Bool(b) => Ok(if b { "TRUE".to_string() } else { "FALSE".to_string() }),
Value::Empty => Ok(String::new()),
Value::Error(_) | Value::ErrorMsg(_, _) => Err(v),
Value::Array(_) => Err(Value::Error(ErrorKind::Value)),
Value::Zoned(z) => Ok(z.to_rfc9557()),
Value::Sparkline(_) => Ok(String::new()),
}
}
pub fn to_bool(v: Value) -> Result<bool, Value> {
match v {
Value::Bool(b) => Ok(b),
Value::Number(n) | Value::Date(n) => Ok(n != 0.0),
Value::Error(_) | Value::ErrorMsg(_, _) => Err(v),
Value::Text(ref s) => match s.to_uppercase().as_str() {
"TRUE" => Ok(true),
"FALSE" => Ok(false),
_ => Err(Value::Error(ErrorKind::Value)),
},
Value::Empty => Err(Value::Error(ErrorKind::Value)),
Value::Zoned(_) => Err(Value::Error(ErrorKind::Value)),
Value::Sparkline(_) => Ok(false),
Value::Array(mut elems) => {
if elems.is_empty() {
return Err(Value::Error(ErrorKind::Value));
}
let mut top = elems.swap_remove(0);
loop {
match top {
Value::Array(mut inner) => {
if inner.is_empty() {
return Err(Value::Error(ErrorKind::Value));
}
top = inner.swap_remove(0);
}
other => return to_bool(other),
}
}
}
}
}
#[cfg(test)]
mod tests;