1use crate::args::{ArgSchema, CoercionPolicy, ShapeKind};
10use crate::function::Function;
11use crate::traits::{ArgumentHandle, FunctionContext};
12use formualizer_common::{ArgKind, ExcelError, ExcelErrorKind, LiteralValue};
13use formualizer_macros::func_caps;
14
15fn column_to_letters(mut col: u32) -> String {
17 if col == 0 {
18 return String::new();
19 }
20
21 let mut result = String::new();
22 while col > 0 {
23 col -= 1;
24 let remainder = col % 26;
25 result.push((b'A' + remainder as u8) as char);
26 col /= 26;
27 }
28 result.chars().rev().collect()
29}
30
31#[derive(Debug)]
32pub struct AddressFn;
33
34impl Function for AddressFn {
35 fn name(&self) -> &'static str {
36 "ADDRESS"
37 }
38
39 fn min_args(&self) -> usize {
40 2
41 }
42
43 func_caps!(PURE);
44
45 fn arg_schema(&self) -> &'static [ArgSchema] {
46 use once_cell::sync::Lazy;
47 static SCHEMA: Lazy<Vec<ArgSchema>> = Lazy::new(|| {
48 vec![
49 ArgSchema {
51 kinds: smallvec::smallvec![ArgKind::Number],
52 required: true,
53 by_ref: false,
54 shape: ShapeKind::Scalar,
55 coercion: CoercionPolicy::NumberStrict,
56 max: None,
57 repeating: None,
58 default: None,
59 },
60 ArgSchema {
62 kinds: smallvec::smallvec![ArgKind::Number],
63 required: true,
64 by_ref: false,
65 shape: ShapeKind::Scalar,
66 coercion: CoercionPolicy::NumberStrict,
67 max: None,
68 repeating: None,
69 default: None,
70 },
71 ArgSchema {
73 kinds: smallvec::smallvec![ArgKind::Number],
74 required: false,
75 by_ref: false,
76 shape: ShapeKind::Scalar,
77 coercion: CoercionPolicy::NumberStrict,
78 max: None,
79 repeating: None,
80 default: Some(LiteralValue::Int(1)),
81 },
82 ArgSchema {
84 kinds: smallvec::smallvec![ArgKind::Logical],
85 required: false,
86 by_ref: false,
87 shape: ShapeKind::Scalar,
88 coercion: CoercionPolicy::Logical,
89 max: None,
90 repeating: None,
91 default: Some(LiteralValue::Boolean(true)),
92 },
93 ArgSchema {
95 kinds: smallvec::smallvec![ArgKind::Text],
96 required: false,
97 by_ref: false,
98 shape: ShapeKind::Scalar,
99 coercion: CoercionPolicy::None,
100 max: None,
101 repeating: None,
102 default: None,
103 },
104 ]
105 });
106 &SCHEMA
107 }
108
109 fn eval_scalar<'a, 'b>(
110 &self,
111 args: &'a [ArgumentHandle<'a, 'b>],
112 _ctx: &dyn FunctionContext,
113 ) -> Result<LiteralValue, ExcelError> {
114 if args.len() < 2 {
115 return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value)));
116 }
117
118 let row_val = args[0].value()?;
120 if let LiteralValue::Error(e) = row_val.as_ref() {
121 return Ok(LiteralValue::Error(e.clone()));
122 }
123 let row = match row_val.as_ref() {
124 LiteralValue::Number(n) => *n as i64,
125 LiteralValue::Int(i) => *i,
126 _ => return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))),
127 };
128
129 if !(1..=1_048_576).contains(&row) {
130 return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value)));
131 }
132
133 let col_val = args[1].value()?;
135 if let LiteralValue::Error(e) = col_val.as_ref() {
136 return Ok(LiteralValue::Error(e.clone()));
137 }
138 let col = match col_val.as_ref() {
139 LiteralValue::Number(n) => *n as i64,
140 LiteralValue::Int(i) => *i,
141 _ => return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value))),
142 };
143
144 if !(1..=16384).contains(&col) {
145 return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value)));
146 }
147
148 let abs_num = if args.len() > 2 {
150 let abs_val = args[2].value()?;
151 if let LiteralValue::Error(e) = abs_val.as_ref() {
152 return Ok(LiteralValue::Error(e.clone()));
153 }
154 match abs_val.as_ref() {
155 LiteralValue::Number(n) => *n as i64,
156 LiteralValue::Int(i) => *i,
157 _ => 1,
158 }
159 } else {
160 1
161 };
162
163 if !(1..=4).contains(&abs_num) {
164 return Ok(LiteralValue::Error(ExcelError::new(ExcelErrorKind::Value)));
165 }
166
167 let a1_style = if args.len() > 3 {
169 let a1_val = args[3].value()?;
170 if let LiteralValue::Error(e) = a1_val.as_ref() {
171 return Ok(LiteralValue::Error(e.clone()));
172 }
173 match a1_val.as_ref() {
174 LiteralValue::Boolean(b) => *b,
175 _ => true,
176 }
177 } else {
178 true
179 };
180
181 let sheet_name = if args.len() > 4 {
183 let sheet_val = args[4].value()?;
184 if let LiteralValue::Error(e) = sheet_val.as_ref() {
185 return Ok(LiteralValue::Error(e.clone()));
186 }
187 match sheet_val.as_ref() {
188 LiteralValue::Text(s) => Some(s.clone()),
189 _ => None,
190 }
191 } else {
192 None
193 };
194
195 let address = if a1_style {
197 let col_letters = column_to_letters(col as u32);
199 let (col_abs, row_abs) = match abs_num {
200 1 => (true, true), 2 => (false, true), 3 => (true, false), 4 => (false, false), _ => (true, true),
205 };
206
207 let col_str = if col_abs {
208 format!("${col_letters}")
209 } else {
210 col_letters
211 };
212 let row_str = if row_abs {
213 format!("${row}")
214 } else {
215 row.to_string()
216 };
217 format!("{col_str}{row_str}")
218 } else {
219 let (col_abs, row_abs) = match abs_num {
221 1 => (true, true),
222 2 => (false, true),
223 3 => (true, false),
224 4 => (false, false),
225 _ => (true, true),
226 };
227
228 let row_str = if row_abs {
229 format!("R{row}")
230 } else {
231 format!("R[{row}]")
232 };
233 let col_str = if col_abs {
234 format!("C{col}")
235 } else {
236 format!("C[{col}]")
237 };
238 format!("{row_str}{col_str}")
239 };
240
241 let final_address = if let Some(sheet) = sheet_name {
243 if sheet.contains(' ') || sheet.contains('!') || sheet.contains('\'') {
245 format!("'{}'!{address}", sheet.replace('\'', "''"))
246 } else {
247 format!("{sheet}!{address}")
248 }
249 } else {
250 address
251 };
252
253 Ok(LiteralValue::Text(final_address))
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::test_workbook::TestWorkbook;
261 use formualizer_parse::parser::{ASTNode, ASTNodeType};
262 use std::sync::Arc;
263
264 fn lit(v: LiteralValue) -> ASTNode {
265 ASTNode::new(ASTNodeType::Literal(v), None)
266 }
267
268 #[test]
269 fn test_column_to_letters() {
270 assert_eq!(column_to_letters(1), "A");
271 assert_eq!(column_to_letters(26), "Z");
272 assert_eq!(column_to_letters(27), "AA");
273 assert_eq!(column_to_letters(52), "AZ");
274 assert_eq!(column_to_letters(53), "BA");
275 assert_eq!(column_to_letters(702), "ZZ");
276 assert_eq!(column_to_letters(703), "AAA");
277 }
278
279 #[test]
280 fn address_basic() {
281 let wb = TestWorkbook::new().with_function(Arc::new(AddressFn));
282 let ctx = wb.interpreter();
283 let f = ctx.context.get_function("", "ADDRESS").unwrap();
284
285 let two = lit(LiteralValue::Int(2));
287 let three = lit(LiteralValue::Int(3));
288
289 let args = vec![
290 ArgumentHandle::new(&two, &ctx),
291 ArgumentHandle::new(&three, &ctx),
292 ];
293
294 let result = f.dispatch(&args, &ctx.function_context(None)).unwrap();
295 assert_eq!(result, LiteralValue::Text("$C$2".into()));
296 }
297
298 #[test]
299 fn address_abs_variations() {
300 let wb = TestWorkbook::new().with_function(Arc::new(AddressFn));
301 let ctx = wb.interpreter();
302 let f = ctx.context.get_function("", "ADDRESS").unwrap();
303
304 let row = lit(LiteralValue::Int(5));
305 let col = lit(LiteralValue::Int(4)); let abs1 = lit(LiteralValue::Int(1));
309 let args1 = vec![
310 ArgumentHandle::new(&row, &ctx),
311 ArgumentHandle::new(&col, &ctx),
312 ArgumentHandle::new(&abs1, &ctx),
313 ];
314 assert_eq!(
315 f.dispatch(&args1, &ctx.function_context(None)).unwrap(),
316 LiteralValue::Text("$D$5".into())
317 );
318
319 let abs2 = lit(LiteralValue::Int(2));
321 let args2 = vec![
322 ArgumentHandle::new(&row, &ctx),
323 ArgumentHandle::new(&col, &ctx),
324 ArgumentHandle::new(&abs2, &ctx),
325 ];
326 assert_eq!(
327 f.dispatch(&args2, &ctx.function_context(None)).unwrap(),
328 LiteralValue::Text("D$5".into())
329 );
330
331 let abs3 = lit(LiteralValue::Int(3));
333 let args3 = vec![
334 ArgumentHandle::new(&row, &ctx),
335 ArgumentHandle::new(&col, &ctx),
336 ArgumentHandle::new(&abs3, &ctx),
337 ];
338 assert_eq!(
339 f.dispatch(&args3, &ctx.function_context(None)).unwrap(),
340 LiteralValue::Text("$D5".into())
341 );
342
343 let abs4 = lit(LiteralValue::Int(4));
345 let args4 = vec![
346 ArgumentHandle::new(&row, &ctx),
347 ArgumentHandle::new(&col, &ctx),
348 ArgumentHandle::new(&abs4, &ctx),
349 ];
350 assert_eq!(
351 f.dispatch(&args4, &ctx.function_context(None)).unwrap(),
352 LiteralValue::Text("D5".into())
353 );
354 }
355
356 #[test]
357 fn address_with_sheet() {
358 let wb = TestWorkbook::new().with_function(Arc::new(AddressFn));
359 let ctx = wb.interpreter();
360 let f = ctx.context.get_function("", "ADDRESS").unwrap();
361
362 let row = lit(LiteralValue::Int(1));
363 let col = lit(LiteralValue::Int(1));
364 let abs_num = lit(LiteralValue::Int(1));
365 let a1_style = lit(LiteralValue::Boolean(true));
366
367 let sheet1 = lit(LiteralValue::Text("Sheet1".into()));
369 let args1 = vec![
370 ArgumentHandle::new(&row, &ctx),
371 ArgumentHandle::new(&col, &ctx),
372 ArgumentHandle::new(&abs_num, &ctx),
373 ArgumentHandle::new(&a1_style, &ctx),
374 ArgumentHandle::new(&sheet1, &ctx),
375 ];
376 assert_eq!(
377 f.dispatch(&args1, &ctx.function_context(None)).unwrap(),
378 LiteralValue::Text("Sheet1!$A$1".into())
379 );
380
381 let sheet2 = lit(LiteralValue::Text("My Sheet".into()));
383 let args2 = vec![
384 ArgumentHandle::new(&row, &ctx),
385 ArgumentHandle::new(&col, &ctx),
386 ArgumentHandle::new(&abs_num, &ctx),
387 ArgumentHandle::new(&a1_style, &ctx),
388 ArgumentHandle::new(&sheet2, &ctx),
389 ];
390 assert_eq!(
391 f.dispatch(&args2, &ctx.function_context(None)).unwrap(),
392 LiteralValue::Text("'My Sheet'!$A$1".into())
393 );
394 }
395
396 #[test]
397 fn address_r1c1_style() {
398 let wb = TestWorkbook::new().with_function(Arc::new(AddressFn));
399 let ctx = wb.interpreter();
400 let f = ctx.context.get_function("", "ADDRESS").unwrap();
401
402 let row = lit(LiteralValue::Int(5));
403 let col = lit(LiteralValue::Int(3));
404 let abs1 = lit(LiteralValue::Int(1));
405 let r1c1 = lit(LiteralValue::Boolean(false));
406
407 let args = vec![
409 ArgumentHandle::new(&row, &ctx),
410 ArgumentHandle::new(&col, &ctx),
411 ArgumentHandle::new(&abs1, &ctx),
412 ArgumentHandle::new(&r1c1, &ctx),
413 ];
414 assert_eq!(
415 f.dispatch(&args, &ctx.function_context(None)).unwrap(),
416 LiteralValue::Text("R5C3".into())
417 );
418
419 let abs4 = lit(LiteralValue::Int(4));
421 let args2 = vec![
422 ArgumentHandle::new(&row, &ctx),
423 ArgumentHandle::new(&col, &ctx),
424 ArgumentHandle::new(&abs4, &ctx),
425 ArgumentHandle::new(&r1c1, &ctx),
426 ];
427 assert_eq!(
428 f.dispatch(&args2, &ctx.function_context(None)).unwrap(),
429 LiteralValue::Text("R[5]C[3]".into())
430 );
431 }
432
433 #[test]
434 fn address_edge_cases() {
435 let wb = TestWorkbook::new().with_function(Arc::new(AddressFn));
436 let ctx = wb.interpreter();
437 let f = ctx.context.get_function("", "ADDRESS").unwrap();
438
439 let big_row = lit(LiteralValue::Int(1_048_577));
441 let col = lit(LiteralValue::Int(1));
442 let args = vec![
443 ArgumentHandle::new(&big_row, &ctx),
444 ArgumentHandle::new(&col, &ctx),
445 ];
446 let result = f.dispatch(&args, &ctx.function_context(None)).unwrap();
447 assert!(matches!(result, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
448
449 let row = lit(LiteralValue::Int(1));
451 let big_col = lit(LiteralValue::Int(16385));
452 let args2 = vec![
453 ArgumentHandle::new(&row, &ctx),
454 ArgumentHandle::new(&big_col, &ctx),
455 ];
456 let result2 = f.dispatch(&args2, &ctx.function_context(None)).unwrap();
457 assert!(matches!(result2, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
458
459 let abs5 = lit(LiteralValue::Int(5));
461 let normal_col = lit(LiteralValue::Int(1));
462 let args3 = vec![
463 ArgumentHandle::new(&row, &ctx),
464 ArgumentHandle::new(&normal_col, &ctx),
465 ArgumentHandle::new(&abs5, &ctx),
466 ];
467 let result3 = f.dispatch(&args3, &ctx.function_context(None)).unwrap();
468 assert!(matches!(result3, LiteralValue::Error(e) if e.kind == ExcelErrorKind::Value));
469 }
470}