1use crate::args::{ArgSchema, CoercionPolicy, ShapeKind};
10use crate::builtins::utils::collapse_if_scalar;
11use crate::function::Function;
12use crate::traits::{ArgumentHandle, FunctionContext};
13use formualizer_common::{ArgKind, ExcelError, ExcelErrorKind, LiteralValue};
14use formualizer_macros::func_caps;
15
16#[derive(Debug)]
17pub struct ChooseFn;
18#[derive(Debug)]
19pub struct ChooseColsFn;
20#[derive(Debug)]
21pub struct ChooseRowsFn;
22
23impl Function for ChooseFn {
68 fn name(&self) -> &'static str {
69 "CHOOSE"
70 }
71
72 fn min_args(&self) -> usize {
73 2
74 }
75
76 func_caps!(PURE, LOOKUP, SHORT_CIRCUIT);
79
80 fn arg_schema(&self) -> &'static [ArgSchema] {
81 use once_cell::sync::Lazy;
82 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
83 vec![
84 ArgSchema {
86 kinds: smallvec::smallvec![ArgKind::Number],
87 required: true,
88 by_ref: false,
89 shape: ShapeKind::Scalar,
90 coercion: CoercionPolicy::NumberStrict,
91 max: None,
92 repeating: None,
93 default: None,
94 },
95 ArgSchema {
97 kinds: smallvec::smallvec![ArgKind::Any],
98 required: true,
99 by_ref: false, shape: ShapeKind::Scalar, coercion: CoercionPolicy::None,
102 max: None,
103 repeating: Some(1), default: None,
105 },
106 ]
107 });
108 &SCHEMA
109 }
110
111 fn eval<'a, 'b, 'c>(
112 &self,
113 args: &'c [ArgumentHandle<'a, 'b>],
114 _ctx: &dyn FunctionContext<'b>,
115 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
116 if args.len() < 2 {
117 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
118 ExcelError::new(ExcelErrorKind::Value),
119 )));
120 }
121
122 let index_val = args[0].value()?.into_literal();
124 if let LiteralValue::Error(e) = index_val {
125 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
126 }
127
128 let index = match index_val {
132 LiteralValue::Number(n) => n as i64,
133 LiteralValue::Int(i) => i,
134 _ => {
135 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
136 ExcelError::new(ExcelErrorKind::Value),
137 )));
138 }
139 };
140
141 if index < 1 || index as usize > args.len() - 1 {
143 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
144 ExcelError::new(ExcelErrorKind::Value),
145 )));
146 }
147
148 let selected_arg = &args[index as usize];
150 selected_arg.value()
151 }
152}
153
154fn materialize_rows_2d<'b>(
157 arg: &ArgumentHandle<'_, 'b>,
158 ctx: &dyn FunctionContext<'b>,
159) -> Result<Vec<Vec<formualizer_common::LiteralValue>>, ExcelError> {
160 if let Ok(r) = arg.as_reference_or_eval() {
161 let mut rows: Vec<Vec<LiteralValue>> = Vec::new();
162 let sheet = ctx.current_sheet();
163 let rv = ctx.resolve_range_view(&r, sheet)?;
164 rv.for_each_row(&mut |row| {
165 rows.push(row.to_vec());
166 Ok(())
167 })?;
168 Ok(rows)
169 } else {
170 let v = arg.value()?.into_literal();
171 match v {
172 LiteralValue::Array(a) => Ok(a),
173 other => Ok(vec![vec![other]]),
174 }
175 }
176}
177
178impl Function for ChooseColsFn {
235 func_caps!(PURE, LOOKUP);
236 fn name(&self) -> &'static str {
237 "CHOOSECOLS"
238 }
239 fn min_args(&self) -> usize {
240 2
241 }
242 fn variadic(&self) -> bool {
243 true
244 }
245 fn arg_schema(&self) -> &'static [ArgSchema] {
246 use once_cell::sync::Lazy;
247 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
248 vec![
249 ArgSchema {
251 kinds: smallvec::smallvec![ArgKind::Range, ArgKind::Any],
252 required: true,
253 by_ref: false,
254 shape: ShapeKind::Range,
255 coercion: CoercionPolicy::None,
256 max: None,
257 repeating: None,
258 default: None,
259 },
260 ArgSchema {
262 kinds: smallvec::smallvec![ArgKind::Number],
263 required: true,
264 by_ref: false,
265 shape: ShapeKind::Scalar,
266 coercion: CoercionPolicy::NumberLenientText,
267 max: None,
268 repeating: Some(1),
269 default: None,
270 },
271 ]
272 });
273 &SCHEMA
274 }
275 fn eval<'a, 'b, 'c>(
276 &self,
277 args: &'c [ArgumentHandle<'a, 'b>],
278 _ctx: &dyn FunctionContext<'b>,
279 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
280 if args.len() < 2 {
281 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
282 ExcelError::new(ExcelErrorKind::Value),
283 )));
284 }
285 let view = args[0].range_view()?;
286 let (rows, cols) = view.dims();
287 if rows == 0 || cols == 0 {
288 return Ok(crate::traits::CalcValue::Range(
289 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
290 ));
291 }
292
293 let mut indices: Vec<usize> = Vec::new();
294 for a in &args[1..] {
295 let v = a.value()?.into_literal();
296 if let LiteralValue::Error(e) = v {
297 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
298 }
299 let raw = match v {
300 LiteralValue::Int(i) => i,
301 LiteralValue::Number(n) => n as i64,
302 _ => {
303 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
304 ExcelError::new(ExcelErrorKind::Value),
305 )));
306 }
307 };
308 if raw == 0 {
309 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
310 ExcelError::new(ExcelErrorKind::Value),
311 )));
312 }
313 let adj = if raw > 0 {
314 raw - 1
315 } else {
316 (cols as i64) + raw
317 };
318 if adj < 0 || adj as usize >= cols {
319 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
320 ExcelError::new(ExcelErrorKind::Value),
321 )));
322 }
323 indices.push(adj as usize);
324 }
325 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(rows);
326 for r in 0..rows {
327 let mut new_row = Vec::with_capacity(indices.len());
328 for &c in &indices {
329 new_row.push(view.get_cell(r, c));
330 }
331 out.push(new_row);
332 }
333
334 Ok(collapse_if_scalar(out, _ctx.date_system()))
335 }
336}
337
338impl Function for ChooseRowsFn {
391 func_caps!(PURE, LOOKUP);
392 fn name(&self) -> &'static str {
393 "CHOOSEROWS"
394 }
395 fn min_args(&self) -> usize {
396 2
397 }
398 fn variadic(&self) -> bool {
399 true
400 }
401 fn arg_schema(&self) -> &'static [ArgSchema] {
402 use once_cell::sync::Lazy;
403 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
404 vec![
405 ArgSchema {
407 kinds: smallvec::smallvec![ArgKind::Range, ArgKind::Any],
408 required: true,
409 by_ref: false,
410 shape: ShapeKind::Range,
411 coercion: CoercionPolicy::None,
412 max: None,
413 repeating: None,
414 default: None,
415 },
416 ArgSchema {
418 kinds: smallvec::smallvec![ArgKind::Number],
419 required: true,
420 by_ref: false,
421 shape: ShapeKind::Scalar,
422 coercion: CoercionPolicy::NumberLenientText,
423 max: None,
424 repeating: Some(1),
425 default: None,
426 },
427 ]
428 });
429 &SCHEMA
430 }
431 fn eval<'a, 'b, 'c>(
432 &self,
433 args: &'c [ArgumentHandle<'a, 'b>],
434 _ctx: &dyn FunctionContext<'b>,
435 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
436 if args.len() < 2 {
437 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
438 ExcelError::new(ExcelErrorKind::Value),
439 )));
440 }
441 let view = args[0].range_view()?;
442 let (rows, cols) = view.dims();
443 if rows == 0 || cols == 0 {
444 return Ok(crate::traits::CalcValue::Range(
445 crate::engine::range_view::RangeView::from_owned_rows(vec![], _ctx.date_system()),
446 ));
447 }
448
449 let mut indices: Vec<usize> = Vec::new();
450 for a in &args[1..] {
451 let v = a.value()?.into_literal();
452 if let LiteralValue::Error(e) = v {
453 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
454 }
455 let raw = match v {
456 LiteralValue::Int(i) => i,
457 LiteralValue::Number(n) => n as i64,
458 _ => {
459 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
460 ExcelError::new(ExcelErrorKind::Value),
461 )));
462 }
463 };
464 if raw == 0 {
465 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
466 ExcelError::new(ExcelErrorKind::Value),
467 )));
468 }
469 let adj = if raw > 0 {
470 raw - 1
471 } else {
472 (rows as i64) + raw
473 };
474 if adj < 0 || adj as usize >= rows {
475 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
476 ExcelError::new(ExcelErrorKind::Value),
477 )));
478 }
479 indices.push(adj as usize);
480 }
481 let mut out: Vec<Vec<LiteralValue>> = Vec::with_capacity(indices.len());
482 for &r in &indices {
483 let mut row_vals = Vec::with_capacity(cols);
484 for c in 0..cols {
485 row_vals.push(view.get_cell(r, c));
486 }
487 out.push(row_vals);
488 }
489
490 Ok(collapse_if_scalar(out, _ctx.date_system()))
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497 use crate::test_workbook::TestWorkbook;
498 use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
499 use std::sync::Arc;
500
501 fn lit(v: LiteralValue) -> ASTNode {
502 ASTNode::new(ASTNodeType::Literal(v), None)
503 }
504
505 #[test]
506 fn choose_basic() {
507 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
508 let ctx = wb.interpreter();
509 let f = ctx.context.get_function("", "CHOOSE").unwrap();
510
511 let two = lit(LiteralValue::Int(2));
513 let a = lit(LiteralValue::Text("A".into()));
514 let b = lit(LiteralValue::Text("B".into()));
515 let c = lit(LiteralValue::Text("C".into()));
516
517 let args = vec![
518 ArgumentHandle::new(&two, &ctx),
519 ArgumentHandle::new(&a, &ctx),
520 ArgumentHandle::new(&b, &ctx),
521 ArgumentHandle::new(&c, &ctx),
522 ];
523
524 let result = f
525 .dispatch(&args, &ctx.function_context(None))
526 .unwrap()
527 .into_literal();
528 assert_eq!(result, LiteralValue::Text("B".into()));
529 }
530
531 #[test]
532 fn choose_numeric_values() {
533 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
534 let ctx = wb.interpreter();
535 let f = ctx.context.get_function("", "CHOOSE").unwrap();
536
537 let three = lit(LiteralValue::Int(3));
539 let ten = lit(LiteralValue::Int(10));
540 let twenty = lit(LiteralValue::Int(20));
541 let thirty = lit(LiteralValue::Int(30));
542 let forty = lit(LiteralValue::Int(40));
543
544 let args = vec![
545 ArgumentHandle::new(&three, &ctx),
546 ArgumentHandle::new(&ten, &ctx),
547 ArgumentHandle::new(&twenty, &ctx),
548 ArgumentHandle::new(&thirty, &ctx),
549 ArgumentHandle::new(&forty, &ctx),
550 ];
551
552 let result = f
553 .dispatch(&args, &ctx.function_context(None))
554 .unwrap()
555 .into_literal();
556 assert_eq!(result, LiteralValue::Int(30));
557 }
558
559 #[test]
560 fn choose_out_of_range() {
561 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
562 let ctx = wb.interpreter();
563 let f = ctx.context.get_function("", "CHOOSE").unwrap();
564
565 let five = lit(LiteralValue::Int(5));
567 let a = lit(LiteralValue::Text("A".into()));
568 let b = lit(LiteralValue::Text("B".into()));
569 let c = lit(LiteralValue::Text("C".into()));
570
571 let args = vec![
572 ArgumentHandle::new(&five, &ctx),
573 ArgumentHandle::new(&a, &ctx),
574 ArgumentHandle::new(&b, &ctx),
575 ArgumentHandle::new(&c, &ctx),
576 ];
577
578 let result = f
579 .dispatch(&args, &ctx.function_context(None))
580 .unwrap()
581 .into_literal();
582 assert!(matches!(result, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
583
584 let zero = lit(LiteralValue::Int(0));
586 let args2 = vec![
587 ArgumentHandle::new(&zero, &ctx),
588 ArgumentHandle::new(&a, &ctx),
589 ArgumentHandle::new(&b, &ctx),
590 ];
591
592 let result2 = f
593 .dispatch(&args2, &ctx.function_context(None))
594 .unwrap()
595 .into_literal();
596 assert!(matches!(result2, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
597 }
598
599 #[test]
600 fn choose_decimal_index() {
601 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
602 let ctx = wb.interpreter();
603 let f = ctx.context.get_function("", "CHOOSE").unwrap();
604
605 let two_seven = lit(LiteralValue::Number(2.7));
607 let a = lit(LiteralValue::Text("A".into()));
608 let b = lit(LiteralValue::Text("B".into()));
609 let c = lit(LiteralValue::Text("C".into()));
610
611 let args = vec![
612 ArgumentHandle::new(&two_seven, &ctx),
613 ArgumentHandle::new(&a, &ctx),
614 ArgumentHandle::new(&b, &ctx),
615 ArgumentHandle::new(&c, &ctx),
616 ];
617
618 let result = f
619 .dispatch(&args, &ctx.function_context(None))
620 .unwrap()
621 .into_literal();
622 assert_eq!(result, LiteralValue::Text("B".into()));
623 }
624
625 #[test]
626 fn choose_text_index_numeric_string() {
627 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
628 let ctx = wb.interpreter();
629 let f = ctx.context.get_function("", "CHOOSE").unwrap();
630 let two_txt = lit(LiteralValue::Text("2".into()));
631 let a = lit(LiteralValue::Text("A".into()));
632 let b = lit(LiteralValue::Text("B".into()));
633 let c = lit(LiteralValue::Text("C".into()));
634 let args = vec![
635 ArgumentHandle::new(&two_txt, &ctx),
636 ArgumentHandle::new(&a, &ctx),
637 ArgumentHandle::new(&b, &ctx),
638 ArgumentHandle::new(&c, &ctx),
639 ];
640 let result = f
641 .dispatch(&args, &ctx.function_context(None))
642 .unwrap()
643 .into_literal();
644 assert!(matches!(result, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
646 }
647
648 #[test]
649 fn choose_decimal_less_than_one() {
650 let wb = TestWorkbook::new().with_function(Arc::new(ChooseFn));
651 let ctx = wb.interpreter();
652 let f = ctx.context.get_function("", "CHOOSE").unwrap();
653 let zero_nine = lit(LiteralValue::Number(0.9));
654 let a = lit(LiteralValue::Text("A".into()));
655 let b = lit(LiteralValue::Text("B".into()));
656 let args = vec![
657 ArgumentHandle::new(&zero_nine, &ctx),
658 ArgumentHandle::new(&a, &ctx),
659 ArgumentHandle::new(&b, &ctx),
660 ];
661 let result = f
662 .dispatch(&args, &ctx.function_context(None))
663 .unwrap()
664 .into_literal();
665 assert!(matches!(result, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
666 }
667
668 fn range(r: &str, sr: u32, sc: u32, er: u32, ec: u32) -> ASTNode {
669 ASTNode::new(
670 ASTNodeType::Reference {
671 original: r.into(),
672 reference: ReferenceType::range(None, Some(sr), Some(sc), Some(er), Some(ec)),
673 },
674 None,
675 )
676 }
677
678 #[test]
679 fn choosecols_basic_and_negative_and_duplicates() {
680 let wb = TestWorkbook::new().with_function(Arc::new(ChooseColsFn));
681 let wb = wb
682 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
683 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(2))
684 .with_cell_a1("Sheet1", "C1", LiteralValue::Int(3))
685 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(10))
686 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(20))
687 .with_cell_a1("Sheet1", "C2", LiteralValue::Int(30));
688 let ctx = wb.interpreter();
689 let arr = range("A1:C2", 1, 1, 2, 3);
690 let f = ctx.context.get_function("", "CHOOSECOLS").unwrap();
691 let one = lit(LiteralValue::Int(1));
692 let three = lit(LiteralValue::Int(3));
693 let neg_one = lit(LiteralValue::Int(-1));
694 let args = vec![
696 ArgumentHandle::new(&arr, &ctx),
697 ArgumentHandle::new(&one, &ctx),
698 ArgumentHandle::new(&three, &ctx),
699 ];
700 let v = f
701 .dispatch(&args, &ctx.function_context(None))
702 .unwrap()
703 .into_literal();
704 match v {
705 LiteralValue::Array(a) => {
706 assert_eq!(a.len(), 2);
707 assert_eq!(
708 a[0],
709 vec![LiteralValue::Number(1.0), LiteralValue::Number(3.0)]
710 );
711 }
712 other => panic!("expected array got {other:?}"),
713 }
714 let args_neg = vec![
716 ArgumentHandle::new(&arr, &ctx),
717 ArgumentHandle::new(&neg_one, &ctx),
718 ];
719 let v2 = f
720 .dispatch(&args_neg, &ctx.function_context(None))
721 .unwrap()
722 .into_literal();
723 match v2 {
724 LiteralValue::Array(a) => {
725 assert_eq!(a[0], vec![LiteralValue::Number(3.0)]);
726 }
727 other => panic!("expected array last col got {other:?}"),
728 }
729 let args_dup = vec![
731 ArgumentHandle::new(&arr, &ctx),
732 ArgumentHandle::new(&one, &ctx),
733 ArgumentHandle::new(&one, &ctx),
734 ];
735 let v3 = f
736 .dispatch(&args_dup, &ctx.function_context(None))
737 .unwrap()
738 .into_literal();
739 match v3 {
740 LiteralValue::Array(a) => {
741 assert_eq!(
742 a[0],
743 vec![LiteralValue::Number(1.0), LiteralValue::Number(1.0)]
744 );
745 }
746 other => panic!("expected dup cols got {other:?}"),
747 }
748 }
749
750 #[test]
751 fn choosecols_out_of_range() {
752 let wb = TestWorkbook::new().with_function(Arc::new(ChooseColsFn));
753 let wb = wb
754 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
755 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(2));
756 let ctx = wb.interpreter();
757 let arr = range("A1:B1", 1, 1, 1, 2);
758 let f = ctx.context.get_function("", "CHOOSECOLS").unwrap();
759 let three = lit(LiteralValue::Int(3));
760 let args = vec![
761 ArgumentHandle::new(&arr, &ctx),
762 ArgumentHandle::new(&three, &ctx),
763 ];
764 let v = f
765 .dispatch(&args, &ctx.function_context(None))
766 .unwrap()
767 .into_literal();
768 match v {
769 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Value),
770 other => panic!("expected #VALUE! got {other:?}"),
771 }
772 }
773
774 #[test]
775 fn chooserows_basic_and_negative() {
776 let wb = TestWorkbook::new().with_function(Arc::new(ChooseRowsFn));
777 let wb = wb
778 .with_cell_a1("Sheet1", "A1", LiteralValue::Int(1))
779 .with_cell_a1("Sheet1", "B1", LiteralValue::Int(2))
780 .with_cell_a1("Sheet1", "A2", LiteralValue::Int(10))
781 .with_cell_a1("Sheet1", "B2", LiteralValue::Int(20))
782 .with_cell_a1("Sheet1", "A3", LiteralValue::Int(100))
783 .with_cell_a1("Sheet1", "B3", LiteralValue::Int(200));
784 let ctx = wb.interpreter();
785 let arr = range("A1:B3", 1, 1, 3, 2);
786 let f = ctx.context.get_function("", "CHOOSEROWS").unwrap();
787 let one = lit(LiteralValue::Int(1));
788 let neg_one = lit(LiteralValue::Int(-1));
789 let args = vec![
790 ArgumentHandle::new(&arr, &ctx),
791 ArgumentHandle::new(&one, &ctx),
792 ArgumentHandle::new(&neg_one, &ctx),
793 ];
794 let v = f
795 .dispatch(&args, &ctx.function_context(None))
796 .unwrap()
797 .into_literal();
798 match v {
799 LiteralValue::Array(a) => {
800 assert_eq!(a.len(), 2);
801 assert_eq!(a[0][0], LiteralValue::Number(1.0));
802 assert_eq!(a[1][0], LiteralValue::Number(100.0));
803 }
804 other => panic!("expected array got {other:?}"),
805 }
806 }
807
808 #[test]
809 fn chooserows_out_of_range() {
810 let wb = TestWorkbook::new().with_function(Arc::new(ChooseRowsFn));
811 let wb = wb.with_cell_a1("Sheet1", "A1", LiteralValue::Int(1));
812 let ctx = wb.interpreter();
813 let arr = range("A1:A1", 1, 1, 1, 1);
814 let f = ctx.context.get_function("", "CHOOSEROWS").unwrap();
815 let two = lit(LiteralValue::Int(2));
816 let args = vec![
817 ArgumentHandle::new(&arr, &ctx),
818 ArgumentHandle::new(&two, &ctx),
819 ];
820 let v = f
821 .dispatch(&args, &ctx.function_context(None))
822 .unwrap()
823 .into_literal();
824 match v {
825 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Value),
826 other => panic!("expected #VALUE! got {other:?}"),
827 }
828 }
829}