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