1use crate::args::ArgSchema;
2use crate::function::Function;
3use crate::traits::{ArgumentHandle, FunctionContext};
4use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
5use formualizer_macros::func_caps;
6
7use super::utils::ARG_ANY_ONE;
8
9#[derive(Debug)]
36pub struct IsNumberFn;
37impl Function for IsNumberFn {
38 func_caps!(PURE);
39 fn name(&self) -> &'static str {
40 "ISNUMBER"
41 }
42 fn min_args(&self) -> usize {
43 1
44 }
45 fn arg_schema(&self) -> &'static [ArgSchema] {
46 &ARG_ANY_ONE[..]
47 }
48 fn eval<'a, 'b, 'c>(
49 &self,
50 args: &'c [ArgumentHandle<'a, 'b>],
51 _ctx: &dyn FunctionContext<'b>,
52 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
53 if args.len() != 1 {
54 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
55 ExcelError::new_value(),
56 )));
57 }
58 let v = args[0].value()?.into_literal();
59 let is_num = matches!(
60 v,
61 LiteralValue::Int(_)
62 | LiteralValue::Number(_)
63 | LiteralValue::Date(_)
64 | LiteralValue::DateTime(_)
65 | LiteralValue::Time(_)
66 | LiteralValue::Duration(_)
67 );
68 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
69 is_num,
70 )))
71 }
72}
73
74#[derive(Debug)]
75pub struct IsTextFn;
76impl Function for IsTextFn {
77 func_caps!(PURE);
78 fn name(&self) -> &'static str {
79 "ISTEXT"
80 }
81 fn min_args(&self) -> usize {
82 1
83 }
84 fn arg_schema(&self) -> &'static [ArgSchema] {
85 &ARG_ANY_ONE[..]
86 }
87 fn eval<'a, 'b, 'c>(
88 &self,
89 args: &'c [ArgumentHandle<'a, 'b>],
90 _ctx: &dyn FunctionContext<'b>,
91 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
92 if args.len() != 1 {
93 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
94 ExcelError::new_value(),
95 )));
96 }
97 let v = args[0].value()?.into_literal();
98 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
99 matches!(v, LiteralValue::Text(_)),
100 )))
101 }
102}
103
104#[derive(Debug)]
105pub struct IsLogicalFn;
106impl Function for IsLogicalFn {
107 func_caps!(PURE);
108 fn name(&self) -> &'static str {
109 "ISLOGICAL"
110 }
111 fn min_args(&self) -> usize {
112 1
113 }
114 fn arg_schema(&self) -> &'static [ArgSchema] {
115 &ARG_ANY_ONE[..]
116 }
117 fn eval<'a, 'b, 'c>(
118 &self,
119 args: &'c [ArgumentHandle<'a, 'b>],
120 _ctx: &dyn FunctionContext<'b>,
121 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
122 if args.len() != 1 {
123 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
124 ExcelError::new_value(),
125 )));
126 }
127 let v = args[0].value()?.into_literal();
128 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
129 matches!(v, LiteralValue::Boolean(_)),
130 )))
131 }
132}
133
134#[derive(Debug)]
135pub struct IsBlankFn;
136impl Function for IsBlankFn {
137 func_caps!(PURE);
138 fn name(&self) -> &'static str {
139 "ISBLANK"
140 }
141 fn min_args(&self) -> usize {
142 1
143 }
144 fn arg_schema(&self) -> &'static [ArgSchema] {
145 &ARG_ANY_ONE[..]
146 }
147 fn eval<'a, 'b, 'c>(
148 &self,
149 args: &'c [ArgumentHandle<'a, 'b>],
150 _ctx: &dyn FunctionContext<'b>,
151 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
152 if args.len() != 1 {
153 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
154 ExcelError::new_value(),
155 )));
156 }
157 let v = args[0].value()?.into_literal();
158 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
159 matches!(v, LiteralValue::Empty),
160 )))
161 }
162}
163
164#[derive(Debug)]
165pub struct IsErrorFn; impl Function for IsErrorFn {
167 func_caps!(PURE);
168 fn name(&self) -> &'static str {
169 "ISERROR"
170 }
171 fn min_args(&self) -> usize {
172 1
173 }
174 fn arg_schema(&self) -> &'static [ArgSchema] {
175 &ARG_ANY_ONE[..]
176 }
177 fn eval<'a, 'b, 'c>(
178 &self,
179 args: &'c [ArgumentHandle<'a, 'b>],
180 _ctx: &dyn FunctionContext<'b>,
181 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
182 if args.len() != 1 {
183 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
184 ExcelError::new_value(),
185 )));
186 }
187 let v = args[0].value()?.into_literal();
188 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
189 matches!(v, LiteralValue::Error(_)),
190 )))
191 }
192}
193
194#[derive(Debug)]
195pub struct IsErrFn; impl Function for IsErrFn {
197 func_caps!(PURE);
198 fn name(&self) -> &'static str {
199 "ISERR"
200 }
201 fn min_args(&self) -> usize {
202 1
203 }
204 fn arg_schema(&self) -> &'static [ArgSchema] {
205 &ARG_ANY_ONE[..]
206 }
207 fn eval<'a, 'b, 'c>(
208 &self,
209 args: &'c [ArgumentHandle<'a, 'b>],
210 _ctx: &dyn FunctionContext<'b>,
211 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
212 if args.len() != 1 {
213 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
214 ExcelError::new_value(),
215 )));
216 }
217 let v = args[0].value()?.into_literal();
218 let is_err = match v {
219 LiteralValue::Error(e) => e.kind != ExcelErrorKind::Na,
220 _ => false,
221 };
222 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
223 is_err,
224 )))
225 }
226}
227
228#[derive(Debug)]
229pub struct IsNaFn; impl Function for IsNaFn {
231 func_caps!(PURE);
232 fn name(&self) -> &'static str {
233 "ISNA"
234 }
235 fn min_args(&self) -> usize {
236 1
237 }
238 fn arg_schema(&self) -> &'static [ArgSchema] {
239 &ARG_ANY_ONE[..]
240 }
241 fn eval<'a, 'b, 'c>(
242 &self,
243 args: &'c [ArgumentHandle<'a, 'b>],
244 _ctx: &dyn FunctionContext<'b>,
245 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
246 if args.len() != 1 {
247 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
248 ExcelError::new_value(),
249 )));
250 }
251 let v = args[0].value()?.into_literal();
252 let is_na = matches!(v, LiteralValue::Error(e) if e.kind==ExcelErrorKind::Na);
253 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
254 is_na,
255 )))
256 }
257}
258
259#[derive(Debug)]
260pub struct IsFormulaFn; impl Function for IsFormulaFn {
262 func_caps!(PURE);
263 fn name(&self) -> &'static str {
264 "ISFORMULA"
265 }
266 fn min_args(&self) -> usize {
267 1
268 }
269 fn arg_schema(&self) -> &'static [ArgSchema] {
270 &ARG_ANY_ONE[..]
271 }
272 fn eval<'a, 'b, 'c>(
273 &self,
274 args: &'c [ArgumentHandle<'a, 'b>],
275 _ctx: &dyn FunctionContext<'b>,
276 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
277 if args.len() != 1 {
278 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
279 ExcelError::new_value(),
280 )));
281 }
282 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
284 false,
285 )))
286 }
287}
288
289#[derive(Debug)]
290pub struct TypeFn;
291impl Function for TypeFn {
292 func_caps!(PURE);
293 fn name(&self) -> &'static str {
294 "TYPE"
295 }
296 fn min_args(&self) -> usize {
297 1
298 }
299 fn arg_schema(&self) -> &'static [ArgSchema] {
300 &ARG_ANY_ONE[..]
301 }
302 fn eval<'a, 'b, 'c>(
303 &self,
304 args: &'c [ArgumentHandle<'a, 'b>],
305 _ctx: &dyn FunctionContext<'b>,
306 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
307 if args.len() != 1 {
308 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
309 ExcelError::new_value(),
310 )));
311 }
312 let v = args[0].value()?.into_literal(); if let LiteralValue::Error(e) = v {
314 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
315 }
316 let code = match v {
317 LiteralValue::Int(_)
318 | LiteralValue::Number(_)
319 | LiteralValue::Empty
320 | LiteralValue::Date(_)
321 | LiteralValue::DateTime(_)
322 | LiteralValue::Time(_)
323 | LiteralValue::Duration(_) => 1,
324 LiteralValue::Text(_) => 2,
325 LiteralValue::Boolean(_) => 4,
326 LiteralValue::Array(_) => 64,
327 LiteralValue::Error(_) => unreachable!(),
328 LiteralValue::Pending => 1, };
330 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(code)))
331 }
332}
333
334#[derive(Debug)]
335pub struct NaFn; impl Function for NaFn {
337 func_caps!(PURE);
338 fn name(&self) -> &'static str {
339 "NA"
340 }
341 fn min_args(&self) -> usize {
342 0
343 }
344 fn eval<'a, 'b, 'c>(
345 &self,
346 _args: &'c [ArgumentHandle<'a, 'b>],
347 _ctx: &dyn FunctionContext<'b>,
348 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
349 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
350 ExcelError::new(ExcelErrorKind::Na),
351 )))
352 }
353}
354
355#[derive(Debug)]
356pub struct NFn; impl Function for NFn {
358 func_caps!(PURE);
359 fn name(&self) -> &'static str {
360 "N"
361 }
362 fn min_args(&self) -> usize {
363 1
364 }
365 fn arg_schema(&self) -> &'static [ArgSchema] {
366 &ARG_ANY_ONE[..]
367 }
368 fn eval<'a, 'b, 'c>(
369 &self,
370 args: &'c [ArgumentHandle<'a, 'b>],
371 _ctx: &dyn FunctionContext<'b>,
372 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
373 if args.len() != 1 {
374 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
375 ExcelError::new_value(),
376 )));
377 }
378 let v = args[0].value()?.into_literal();
379 match v {
380 LiteralValue::Int(i) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(i))),
381 LiteralValue::Number(n) => {
382 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(n)))
383 }
384 LiteralValue::Date(_)
385 | LiteralValue::DateTime(_)
386 | LiteralValue::Time(_)
387 | LiteralValue::Duration(_) => {
388 if let Some(serial) = v.as_serial_number() {
390 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
391 serial,
392 )))
393 } else {
394 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(0)))
395 }
396 }
397 LiteralValue::Boolean(b) => {
398 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(if b {
399 1
400 } else {
401 0
402 })))
403 }
404 LiteralValue::Text(_) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(0))),
405 LiteralValue::Empty => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(0))),
406 LiteralValue::Array(_) => {
407 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(0)))
409 }
410 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
411 LiteralValue::Pending => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(0))),
412 }
413 }
414}
415
416#[derive(Debug)]
417pub struct TFn; impl Function for TFn {
419 func_caps!(PURE);
420 fn name(&self) -> &'static str {
421 "T"
422 }
423 fn min_args(&self) -> usize {
424 1
425 }
426 fn arg_schema(&self) -> &'static [ArgSchema] {
427 &ARG_ANY_ONE[..]
428 }
429 fn eval<'a, 'b, 'c>(
430 &self,
431 args: &'c [ArgumentHandle<'a, 'b>],
432 _ctx: &dyn FunctionContext<'b>,
433 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
434 if args.len() != 1 {
435 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
436 ExcelError::new_value(),
437 )));
438 }
439 let v = args[0].value()?.into_literal();
440 match v {
441 LiteralValue::Text(s) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(s))),
442 LiteralValue::Error(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
443 _ => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
444 String::new(),
445 ))),
446 }
447 }
448}
449
450#[derive(Debug)]
452pub struct IsEvenFn;
453impl Function for IsEvenFn {
454 func_caps!(PURE);
455 fn name(&self) -> &'static str {
456 "ISEVEN"
457 }
458 fn min_args(&self) -> usize {
459 1
460 }
461 fn arg_schema(&self) -> &'static [ArgSchema] {
462 &ARG_ANY_ONE[..]
463 }
464 fn eval<'a, 'b, 'c>(
465 &self,
466 args: &'c [ArgumentHandle<'a, 'b>],
467 _ctx: &dyn FunctionContext<'b>,
468 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
469 if args.len() != 1 {
470 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
471 ExcelError::new_value(),
472 )));
473 }
474 let v = args[0].value()?.into_literal();
475 let n = match v {
476 LiteralValue::Error(e) => {
477 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
478 }
479 LiteralValue::Int(i) => i as f64,
480 LiteralValue::Number(n) => n,
481 LiteralValue::Boolean(b) => {
482 if b {
483 1.0
484 } else {
485 0.0
486 }
487 }
488 _ => {
489 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
490 ExcelError::new_value(),
491 )));
492 }
493 };
494 let n = n.trunc() as i64;
496 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
497 n % 2 == 0,
498 )))
499 }
500}
501
502#[derive(Debug)]
504pub struct IsOddFn;
505impl Function for IsOddFn {
506 func_caps!(PURE);
507 fn name(&self) -> &'static str {
508 "ISODD"
509 }
510 fn min_args(&self) -> usize {
511 1
512 }
513 fn arg_schema(&self) -> &'static [ArgSchema] {
514 &ARG_ANY_ONE[..]
515 }
516 fn eval<'a, 'b, 'c>(
517 &self,
518 args: &'c [ArgumentHandle<'a, 'b>],
519 _ctx: &dyn FunctionContext<'b>,
520 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
521 if args.len() != 1 {
522 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
523 ExcelError::new_value(),
524 )));
525 }
526 let v = args[0].value()?.into_literal();
527 let n = match v {
528 LiteralValue::Error(e) => {
529 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
530 }
531 LiteralValue::Int(i) => i as f64,
532 LiteralValue::Number(n) => n,
533 LiteralValue::Boolean(b) => {
534 if b {
535 1.0
536 } else {
537 0.0
538 }
539 }
540 _ => {
541 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
542 ExcelError::new_value(),
543 )));
544 }
545 };
546 let n = n.trunc() as i64;
547 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Boolean(
548 n % 2 != 0,
549 )))
550 }
551}
552
553#[derive(Debug)]
567pub struct ErrorTypeFn;
568impl Function for ErrorTypeFn {
569 func_caps!(PURE);
570 fn name(&self) -> &'static str {
571 "ERROR.TYPE"
572 }
573 fn min_args(&self) -> usize {
574 1
575 }
576 fn arg_schema(&self) -> &'static [ArgSchema] {
577 &ARG_ANY_ONE[..]
578 }
579 fn eval<'a, 'b, 'c>(
580 &self,
581 args: &'c [ArgumentHandle<'a, 'b>],
582 _ctx: &dyn FunctionContext<'b>,
583 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
584 if args.len() != 1 {
585 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
586 ExcelError::new_value(),
587 )));
588 }
589 let v = args[0].value()?.into_literal();
590 match v {
591 LiteralValue::Error(e) => {
592 let code = match e.kind {
593 ExcelErrorKind::Null => 1,
594 ExcelErrorKind::Div => 2,
595 ExcelErrorKind::Value => 3,
596 ExcelErrorKind::Ref => 4,
597 ExcelErrorKind::Name => 5,
598 ExcelErrorKind::Num => 6,
599 ExcelErrorKind::Na => 7,
600 ExcelErrorKind::Error => 8,
601 ExcelErrorKind::NImpl => 9,
603 ExcelErrorKind::Spill => 10,
604 ExcelErrorKind::Calc => 11,
605 ExcelErrorKind::Circ => 12,
606 ExcelErrorKind::Cancelled => 13,
607 };
608 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(code)))
609 }
610 _ => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
611 ExcelError::new_na(),
612 ))),
613 }
614 }
615}
616
617pub fn register_builtins() {
618 use std::sync::Arc;
619 crate::function_registry::register_function(Arc::new(IsNumberFn));
620 crate::function_registry::register_function(Arc::new(IsTextFn));
621 crate::function_registry::register_function(Arc::new(IsLogicalFn));
622 crate::function_registry::register_function(Arc::new(IsBlankFn));
623 crate::function_registry::register_function(Arc::new(IsErrorFn));
624 crate::function_registry::register_function(Arc::new(IsErrFn));
625 crate::function_registry::register_function(Arc::new(IsNaFn));
626 crate::function_registry::register_function(Arc::new(IsFormulaFn));
627 crate::function_registry::register_function(Arc::new(IsEvenFn));
628 crate::function_registry::register_function(Arc::new(IsOddFn));
629 crate::function_registry::register_function(Arc::new(ErrorTypeFn));
630 crate::function_registry::register_function(Arc::new(TypeFn));
631 crate::function_registry::register_function(Arc::new(NaFn));
632 crate::function_registry::register_function(Arc::new(NFn));
633 crate::function_registry::register_function(Arc::new(TFn));
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use crate::test_workbook::TestWorkbook;
640 use formualizer_parse::parser::{ASTNode, ASTNodeType};
641 fn interp(wb: &TestWorkbook) -> crate::interpreter::Interpreter<'_> {
642 wb.interpreter()
643 }
644
645 #[test]
646 fn isnumber_numeric_and_date() {
647 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IsNumberFn));
648 let ctx = interp(&wb);
649 let f = ctx.context.get_function("", "ISNUMBER").unwrap();
650 let num = ASTNode::new(
651 ASTNodeType::Literal(LiteralValue::Number(std::f64::consts::PI)),
652 None,
653 );
654 let date = ASTNode::new(
655 ASTNodeType::Literal(LiteralValue::Date(
656 chrono::NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
657 )),
658 None,
659 );
660 let txt = ASTNode::new(ASTNodeType::Literal(LiteralValue::Text("x".into())), None);
661 let args_num = vec![crate::traits::ArgumentHandle::new(&num, &ctx)];
662 let args_date = vec![crate::traits::ArgumentHandle::new(&date, &ctx)];
663 let args_txt = vec![crate::traits::ArgumentHandle::new(&txt, &ctx)];
664 assert_eq!(
665 f.dispatch(&args_num, &ctx.function_context(None))
666 .unwrap()
667 .into_literal(),
668 LiteralValue::Boolean(true)
669 );
670 assert_eq!(
671 f.dispatch(&args_date, &ctx.function_context(None))
672 .unwrap()
673 .into_literal(),
674 LiteralValue::Boolean(true)
675 );
676 assert_eq!(
677 f.dispatch(&args_txt, &ctx.function_context(None))
678 .unwrap()
679 .into_literal(),
680 LiteralValue::Boolean(false)
681 );
682 }
683
684 #[test]
685 fn istest_and_isblank() {
686 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IsTextFn));
687 let ctx = interp(&wb);
688 let f = ctx.context.get_function("", "ISTEXT").unwrap();
689 let t = ASTNode::new(ASTNodeType::Literal(LiteralValue::Text("abc".into())), None);
690 let n = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(5)), None);
691 let args_t = vec![crate::traits::ArgumentHandle::new(&t, &ctx)];
692 let args_n = vec![crate::traits::ArgumentHandle::new(&n, &ctx)];
693 assert_eq!(
694 f.dispatch(&args_t, &ctx.function_context(None))
695 .unwrap()
696 .into_literal(),
697 LiteralValue::Boolean(true)
698 );
699 assert_eq!(
700 f.dispatch(&args_n, &ctx.function_context(None))
701 .unwrap()
702 .into_literal(),
703 LiteralValue::Boolean(false)
704 );
705
706 let wb2 = TestWorkbook::new().with_function(std::sync::Arc::new(IsBlankFn));
708 let ctx2 = interp(&wb2);
709 let f2 = ctx2.context.get_function("", "ISBLANK").unwrap();
710 let blank = ASTNode::new(ASTNodeType::Literal(LiteralValue::Empty), None);
711 let blank_args = vec![crate::traits::ArgumentHandle::new(&blank, &ctx2)];
712 assert_eq!(
713 f2.dispatch(&blank_args, &ctx2.function_context(None))
714 .unwrap()
715 .into_literal(),
716 LiteralValue::Boolean(true)
717 );
718 }
719
720 #[test]
721 fn iserror_variants() {
722 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(IsErrorFn));
723 let ctx = interp(&wb);
724 let f = ctx.context.get_function("", "ISERROR").unwrap();
725 let err = ASTNode::new(
726 ASTNodeType::Literal(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Div))),
727 None,
728 );
729 let ok = ASTNode::new(ASTNodeType::Literal(LiteralValue::Int(1)), None);
730 let a_err = vec![crate::traits::ArgumentHandle::new(&err, &ctx)];
731 let a_ok = vec![crate::traits::ArgumentHandle::new(&ok, &ctx)];
732 assert_eq!(
733 f.dispatch(&a_err, &ctx.function_context(None))
734 .unwrap()
735 .into_literal(),
736 LiteralValue::Boolean(true)
737 );
738 assert_eq!(
739 f.dispatch(&a_ok, &ctx.function_context(None))
740 .unwrap()
741 .into_literal(),
742 LiteralValue::Boolean(false)
743 );
744 }
745
746 #[test]
747 fn type_codes_basic() {
748 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(TypeFn));
749 let ctx = interp(&wb);
750 let f = ctx.context.get_function("", "TYPE").unwrap();
751 let v_num = ASTNode::new(ASTNodeType::Literal(LiteralValue::Number(2.0)), None);
752 let v_txt = ASTNode::new(ASTNodeType::Literal(LiteralValue::Text("hi".into())), None);
753 let v_bool = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(true)), None);
754 let v_err = ASTNode::new(
755 ASTNodeType::Literal(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))),
756 None,
757 );
758 let v_arr = ASTNode::new(
759 ASTNodeType::Literal(LiteralValue::Array(vec![vec![LiteralValue::Int(1)]])),
760 None,
761 );
762 let a_num = vec![crate::traits::ArgumentHandle::new(&v_num, &ctx)];
763 let a_txt = vec![crate::traits::ArgumentHandle::new(&v_txt, &ctx)];
764 let a_bool = vec![crate::traits::ArgumentHandle::new(&v_bool, &ctx)];
765 let a_err = vec![crate::traits::ArgumentHandle::new(&v_err, &ctx)];
766 let a_arr = vec![crate::traits::ArgumentHandle::new(&v_arr, &ctx)];
767 assert_eq!(
768 f.dispatch(&a_num, &ctx.function_context(None))
769 .unwrap()
770 .into_literal(),
771 LiteralValue::Int(1)
772 );
773 assert_eq!(
774 f.dispatch(&a_txt, &ctx.function_context(None))
775 .unwrap()
776 .into_literal(),
777 LiteralValue::Int(2)
778 );
779 assert_eq!(
780 f.dispatch(&a_bool, &ctx.function_context(None))
781 .unwrap()
782 .into_literal(),
783 LiteralValue::Int(4)
784 );
785 match f
786 .dispatch(&a_err, &ctx.function_context(None))
787 .unwrap()
788 .into_literal()
789 {
790 LiteralValue::Error(e) => assert_eq!(e, "#VALUE!"),
791 _ => panic!(),
792 }
793 assert_eq!(
794 f.dispatch(&a_arr, &ctx.function_context(None))
795 .unwrap()
796 .into_literal(),
797 LiteralValue::Int(64)
798 );
799 }
800
801 #[test]
802 fn na_and_n_and_t() {
803 let wb = TestWorkbook::new()
804 .with_function(std::sync::Arc::new(NaFn))
805 .with_function(std::sync::Arc::new(NFn))
806 .with_function(std::sync::Arc::new(TFn));
807 let ctx = wb.interpreter();
808 let na_fn = ctx.context.get_function("", "NA").unwrap();
810 match na_fn
811 .eval(&[], &ctx.function_context(None))
812 .unwrap()
813 .into_literal()
814 {
815 LiteralValue::Error(e) => assert_eq!(e, "#N/A"),
816 _ => panic!(),
817 }
818 let n_fn = ctx.context.get_function("", "N").unwrap();
820 let val = ASTNode::new(ASTNodeType::Literal(LiteralValue::Boolean(true)), None);
821 let args = vec![crate::traits::ArgumentHandle::new(&val, &ctx)];
822 assert_eq!(
823 n_fn.dispatch(&args, &ctx.function_context(None))
824 .unwrap()
825 .into_literal(),
826 LiteralValue::Int(1)
827 );
828 let t_fn = ctx.context.get_function("", "T").unwrap();
830 let txt = ASTNode::new(ASTNodeType::Literal(LiteralValue::Text("abc".into())), None);
831 let args_t = vec![crate::traits::ArgumentHandle::new(&txt, &ctx)];
832 assert_eq!(
833 t_fn.dispatch(&args_t, &ctx.function_context(None))
834 .unwrap()
835 .into_literal(),
836 LiteralValue::Text("abc".into())
837 );
838 }
839}