use crate::eval::functions::{check_arity, check_arity_len, EvalCtx};
use crate::eval::evaluate_expr;
use crate::parser::ast::Expr;
use crate::types::{ErrorKind, Value};
pub fn isnumber_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) { return err; }
Value::Bool(matches!(args[0], Value::Number(_) | Value::Date(_)))
}
pub fn istext_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) { return err; }
Value::Bool(matches!(args[0], Value::Text(_)))
}
pub fn iserror_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) { return err; }
Value::Bool(matches!(args[0], Value::Error(_)))
}
pub fn isblank_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) { return err; }
Value::Bool(matches!(args[0], Value::Empty))
}
pub fn isna_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) { return err; }
Value::Bool(matches!(args[0], Value::Error(ErrorKind::NA)))
}
pub fn isnumber_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
let scalar = match val {
Value::Array(ref elems) => elems.first().cloned().unwrap_or(Value::Empty),
other => other,
};
Value::Bool(matches!(scalar, Value::Number(_) | Value::Date(_)))
}
pub fn istext_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(matches!(val, Value::Text(_)))
}
pub fn iserror_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(matches!(val, Value::Error(_)))
}
pub fn isblank_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(matches!(val, Value::Empty))
}
pub fn isna_lazy_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(matches!(val, Value::Error(ErrorKind::NA)))
}
pub fn iserr_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
let is_err = match &val {
Value::Error(e) => *e != ErrorKind::NA,
_ => false,
};
Value::Bool(is_err)
}
pub fn islogical_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(matches!(val, Value::Bool(_)))
}
pub fn isnontext_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
Value::Bool(!matches!(val, Value::Text(_)))
}
pub fn isref_fn(args: &[Expr], _ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
Value::Bool(matches!(args[0], Expr::Variable(_, _)))
}
pub fn isformula_fn(args: &[Expr], _ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
if !matches!(args[0], Expr::Variable(_, _)) {
return Value::Error(ErrorKind::NA);
}
Value::Bool(false)
}
#[cfg(test)]
mod tests;
pub fn isdate_fn(args: &[Expr], ctx: &mut EvalCtx<'_>) -> Value {
if check_arity_len(args.len(), 1, 1).is_some() {
return Value::Error(ErrorKind::NA);
}
let val = evaluate_expr(&args[0], ctx);
match val {
Value::Error(_) => val,
Value::Date(_) => Value::Bool(true),
Value::Text(ref s) => Value::Bool(is_date_string(s)),
_ => Value::Bool(false),
}
}
fn is_date_string(s: &str) -> bool {
let parts: Vec<&str> = s.split('-').collect();
if parts.len() != 3 { return false; }
let ok_year = parts[0].len() == 4 && parts[0].chars().all(|c| c.is_ascii_digit());
let ok_month = parts[1].len() == 2 && parts[1].chars().all(|c| c.is_ascii_digit());
let ok_day = parts[2].len() == 2 && parts[2].chars().all(|c| c.is_ascii_digit());
if !(ok_year && ok_month && ok_day) { return false; }
let month: u32 = parts[1].parse().unwrap_or(0);
let day: u32 = parts[2].parse().unwrap_or(0);
(1..=12).contains(&month) && (1..=31).contains(&day)
}