1use crate::args::{ArgSchema, CoercionPolicy, ShapeKind};
12use crate::function::Function;
13use crate::traits::{ArgumentHandle, FunctionContext};
14use formualizer_common::{ArgKind, ExcelError, ExcelErrorKind, LiteralValue};
15use formualizer_macros::func_caps;
16use formualizer_parse::parser::ReferenceType;
17
18#[derive(Debug)]
19pub struct RowFn;
20
21impl Function for RowFn {
66 fn name(&self) -> &'static str {
67 "ROW"
68 }
69
70 fn min_args(&self) -> usize {
71 0
72 }
73
74 func_caps!(PURE);
75
76 fn arg_schema(&self) -> &'static [ArgSchema] {
77 use once_cell::sync::Lazy;
78 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
79 vec![
80 ArgSchema {
82 kinds: smallvec::smallvec![ArgKind::Range],
83 required: false,
84 by_ref: true,
85 shape: ShapeKind::Range,
86 coercion: CoercionPolicy::None,
87 max: None,
88 repeating: None,
89 default: None,
90 },
91 ]
92 });
93 &SCHEMA
94 }
95
96 fn eval<'a, 'b, 'c>(
97 &self,
98 args: &'c [ArgumentHandle<'a, 'b>],
99 ctx: &dyn FunctionContext<'b>,
100 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
101 if args.is_empty() {
102 if let Some(cell_ref) = ctx.current_cell() {
104 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
105 cell_ref.coord.row() as i64 + 1,
106 )));
107 }
108 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
109 ExcelError::new(ExcelErrorKind::Value),
110 )));
111 }
112
113 let reference = match args[0].as_reference_or_eval() {
115 Ok(r) => r,
116 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
117 };
118
119 let row_1based = match &reference {
121 ReferenceType::Cell { row, .. } => *row as i64,
122 ReferenceType::Range {
123 start_row: Some(sr),
124 ..
125 } => *sr as i64,
126 ReferenceType::Range {
128 start_row: None,
129 end_row: None,
130 ..
131 } => 1,
132 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
134 Ok(view) => {
135 if view.is_empty() {
136 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
137 ExcelError::new(ExcelErrorKind::Ref),
138 )));
139 }
140 view.start_row() as i64 + 1
141 }
142 Err(e) => {
143 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
144 }
145 },
146 };
147
148 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
149 row_1based,
150 )))
151 }
152}
153
154#[derive(Debug)]
155pub struct RowsFn;
156
157impl Function for RowsFn {
202 fn name(&self) -> &'static str {
203 "ROWS"
204 }
205
206 fn min_args(&self) -> usize {
207 1
208 }
209
210 func_caps!(PURE);
211
212 fn arg_schema(&self) -> &'static [ArgSchema] {
213 use once_cell::sync::Lazy;
214 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
215 vec![
216 ArgSchema {
218 kinds: smallvec::smallvec![ArgKind::Any],
219 required: true,
220 by_ref: false,
221 shape: ShapeKind::Range,
222 coercion: CoercionPolicy::None,
223 max: None,
224 repeating: None,
225 default: None,
226 },
227 ]
228 });
229 &SCHEMA
230 }
231
232 fn eval<'a, 'b, 'c>(
233 &self,
234 args: &'c [ArgumentHandle<'a, 'b>],
235 ctx: &dyn FunctionContext<'b>,
236 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
237 const EXCEL_MAX_ROWS: i64 = 1_048_576;
238
239 if args.is_empty() {
240 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
241 ExcelError::new(ExcelErrorKind::Value),
242 )));
243 }
244
245 if let Ok(reference) = args[0].as_reference_or_eval() {
247 let rows = match &reference {
249 ReferenceType::Cell { .. } => 1,
250 ReferenceType::Range {
251 start_row: Some(sr),
252 end_row: Some(er),
253 ..
254 } => {
255 if *er >= *sr {
256 (*er - *sr + 1) as i64
257 } else {
258 1
259 }
260 }
261 ReferenceType::Range {
263 start_row: None,
264 end_row: None,
265 ..
266 } => EXCEL_MAX_ROWS,
267 ReferenceType::Range {
269 start_row: Some(sr),
270 end_row: None,
271 ..
272 } => EXCEL_MAX_ROWS.saturating_sub(*sr as i64).saturating_add(1),
273 ReferenceType::Range {
275 start_row: None,
276 end_row: Some(er),
277 ..
278 } => *er as i64,
279 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
281 Ok(view) => view.dims().0 as i64,
282 Err(e) => {
283 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
284 }
285 },
286 };
287 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
288 } else {
289 let v = args[0].value()?.into_literal();
291 let rows = match v {
292 LiteralValue::Array(arr) => arr.len() as i64,
293 _ => 1,
294 };
295 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
296 }
297 }
298}
299
300#[derive(Debug)]
301pub struct ColumnFn;
302
303impl Function for ColumnFn {
348 fn name(&self) -> &'static str {
349 "COLUMN"
350 }
351
352 fn min_args(&self) -> usize {
353 0
354 }
355
356 func_caps!(PURE);
357
358 fn arg_schema(&self) -> &'static [ArgSchema] {
359 use once_cell::sync::Lazy;
360 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
361 vec![
362 ArgSchema {
364 kinds: smallvec::smallvec![ArgKind::Range],
365 required: false,
366 by_ref: true,
367 shape: ShapeKind::Range,
368 coercion: CoercionPolicy::None,
369 max: None,
370 repeating: None,
371 default: None,
372 },
373 ]
374 });
375 &SCHEMA
376 }
377
378 fn eval<'a, 'b, 'c>(
379 &self,
380 args: &'c [ArgumentHandle<'a, 'b>],
381 ctx: &dyn FunctionContext<'b>,
382 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
383 if args.is_empty() {
384 if let Some(cell_ref) = ctx.current_cell() {
386 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
387 cell_ref.coord.col() as i64 + 1,
388 )));
389 }
390 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
391 ExcelError::new(ExcelErrorKind::Value),
392 )));
393 }
394
395 let reference = match args[0].as_reference_or_eval() {
397 Ok(r) => r,
398 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
399 };
400
401 let col_1based = match &reference {
403 ReferenceType::Cell { col, .. } => *col as i64,
404 ReferenceType::Range {
405 start_col: Some(sc),
406 ..
407 } => *sc as i64,
408 ReferenceType::Range {
410 start_col: None,
411 end_col: None,
412 ..
413 } => 1,
414 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
416 Ok(view) => {
417 if view.is_empty() {
418 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
419 ExcelError::new(ExcelErrorKind::Ref),
420 )));
421 }
422 view.start_col() as i64 + 1
423 }
424 Err(e) => {
425 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
426 }
427 },
428 };
429
430 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
431 col_1based,
432 )))
433 }
434}
435
436#[derive(Debug)]
437pub struct ColumnsFn;
438
439impl Function for ColumnsFn {
484 fn name(&self) -> &'static str {
485 "COLUMNS"
486 }
487
488 fn min_args(&self) -> usize {
489 1
490 }
491
492 func_caps!(PURE);
493
494 fn arg_schema(&self) -> &'static [ArgSchema] {
495 use once_cell::sync::Lazy;
496 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
497 vec![
498 ArgSchema {
500 kinds: smallvec::smallvec![ArgKind::Any],
501 required: true,
502 by_ref: false,
503 shape: ShapeKind::Range,
504 coercion: CoercionPolicy::None,
505 max: None,
506 repeating: None,
507 default: None,
508 },
509 ]
510 });
511 &SCHEMA
512 }
513
514 fn eval<'a, 'b, 'c>(
515 &self,
516 args: &'c [ArgumentHandle<'a, 'b>],
517 ctx: &dyn FunctionContext<'b>,
518 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
519 const EXCEL_MAX_COLS: i64 = 16_384;
520
521 if args.is_empty() {
522 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
523 ExcelError::new(ExcelErrorKind::Value),
524 )));
525 }
526
527 if let Ok(reference) = args[0].as_reference_or_eval() {
529 let cols = match &reference {
531 ReferenceType::Cell { .. } => 1,
532 ReferenceType::Range {
533 start_col: Some(sc),
534 end_col: Some(ec),
535 ..
536 } => {
537 if *ec >= *sc {
538 (*ec - *sc + 1) as i64
539 } else {
540 1
541 }
542 }
543 ReferenceType::Range {
545 start_col: None,
546 end_col: None,
547 ..
548 } => EXCEL_MAX_COLS,
549 ReferenceType::Range {
551 start_col: Some(sc),
552 end_col: None,
553 ..
554 } => EXCEL_MAX_COLS.saturating_sub(*sc as i64).saturating_add(1),
555 ReferenceType::Range {
557 start_col: None,
558 end_col: Some(ec),
559 ..
560 } => *ec as i64,
561 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
563 Ok(view) => view.dims().1 as i64,
564 Err(e) => {
565 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
566 }
567 },
568 };
569 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
570 } else {
571 let v = args[0].value()?.into_literal();
573 let cols = match v {
574 LiteralValue::Array(arr) => arr.first().map(|r| r.len()).unwrap_or(0) as i64,
575 _ => 1,
576 };
577 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
578 }
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use super::*;
585 use crate::test_workbook::TestWorkbook;
586 use crate::{CellRef, Coord};
587 use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
588 use std::sync::Arc;
589
590 #[test]
591 fn row_with_reference() {
592 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
593 let ctx = wb.interpreter();
594 let f = ctx.context.get_function("", "ROW").unwrap();
595
596 let b5_ref = ASTNode::new(
598 ASTNodeType::Reference {
599 original: "B5".into(),
600 reference: ReferenceType::cell(None, 5, 2),
601 },
602 None,
603 );
604
605 let args = vec![ArgumentHandle::new(&b5_ref, &ctx)];
606 let result = f
607 .dispatch(&args, &ctx.function_context(None))
608 .unwrap()
609 .into_literal();
610 assert_eq!(result, LiteralValue::Int(5));
611
612 let range_ref = ASTNode::new(
614 ASTNodeType::Reference {
615 original: "A1:C3".into(),
616 reference: ReferenceType::range(None, Some(1), Some(1), Some(3), Some(3)),
617 },
618 None,
619 );
620
621 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
622 let result2 = f
623 .dispatch(&args2, &ctx.function_context(None))
624 .unwrap()
625 .into_literal();
626 assert_eq!(result2, LiteralValue::Int(1));
627 }
628
629 #[test]
630 fn row_no_arg_uses_current_cell_1_based() {
631 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
632 let ctx = wb.interpreter();
633 let f = ctx.context.get_function("", "ROW").unwrap();
634
635 let current = CellRef::new(0, Coord::from_excel(7, 4, false, false));
636 let result = f
637 .dispatch(&[], &ctx.function_context(Some(¤t)))
638 .unwrap()
639 .into_literal();
640 assert_eq!(result, LiteralValue::Int(7));
641 }
642
643 #[test]
644 fn row_full_column_reference_returns_first_row() {
645 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
646 let ctx = wb.interpreter();
647 let f = ctx.context.get_function("", "ROW").unwrap();
648
649 let col_range_ref = ASTNode::new(
651 ASTNodeType::Reference {
652 original: "A:A".into(),
653 reference: ReferenceType::range(None, None, Some(1), None, Some(1)),
654 },
655 None,
656 );
657
658 let args = vec![ArgumentHandle::new(&col_range_ref, &ctx)];
659 let result = f
660 .dispatch(&args, &ctx.function_context(None))
661 .unwrap()
662 .into_literal();
663 assert_eq!(result, LiteralValue::Int(1));
664 }
665
666 #[test]
667 fn row_named_range_falls_back_to_resolved_range_view() {
668 let wb = TestWorkbook::new()
669 .with_named_range("MyRow", vec![vec![LiteralValue::Int(42)]])
670 .with_function(Arc::new(RowFn));
671 let ctx = wb.interpreter();
672 let f = ctx.context.get_function("", "ROW").unwrap();
673
674 let named_ref = ASTNode::new(
675 ASTNodeType::Reference {
676 original: "MyRow".into(),
677 reference: ReferenceType::NamedRange("MyRow".into()),
678 },
679 None,
680 );
681
682 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
683 let result = f
684 .dispatch(&args, &ctx.function_context(None))
685 .unwrap()
686 .into_literal();
687 assert_eq!(result, LiteralValue::Int(1));
688 }
689
690 #[test]
691 fn rows_function() {
692 let wb = TestWorkbook::new().with_function(Arc::new(RowsFn));
693 let ctx = wb.interpreter();
694 let f = ctx.context.get_function("", "ROWS").unwrap();
695
696 let range_ref = ASTNode::new(
698 ASTNodeType::Reference {
699 original: "A1:A5".into(),
700 reference: ReferenceType::range(None, Some(1), Some(1), Some(5), Some(1)),
701 },
702 None,
703 );
704
705 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
706 let result = f
707 .dispatch(&args, &ctx.function_context(None))
708 .unwrap()
709 .into_literal();
710 assert_eq!(result, LiteralValue::Int(5));
711
712 let range_ref2 = ASTNode::new(
714 ASTNodeType::Reference {
715 original: "B2:D10".into(),
716 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
717 },
718 None,
719 );
720
721 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
722 let result2 = f
723 .dispatch(&args2, &ctx.function_context(None))
724 .unwrap()
725 .into_literal();
726 assert_eq!(result2, LiteralValue::Int(9));
727
728 let cell_ref = ASTNode::new(
730 ASTNodeType::Reference {
731 original: "A1".into(),
732 reference: ReferenceType::cell(None, 1, 1),
733 },
734 None,
735 );
736
737 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
738 let result3 = f
739 .dispatch(&args3, &ctx.function_context(None))
740 .unwrap()
741 .into_literal();
742 assert_eq!(result3, LiteralValue::Int(1));
743 }
744
745 #[test]
746 fn rows_full_column_reference_returns_sheet_height() {
747 let wb = TestWorkbook::new().with_function(Arc::new(RowsFn));
748 let ctx = wb.interpreter();
749 let f = ctx.context.get_function("", "ROWS").unwrap();
750
751 let col_range_ref = ASTNode::new(
753 ASTNodeType::Reference {
754 original: "A:A".into(),
755 reference: ReferenceType::range(None, None, Some(1), None, Some(1)),
756 },
757 None,
758 );
759
760 let args = vec![ArgumentHandle::new(&col_range_ref, &ctx)];
761 let result = f
762 .dispatch(&args, &ctx.function_context(None))
763 .unwrap()
764 .into_literal();
765 assert_eq!(result, LiteralValue::Int(1_048_576));
766 }
767
768 #[test]
769 fn rows_named_range_falls_back_to_resolved_range_view() {
770 let wb = TestWorkbook::new()
771 .with_named_range(
772 "MyRows",
773 vec![
774 vec![LiteralValue::Int(1)],
775 vec![LiteralValue::Int(2)],
776 vec![LiteralValue::Int(3)],
777 ],
778 )
779 .with_function(Arc::new(RowsFn));
780 let ctx = wb.interpreter();
781 let f = ctx.context.get_function("", "ROWS").unwrap();
782
783 let named_ref = ASTNode::new(
784 ASTNodeType::Reference {
785 original: "MyRows".into(),
786 reference: ReferenceType::NamedRange("MyRows".into()),
787 },
788 None,
789 );
790
791 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
792 let result = f
793 .dispatch(&args, &ctx.function_context(None))
794 .unwrap()
795 .into_literal();
796 assert_eq!(result, LiteralValue::Int(3));
797 }
798
799 #[test]
800 fn column_with_reference() {
801 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
802 let ctx = wb.interpreter();
803 let f = ctx.context.get_function("", "COLUMN").unwrap();
804
805 let c5_ref = ASTNode::new(
807 ASTNodeType::Reference {
808 original: "C5".into(),
809 reference: ReferenceType::cell(None, 5, 3),
810 },
811 None,
812 );
813
814 let args = vec![ArgumentHandle::new(&c5_ref, &ctx)];
815 let result = f
816 .dispatch(&args, &ctx.function_context(None))
817 .unwrap()
818 .into_literal();
819 assert_eq!(result, LiteralValue::Int(3));
820
821 let range_ref = ASTNode::new(
823 ASTNodeType::Reference {
824 original: "B2:D4".into(),
825 reference: ReferenceType::range(None, Some(2), Some(2), Some(4), Some(4)),
826 },
827 None,
828 );
829
830 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
831 let result2 = f
832 .dispatch(&args2, &ctx.function_context(None))
833 .unwrap()
834 .into_literal();
835 assert_eq!(result2, LiteralValue::Int(2));
836 }
837
838 #[test]
839 fn column_no_arg_uses_current_cell_1_based() {
840 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
841 let ctx = wb.interpreter();
842 let f = ctx.context.get_function("", "COLUMN").unwrap();
843
844 let current = CellRef::new(0, Coord::from_excel(7, 4, false, false));
845 let result = f
846 .dispatch(&[], &ctx.function_context(Some(¤t)))
847 .unwrap()
848 .into_literal();
849 assert_eq!(result, LiteralValue::Int(4));
850 }
851
852 #[test]
853 fn column_full_row_reference_returns_first_column() {
854 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
855 let ctx = wb.interpreter();
856 let f = ctx.context.get_function("", "COLUMN").unwrap();
857
858 let row_range_ref = ASTNode::new(
860 ASTNodeType::Reference {
861 original: "5:5".into(),
862 reference: ReferenceType::range(None, Some(5), None, Some(5), None),
863 },
864 None,
865 );
866
867 let args = vec![ArgumentHandle::new(&row_range_ref, &ctx)];
868 let result = f
869 .dispatch(&args, &ctx.function_context(None))
870 .unwrap()
871 .into_literal();
872 assert_eq!(result, LiteralValue::Int(1));
873 }
874
875 #[test]
876 fn column_named_range_falls_back_to_resolved_range_view() {
877 let wb = TestWorkbook::new()
878 .with_named_range("MyRange", vec![vec![LiteralValue::Int(42)]])
879 .with_function(Arc::new(ColumnFn));
880 let ctx = wb.interpreter();
881 let f = ctx.context.get_function("", "COLUMN").unwrap();
882
883 let named_ref = ASTNode::new(
884 ASTNodeType::Reference {
885 original: "MyRange".into(),
886 reference: ReferenceType::NamedRange("MyRange".into()),
887 },
888 None,
889 );
890
891 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
892 let result = f
893 .dispatch(&args, &ctx.function_context(None))
894 .unwrap()
895 .into_literal();
896 assert_eq!(result, LiteralValue::Int(1));
897 }
898
899 #[test]
900 fn columns_function() {
901 let wb = TestWorkbook::new().with_function(Arc::new(ColumnsFn));
902 let ctx = wb.interpreter();
903 let f = ctx.context.get_function("", "COLUMNS").unwrap();
904
905 let range_ref = ASTNode::new(
907 ASTNodeType::Reference {
908 original: "A1:E1".into(),
909 reference: ReferenceType::range(None, Some(1), Some(1), Some(1), Some(5)),
910 },
911 None,
912 );
913
914 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
915 let result = f
916 .dispatch(&args, &ctx.function_context(None))
917 .unwrap()
918 .into_literal();
919 assert_eq!(result, LiteralValue::Int(5));
920
921 let range_ref2 = ASTNode::new(
923 ASTNodeType::Reference {
924 original: "B2:D10".into(),
925 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
926 },
927 None,
928 );
929
930 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
931 let result2 = f
932 .dispatch(&args2, &ctx.function_context(None))
933 .unwrap()
934 .into_literal();
935 assert_eq!(result2, LiteralValue::Int(3));
936
937 let cell_ref = ASTNode::new(
939 ASTNodeType::Reference {
940 original: "A1".into(),
941 reference: ReferenceType::cell(None, 1, 1),
942 },
943 None,
944 );
945
946 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
947 let result3 = f
948 .dispatch(&args3, &ctx.function_context(None))
949 .unwrap()
950 .into_literal();
951 assert_eq!(result3, LiteralValue::Int(1));
952 }
953
954 #[test]
955 fn columns_full_row_reference_returns_sheet_width() {
956 let wb = TestWorkbook::new().with_function(Arc::new(ColumnsFn));
957 let ctx = wb.interpreter();
958 let f = ctx.context.get_function("", "COLUMNS").unwrap();
959
960 let row_range_ref = ASTNode::new(
962 ASTNodeType::Reference {
963 original: "1:1".into(),
964 reference: ReferenceType::range(None, Some(1), None, Some(1), None),
965 },
966 None,
967 );
968
969 let args = vec![ArgumentHandle::new(&row_range_ref, &ctx)];
970 let result = f
971 .dispatch(&args, &ctx.function_context(None))
972 .unwrap()
973 .into_literal();
974 assert_eq!(result, LiteralValue::Int(16_384));
975 }
976
977 #[test]
978 fn columns_named_range_falls_back_to_resolved_range_view() {
979 let wb = TestWorkbook::new()
980 .with_named_range(
981 "MyCols",
982 vec![
983 vec![
984 LiteralValue::Int(1),
985 LiteralValue::Int(2),
986 LiteralValue::Int(3),
987 ],
988 vec![
989 LiteralValue::Int(4),
990 LiteralValue::Int(5),
991 LiteralValue::Int(6),
992 ],
993 ],
994 )
995 .with_function(Arc::new(ColumnsFn));
996 let ctx = wb.interpreter();
997 let f = ctx.context.get_function("", "COLUMNS").unwrap();
998
999 let named_ref = ASTNode::new(
1000 ASTNodeType::Reference {
1001 original: "MyCols".into(),
1002 reference: ReferenceType::NamedRange("MyCols".into()),
1003 },
1004 None,
1005 );
1006
1007 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
1008 let result = f
1009 .dispatch(&args, &ctx.function_context(None))
1010 .unwrap()
1011 .into_literal();
1012 assert_eq!(result, LiteralValue::Int(3));
1013 }
1014
1015 #[test]
1016 fn rows_columns_reversed_range() {
1017 let wb = TestWorkbook::new()
1019 .with_function(Arc::new(RowsFn))
1020 .with_function(Arc::new(ColumnsFn));
1021 let ctx = wb.interpreter();
1022 let rows_f = ctx.context.get_function("", "ROWS").unwrap();
1023 let cols_f = ctx.context.get_function("", "COLUMNS").unwrap();
1024 let rev_range = ASTNode::new(
1025 ASTNodeType::Reference {
1026 original: "A5:A1".into(),
1027 reference: ReferenceType::range(None, Some(5), Some(1), Some(1), Some(1)),
1028 },
1029 None,
1030 );
1031 let args = vec![ArgumentHandle::new(&rev_range, &ctx)];
1032 let r_count = rows_f
1033 .dispatch(&args, &ctx.function_context(None))
1034 .unwrap()
1035 .into_literal();
1036 let c_count = cols_f
1037 .dispatch(&args, &ctx.function_context(None))
1038 .unwrap()
1039 .into_literal();
1040 assert_eq!(r_count, LiteralValue::Int(1));
1041 assert_eq!(c_count, LiteralValue::Int(1));
1042 }
1043}