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,
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 = match &reference {
77 ReferenceType::Cell { row, .. } => *row,
78 ReferenceType::Range {
79 start_row: Some(sr),
80 ..
81 } => *sr,
82 _ => {
83 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
84 ExcelError::new(ExcelErrorKind::Ref),
85 )));
86 }
87 };
88
89 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
90 row as i64,
91 )))
92 }
93}
94
95#[derive(Debug)]
96pub struct RowsFn;
97
98impl Function for RowsFn {
99 fn name(&self) -> &'static str {
100 "ROWS"
101 }
102
103 fn min_args(&self) -> usize {
104 1
105 }
106
107 func_caps!(PURE);
108
109 fn arg_schema(&self) -> &'static [ArgSchema] {
110 use once_cell::sync::Lazy;
111 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
112 vec![
113 ArgSchema {
115 kinds: smallvec::smallvec![ArgKind::Any],
116 required: true,
117 by_ref: false,
118 shape: ShapeKind::Range,
119 coercion: CoercionPolicy::None,
120 max: None,
121 repeating: None,
122 default: None,
123 },
124 ]
125 });
126 &SCHEMA
127 }
128
129 fn eval<'a, 'b, 'c>(
130 &self,
131 args: &'c [ArgumentHandle<'a, 'b>],
132 _ctx: &dyn FunctionContext<'b>,
133 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
134 if args.is_empty() {
135 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
136 ExcelError::new(ExcelErrorKind::Value),
137 )));
138 }
139
140 if let Ok(reference) = args[0].as_reference_or_eval() {
142 let rows = match &reference {
144 ReferenceType::Cell { .. } => 1,
145 ReferenceType::Range {
146 start_row: Some(sr),
147 end_row: Some(er),
148 ..
149 } => {
150 if *er >= *sr {
151 (*er - *sr + 1) as i64
152 } else {
153 1
154 }
155 }
156 _ => {
157 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
158 ExcelError::new(ExcelErrorKind::Ref),
159 )));
160 }
161 };
162 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
163 } else {
164 let v = args[0].value()?.into_literal();
166 let rows = match v {
167 LiteralValue::Array(arr) => arr.len() as i64,
168 _ => 1,
169 };
170 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(rows)))
171 }
172 }
173}
174
175#[derive(Debug)]
176pub struct ColumnFn;
177
178impl Function for ColumnFn {
179 fn name(&self) -> &'static str {
180 "COLUMN"
181 }
182
183 fn min_args(&self) -> usize {
184 0
185 }
186
187 func_caps!(PURE);
188
189 fn arg_schema(&self) -> &'static [ArgSchema] {
190 use once_cell::sync::Lazy;
191 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
192 vec![
193 ArgSchema {
195 kinds: smallvec::smallvec![ArgKind::Range],
196 required: false,
197 by_ref: true,
198 shape: ShapeKind::Range,
199 coercion: CoercionPolicy::None,
200 max: None,
201 repeating: None,
202 default: None,
203 },
204 ]
205 });
206 &SCHEMA
207 }
208
209 fn eval<'a, 'b, 'c>(
210 &self,
211 args: &'c [ArgumentHandle<'a, 'b>],
212 ctx: &dyn FunctionContext<'b>,
213 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
214 if args.is_empty() {
215 if let Some(cell_ref) = ctx.current_cell() {
217 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
218 cell_ref.coord.col() as i64,
219 )));
220 }
221 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
222 ExcelError::new(ExcelErrorKind::Value),
223 )));
224 }
225
226 let reference = match args[0].as_reference_or_eval() {
228 Ok(r) => r,
229 Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
230 };
231
232 let col = match &reference {
234 ReferenceType::Cell { col, .. } => *col,
235 ReferenceType::Range {
236 start_col: Some(sc),
237 ..
238 } => *sc,
239 _ => {
240 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
241 ExcelError::new(ExcelErrorKind::Ref),
242 )));
243 }
244 };
245
246 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(
247 col as i64,
248 )))
249 }
250}
251
252#[derive(Debug)]
253pub struct ColumnsFn;
254
255impl Function for ColumnsFn {
256 fn name(&self) -> &'static str {
257 "COLUMNS"
258 }
259
260 fn min_args(&self) -> usize {
261 1
262 }
263
264 func_caps!(PURE);
265
266 fn arg_schema(&self) -> &'static [ArgSchema] {
267 use once_cell::sync::Lazy;
268 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
269 vec![
270 ArgSchema {
272 kinds: smallvec::smallvec![ArgKind::Any],
273 required: true,
274 by_ref: false,
275 shape: ShapeKind::Range,
276 coercion: CoercionPolicy::None,
277 max: None,
278 repeating: None,
279 default: None,
280 },
281 ]
282 });
283 &SCHEMA
284 }
285
286 fn eval<'a, 'b, 'c>(
287 &self,
288 args: &'c [ArgumentHandle<'a, 'b>],
289 _ctx: &dyn FunctionContext<'b>,
290 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
291 if args.is_empty() {
292 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
293 ExcelError::new(ExcelErrorKind::Value),
294 )));
295 }
296
297 if let Ok(reference) = args[0].as_reference_or_eval() {
299 let cols = match &reference {
301 ReferenceType::Cell { .. } => 1,
302 ReferenceType::Range {
303 start_col: Some(sc),
304 end_col: Some(ec),
305 ..
306 } => {
307 if *ec >= *sc {
308 (*ec - *sc + 1) as i64
309 } else {
310 1
311 }
312 }
313 _ => {
314 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
315 ExcelError::new(ExcelErrorKind::Ref),
316 )));
317 }
318 };
319 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
320 } else {
321 let v = args[0].value()?.into_literal();
323 let cols = match v {
324 LiteralValue::Array(arr) => arr.first().map(|r| r.len()).unwrap_or(0) as i64,
325 _ => 1,
326 };
327 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(cols)))
328 }
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use crate::test_workbook::TestWorkbook;
336 use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
337 use std::sync::Arc;
338
339 #[test]
340 fn row_with_reference() {
341 let wb = TestWorkbook::new().with_function(Arc::new(RowFn));
342 let ctx = wb.interpreter();
343 let f = ctx.context.get_function("", "ROW").unwrap();
344
345 let b5_ref = ASTNode::new(
347 ASTNodeType::Reference {
348 original: "B5".into(),
349 reference: ReferenceType::cell(None, 5, 2),
350 },
351 None,
352 );
353
354 let args = vec![ArgumentHandle::new(&b5_ref, &ctx)];
355 let result = f
356 .dispatch(&args, &ctx.function_context(None))
357 .unwrap()
358 .into_literal();
359 assert_eq!(result, LiteralValue::Int(5));
360
361 let range_ref = ASTNode::new(
363 ASTNodeType::Reference {
364 original: "A1:C3".into(),
365 reference: ReferenceType::range(None, Some(1), Some(1), Some(3), Some(3)),
366 },
367 None,
368 );
369
370 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
371 let result2 = f
372 .dispatch(&args2, &ctx.function_context(None))
373 .unwrap()
374 .into_literal();
375 assert_eq!(result2, LiteralValue::Int(1));
376 }
377
378 #[test]
379 fn rows_function() {
380 let wb = TestWorkbook::new().with_function(Arc::new(RowsFn));
381 let ctx = wb.interpreter();
382 let f = ctx.context.get_function("", "ROWS").unwrap();
383
384 let range_ref = ASTNode::new(
386 ASTNodeType::Reference {
387 original: "A1:A5".into(),
388 reference: ReferenceType::range(None, Some(1), Some(1), Some(5), Some(1)),
389 },
390 None,
391 );
392
393 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
394 let result = f
395 .dispatch(&args, &ctx.function_context(None))
396 .unwrap()
397 .into_literal();
398 assert_eq!(result, LiteralValue::Int(5));
399
400 let range_ref2 = ASTNode::new(
402 ASTNodeType::Reference {
403 original: "B2:D10".into(),
404 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
405 },
406 None,
407 );
408
409 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
410 let result2 = f
411 .dispatch(&args2, &ctx.function_context(None))
412 .unwrap()
413 .into_literal();
414 assert_eq!(result2, LiteralValue::Int(9));
415
416 let cell_ref = ASTNode::new(
418 ASTNodeType::Reference {
419 original: "A1".into(),
420 reference: ReferenceType::cell(None, 1, 1),
421 },
422 None,
423 );
424
425 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
426 let result3 = f
427 .dispatch(&args3, &ctx.function_context(None))
428 .unwrap()
429 .into_literal();
430 assert_eq!(result3, LiteralValue::Int(1));
431 }
432
433 #[test]
434 fn column_with_reference() {
435 let wb = TestWorkbook::new().with_function(Arc::new(ColumnFn));
436 let ctx = wb.interpreter();
437 let f = ctx.context.get_function("", "COLUMN").unwrap();
438
439 let c5_ref = ASTNode::new(
441 ASTNodeType::Reference {
442 original: "C5".into(),
443 reference: ReferenceType::cell(None, 5, 3),
444 },
445 None,
446 );
447
448 let args = vec![ArgumentHandle::new(&c5_ref, &ctx)];
449 let result = f
450 .dispatch(&args, &ctx.function_context(None))
451 .unwrap()
452 .into_literal();
453 assert_eq!(result, LiteralValue::Int(3));
454
455 let range_ref = ASTNode::new(
457 ASTNodeType::Reference {
458 original: "B2:D4".into(),
459 reference: ReferenceType::range(None, Some(2), Some(2), Some(4), Some(4)),
460 },
461 None,
462 );
463
464 let args2 = vec![ArgumentHandle::new(&range_ref, &ctx)];
465 let result2 = f
466 .dispatch(&args2, &ctx.function_context(None))
467 .unwrap()
468 .into_literal();
469 assert_eq!(result2, LiteralValue::Int(2));
470 }
471
472 #[test]
473 fn columns_function() {
474 let wb = TestWorkbook::new().with_function(Arc::new(ColumnsFn));
475 let ctx = wb.interpreter();
476 let f = ctx.context.get_function("", "COLUMNS").unwrap();
477
478 let range_ref = ASTNode::new(
480 ASTNodeType::Reference {
481 original: "A1:E1".into(),
482 reference: ReferenceType::range(None, Some(1), Some(1), Some(1), Some(5)),
483 },
484 None,
485 );
486
487 let args = vec![ArgumentHandle::new(&range_ref, &ctx)];
488 let result = f
489 .dispatch(&args, &ctx.function_context(None))
490 .unwrap()
491 .into_literal();
492 assert_eq!(result, LiteralValue::Int(5));
493
494 let range_ref2 = ASTNode::new(
496 ASTNodeType::Reference {
497 original: "B2:D10".into(),
498 reference: ReferenceType::range(None, Some(2), Some(2), Some(10), Some(4)),
499 },
500 None,
501 );
502
503 let args2 = vec![ArgumentHandle::new(&range_ref2, &ctx)];
504 let result2 = f
505 .dispatch(&args2, &ctx.function_context(None))
506 .unwrap()
507 .into_literal();
508 assert_eq!(result2, LiteralValue::Int(3));
509
510 let cell_ref = ASTNode::new(
512 ASTNodeType::Reference {
513 original: "A1".into(),
514 reference: ReferenceType::cell(None, 1, 1),
515 },
516 None,
517 );
518
519 let args3 = vec![ArgumentHandle::new(&cell_ref, &ctx)];
520 let result3 = f
521 .dispatch(&args3, &ctx.function_context(None))
522 .unwrap()
523 .into_literal();
524 assert_eq!(result3, LiteralValue::Int(1));
525 }
526
527 #[test]
528 fn rows_columns_reversed_range() {
529 let wb = TestWorkbook::new()
531 .with_function(Arc::new(RowsFn))
532 .with_function(Arc::new(ColumnsFn));
533 let ctx = wb.interpreter();
534 let rows_f = ctx.context.get_function("", "ROWS").unwrap();
535 let cols_f = ctx.context.get_function("", "COLUMNS").unwrap();
536 let rev_range = ASTNode::new(
537 ASTNodeType::Reference {
538 original: "A5:A1".into(),
539 reference: ReferenceType::range(None, Some(5), Some(1), Some(1), Some(1)),
540 },
541 None,
542 );
543 let args = vec![ArgumentHandle::new(&rev_range, &ctx)];
544 let r_count = rows_f
545 .dispatch(&args, &ctx.function_context(None))
546 .unwrap()
547 .into_literal();
548 let c_count = cols_f
549 .dispatch(&args, &ctx.function_context(None))
550 .unwrap()
551 .into_literal();
552 assert_eq!(r_count, LiteralValue::Int(1));
553 assert_eq!(c_count, LiteralValue::Int(1));
554 }
555}