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 {
22 fn name(&self) -> &'static str {
23 "ROW"
24 }
25
26 fn min_args(&self) -> usize {
27 0
28 }
29
30 func_caps!(PURE);
31
32 fn arg_schema(&self) -> &'static [ArgSchema] {
33 use once_cell::sync::Lazy;
34 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
35 vec![
36 ArgSchema {
38 kinds: smallvec::smallvec![ArgKind::Range],
39 required: false,
40 by_ref: true,
41 shape: ShapeKind::Range,
42 coercion: CoercionPolicy::None,
43 max: None,
44 repeating: None,
45 default: None,
46 },
47 ]
48 });
49 &SCHEMA
50 }
51
52 fn eval<'a, 'b, 'c>(
53 &self,
54 args: &'c [ArgumentHandle<'a, 'b>],
55 ctx: &dyn FunctionContext<'b>,
56 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
57 if args.is_empty() {
58 if let Some(cell_ref) = ctx.current_cell() {
60 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
61 cell_ref.coord.row() as i64 + 1,
62 )));
63 }
64 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
65 ExcelError::new(ExcelErrorKind::Value),
66 )));
67 }
68
69 let reference = match args[0].as_reference_or_eval() {
71 Ok(r) => r,
72 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
73 };
74
75 let row_1based = match &reference {
77 ReferenceType::Cell { row, .. } => *row as i64,
78 ReferenceType::Range {
79 start_row: Some(sr),
80 ..
81 } => *sr as i64,
82 ReferenceType::Range {
84 start_row: None,
85 end_row: None,
86 ..
87 } => 1,
88 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
90 Ok(view) => {
91 if view.is_empty() {
92 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
93 ExcelError::new(ExcelErrorKind::Ref),
94 )));
95 }
96 view.start_row() as i64 + 1
97 }
98 Err(e) => {
99 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
100 }
101 },
102 };
103
104 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
105 row_1based,
106 )))
107 }
108}
109
110#[derive(Debug)]
111pub struct RowsFn;
112
113impl Function for RowsFn {
114 fn name(&self) -> &'static str {
115 "ROWS"
116 }
117
118 fn min_args(&self) -> usize {
119 1
120 }
121
122 func_caps!(PURE);
123
124 fn arg_schema(&self) -> &'static [ArgSchema] {
125 use once_cell::sync::Lazy;
126 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
127 vec![
128 ArgSchema {
130 kinds: smallvec::smallvec![ArgKind::Any],
131 required: true,
132 by_ref: false,
133 shape: ShapeKind::Range,
134 coercion: CoercionPolicy::None,
135 max: None,
136 repeating: None,
137 default: None,
138 },
139 ]
140 });
141 &SCHEMA
142 }
143
144 fn eval<'a, 'b, 'c>(
145 &self,
146 args: &'c [ArgumentHandle<'a, 'b>],
147 ctx: &dyn FunctionContext<'b>,
148 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
149 const EXCEL_MAX_ROWS: i64 = 1_048_576;
150
151 if args.is_empty() {
152 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
153 ExcelError::new(ExcelErrorKind::Value),
154 )));
155 }
156
157 if let Ok(reference) = args[0].as_reference_or_eval() {
159 let rows = match &reference {
161 ReferenceType::Cell { .. } => 1,
162 ReferenceType::Range {
163 start_row: Some(sr),
164 end_row: Some(er),
165 ..
166 } => {
167 if *er >= *sr {
168 (*er - *sr + 1) as i64
169 } else {
170 1
171 }
172 }
173 ReferenceType::Range {
175 start_row: None,
176 end_row: None,
177 ..
178 } => EXCEL_MAX_ROWS,
179 ReferenceType::Range {
181 start_row: Some(sr),
182 end_row: None,
183 ..
184 } => EXCEL_MAX_ROWS.saturating_sub(*sr as i64).saturating_add(1),
185 ReferenceType::Range {
187 start_row: None,
188 end_row: Some(er),
189 ..
190 } => *er as i64,
191 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
193 Ok(view) => view.dims().0 as i64,
194 Err(e) => {
195 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
196 }
197 },
198 };
199 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
200 } else {
201 let v = args[0].value()?.into_literal();
203 let rows = match v {
204 LiteralValue::Array(arr) => arr.len() as i64,
205 _ => 1,
206 };
207 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
208 }
209 }
210}
211
212#[derive(Debug)]
213pub struct ColumnFn;
214
215impl Function for ColumnFn {
216 fn name(&self) -> &'static str {
217 "COLUMN"
218 }
219
220 fn min_args(&self) -> usize {
221 0
222 }
223
224 func_caps!(PURE);
225
226 fn arg_schema(&self) -> &'static [ArgSchema] {
227 use once_cell::sync::Lazy;
228 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
229 vec![
230 ArgSchema {
232 kinds: smallvec::smallvec![ArgKind::Range],
233 required: false,
234 by_ref: true,
235 shape: ShapeKind::Range,
236 coercion: CoercionPolicy::None,
237 max: None,
238 repeating: None,
239 default: None,
240 },
241 ]
242 });
243 &SCHEMA
244 }
245
246 fn eval<'a, 'b, 'c>(
247 &self,
248 args: &'c [ArgumentHandle<'a, 'b>],
249 ctx: &dyn FunctionContext<'b>,
250 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
251 if args.is_empty() {
252 if let Some(cell_ref) = ctx.current_cell() {
254 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
255 cell_ref.coord.col() as i64 + 1,
256 )));
257 }
258 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
259 ExcelError::new(ExcelErrorKind::Value),
260 )));
261 }
262
263 let reference = match args[0].as_reference_or_eval() {
265 Ok(r) => r,
266 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
267 };
268
269 let col_1based = match &reference {
271 ReferenceType::Cell { col, .. } => *col as i64,
272 ReferenceType::Range {
273 start_col: Some(sc),
274 ..
275 } => *sc as i64,
276 ReferenceType::Range {
278 start_col: None,
279 end_col: None,
280 ..
281 } => 1,
282 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
284 Ok(view) => {
285 if view.is_empty() {
286 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
287 ExcelError::new(ExcelErrorKind::Ref),
288 )));
289 }
290 view.start_col() as i64 + 1
291 }
292 Err(e) => {
293 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
294 }
295 },
296 };
297
298 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
299 col_1based,
300 )))
301 }
302}
303
304#[derive(Debug)]
305pub struct ColumnsFn;
306
307impl Function for ColumnsFn {
308 fn name(&self) -> &'static str {
309 "COLUMNS"
310 }
311
312 fn min_args(&self) -> usize {
313 1
314 }
315
316 func_caps!(PURE);
317
318 fn arg_schema(&self) -> &'static [ArgSchema] {
319 use once_cell::sync::Lazy;
320 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
321 vec![
322 ArgSchema {
324 kinds: smallvec::smallvec![ArgKind::Any],
325 required: true,
326 by_ref: false,
327 shape: ShapeKind::Range,
328 coercion: CoercionPolicy::None,
329 max: None,
330 repeating: None,
331 default: None,
332 },
333 ]
334 });
335 &SCHEMA
336 }
337
338 fn eval<'a, 'b, 'c>(
339 &self,
340 args: &'c [ArgumentHandle<'a, 'b>],
341 ctx: &dyn FunctionContext<'b>,
342 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
343 const EXCEL_MAX_COLS: i64 = 16_384;
344
345 if args.is_empty() {
346 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
347 ExcelError::new(ExcelErrorKind::Value),
348 )));
349 }
350
351 if let Ok(reference) = args[0].as_reference_or_eval() {
353 let cols = match &reference {
355 ReferenceType::Cell { .. } => 1,
356 ReferenceType::Range {
357 start_col: Some(sc),
358 end_col: Some(ec),
359 ..
360 } => {
361 if *ec >= *sc {
362 (*ec - *sc + 1) as i64
363 } else {
364 1
365 }
366 }
367 ReferenceType::Range {
369 start_col: None,
370 end_col: None,
371 ..
372 } => EXCEL_MAX_COLS,
373 ReferenceType::Range {
375 start_col: Some(sc),
376 end_col: None,
377 ..
378 } => EXCEL_MAX_COLS.saturating_sub(*sc as i64).saturating_add(1),
379 ReferenceType::Range {
381 start_col: None,
382 end_col: Some(ec),
383 ..
384 } => *ec as i64,
385 _ => match ctx.resolve_range_view(&reference, ctx.current_sheet()) {
387 Ok(view) => view.dims().1 as i64,
388 Err(e) => {
389 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
390 }
391 },
392 };
393 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
394 } else {
395 let v = args[0].value()?.into_literal();
397 let cols = match v {
398 LiteralValue::Array(arr) => arr.first().map(|r| r.len()).unwrap_or(0) as i64,
399 _ => 1,
400 };
401 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
402 }
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409 use crate::test_workbook::TestWorkbook;
410 use crate::{CellRef, Coord};
411 use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
412 use std::sync::Arc;
413
414 #[test]
415 fn row_with_reference() {
416 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
417 let ctx = wb.interpreter();
418 let f = ctx.context.get_function("", "ROW").unwrap();
419
420 let b5_ref = ASTNode::new(
422 ASTNodeType::Reference {
423 original: "B5".into(),
424 reference: ReferenceType::cell(None, 5, 2),
425 },
426 None,
427 );
428
429 let args = vec![ArgumentHandle::new(&b5_ref, &ctx)];
430 let result = f
431 .dispatch(&args, &ctx.function_context(None))
432 .unwrap()
433 .into_literal();
434 assert_eq!(result, LiteralValue::Int(5));
435
436 let range_ref = ASTNode::new(
438 ASTNodeType::Reference {
439 original: "A1:C3".into(),
440 reference: ReferenceType::range(None, Some(1), Some(1), Some(3), Some(3)),
441 },
442 None,
443 );
444
445 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
446 let result2 = f
447 .dispatch(&args2, &ctx.function_context(None))
448 .unwrap()
449 .into_literal();
450 assert_eq!(result2, LiteralValue::Int(1));
451 }
452
453 #[test]
454 fn row_no_arg_uses_current_cell_1_based() {
455 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
456 let ctx = wb.interpreter();
457 let f = ctx.context.get_function("", "ROW").unwrap();
458
459 let current = CellRef::new(0, Coord::from_excel(7, 4, false, false));
460 let result = f
461 .dispatch(&[], &ctx.function_context(Some(¤t)))
462 .unwrap()
463 .into_literal();
464 assert_eq!(result, LiteralValue::Int(7));
465 }
466
467 #[test]
468 fn row_full_column_reference_returns_first_row() {
469 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
470 let ctx = wb.interpreter();
471 let f = ctx.context.get_function("", "ROW").unwrap();
472
473 let col_range_ref = ASTNode::new(
475 ASTNodeType::Reference {
476 original: "A:A".into(),
477 reference: ReferenceType::range(None, None, Some(1), None, Some(1)),
478 },
479 None,
480 );
481
482 let args = vec![ArgumentHandle::new(&col_range_ref, &ctx)];
483 let result = f
484 .dispatch(&args, &ctx.function_context(None))
485 .unwrap()
486 .into_literal();
487 assert_eq!(result, LiteralValue::Int(1));
488 }
489
490 #[test]
491 fn row_named_range_falls_back_to_resolved_range_view() {
492 let wb = TestWorkbook::new()
493 .with_named_range("MyRow", vec![vec![LiteralValue::Int(42)]])
494 .with_function(Arc::new(RowFn));
495 let ctx = wb.interpreter();
496 let f = ctx.context.get_function("", "ROW").unwrap();
497
498 let named_ref = ASTNode::new(
499 ASTNodeType::Reference {
500 original: "MyRow".into(),
501 reference: ReferenceType::NamedRange("MyRow".into()),
502 },
503 None,
504 );
505
506 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
507 let result = f
508 .dispatch(&args, &ctx.function_context(None))
509 .unwrap()
510 .into_literal();
511 assert_eq!(result, LiteralValue::Int(1));
512 }
513
514 #[test]
515 fn rows_function() {
516 let wb = TestWorkbook::new().with_function(Arc::new(RowsFn));
517 let ctx = wb.interpreter();
518 let f = ctx.context.get_function("", "ROWS").unwrap();
519
520 let range_ref = ASTNode::new(
522 ASTNodeType::Reference {
523 original: "A1:A5".into(),
524 reference: ReferenceType::range(None, Some(1), Some(1), Some(5), Some(1)),
525 },
526 None,
527 );
528
529 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
530 let result = f
531 .dispatch(&args, &ctx.function_context(None))
532 .unwrap()
533 .into_literal();
534 assert_eq!(result, LiteralValue::Int(5));
535
536 let range_ref2 = ASTNode::new(
538 ASTNodeType::Reference {
539 original: "B2:D10".into(),
540 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
541 },
542 None,
543 );
544
545 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
546 let result2 = f
547 .dispatch(&args2, &ctx.function_context(None))
548 .unwrap()
549 .into_literal();
550 assert_eq!(result2, LiteralValue::Int(9));
551
552 let cell_ref = ASTNode::new(
554 ASTNodeType::Reference {
555 original: "A1".into(),
556 reference: ReferenceType::cell(None, 1, 1),
557 },
558 None,
559 );
560
561 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
562 let result3 = f
563 .dispatch(&args3, &ctx.function_context(None))
564 .unwrap()
565 .into_literal();
566 assert_eq!(result3, LiteralValue::Int(1));
567 }
568
569 #[test]
570 fn rows_full_column_reference_returns_sheet_height() {
571 let wb = TestWorkbook::new().with_function(Arc::new(RowsFn));
572 let ctx = wb.interpreter();
573 let f = ctx.context.get_function("", "ROWS").unwrap();
574
575 let col_range_ref = ASTNode::new(
577 ASTNodeType::Reference {
578 original: "A:A".into(),
579 reference: ReferenceType::range(None, None, Some(1), None, Some(1)),
580 },
581 None,
582 );
583
584 let args = vec![ArgumentHandle::new(&col_range_ref, &ctx)];
585 let result = f
586 .dispatch(&args, &ctx.function_context(None))
587 .unwrap()
588 .into_literal();
589 assert_eq!(result, LiteralValue::Int(1_048_576));
590 }
591
592 #[test]
593 fn rows_named_range_falls_back_to_resolved_range_view() {
594 let wb = TestWorkbook::new()
595 .with_named_range(
596 "MyRows",
597 vec![
598 vec![LiteralValue::Int(1)],
599 vec![LiteralValue::Int(2)],
600 vec![LiteralValue::Int(3)],
601 ],
602 )
603 .with_function(Arc::new(RowsFn));
604 let ctx = wb.interpreter();
605 let f = ctx.context.get_function("", "ROWS").unwrap();
606
607 let named_ref = ASTNode::new(
608 ASTNodeType::Reference {
609 original: "MyRows".into(),
610 reference: ReferenceType::NamedRange("MyRows".into()),
611 },
612 None,
613 );
614
615 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
616 let result = f
617 .dispatch(&args, &ctx.function_context(None))
618 .unwrap()
619 .into_literal();
620 assert_eq!(result, LiteralValue::Int(3));
621 }
622
623 #[test]
624 fn column_with_reference() {
625 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
626 let ctx = wb.interpreter();
627 let f = ctx.context.get_function("", "COLUMN").unwrap();
628
629 let c5_ref = ASTNode::new(
631 ASTNodeType::Reference {
632 original: "C5".into(),
633 reference: ReferenceType::cell(None, 5, 3),
634 },
635 None,
636 );
637
638 let args = vec![ArgumentHandle::new(&c5_ref, &ctx)];
639 let result = f
640 .dispatch(&args, &ctx.function_context(None))
641 .unwrap()
642 .into_literal();
643 assert_eq!(result, LiteralValue::Int(3));
644
645 let range_ref = ASTNode::new(
647 ASTNodeType::Reference {
648 original: "B2:D4".into(),
649 reference: ReferenceType::range(None, Some(2), Some(2), Some(4), Some(4)),
650 },
651 None,
652 );
653
654 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
655 let result2 = f
656 .dispatch(&args2, &ctx.function_context(None))
657 .unwrap()
658 .into_literal();
659 assert_eq!(result2, LiteralValue::Int(2));
660 }
661
662 #[test]
663 fn column_no_arg_uses_current_cell_1_based() {
664 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
665 let ctx = wb.interpreter();
666 let f = ctx.context.get_function("", "COLUMN").unwrap();
667
668 let current = CellRef::new(0, Coord::from_excel(7, 4, false, false));
669 let result = f
670 .dispatch(&[], &ctx.function_context(Some(¤t)))
671 .unwrap()
672 .into_literal();
673 assert_eq!(result, LiteralValue::Int(4));
674 }
675
676 #[test]
677 fn column_full_row_reference_returns_first_column() {
678 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
679 let ctx = wb.interpreter();
680 let f = ctx.context.get_function("", "COLUMN").unwrap();
681
682 let row_range_ref = ASTNode::new(
684 ASTNodeType::Reference {
685 original: "5:5".into(),
686 reference: ReferenceType::range(None, Some(5), None, Some(5), None),
687 },
688 None,
689 );
690
691 let args = vec![ArgumentHandle::new(&row_range_ref, &ctx)];
692 let result = f
693 .dispatch(&args, &ctx.function_context(None))
694 .unwrap()
695 .into_literal();
696 assert_eq!(result, LiteralValue::Int(1));
697 }
698
699 #[test]
700 fn column_named_range_falls_back_to_resolved_range_view() {
701 let wb = TestWorkbook::new()
702 .with_named_range("MyRange", vec![vec![LiteralValue::Int(42)]])
703 .with_function(Arc::new(ColumnFn));
704 let ctx = wb.interpreter();
705 let f = ctx.context.get_function("", "COLUMN").unwrap();
706
707 let named_ref = ASTNode::new(
708 ASTNodeType::Reference {
709 original: "MyRange".into(),
710 reference: ReferenceType::NamedRange("MyRange".into()),
711 },
712 None,
713 );
714
715 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
716 let result = f
717 .dispatch(&args, &ctx.function_context(None))
718 .unwrap()
719 .into_literal();
720 assert_eq!(result, LiteralValue::Int(1));
721 }
722
723 #[test]
724 fn columns_function() {
725 let wb = TestWorkbook::new().with_function(Arc::new(ColumnsFn));
726 let ctx = wb.interpreter();
727 let f = ctx.context.get_function("", "COLUMNS").unwrap();
728
729 let range_ref = ASTNode::new(
731 ASTNodeType::Reference {
732 original: "A1:E1".into(),
733 reference: ReferenceType::range(None, Some(1), Some(1), Some(1), Some(5)),
734 },
735 None,
736 );
737
738 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
739 let result = f
740 .dispatch(&args, &ctx.function_context(None))
741 .unwrap()
742 .into_literal();
743 assert_eq!(result, LiteralValue::Int(5));
744
745 let range_ref2 = ASTNode::new(
747 ASTNodeType::Reference {
748 original: "B2:D10".into(),
749 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
750 },
751 None,
752 );
753
754 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
755 let result2 = f
756 .dispatch(&args2, &ctx.function_context(None))
757 .unwrap()
758 .into_literal();
759 assert_eq!(result2, LiteralValue::Int(3));
760
761 let cell_ref = ASTNode::new(
763 ASTNodeType::Reference {
764 original: "A1".into(),
765 reference: ReferenceType::cell(None, 1, 1),
766 },
767 None,
768 );
769
770 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
771 let result3 = f
772 .dispatch(&args3, &ctx.function_context(None))
773 .unwrap()
774 .into_literal();
775 assert_eq!(result3, LiteralValue::Int(1));
776 }
777
778 #[test]
779 fn columns_full_row_reference_returns_sheet_width() {
780 let wb = TestWorkbook::new().with_function(Arc::new(ColumnsFn));
781 let ctx = wb.interpreter();
782 let f = ctx.context.get_function("", "COLUMNS").unwrap();
783
784 let row_range_ref = ASTNode::new(
786 ASTNodeType::Reference {
787 original: "1:1".into(),
788 reference: ReferenceType::range(None, Some(1), None, Some(1), None),
789 },
790 None,
791 );
792
793 let args = vec![ArgumentHandle::new(&row_range_ref, &ctx)];
794 let result = f
795 .dispatch(&args, &ctx.function_context(None))
796 .unwrap()
797 .into_literal();
798 assert_eq!(result, LiteralValue::Int(16_384));
799 }
800
801 #[test]
802 fn columns_named_range_falls_back_to_resolved_range_view() {
803 let wb = TestWorkbook::new()
804 .with_named_range(
805 "MyCols",
806 vec![
807 vec![
808 LiteralValue::Int(1),
809 LiteralValue::Int(2),
810 LiteralValue::Int(3),
811 ],
812 vec![
813 LiteralValue::Int(4),
814 LiteralValue::Int(5),
815 LiteralValue::Int(6),
816 ],
817 ],
818 )
819 .with_function(Arc::new(ColumnsFn));
820 let ctx = wb.interpreter();
821 let f = ctx.context.get_function("", "COLUMNS").unwrap();
822
823 let named_ref = ASTNode::new(
824 ASTNodeType::Reference {
825 original: "MyCols".into(),
826 reference: ReferenceType::NamedRange("MyCols".into()),
827 },
828 None,
829 );
830
831 let args = vec![ArgumentHandle::new(&named_ref, &ctx)];
832 let result = f
833 .dispatch(&args, &ctx.function_context(None))
834 .unwrap()
835 .into_literal();
836 assert_eq!(result, LiteralValue::Int(3));
837 }
838
839 #[test]
840 fn rows_columns_reversed_range() {
841 let wb = TestWorkbook::new()
843 .with_function(Arc::new(RowsFn))
844 .with_function(Arc::new(ColumnsFn));
845 let ctx = wb.interpreter();
846 let rows_f = ctx.context.get_function("", "ROWS").unwrap();
847 let cols_f = ctx.context.get_function("", "COLUMNS").unwrap();
848 let rev_range = ASTNode::new(
849 ASTNodeType::Reference {
850 original: "A5:A1".into(),
851 reference: ReferenceType::range(None, Some(5), Some(1), Some(1), Some(1)),
852 },
853 None,
854 );
855 let args = vec![ArgumentHandle::new(&rev_range, &ctx)];
856 let r_count = rows_f
857 .dispatch(&args, &ctx.function_context(None))
858 .unwrap()
859 .into_literal();
860 let c_count = cols_f
861 .dispatch(&args, &ctx.function_context(None))
862 .unwrap()
863 .into_literal();
864 assert_eq!(r_count, LiteralValue::Int(1));
865 assert_eq!(c_count, LiteralValue::Int(1));
866 }
867}