1use super::utils::ARG_ANY_ONE;
2use crate::args::ArgSchema;
3use crate::function::Function;
4use crate::traits::{ArgumentHandle, FunctionContext};
5use formualizer_common::{ExcelError, LiteralValue};
6use formualizer_macros::func_caps;
7
8#[derive(Debug)]
11pub struct NotFn;
12impl Function for NotFn {
13 func_caps!(PURE);
14 fn name(&self) -> &'static str {
15 "NOT"
16 }
17 fn min_args(&self) -> usize {
18 1
19 }
20 fn arg_schema(&self) -> &'static [ArgSchema] {
21 &ARG_ANY_ONE[..]
22 }
23 fn eval_scalar<'a, 'b>(
24 &self,
25 args: &'a [ArgumentHandle<'a, 'b>],
26 _ctx: &dyn FunctionContext,
27 ) -> Result<LiteralValue, ExcelError> {
28 if args.len() != 1 {
29 return Ok(LiteralValue::Error(ExcelError::new_value()));
30 }
31 let v = args[0].value()?;
32 let b = match v.as_ref() {
33 LiteralValue::Boolean(b) => !*b,
34 LiteralValue::Number(n) => *n == 0.0,
35 LiteralValue::Int(i) => *i == 0,
36 LiteralValue::Empty => true,
37 LiteralValue::Error(e) => return Ok(LiteralValue::Error(e.clone())),
38 _ => {
39 return Ok(LiteralValue::Error(ExcelError::new_value()));
40 }
41 };
42 Ok(LiteralValue::Boolean(b))
43 }
44}
45
46#[derive(Debug)]
47pub struct XorFn;
48impl Function for XorFn {
49 func_caps!(PURE, REDUCTION, BOOL_ONLY);
50 fn name(&self) -> &'static str {
51 "XOR"
52 }
53 fn min_args(&self) -> usize {
54 1
55 }
56 fn variadic(&self) -> bool {
57 true
58 }
59 fn arg_schema(&self) -> &'static [ArgSchema] {
60 &ARG_ANY_ONE[..]
61 }
62 fn eval_scalar<'a, 'b>(
63 &self,
64 args: &'a [ArgumentHandle<'a, 'b>],
65 _ctx: &dyn FunctionContext,
66 ) -> Result<LiteralValue, ExcelError> {
67 let mut true_count = 0usize;
68 let mut first_error: Option<LiteralValue> = None;
69 for a in args {
70 if let Ok(view) = a.range_view() {
71 let mut err: Option<LiteralValue> = None;
72 view.for_each_cell(&mut |val| {
73 match val {
74 LiteralValue::Boolean(b) => {
75 if *b {
76 true_count += 1;
77 }
78 }
79 LiteralValue::Number(n) => {
80 if *n != 0.0 {
81 true_count += 1;
82 }
83 }
84 LiteralValue::Int(i) => {
85 if *i != 0 {
86 true_count += 1;
87 }
88 }
89 LiteralValue::Empty => {}
90 LiteralValue::Error(_) => {
91 if first_error.is_none() {
92 err = Some(val.clone());
93 }
94 }
95 _ => {
96 if first_error.is_none() {
97 err = Some(LiteralValue::Error(ExcelError::from_error_string(
98 "#VALUE!",
99 )));
100 }
101 }
102 }
103 Ok(())
104 })?;
105 if first_error.is_none() {
106 first_error = err;
107 }
108 } else {
109 let v = a.value()?;
110 match v.as_ref() {
111 LiteralValue::Boolean(b) => {
112 if *b {
113 true_count += 1;
114 }
115 }
116 LiteralValue::Number(n) => {
117 if *n != 0.0 {
118 true_count += 1;
119 }
120 }
121 LiteralValue::Int(i) => {
122 if *i != 0 {
123 true_count += 1;
124 }
125 }
126 LiteralValue::Empty => {}
127 LiteralValue::Error(e) => {
128 if first_error.is_none() {
129 first_error = Some(LiteralValue::Error(e.clone()));
130 }
131 }
132 _ => {
133 if first_error.is_none() {
134 first_error = Some(LiteralValue::Error(ExcelError::from_error_string(
135 "#VALUE!",
136 )));
137 }
138 }
139 }
140 }
141 }
142 if let Some(err) = first_error {
143 return Ok(err);
144 }
145 Ok(LiteralValue::Boolean(true_count % 2 == 1))
146 }
147}
148
149#[derive(Debug)]
150pub struct IfErrorFn; impl Function for IfErrorFn {
152 func_caps!(PURE);
153 fn name(&self) -> &'static str {
154 "IFERROR"
155 }
156 fn min_args(&self) -> usize {
157 2
158 }
159 fn variadic(&self) -> bool {
160 false
161 }
162 fn arg_schema(&self) -> &'static [ArgSchema] {
163 use std::sync::LazyLock;
164 static TWO: LazyLock<Vec<ArgSchema>> =
166 LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::any()]);
167 &TWO[..]
168 }
169 fn eval_scalar<'a, 'b>(
170 &self,
171 args: &'a [ArgumentHandle<'a, 'b>],
172 _ctx: &dyn FunctionContext,
173 ) -> Result<LiteralValue, ExcelError> {
174 if args.len() != 2 {
175 return Ok(LiteralValue::Error(ExcelError::new_value()));
176 }
177 let v = args[0].value()?;
178 match v.as_ref() {
179 LiteralValue::Error(_) => Ok(args[1].value()?.into_owned()),
180 other => Ok(other.clone()),
181 }
182 }
183}
184
185#[derive(Debug)]
186pub struct IfNaFn; impl Function for IfNaFn {
188 func_caps!(PURE);
189 fn name(&self) -> &'static str {
190 "IFNA"
191 }
192 fn min_args(&self) -> usize {
193 2
194 }
195 fn variadic(&self) -> bool {
196 false
197 }
198 fn arg_schema(&self) -> &'static [ArgSchema] {
199 use std::sync::LazyLock;
200 static TWO: LazyLock<Vec<ArgSchema>> =
201 LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::any()]);
202 &TWO[..]
203 }
204 fn eval_scalar<'a, 'b>(
205 &self,
206 args: &'a [ArgumentHandle<'a, 'b>],
207 _ctx: &dyn FunctionContext,
208 ) -> Result<LiteralValue, ExcelError> {
209 if args.len() != 2 {
210 return Ok(LiteralValue::Error(ExcelError::new_value()));
211 }
212 let v = args[0].value()?;
213 match v.as_ref() {
214 LiteralValue::Error(e) if *e == "#N/A" => Ok(args[1].value()?.into_owned()),
215 LiteralValue::Error(_) => Ok(v.into_owned()),
216 other => Ok(other.clone()),
217 }
218 }
219}
220
221#[derive(Debug)]
222pub struct IfsFn; impl Function for IfsFn {
224 func_caps!(PURE, SHORT_CIRCUIT);
225 fn name(&self) -> &'static str {
226 "IFS"
227 }
228 fn min_args(&self) -> usize {
229 2
230 }
231 fn variadic(&self) -> bool {
232 true
233 }
234 fn arg_schema(&self) -> &'static [ArgSchema] {
235 &ARG_ANY_ONE[..]
236 }
237 fn eval_scalar<'a, 'b>(
238 &self,
239 args: &'a [ArgumentHandle<'a, 'b>],
240 _ctx: &dyn FunctionContext,
241 ) -> Result<LiteralValue, ExcelError> {
242 if args.len() < 2 || args.len() % 2 != 0 {
243 return Ok(LiteralValue::Error(ExcelError::new_value()));
244 }
245 for pair in args.chunks(2) {
246 let cond = pair[0].value()?;
247 let is_true = match cond.as_ref() {
248 LiteralValue::Boolean(b) => *b,
249 LiteralValue::Number(n) => *n != 0.0,
250 LiteralValue::Int(i) => *i != 0,
251 LiteralValue::Empty => false,
252 LiteralValue::Error(e) => return Ok(LiteralValue::Error(e.clone())),
253 _ => {
254 return Ok(LiteralValue::Error(ExcelError::from_error_string(
255 "#VALUE!",
256 )));
257 }
258 };
259 if is_true {
260 return pair[1].value().map(|c| c.into_owned());
261 }
262 }
263 Ok(LiteralValue::Error(ExcelError::new_na()))
264 }
265}
266
267pub fn register_builtins() {
268 use std::sync::Arc;
269 crate::function_registry::register_function(Arc::new(NotFn));
270 crate::function_registry::register_function(Arc::new(XorFn));
271 crate::function_registry::register_function(Arc::new(IfErrorFn));
272 crate::function_registry::register_function(Arc::new(IfNaFn));
273 crate::function_registry::register_function(Arc::new(IfsFn));
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use crate::test_workbook::TestWorkbook;
280 use crate::traits::ArgumentHandle;
281 use formualizer_common::LiteralValue;
282 use formualizer_parse::parser::{ASTNode, ASTNodeType};
283
284 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
285 wb.interpreter()
286 }
287
288 #[test]
289 fn not_basic() {
290 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(NotFn));
291 let ctx = interp(&wb);
292 let t = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(true)), None);
293 let args = vec![ArgumentHandle::new(&t, &ctx)];
294 let f = ctx.context.get_function("", "NOT").unwrap();
295 assert_eq!(
296 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
297 LiteralValue::Boolean(false)
298 );
299 }
300
301 #[test]
302 fn xor_range_and_scalars() {
303 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(XorFn));
304 let ctx = interp(&wb);
305 let arr = ASTNode::new(
306 ASTNodeType::Literal(LiteralValue::Array(vec![vec![
307 LiteralValue::Int(1),
308 LiteralValue::Int(0),
309 LiteralValue::Int(2),
310 ]])),
311 None,
312 );
313 let zero = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(0)), None);
314 let args = vec![
315 ArgumentHandle::new(&arr, &ctx),
316 ArgumentHandle::new(&zero, &ctx),
317 ];
318 let f = ctx.context.get_function("", "XOR").unwrap();
319 assert_eq!(
321 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
322 LiteralValue::Boolean(false)
323 );
324 }
325
326 #[test]
327 fn iferror_fallback() {
328 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfErrorFn));
329 let ctx = interp(&wb);
330 let err = ASTNode::new(
331 ASTNodeType::Literal(LiteralValue::Error(ExcelError::from_error_string(
332 "#DIV/0!",
333 ))),
334 None,
335 );
336 let fb = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(5)), None);
337 let args = vec![
338 ArgumentHandle::new(&err, &ctx),
339 ArgumentHandle::new(&fb, &ctx),
340 ];
341 let f = ctx.context.get_function("", "IFERROR").unwrap();
342 assert_eq!(
343 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
344 LiteralValue::Int(5)
345 );
346 }
347
348 #[test]
349 fn iferror_passthrough_non_error() {
350 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfErrorFn));
351 let ctx = interp(&wb);
352 let val = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(11)), None);
353 let fb = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(5)), None);
354 let args = vec![
355 ArgumentHandle::new(&val, &ctx),
356 ArgumentHandle::new(&fb, &ctx),
357 ];
358 let f = ctx.context.get_function("", "IFERROR").unwrap();
359 assert_eq!(
360 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
361 LiteralValue::Int(11)
362 );
363 }
364
365 #[test]
366 fn ifna_only_handles_na() {
367 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfNaFn));
368 let ctx = interp(&wb);
369 let na = ASTNode::new(
370 ASTNodeType::Literal(LiteralValue::Error(ExcelError::new_na())),
371 None,
372 );
373 let other_err = ASTNode::new(
374 ASTNodeType::Literal(LiteralValue::Error(ExcelError::new_value())),
375 None,
376 );
377 let fb = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(7)), None);
378 let args_na = vec![
379 ArgumentHandle::new(&na, &ctx),
380 ArgumentHandle::new(&fb, &ctx),
381 ];
382 let args_val = vec![
383 ArgumentHandle::new(&other_err, &ctx),
384 ArgumentHandle::new(&fb, &ctx),
385 ];
386 let f = ctx.context.get_function("", "IFNA").unwrap();
387 assert_eq!(
388 f.dispatch(&args_na, &ctx.function_context(None)).unwrap(),
389 LiteralValue::Int(7)
390 );
391 match f.dispatch(&args_val, &ctx.function_context(None)).unwrap() {
392 LiteralValue::Error(e) => assert_eq!(e, "#VALUE!"),
393 _ => panic!(),
394 }
395 }
396
397 #[test]
398 fn ifna_value_passthrough() {
399 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfNaFn));
400 let ctx = interp(&wb);
401 let val = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(22)), None);
402 let fb = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(9)), None);
403 let args = vec![
404 ArgumentHandle::new(&val, &ctx),
405 ArgumentHandle::new(&fb, &ctx),
406 ];
407 let f = ctx.context.get_function("", "IFNA").unwrap();
408 assert_eq!(
409 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
410 LiteralValue::Int(22)
411 );
412 }
413
414 #[test]
415 fn ifs_short_circuits() {
416 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfsFn));
417 let ctx = interp(&wb);
418 let cond_true = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(true)), None);
419 let val1 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(9)), None);
420 let cond_false = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(false)), None);
421 let val2 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
422 let args = vec![
423 ArgumentHandle::new(&cond_true, &ctx),
424 ArgumentHandle::new(&val1, &ctx),
425 ArgumentHandle::new(&cond_false, &ctx),
426 ArgumentHandle::new(&val2, &ctx),
427 ];
428 let f = ctx.context.get_function("", "IFS").unwrap();
429 assert_eq!(
430 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
431 LiteralValue::Int(9)
432 );
433 }
434
435 #[test]
436 fn ifs_no_match_returns_na_error() {
437 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IfsFn));
438 let ctx = interp(&wb);
439 let cond_false1 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(false)), None);
440 let val1 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(9)), None);
441 let cond_false2 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(false)), None);
442 let val2 = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
443 let args = vec![
444 ArgumentHandle::new(&cond_false1, &ctx),
445 ArgumentHandle::new(&val1, &ctx),
446 ArgumentHandle::new(&cond_false2, &ctx),
447 ArgumentHandle::new(&val2, &ctx),
448 ];
449 let f = ctx.context.get_function("", "IFS").unwrap();
450 match f.dispatch(&args, &ctx.function_context(None)).unwrap() {
451 LiteralValue::Error(e) => assert_eq!(e, "#N/A"),
452 other => panic!("expected #N/A got {other:?}"),
453 }
454 }
455
456 #[test]
457 fn not_number_zero_and_nonzero() {
458 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(NotFn));
459 let ctx = interp(&wb);
460 let zero = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(0)), None);
461 let one = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
462 let f = ctx.context.get_function("", "NOT").unwrap();
463 assert_eq!(
464 f.dispatch(
465 &[ArgumentHandle::new(&zero, &ctx)],
466 &ctx.function_context(None)
467 )
468 .unwrap(),
469 LiteralValue::Boolean(true)
470 );
471 assert_eq!(
472 f.dispatch(
473 &[ArgumentHandle::new(&one, &ctx)],
474 &ctx.function_context(None)
475 )
476 .unwrap(),
477 LiteralValue::Boolean(false)
478 );
479 }
480
481 #[test]
482 fn xor_error_propagation() {
483 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(XorFn));
484 let ctx = interp(&wb);
485 let err = ASTNode::new(
486 ASTNodeType::Literal(LiteralValue::Error(ExcelError::new_value())),
487 None,
488 );
489 let one = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
490 let f = ctx.context.get_function("", "XOR").unwrap();
491 match f
492 .dispatch(
493 &[
494 ArgumentHandle::new(&err, &ctx),
495 ArgumentHandle::new(&one, &ctx),
496 ],
497 &ctx.function_context(None),
498 )
499 .unwrap()
500 {
501 LiteralValue::Error(e) => assert_eq!(e, "#VALUE!"),
502 _ => panic!("expected value error"),
503 }
504 }
505}