1use super::{
2 stringify::{stringify_reference, DisplaceData},
3 ArrayNode, Node, Reference,
4};
5use crate::{
6 constants::{LAST_COLUMN, LAST_ROW},
7 expressions::token::OpUnary,
8 language::Language,
9 locale::Locale,
10};
11use crate::{
12 expressions::types::{Area, CellReferenceRC},
13 number_format::to_excel_precision_str,
14};
15
16pub(crate) fn ref_is_in_area(sheet: u32, row: i32, column: i32, area: &Area) -> bool {
17 if area.sheet != sheet {
18 return false;
19 }
20 if row < area.row || row > area.row + area.height - 1 {
21 return false;
22 }
23 if column < area.column || column > area.column + area.width - 1 {
24 return false;
25 }
26 true
27}
28
29pub(crate) struct MoveContext<'a> {
30 pub source_sheet_name: &'a str,
31 pub row: i32,
32 pub column: i32,
33 pub area: &'a Area,
34 pub target_sheet_name: &'a str,
35 pub row_delta: i32,
36 pub column_delta: i32,
37}
38
39pub(crate) fn move_formula(
44 node: &Node,
45 move_context: &MoveContext,
46 locale: &Locale,
47 language: &Language,
48) -> String {
49 to_string_moved(node, move_context, locale, language)
50}
51
52fn move_function(
53 name: &str,
54 args: &Vec<Node>,
55 move_context: &MoveContext,
56 locale: &Locale,
57 language: &Language,
58) -> String {
59 let mut first = true;
60 let mut arguments = "".to_string();
61 for el in args {
62 if !first {
63 arguments = format!(
64 "{},{}",
65 arguments,
66 to_string_moved(el, move_context, locale, language)
67 );
68 } else {
69 first = false;
70 arguments = to_string_moved(el, move_context, locale, language);
71 }
72 }
73 format!("{name}({arguments})")
74}
75
76fn format_number_locale(number: f64, locale: &Locale) -> String {
77 let s = to_excel_precision_str(number);
78 let decimal = &locale.numbers.symbols.decimal;
79 if decimal == "." {
80 s
81 } else {
82 s.replace('.', decimal)
83 }
84}
85
86pub(crate) fn to_string_array_node(
87 node: &ArrayNode,
88 locale: &Locale,
89 language: &Language,
90) -> String {
91 match node {
92 ArrayNode::Boolean(value) => {
93 if *value {
94 language.booleans.r#true.to_uppercase()
95 } else {
96 language.booleans.r#false.to_uppercase()
97 }
98 }
99 ArrayNode::Number(number) => format_number_locale(*number, locale),
100 ArrayNode::String(value) => format!("\"{value}\""),
101 ArrayNode::Error(kind) => format!("{kind}"),
102 ArrayNode::Empty => "0".to_string(),
103 }
104}
105
106fn to_string_moved(
107 node: &Node,
108 move_context: &MoveContext,
109 locale: &Locale,
110 language: &Language,
111) -> String {
112 use self::Node::*;
113 match node {
114 BooleanKind(value) => format!("{value}").to_uppercase(),
115 NumberKind(number) => format_number_locale(*number, locale),
116 StringKind(value) => format!("\"{value}\""),
117 ReferenceKind {
118 sheet_name,
119 sheet_index,
120 absolute_row,
121 absolute_column,
122 row,
123 column,
124 } => {
125 let reference_row = if *absolute_row {
126 *row
127 } else {
128 row + move_context.row
129 };
130 let reference_column = if *absolute_column {
131 *column
132 } else {
133 column + move_context.column
134 };
135
136 let new_row;
137 let new_column;
138 let mut ref_sheet_name = sheet_name;
139 let source_sheet_name = &Some(move_context.source_sheet_name.to_string());
140
141 if ref_is_in_area(
142 *sheet_index,
143 reference_row,
144 reference_column,
145 move_context.area,
146 ) {
147 new_row = row + move_context.row_delta;
149 new_column = column + move_context.column_delta;
150 } else {
151 new_row = *row;
153 new_column = *column;
154 if move_context.target_sheet_name != move_context.source_sheet_name
155 && sheet_name.is_none()
156 {
157 ref_sheet_name = source_sheet_name;
158 }
159 };
160 let context = CellReferenceRC {
161 sheet: move_context.source_sheet_name.to_string(),
162 column: move_context.column,
163 row: move_context.row,
164 };
165 stringify_reference(
166 Some(&context),
167 &DisplaceData::None,
168 &Reference {
169 sheet_name: ref_sheet_name,
170 sheet_index: *sheet_index,
171 absolute_row: *absolute_row,
172 absolute_column: *absolute_column,
173 row: new_row,
174 column: new_column,
175 },
176 false,
177 false,
178 )
179 }
180 RangeKind {
181 sheet_name,
182 sheet_index,
183 absolute_row1,
184 absolute_column1,
185 row1,
186 column1,
187 absolute_row2,
188 absolute_column2,
189 row2,
190 column2,
191 } => {
192 let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
193 let full_column = *absolute_column1
194 && *absolute_column2
195 && (*column1 == 1)
196 && (*column2 == LAST_COLUMN);
197
198 let reference_row1 = if *absolute_row1 {
199 *row1
200 } else {
201 row1 + move_context.row
202 };
203 let reference_column1 = if *absolute_column1 {
204 *column1
205 } else {
206 column1 + move_context.column
207 };
208
209 let reference_row2 = if *absolute_row2 {
210 *row2
211 } else {
212 row2 + move_context.row
213 };
214 let reference_column2 = if *absolute_column2 {
215 *column2
216 } else {
217 column2 + move_context.column
218 };
219
220 let new_row1;
221 let new_column1;
222 let new_row2;
223 let new_column2;
224 let mut ref_sheet_name = sheet_name;
225 let source_sheet_name = &Some(move_context.source_sheet_name.to_string());
226 if ref_is_in_area(
227 *sheet_index,
228 reference_row1,
229 reference_column1,
230 move_context.area,
231 ) && ref_is_in_area(
232 *sheet_index,
233 reference_row2,
234 reference_column2,
235 move_context.area,
236 ) {
237 new_row1 = row1 + move_context.row_delta;
239 new_column1 = column1 + move_context.column_delta;
240 new_row2 = row2 + move_context.row_delta;
241 new_column2 = column2 + move_context.column_delta;
242 } else {
243 new_row1 = *row1;
245 new_column1 = *column1;
246 new_row2 = *row2;
247 new_column2 = *column2;
248 if move_context.target_sheet_name != move_context.source_sheet_name
249 && sheet_name.is_none()
250 {
251 ref_sheet_name = source_sheet_name;
252 }
253 };
254 let context = CellReferenceRC {
255 sheet: move_context.source_sheet_name.to_string(),
256 column: move_context.column,
257 row: move_context.row,
258 };
259 let s1 = stringify_reference(
260 Some(&context),
261 &DisplaceData::None,
262 &Reference {
263 sheet_name: ref_sheet_name,
264 sheet_index: *sheet_index,
265 absolute_row: *absolute_row1,
266 absolute_column: *absolute_column1,
267 row: new_row1,
268 column: new_column1,
269 },
270 full_row,
271 full_column,
272 );
273 let s2 = stringify_reference(
274 Some(&context),
275 &DisplaceData::None,
276 &Reference {
277 sheet_name: &None,
278 sheet_index: *sheet_index,
279 absolute_row: *absolute_row2,
280 absolute_column: *absolute_column2,
281 row: new_row2,
282 column: new_column2,
283 },
284 full_row,
285 full_column,
286 );
287 format!("{s1}:{s2}")
288 }
289 WrongReferenceKind {
290 sheet_name,
291 absolute_row,
292 absolute_column,
293 row,
294 column,
295 } => {
296 let context = CellReferenceRC {
298 sheet: move_context.source_sheet_name.to_string(),
299 column: move_context.column,
300 row: move_context.row,
301 };
302 stringify_reference(
306 Some(&context),
307 &DisplaceData::None,
308 &Reference {
309 sheet_name,
310 sheet_index: 0, row: *row,
312 column: *column,
313 absolute_row: *absolute_row,
314 absolute_column: *absolute_column,
315 },
316 false,
317 false,
318 )
319 }
320 WrongRangeKind {
321 sheet_name,
322 absolute_row1,
323 absolute_column1,
324 row1,
325 column1,
326 absolute_row2,
327 absolute_column2,
328 row2,
329 column2,
330 } => {
331 let full_row = *absolute_row1 && *absolute_row2 && (*row1 == 1) && (*row2 == LAST_ROW);
332 let full_column = *absolute_column1
333 && *absolute_column2
334 && (*column1 == 1)
335 && (*column2 == LAST_COLUMN);
336
337 let context = CellReferenceRC {
339 sheet: move_context.source_sheet_name.to_string(),
340 column: move_context.column,
341 row: move_context.row,
342 };
343 let s1 = stringify_reference(
344 Some(&context),
345 &DisplaceData::None,
346 &Reference {
347 sheet_name,
348 sheet_index: 0, row: *row1,
350 column: *column1,
351 absolute_row: *absolute_row1,
352 absolute_column: *absolute_column1,
353 },
354 full_row,
355 full_column,
356 );
357 let s2 = stringify_reference(
358 Some(&context),
359 &DisplaceData::None,
360 &Reference {
361 sheet_name: &None,
362 sheet_index: 0, row: *row2,
364 column: *column2,
365 absolute_row: *absolute_row2,
366 absolute_column: *absolute_column2,
367 },
368 full_row,
369 full_column,
370 );
371 format!("{s1}:{s2}")
372 }
373 OpRangeKind { left, right } => format!(
374 "{}:{}",
375 to_string_moved(left, move_context, locale, language),
376 to_string_moved(right, move_context, locale, language),
377 ),
378 OpConcatenateKind { left, right } => format!(
379 "{}&{}",
380 to_string_moved(left, move_context, locale, language),
381 to_string_moved(right, move_context, locale, language),
382 ),
383 OpSumKind { kind, left, right } => format!(
384 "{}{}{}",
385 to_string_moved(left, move_context, locale, language),
386 kind,
387 to_string_moved(right, move_context, locale, language),
388 ),
389 OpProductKind { kind, left, right } => {
390 let x = match **left {
391 OpSumKind { .. } => format!(
392 "({})",
393 to_string_moved(left, move_context, locale, language)
394 ),
395 CompareKind { .. } => format!(
396 "({})",
397 to_string_moved(left, move_context, locale, language)
398 ),
399 _ => to_string_moved(left, move_context, locale, language),
400 };
401 let y = match **right {
402 OpSumKind { .. } => format!(
403 "({})",
404 to_string_moved(right, move_context, locale, language)
405 ),
406 CompareKind { .. } => format!(
407 "({})",
408 to_string_moved(right, move_context, locale, language)
409 ),
410 OpProductKind { .. } => format!(
411 "({})",
412 to_string_moved(right, move_context, locale, language)
413 ),
414 UnaryKind { .. } => {
415 format!(
416 "({})",
417 to_string_moved(right, move_context, locale, language)
418 )
419 }
420 _ => to_string_moved(right, move_context, locale, language),
421 };
422 format!("{x}{kind}{y}")
423 }
424 OpPowerKind { left, right } => format!(
425 "{}^{}",
426 to_string_moved(left, move_context, locale, language),
427 to_string_moved(right, move_context, locale, language),
428 ),
429 NamedFunctionKind { name, args, id: _ } => {
430 move_function(name, args, move_context, locale, language)
431 }
432 FunctionKind { kind, args } => {
433 let name = &kind.to_localized_name(language);
434 move_function(name, args, move_context, locale, language)
435 }
436 ArrayKind(args) => {
437 let mut first_row = true;
438 let mut matrix_string = String::new();
439
440 let row_separator = if locale.numbers.symbols.decimal == "." {
442 ';'
443 } else {
444 '/'
445 };
446 let col_separator = if row_separator == ';' { ',' } else { ';' };
447 for row in args {
448 if !first_row {
449 matrix_string.push(col_separator);
450 } else {
451 first_row = false;
452 }
453
454 let mut first_col = true;
456 let mut row_string = String::new();
457 for el in row {
458 if !first_col {
459 row_string.push(row_separator);
460 } else {
461 first_col = false;
462 }
463
464 row_string.push_str(&to_string_array_node(el, locale, language));
466 }
467
468 matrix_string.push('{');
470 matrix_string.push_str(&row_string);
471 matrix_string.push('}');
472 }
473
474 format!("{{{matrix_string}}}")
476 }
477 DefinedNameKind((name, ..)) => name.to_string(),
478 TableNameKind(name) => name.to_string(),
479 NamedVariableKind { name, id: _ } => name.to_string(),
480 CompareKind { kind, left, right } => format!(
481 "{}{}{}",
482 to_string_moved(left, move_context, locale, language),
483 kind,
484 to_string_moved(right, move_context, locale, language),
485 ),
486 UnaryKind { kind, right } => match kind {
487 OpUnary::Minus => format!(
488 "-{}",
489 to_string_moved(right, move_context, locale, language)
490 ),
491 OpUnary::Percentage => format!(
492 "{}%",
493 to_string_moved(right, move_context, locale, language)
494 ),
495 },
496 ErrorKind(kind) => format!("{kind}"),
497 ParseErrorKind { formula, .. } => formula.to_string(),
498 EmptyArgKind => "".to_string(),
499 ImplicitIntersection {
500 automatic: _,
501 child,
502 } => {
503 format!(
504 "@{}",
505 to_string_moved(child, move_context, locale, language)
506 )
507 }
508 SpillRangeOperator { child } => {
509 format!(
510 "{}#",
511 to_string_moved(child, move_context, locale, language)
512 )
513 }
514 LambdaDefKind { parameters, body } => {
515 let mut parts: Vec<String> = parameters.iter().map(|p| p.name.clone()).collect();
516 parts.push(to_string_moved(body, move_context, locale, language));
517 format!("LAMBDA({})", parts.join(","))
518 }
519 LambdaCallKind { lambda, args } => {
520 let lambda_str = to_string_moved(lambda, move_context, locale, language);
521 let call_args: Vec<String> = args
522 .iter()
523 .map(|a| to_string_moved(a, move_context, locale, language))
524 .collect();
525 format!("{}({})", lambda_str, call_args.join(","))
526 }
527 }
528}