1pub mod date_time;
7pub mod engineering;
8pub mod financial;
9pub mod information;
10pub mod logical;
11pub mod lookup;
12pub mod math;
13pub mod statistical;
14pub mod text;
15
16use crate::cell::CellValue;
17use crate::error::{Error, Result};
18use crate::formula::ast::Expr;
19use crate::formula::eval::{coerce_to_number, coerce_to_string, Evaluator};
20
21pub type FunctionFn = fn(&[Expr], &mut Evaluator) -> Result<CellValue>;
26
27pub fn lookup_function(name: &str) -> Option<FunctionFn> {
29 match name.to_ascii_uppercase().as_str() {
30 "SUM" => Some(fn_sum),
31 "AVERAGE" => Some(fn_average),
32 "COUNT" => Some(fn_count),
33 "COUNTA" => Some(fn_counta),
34 "MIN" => Some(fn_min),
35 "MAX" => Some(fn_max),
36 "IF" => Some(fn_if),
37 "ABS" => Some(fn_abs),
38 "INT" => Some(fn_int),
39 "ROUND" => Some(fn_round),
40 "MOD" => Some(fn_mod),
41 "POWER" => Some(fn_power),
42 "SQRT" => Some(fn_sqrt),
43 "LEN" => Some(fn_len),
44 "LOWER" => Some(fn_lower),
45 "UPPER" => Some(fn_upper),
46 "TRIM" => Some(fn_trim),
47 "LEFT" => Some(fn_left),
48 "RIGHT" => Some(fn_right),
49 "MID" => Some(fn_mid),
50 "CONCATENATE" => Some(fn_concatenate),
51 "AND" => Some(fn_and),
52 "OR" => Some(fn_or),
53 "NOT" => Some(fn_not),
54 "ISNUMBER" => Some(fn_isnumber),
55 "ISTEXT" => Some(fn_istext),
56 "ISBLANK" => Some(fn_isblank),
57 "ISERROR" => Some(fn_iserror),
58 "VALUE" => Some(fn_value),
59 "TEXT" => Some(fn_text),
60 "SUMIF" => Some(math::fn_sumif),
61 "SUMIFS" => Some(math::fn_sumifs),
62 "ROUNDUP" => Some(math::fn_roundup),
63 "ROUNDDOWN" => Some(math::fn_rounddown),
64 "CEILING" => Some(math::fn_ceiling),
65 "FLOOR" => Some(math::fn_floor),
66 "SIGN" => Some(math::fn_sign),
67 "RAND" => Some(math::fn_rand),
68 "RANDBETWEEN" => Some(math::fn_randbetween),
69 "PI" => Some(math::fn_pi),
70 "LOG" => Some(math::fn_log),
71 "LOG10" => Some(math::fn_log10),
72 "LN" => Some(math::fn_ln),
73 "EXP" => Some(math::fn_exp),
74 "PRODUCT" => Some(math::fn_product),
75 "QUOTIENT" => Some(math::fn_quotient),
76 "FACT" => Some(math::fn_fact),
77 "AVERAGEIF" => Some(statistical::fn_averageif),
78 "AVERAGEIFS" => Some(statistical::fn_averageifs),
79 "COUNTBLANK" => Some(statistical::fn_countblank),
80 "COUNTIF" => Some(statistical::fn_countif),
81 "COUNTIFS" => Some(statistical::fn_countifs),
82 "MEDIAN" => Some(statistical::fn_median),
83 "MODE" => Some(statistical::fn_mode),
84 "LARGE" => Some(statistical::fn_large),
85 "SMALL" => Some(statistical::fn_small),
86 "RANK" => Some(statistical::fn_rank),
87 "ISERR" => Some(information::fn_iserr),
88 "ISNA" => Some(information::fn_isna),
89 "ISLOGICAL" => Some(information::fn_islogical),
90 "ISEVEN" => Some(information::fn_iseven),
91 "ISODD" => Some(information::fn_isodd),
92 "TYPE" => Some(information::fn_type),
93 "N" => Some(information::fn_n),
94 "NA" => Some(information::fn_na),
95 "ERROR.TYPE" => Some(information::fn_error_type),
96 "CONCAT" => Some(text::fn_concat),
97 "FIND" => Some(text::fn_find),
98 "SEARCH" => Some(text::fn_search),
99 "SUBSTITUTE" => Some(text::fn_substitute),
100 "REPLACE" => Some(text::fn_replace),
101 "REPT" => Some(text::fn_rept),
102 "EXACT" => Some(text::fn_exact),
103 "T" => Some(text::fn_t),
104 "PROPER" => Some(text::fn_proper),
105 "TRUE" => Some(logical::fn_true),
106 "FALSE" => Some(logical::fn_false),
107 "IFERROR" => Some(logical::fn_iferror),
108 "IFNA" => Some(logical::fn_ifna),
109 "IFS" => Some(logical::fn_ifs),
110 "SWITCH" => Some(logical::fn_switch),
111 "XOR" => Some(logical::fn_xor),
112 "DATE" => Some(date_time::fn_date),
113 "TODAY" => Some(date_time::fn_today),
114 "NOW" => Some(date_time::fn_now),
115 "YEAR" => Some(date_time::fn_year),
116 "MONTH" => Some(date_time::fn_month),
117 "DAY" => Some(date_time::fn_day),
118 "HOUR" => Some(date_time::fn_hour),
119 "MINUTE" => Some(date_time::fn_minute),
120 "SECOND" => Some(date_time::fn_second),
121 "DATEDIF" => Some(date_time::fn_datedif),
122 "EDATE" => Some(date_time::fn_edate),
123 "EOMONTH" => Some(date_time::fn_eomonth),
124 "DATEVALUE" => Some(date_time::fn_datevalue),
125 "WEEKDAY" => Some(date_time::fn_weekday),
126 "WEEKNUM" => Some(date_time::fn_weeknum),
127 "NETWORKDAYS" => Some(date_time::fn_networkdays),
128 "WORKDAY" => Some(date_time::fn_workday),
129 "VLOOKUP" => Some(lookup::fn_vlookup),
130 "HLOOKUP" => Some(lookup::fn_hlookup),
131 "INDEX" => Some(lookup::fn_index),
132 "MATCH" => Some(lookup::fn_match),
133 "LOOKUP" => Some(lookup::fn_lookup),
134 "ROW" => Some(lookup::fn_row),
135 "COLUMN" => Some(lookup::fn_column),
136 "ROWS" => Some(lookup::fn_rows),
137 "COLUMNS" => Some(lookup::fn_columns),
138 "CHOOSE" => Some(lookup::fn_choose),
139 "ADDRESS" => Some(lookup::fn_address),
140 "FV" => Some(financial::fn_fv),
141 "PV" => Some(financial::fn_pv),
142 "NPV" => Some(financial::fn_npv),
143 "IRR" => Some(financial::fn_irr),
144 "PMT" => Some(financial::fn_pmt),
145 "IPMT" => Some(financial::fn_ipmt),
146 "PPMT" => Some(financial::fn_ppmt),
147 "RATE" => Some(financial::fn_rate),
148 "NPER" => Some(financial::fn_nper),
149 "DB" => Some(financial::fn_db),
150 "DDB" => Some(financial::fn_ddb),
151 "SLN" => Some(financial::fn_sln),
152 "SYD" => Some(financial::fn_syd),
153 "EFFECT" => Some(financial::fn_effect),
154 "NOMINAL" => Some(financial::fn_nominal),
155 "DOLLARDE" => Some(financial::fn_dollarde),
156 "DOLLARFR" => Some(financial::fn_dollarfr),
157 "CUMIPMT" => Some(financial::fn_cumipmt),
158 "CUMPRINC" => Some(financial::fn_cumprinc),
159 "XNPV" => Some(financial::fn_xnpv),
160 "XIRR" => Some(financial::fn_xirr),
161 "BIN2DEC" => Some(engineering::fn_bin2dec),
162 "BIN2HEX" => Some(engineering::fn_bin2hex),
163 "BIN2OCT" => Some(engineering::fn_bin2oct),
164 "DEC2BIN" => Some(engineering::fn_dec2bin),
165 "DEC2HEX" => Some(engineering::fn_dec2hex),
166 "DEC2OCT" => Some(engineering::fn_dec2oct),
167 "HEX2BIN" => Some(engineering::fn_hex2bin),
168 "HEX2DEC" => Some(engineering::fn_hex2dec),
169 "HEX2OCT" => Some(engineering::fn_hex2oct),
170 "OCT2BIN" => Some(engineering::fn_oct2bin),
171 "OCT2DEC" => Some(engineering::fn_oct2dec),
172 "OCT2HEX" => Some(engineering::fn_oct2hex),
173 "DELTA" => Some(engineering::fn_delta),
174 "GESTEP" => Some(engineering::fn_gestep),
175 "ERF" => Some(engineering::fn_erf),
176 "ERFC" => Some(engineering::fn_erfc),
177 "COMPLEX" => Some(engineering::fn_complex),
178 "IMREAL" => Some(engineering::fn_imreal),
179 "IMAGINARY" => Some(engineering::fn_imaginary),
180 "IMABS" => Some(engineering::fn_imabs),
181 "IMARGUMENT" => Some(engineering::fn_imargument),
182 "IMCONJUGATE" => Some(engineering::fn_imconjugate),
183 "IMSUM" => Some(engineering::fn_imsum),
184 "IMSUB" => Some(engineering::fn_imsub),
185 "IMPRODUCT" => Some(engineering::fn_improduct),
186 "IMDIV" => Some(engineering::fn_imdiv),
187 "IMPOWER" => Some(engineering::fn_impower),
188 "IMSQRT" => Some(engineering::fn_imsqrt),
189 "CONVERT" => Some(engineering::fn_convert),
190 "BESSELI" => Some(engineering::fn_besseli),
191 "BESSELJ" => Some(engineering::fn_besselj),
192 "BESSELK" => Some(engineering::fn_besselk),
193 "BESSELY" => Some(engineering::fn_bessely),
194 _ => None,
195 }
196}
197
198pub fn check_arg_count(name: &str, args: &[Expr], min: usize, max: usize) -> Result<()> {
200 if args.len() < min || args.len() > max {
201 let expected = if min == max {
202 format!("{min}")
203 } else {
204 format!("{min}..{max}")
205 };
206 return Err(Error::WrongArgCount {
207 name: name.to_string(),
208 expected,
209 got: args.len(),
210 });
211 }
212 Ok(())
213}
214
215pub fn matches_criteria(cell_value: &CellValue, criteria: &str) -> bool {
222 if criteria.is_empty() {
223 return matches!(cell_value, CellValue::Empty);
224 }
225
226 let (op, val_str) = if let Some(rest) = criteria.strip_prefix("<=") {
227 ("<=", rest)
228 } else if let Some(rest) = criteria.strip_prefix(">=") {
229 (">=", rest)
230 } else if let Some(rest) = criteria.strip_prefix("<>") {
231 ("<>", rest)
232 } else if let Some(rest) = criteria.strip_prefix('<') {
233 ("<", rest)
234 } else if let Some(rest) = criteria.strip_prefix('>') {
235 (">", rest)
236 } else if let Some(rest) = criteria.strip_prefix('=') {
237 ("=", rest)
238 } else {
239 ("=", criteria)
240 };
241
242 let cell_num = coerce_to_number(cell_value).ok();
243 let crit_num: Option<f64> = val_str.parse().ok();
244
245 if let (Some(cn), Some(crn)) = (cell_num, crit_num) {
246 return match op {
247 "<=" => cn <= crn,
248 ">=" => cn >= crn,
249 "<>" => (cn - crn).abs() > f64::EPSILON,
250 "<" => cn < crn,
251 ">" => cn > crn,
252 "=" => (cn - crn).abs() < f64::EPSILON,
253 _ => false,
254 };
255 }
256
257 let cell_str = coerce_to_string(cell_value).to_ascii_lowercase();
258 let crit_lower = val_str.to_ascii_lowercase();
259
260 match op {
261 "=" => {
262 if crit_lower.contains('*') || crit_lower.contains('?') {
263 wildcard_match(&cell_str, &crit_lower)
264 } else {
265 cell_str == crit_lower
266 }
267 }
268 "<>" => {
269 if crit_lower.contains('*') || crit_lower.contains('?') {
270 !wildcard_match(&cell_str, &crit_lower)
271 } else {
272 cell_str != crit_lower
273 }
274 }
275 "<" => cell_str < crit_lower,
276 ">" => cell_str > crit_lower,
277 "<=" => cell_str <= crit_lower,
278 ">=" => cell_str >= crit_lower,
279 _ => false,
280 }
281}
282
283fn wildcard_match(text: &str, pattern: &str) -> bool {
284 let t: Vec<char> = text.chars().collect();
285 let p: Vec<char> = pattern.chars().collect();
286 let (tlen, plen) = (t.len(), p.len());
287 let mut dp = vec![vec![false; plen + 1]; tlen + 1];
288 dp[0][0] = true;
289 for j in 1..=plen {
290 if p[j - 1] == '*' {
291 dp[0][j] = dp[0][j - 1];
292 }
293 }
294 for i in 1..=tlen {
295 for j in 1..=plen {
296 if p[j - 1] == '*' {
297 dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
298 } else if p[j - 1] == '?' || p[j - 1] == t[i - 1] {
299 dp[i][j] = dp[i - 1][j - 1];
300 }
301 }
302 }
303 dp[tlen][plen]
304}
305
306pub fn collect_criteria_range_values(arg: &Expr, ctx: &mut Evaluator) -> Result<Vec<CellValue>> {
308 match arg {
309 Expr::Range { start, end } => ctx.expand_range(start, end),
310 _ => {
311 let v = ctx.eval_expr(arg)?;
312 Ok(vec![v])
313 }
314 }
315}
316
317fn fn_sum(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
320 check_arg_count("SUM", args, 1, 255)?;
321 let nums = ctx.collect_numbers(args)?;
322 Ok(CellValue::Number(nums.iter().sum()))
323}
324
325fn fn_average(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
326 check_arg_count("AVERAGE", args, 1, 255)?;
327 let nums = ctx.collect_numbers(args)?;
328 if nums.is_empty() {
329 return Ok(CellValue::Error("#DIV/0!".to_string()));
330 }
331 let sum: f64 = nums.iter().sum();
332 Ok(CellValue::Number(sum / nums.len() as f64))
333}
334
335fn fn_count(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
336 check_arg_count("COUNT", args, 1, 255)?;
337 let values = ctx.flatten_args_to_values(args)?;
338 let count = values
339 .iter()
340 .filter(|v| matches!(v, CellValue::Number(_) | CellValue::Date(_)))
341 .count();
342 Ok(CellValue::Number(count as f64))
343}
344
345fn fn_counta(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
346 check_arg_count("COUNTA", args, 1, 255)?;
347 let values = ctx.flatten_args_to_values(args)?;
348 let count = values
349 .iter()
350 .filter(|v| !matches!(v, CellValue::Empty))
351 .count();
352 Ok(CellValue::Number(count as f64))
353}
354
355fn fn_min(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
356 check_arg_count("MIN", args, 1, 255)?;
357 let nums = ctx.collect_numbers(args)?;
358 if nums.is_empty() {
359 return Ok(CellValue::Number(0.0));
360 }
361 let min = nums.iter().copied().fold(f64::INFINITY, f64::min);
362 Ok(CellValue::Number(min))
363}
364
365fn fn_max(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
366 check_arg_count("MAX", args, 1, 255)?;
367 let nums = ctx.collect_numbers(args)?;
368 if nums.is_empty() {
369 return Ok(CellValue::Number(0.0));
370 }
371 let max = nums.iter().copied().fold(f64::NEG_INFINITY, f64::max);
372 Ok(CellValue::Number(max))
373}
374
375fn fn_if(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
378 check_arg_count("IF", args, 1, 3)?;
379 let cond = ctx.eval_expr(&args[0])?;
380 let truth = crate::formula::eval::coerce_to_bool(&cond)?;
381 if truth {
382 if args.len() > 1 {
383 ctx.eval_expr(&args[1])
384 } else {
385 Ok(CellValue::Bool(true))
386 }
387 } else if args.len() > 2 {
388 ctx.eval_expr(&args[2])
389 } else {
390 Ok(CellValue::Bool(false))
391 }
392}
393
394fn fn_and(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
395 check_arg_count("AND", args, 1, 255)?;
396 let values = ctx.flatten_args_to_values(args)?;
397 for v in &values {
398 if matches!(v, CellValue::Empty) {
399 continue;
400 }
401 if !crate::formula::eval::coerce_to_bool(v)? {
402 return Ok(CellValue::Bool(false));
403 }
404 }
405 Ok(CellValue::Bool(true))
406}
407
408fn fn_or(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
409 check_arg_count("OR", args, 1, 255)?;
410 let values = ctx.flatten_args_to_values(args)?;
411 for v in &values {
412 if matches!(v, CellValue::Empty) {
413 continue;
414 }
415 if crate::formula::eval::coerce_to_bool(v)? {
416 return Ok(CellValue::Bool(true));
417 }
418 }
419 Ok(CellValue::Bool(false))
420}
421
422fn fn_not(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
423 check_arg_count("NOT", args, 1, 1)?;
424 let v = ctx.eval_expr(&args[0])?;
425 let b = crate::formula::eval::coerce_to_bool(&v)?;
426 Ok(CellValue::Bool(!b))
427}
428
429fn fn_abs(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
432 check_arg_count("ABS", args, 1, 1)?;
433 let v = ctx.eval_expr(&args[0])?;
434 let n = crate::formula::eval::coerce_to_number(&v)?;
435 Ok(CellValue::Number(n.abs()))
436}
437
438fn fn_int(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
439 check_arg_count("INT", args, 1, 1)?;
440 let v = ctx.eval_expr(&args[0])?;
441 let n = crate::formula::eval::coerce_to_number(&v)?;
442 Ok(CellValue::Number(n.floor()))
443}
444
445fn fn_round(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
446 check_arg_count("ROUND", args, 2, 2)?;
447 let v = ctx.eval_expr(&args[0])?;
448 let d = ctx.eval_expr(&args[1])?;
449 let n = crate::formula::eval::coerce_to_number(&v)?;
450 let digits = crate::formula::eval::coerce_to_number(&d)? as i32;
451 let factor = 10f64.powi(digits);
452 Ok(CellValue::Number((n * factor).round() / factor))
453}
454
455fn fn_mod(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
456 check_arg_count("MOD", args, 2, 2)?;
457 let a = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[0])?)?;
458 let b = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[1])?)?;
459 if b == 0.0 {
460 return Ok(CellValue::Error("#DIV/0!".to_string()));
461 }
462 let result = a - (a / b).floor() * b;
464 Ok(CellValue::Number(result))
465}
466
467fn fn_power(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
468 check_arg_count("POWER", args, 2, 2)?;
469 let base = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[0])?)?;
470 let exp = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[1])?)?;
471 Ok(CellValue::Number(base.powf(exp)))
472}
473
474fn fn_sqrt(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
475 check_arg_count("SQRT", args, 1, 1)?;
476 let n = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[0])?)?;
477 if n < 0.0 {
478 return Ok(CellValue::Error("#NUM!".to_string()));
479 }
480 Ok(CellValue::Number(n.sqrt()))
481}
482
483fn fn_len(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
486 check_arg_count("LEN", args, 1, 1)?;
487 let v = ctx.eval_expr(&args[0])?;
488 let s = crate::formula::eval::coerce_to_string(&v);
489 Ok(CellValue::Number(s.len() as f64))
490}
491
492fn fn_lower(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
493 check_arg_count("LOWER", args, 1, 1)?;
494 let v = ctx.eval_expr(&args[0])?;
495 let s = crate::formula::eval::coerce_to_string(&v);
496 Ok(CellValue::String(s.to_lowercase()))
497}
498
499fn fn_upper(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
500 check_arg_count("UPPER", args, 1, 1)?;
501 let v = ctx.eval_expr(&args[0])?;
502 let s = crate::formula::eval::coerce_to_string(&v);
503 Ok(CellValue::String(s.to_uppercase()))
504}
505
506fn fn_trim(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
507 check_arg_count("TRIM", args, 1, 1)?;
508 let v = ctx.eval_expr(&args[0])?;
509 let s = crate::formula::eval::coerce_to_string(&v);
510 let trimmed: String = s.split_whitespace().collect::<Vec<_>>().join(" ");
512 Ok(CellValue::String(trimmed))
513}
514
515fn fn_left(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
516 check_arg_count("LEFT", args, 1, 2)?;
517 let v = ctx.eval_expr(&args[0])?;
518 let s = crate::formula::eval::coerce_to_string(&v);
519 let n = if args.len() > 1 {
520 crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[1])?)? as usize
521 } else {
522 1
523 };
524 let result: String = s.chars().take(n).collect();
525 Ok(CellValue::String(result))
526}
527
528fn fn_right(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
529 check_arg_count("RIGHT", args, 1, 2)?;
530 let v = ctx.eval_expr(&args[0])?;
531 let s = crate::formula::eval::coerce_to_string(&v);
532 let n = if args.len() > 1 {
533 crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[1])?)? as usize
534 } else {
535 1
536 };
537 let chars: Vec<char> = s.chars().collect();
538 let start = chars.len().saturating_sub(n);
539 let result: String = chars[start..].iter().collect();
540 Ok(CellValue::String(result))
541}
542
543fn fn_mid(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
544 check_arg_count("MID", args, 3, 3)?;
545 let v = ctx.eval_expr(&args[0])?;
546 let s = crate::formula::eval::coerce_to_string(&v);
547 let start = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[1])?)? as usize;
548 let count = crate::formula::eval::coerce_to_number(&ctx.eval_expr(&args[2])?)? as usize;
549 if start < 1 {
550 return Ok(CellValue::Error("#VALUE!".to_string()));
551 }
552 let result: String = s.chars().skip(start - 1).take(count).collect();
553 Ok(CellValue::String(result))
554}
555
556fn fn_concatenate(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
557 check_arg_count("CONCATENATE", args, 1, 255)?;
558 let mut result = String::new();
559 for arg in args {
560 let v = ctx.eval_expr(arg)?;
561 result.push_str(&crate::formula::eval::coerce_to_string(&v));
562 }
563 Ok(CellValue::String(result))
564}
565
566fn fn_isnumber(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
569 check_arg_count("ISNUMBER", args, 1, 1)?;
570 let v = ctx.eval_expr(&args[0])?;
571 Ok(CellValue::Bool(matches!(
572 v,
573 CellValue::Number(_) | CellValue::Date(_)
574 )))
575}
576
577fn fn_istext(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
578 check_arg_count("ISTEXT", args, 1, 1)?;
579 let v = ctx.eval_expr(&args[0])?;
580 Ok(CellValue::Bool(matches!(v, CellValue::String(_))))
581}
582
583fn fn_isblank(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
584 check_arg_count("ISBLANK", args, 1, 1)?;
585 let v = ctx.eval_expr(&args[0])?;
586 Ok(CellValue::Bool(matches!(v, CellValue::Empty)))
587}
588
589fn fn_iserror(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
590 check_arg_count("ISERROR", args, 1, 1)?;
591 let v = ctx.eval_expr(&args[0])?;
592 Ok(CellValue::Bool(matches!(v, CellValue::Error(_))))
593}
594
595fn fn_value(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
598 check_arg_count("VALUE", args, 1, 1)?;
599 let v = ctx.eval_expr(&args[0])?;
600 match crate::formula::eval::coerce_to_number(&v) {
601 Ok(n) => Ok(CellValue::Number(n)),
602 Err(_) => Ok(CellValue::Error("#VALUE!".to_string())),
603 }
604}
605
606fn fn_text(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
607 check_arg_count("TEXT", args, 2, 2)?;
608 let v = ctx.eval_expr(&args[0])?;
609 let _fmt = ctx.eval_expr(&args[1])?;
610 Ok(CellValue::String(crate::formula::eval::coerce_to_string(
612 &v,
613 )))
614}