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