1use super::utils::ARG_ANY_ONE;
4use crate::args::ArgSchema;
5use crate::function::Function;
6use crate::traits::{ArgumentHandle, FunctionContext};
7use formualizer_common::{ExcelError, LiteralValue};
8use formualizer_macros::func_caps;
9
10#[derive(Debug)]
13pub struct TrueFn;
14
15impl Function for TrueFn {
16 func_caps!(PURE);
17
18 fn name(&self) -> &'static str {
19 "TRUE"
20 }
21 fn min_args(&self) -> usize {
22 0
23 }
24
25 fn eval_scalar<'a, 'b>(
26 &self,
27 _args: &'a [ArgumentHandle<'a, 'b>],
28 _ctx: &dyn FunctionContext,
29 ) -> Result<LiteralValue, ExcelError> {
30 Ok(LiteralValue::Boolean(true))
31 }
32}
33
34#[derive(Debug)]
37pub struct FalseFn;
38
39impl Function for FalseFn {
40 func_caps!(PURE);
41
42 fn name(&self) -> &'static str {
43 "FALSE"
44 }
45 fn min_args(&self) -> usize {
46 0
47 }
48
49 fn eval_scalar<'a, 'b>(
50 &self,
51 _args: &'a [ArgumentHandle<'a, 'b>],
52 _ctx: &dyn FunctionContext,
53 ) -> Result<LiteralValue, ExcelError> {
54 Ok(LiteralValue::Boolean(false))
55 }
56}
57
58#[derive(Debug)]
61pub struct AndFn;
62
63impl Function for AndFn {
64 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
65
66 fn name(&self) -> &'static str {
67 "AND"
68 }
69 fn min_args(&self) -> usize {
70 1
71 }
72 fn variadic(&self) -> bool {
73 true
74 }
75 fn arg_schema(&self) -> &'static [ArgSchema] {
76 &ARG_ANY_ONE[..]
77 }
78
79 fn eval_scalar<'a, 'b>(
80 &self,
81 args: &'a [ArgumentHandle<'a, 'b>],
82 _ctx: &dyn FunctionContext,
83 ) -> Result<LiteralValue, ExcelError> {
84 let mut first_error: Option<LiteralValue> = None;
85 for h in args {
86 let it = h.lazy_values_owned()?;
87 for v in it {
88 match v {
89 LiteralValue::Error(_) => {
90 if first_error.is_none() {
91 first_error = Some(v);
92 }
93 }
94 LiteralValue::Empty => {
95 return Ok(LiteralValue::Boolean(false));
96 }
97 LiteralValue::Boolean(b) => {
98 if !b {
99 return Ok(LiteralValue::Boolean(false));
100 }
101 }
102 LiteralValue::Number(n) => {
103 if n == 0.0 {
104 return Ok(LiteralValue::Boolean(false));
105 }
106 }
107 LiteralValue::Int(i) => {
108 if i == 0 {
109 return Ok(LiteralValue::Boolean(false));
110 }
111 }
112 _ => {
113 if first_error.is_none() {
115 first_error =
116 Some(LiteralValue::Error(ExcelError::new_value().with_message(
117 "AND expects logical/numeric inputs; text is not coercible",
118 )));
119 }
120 }
121 }
122 }
123 }
124 if let Some(err) = first_error {
125 return Ok(err);
126 }
127 Ok(LiteralValue::Boolean(true))
128 }
129}
130
131#[derive(Debug)]
134pub struct OrFn;
135
136impl Function for OrFn {
137 func_caps!(PURE, REDUCTION, BOOL_ONLY, SHORT_CIRCUIT);
138
139 fn name(&self) -> &'static str {
140 "OR"
141 }
142 fn min_args(&self) -> usize {
143 1
144 }
145 fn variadic(&self) -> bool {
146 true
147 }
148 fn arg_schema(&self) -> &'static [ArgSchema] {
149 &ARG_ANY_ONE[..]
150 }
151
152 fn eval_scalar<'a, 'b>(
153 &self,
154 args: &'a [ArgumentHandle<'a, 'b>],
155 _ctx: &dyn FunctionContext,
156 ) -> Result<LiteralValue, ExcelError> {
157 let mut first_error: Option<LiteralValue> = None;
158 for h in args {
159 let it = h.lazy_values_owned()?;
160 for v in it {
161 match v {
162 LiteralValue::Error(_) => {
163 if first_error.is_none() {
164 first_error = Some(v);
165 }
166 }
167 LiteralValue::Empty => {
168 }
170 LiteralValue::Boolean(b) => {
171 if b {
172 return Ok(LiteralValue::Boolean(true));
173 }
174 }
175 LiteralValue::Number(n) => {
176 if n != 0.0 {
177 return Ok(LiteralValue::Boolean(true));
178 }
179 }
180 LiteralValue::Int(i) => {
181 if i != 0 {
182 return Ok(LiteralValue::Boolean(true));
183 }
184 }
185 _ => {
186 if first_error.is_none() {
188 first_error =
189 Some(LiteralValue::Error(ExcelError::new_value().with_message(
190 "OR expects logical/numeric inputs; text is not coercible",
191 )));
192 }
193 }
194 }
195 }
196 }
197 if let Some(err) = first_error {
198 return Ok(err);
199 }
200 Ok(LiteralValue::Boolean(false))
201 }
202}
203
204#[derive(Debug)]
207pub struct IfFn;
208
209impl Function for IfFn {
210 func_caps!(PURE, SHORT_CIRCUIT);
211
212 fn name(&self) -> &'static str {
213 "IF"
214 }
215 fn min_args(&self) -> usize {
216 2
217 }
218 fn variadic(&self) -> bool {
219 true
220 }
221
222 fn arg_schema(&self) -> &'static [ArgSchema] {
223 use std::sync::LazyLock;
224 static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
226 &ONE[..]
227 }
228
229 fn eval_scalar<'a, 'b>(
230 &self,
231 args: &'a [ArgumentHandle<'a, 'b>],
232 _ctx: &dyn FunctionContext,
233 ) -> Result<LiteralValue, ExcelError> {
234 if args.len() < 2 || args.len() > 3 {
235 return Ok(LiteralValue::Error(ExcelError::new_value().with_message(
236 format!("IF expects 2 or 3 arguments, got {}", args.len()),
237 )));
238 }
239
240 let condition = args[0].value()?;
241 let b = match condition.as_ref() {
242 LiteralValue::Boolean(b) => *b,
243 LiteralValue::Number(n) => *n != 0.0,
244 LiteralValue::Int(i) => *i != 0,
245 _ => {
246 return Ok(LiteralValue::Error(
247 ExcelError::new_value().with_message("IF condition must be boolean or number"),
248 ));
249 }
250 };
251
252 if b {
253 args[1].value().map(|cow| cow.into_owned())
254 } else if let Some(arg) = args.get(2) {
255 arg.value().map(|cow| cow.into_owned())
256 } else {
257 Ok(LiteralValue::Boolean(false))
258 }
259 }
260}
261
262pub fn register_builtins() {
263 crate::function_registry::register_function(std::sync::Arc::new(TrueFn));
264 crate::function_registry::register_function(std::sync::Arc::new(FalseFn));
265 crate::function_registry::register_function(std::sync::Arc::new(AndFn));
266 crate::function_registry::register_function(std::sync::Arc::new(OrFn));
267 crate::function_registry::register_function(std::sync::Arc::new(IfFn));
268}
269
270#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::traits::{ArgumentHandle, DefaultFunctionContext};
276 use crate::{interpreter::Interpreter, test_workbook::TestWorkbook};
277 use formualizer_parse::LiteralValue;
278 use std::sync::{
279 Arc,
280 atomic::{AtomicUsize, Ordering},
281 };
282
283 #[derive(Debug)]
284 struct CountFn(Arc<AtomicUsize>);
285 impl Function for CountFn {
286 func_caps!(PURE);
287 fn name(&self) -> &'static str {
288 "COUNTING"
289 }
290 fn min_args(&self) -> usize {
291 0
292 }
293 fn eval_scalar<'a, 'b>(
294 &self,
295 _args: &'a [ArgumentHandle<'a, 'b>],
296 _ctx: &dyn FunctionContext,
297 ) -> Result<LiteralValue, ExcelError> {
298 self.0.fetch_add(1, Ordering::SeqCst);
299 Ok(LiteralValue::Boolean(true))
300 }
301 }
302
303 #[derive(Debug)]
304 struct ErrorFn(Arc<AtomicUsize>);
305 impl Function for ErrorFn {
306 func_caps!(PURE);
307 fn name(&self) -> &'static str {
308 "ERRORFN"
309 }
310 fn min_args(&self) -> usize {
311 0
312 }
313 fn eval_scalar<'a, 'b>(
314 &self,
315 _args: &'a [ArgumentHandle<'a, 'b>],
316 _ctx: &dyn FunctionContext,
317 ) -> Result<LiteralValue, ExcelError> {
318 self.0.fetch_add(1, Ordering::SeqCst);
319 Ok(LiteralValue::Error(ExcelError::new_value()))
320 }
321 }
322
323 fn interp(wb: &TestWorkbook) -> Interpreter<'_> {
324 wb.interpreter()
325 }
326
327 #[test]
328 fn test_true_false() {
329 let wb = TestWorkbook::new()
330 .with_function(std::sync::Arc::new(TrueFn))
331 .with_function(std::sync::Arc::new(FalseFn));
332
333 let ctx = interp(&wb);
334 let t = ctx.context.get_function("", "TRUE").unwrap();
335 let fctx = DefaultFunctionContext::new(ctx.context, None);
336 assert_eq!(
337 t.eval_scalar(&[], &fctx).unwrap(),
338 LiteralValue::Boolean(true)
339 );
340
341 let f = ctx.context.get_function("", "FALSE").unwrap();
342 assert_eq!(
343 f.eval_scalar(&[], &fctx).unwrap(),
344 LiteralValue::Boolean(false)
345 );
346 }
347
348 #[test]
349 fn test_and_or() {
350 let wb = TestWorkbook::new()
351 .with_function(std::sync::Arc::new(AndFn))
352 .with_function(std::sync::Arc::new(OrFn));
353 let ctx = interp(&wb);
354 let fctx = crate::traits::DefaultFunctionContext::new(ctx.context, None);
355
356 let and = ctx.context.get_function("", "AND").unwrap();
357 let or = ctx.context.get_function("", "OR").unwrap();
358 let dummy_ast = formualizer_parse::parser::ASTNode::new(
360 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
361 None,
362 );
363 let dummy_ast_false = formualizer_parse::parser::ASTNode::new(
364 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
365 None,
366 );
367 let dummy_ast_one = formualizer_parse::parser::ASTNode::new(
368 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
369 None,
370 );
371 let hs = vec![
372 ArgumentHandle::new(&dummy_ast, &ctx),
373 ArgumentHandle::new(&dummy_ast_one, &ctx),
374 ];
375 assert_eq!(
376 and.eval_scalar(&hs, &fctx).unwrap(),
377 LiteralValue::Boolean(true)
378 );
379
380 let hs2 = vec![
381 ArgumentHandle::new(&dummy_ast_false, &ctx),
382 ArgumentHandle::new(&dummy_ast_one, &ctx),
383 ];
384 assert_eq!(
385 and.eval_scalar(&hs2, &fctx).unwrap(),
386 LiteralValue::Boolean(false)
387 );
388 assert_eq!(
389 or.eval_scalar(&hs2, &fctx).unwrap(),
390 LiteralValue::Boolean(true)
391 );
392 }
393
394 #[test]
395 fn and_short_circuits_on_false_without_evaluating_rest() {
396 let counter = Arc::new(AtomicUsize::new(0));
397 let wb = TestWorkbook::new()
398 .with_function(Arc::new(AndFn))
399 .with_function(Arc::new(CountFn(counter.clone())));
400 let ctx = interp(&wb);
401 let fctx = DefaultFunctionContext::new(ctx.context, None);
402 let and = ctx.context.get_function("", "AND").unwrap();
403
404 let a_false = formualizer_parse::parser::ASTNode::new(
406 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(false)),
407 None,
408 );
409 let counting_call = formualizer_parse::parser::ASTNode::new(
410 formualizer_parse::parser::ASTNodeType::Function {
411 name: "COUNTING".into(),
412 args: vec![],
413 },
414 None,
415 );
416 let hs = vec![
417 ArgumentHandle::new(&a_false, &ctx),
418 ArgumentHandle::new(&counting_call, &ctx),
419 ];
420 let out = and.eval_scalar(&hs, &fctx).unwrap();
421 assert_eq!(out, LiteralValue::Boolean(false));
422 assert_eq!(
423 counter.load(Ordering::SeqCst),
424 0,
425 "COUNTING should not be evaluated"
426 );
427 }
428
429 #[test]
430 fn or_short_circuits_on_true_without_evaluating_rest() {
431 let counter = Arc::new(AtomicUsize::new(0));
432 let wb = TestWorkbook::new()
433 .with_function(Arc::new(OrFn))
434 .with_function(Arc::new(CountFn(counter.clone())));
435 let ctx = interp(&wb);
436 let fctx = DefaultFunctionContext::new(ctx.context, None);
437 let or = ctx.context.get_function("", "OR").unwrap();
438
439 let a_true = formualizer_parse::parser::ASTNode::new(
441 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
442 None,
443 );
444 let counting_call = formualizer_parse::parser::ASTNode::new(
445 formualizer_parse::parser::ASTNodeType::Function {
446 name: "COUNTING".into(),
447 args: vec![],
448 },
449 None,
450 );
451 let hs = vec![
452 ArgumentHandle::new(&a_true, &ctx),
453 ArgumentHandle::new(&counting_call, &ctx),
454 ];
455 let out = or.eval_scalar(&hs, &fctx).unwrap();
456 assert_eq!(out, LiteralValue::Boolean(true));
457 assert_eq!(
458 counter.load(Ordering::SeqCst),
459 0,
460 "COUNTING should not be evaluated"
461 );
462 }
463
464 #[test]
465 fn or_range_arg_short_circuits_on_first_true_before_evaluating_next_arg() {
466 let counter = Arc::new(AtomicUsize::new(0));
467 let wb = TestWorkbook::new()
468 .with_function(Arc::new(OrFn))
469 .with_function(Arc::new(CountFn(counter.clone())));
470 let ctx = interp(&wb);
471 let fctx = DefaultFunctionContext::new(ctx.context, None);
472 let or = ctx.context.get_function("", "OR").unwrap();
473
474 let arr = formualizer_parse::parser::ASTNode::new(
476 formualizer_parse::parser::ASTNodeType::Array(vec![
477 vec![formualizer_parse::parser::ASTNode::new(
478 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
479 None,
480 )],
481 vec![formualizer_parse::parser::ASTNode::new(
482 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(0)),
483 None,
484 )],
485 ]),
486 None,
487 );
488 let counting_call = formualizer_parse::parser::ASTNode::new(
489 formualizer_parse::parser::ASTNodeType::Function {
490 name: "COUNTING".into(),
491 args: vec![],
492 },
493 None,
494 );
495 let hs = vec![
496 ArgumentHandle::new(&arr, &ctx),
497 ArgumentHandle::new(&counting_call, &ctx),
498 ];
499 let out = or.eval_scalar(&hs, &fctx).unwrap();
500 assert_eq!(out, LiteralValue::Boolean(true));
501 assert_eq!(
502 counter.load(Ordering::SeqCst),
503 0,
504 "COUNTING should not be evaluated"
505 );
506 }
507
508 #[test]
509 fn and_returns_first_error_when_no_decisive_false() {
510 let err_counter = Arc::new(AtomicUsize::new(0));
511 let wb = TestWorkbook::new()
512 .with_function(Arc::new(AndFn))
513 .with_function(Arc::new(ErrorFn(err_counter.clone())));
514 let ctx = interp(&wb);
515 let fctx = DefaultFunctionContext::new(ctx.context, None);
516 let and = ctx.context.get_function("", "AND").unwrap();
517
518 let one = formualizer_parse::parser::ASTNode::new(
520 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Int(1)),
521 None,
522 );
523 let errcall = formualizer_parse::parser::ASTNode::new(
524 formualizer_parse::parser::ASTNodeType::Function {
525 name: "ERRORFN".into(),
526 args: vec![],
527 },
528 None,
529 );
530 let hs = vec![
531 ArgumentHandle::new(&one, &ctx),
532 ArgumentHandle::new(&errcall, &ctx),
533 ArgumentHandle::new(&one, &ctx),
534 ];
535 let out = and.eval_scalar(&hs, &fctx).unwrap();
536 match out {
537 LiteralValue::Error(e) => assert_eq!(e.to_string(), "#VALUE!"),
538 _ => panic!("Expected error"),
539 }
540 assert_eq!(
541 err_counter.load(Ordering::SeqCst),
542 1,
543 "ERRORFN should be evaluated once"
544 );
545 }
546
547 #[test]
548 fn or_does_not_evaluate_error_after_true() {
549 let err_counter = Arc::new(AtomicUsize::new(0));
550 let wb = TestWorkbook::new()
551 .with_function(Arc::new(OrFn))
552 .with_function(Arc::new(ErrorFn(err_counter.clone())));
553 let ctx = interp(&wb);
554 let fctx = DefaultFunctionContext::new(ctx.context, None);
555 let or = ctx.context.get_function("", "OR").unwrap();
556
557 let a_true = formualizer_parse::parser::ASTNode::new(
559 formualizer_parse::parser::ASTNodeType::Literal(LiteralValue::Boolean(true)),
560 None,
561 );
562 let errcall = formualizer_parse::parser::ASTNode::new(
563 formualizer_parse::parser::ASTNodeType::Function {
564 name: "ERRORFN".into(),
565 args: vec![],
566 },
567 None,
568 );
569 let hs = vec![
570 ArgumentHandle::new(&a_true, &ctx),
571 ArgumentHandle::new(&errcall, &ctx),
572 ];
573 let out = or.eval_scalar(&hs, &fctx).unwrap();
574 assert_eq!(out, LiteralValue::Boolean(true));
575 assert_eq!(
576 err_counter.load(Ordering::SeqCst),
577 0,
578 "ERRORFN should not be evaluated"
579 );
580 }
581}