1#![allow(dead_code, unused_imports)]
9
10use quick_xml::de::from_reader as xml_from_reader;
11use quick_xml::se::to_string as xml_to_string;
12
13use crate::constants::{MAX_COLUMNS, TOTAL_ROWS};
14use crate::errors::{ErrColumnNumber, ErrMaxRows, Result, new_not_worksheet_error};
15use crate::file::{File, namespace_strict_to_transitional};
16use crate::lib_util::{
17 cell_name_to_coordinates, column_number_to_name, coordinates_to_cell_name,
18 coordinates_to_range_ref, join_cell_name, range_ref_to_coordinates, split_cell_name,
19};
20use crate::xml::common::XlsxInnerXml;
21use crate::xml::drawing::{XdrCellAnchor, XlsxFrom, XlsxTo};
22use crate::xml::table::{XlsxAutoFilter, XlsxTable};
23use crate::xml::workbook::XlsxWorkbook;
24use crate::xml::worksheet::{
25 XlsxC, XlsxConditionalFormatting, XlsxDataValidation, XlsxDataValidations, XlsxF,
26 XlsxHyperlinks, XlsxMergeCells, XlsxRow, XlsxTableParts, XlsxWorksheet,
27};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum AdjustDirection {
32 Columns,
33 Rows,
34}
35
36impl File {
37 pub(crate) fn adjust_helper(
39 &self,
40 sheet: &str,
41 dir: AdjustDirection,
42 num: i32,
43 offset: i32,
44 ) -> Result<()> {
45 let path =
46 self.get_sheet_xml_path(sheet)
47 .ok_or_else(|| crate::errors::ErrSheetNotExist {
48 sheet_name: sheet.to_string(),
49 })?;
50 let mut ws = self.work_sheet_reader(sheet)?;
51 self.clear_calc_cache();
52 let sheet_id = self.get_sheet_id(sheet);
53
54 if dir == AdjustDirection::Rows {
55 self.adjust_row_dimensions(sheet, &mut ws, num, offset)?;
56 } else {
57 self.adjust_col_dimensions(sheet, &mut ws, num, offset)?;
58 }
59
60 self.adjust_hyperlinks(&mut ws, sheet, dir, num, offset);
61 let _ = self.check_sheet(&mut ws);
62 let _ = self.check_row(&mut ws);
63
64 self.adjust_conditional_formats(&mut ws, sheet, dir, num, offset, sheet_id)?;
65 self.adjust_data_validations(&mut ws, sheet, dir, num, offset, sheet_id)?;
66 self.adjust_defined_names(sheet, dir, num, offset)?;
67 self.adjust_drawings(&mut ws, sheet, dir, num, offset)?;
68 self.adjust_merge_cells(&mut ws, sheet, dir, num, offset, sheet_id)?;
69 self.adjust_auto_filter(&mut ws, sheet, dir, num, offset, sheet_id)?;
70 self.adjust_calc_chain(&mut ws, sheet, dir, num, offset, sheet_id)?;
71 self.adjust_table(&mut ws, sheet, dir, num, offset, sheet_id)?;
72 self.adjust_volatile_deps(&mut ws, sheet, dir, num, offset, sheet_id)?;
73
74 if let Some(merge_cells) = &ws.merge_cells {
75 if merge_cells.cells.is_empty() {
76 ws.merge_cells = None;
77 }
78 }
79
80 self.sheet.insert(path, ws);
81 Ok(())
82 }
83
84 fn adjust_cols(&self, ws: &mut XlsxWorksheet, col: i32, offset: i32) -> Result<()> {
89 let cols = match ws.cols.as_mut() {
90 Some(c) => c,
91 None => return Ok(()),
92 };
93
94 let mut i = 0;
95 while i < cols.col.len() {
96 if offset > 0 {
97 if cols.col[i].min >= col as i64 {
98 let new_min = cols.col[i].min + offset as i64;
99 if new_min > MAX_COLUMNS as i64 {
100 cols.col.remove(i);
101 continue;
102 }
103 cols.col[i].min = new_min;
104 }
105 if cols.col[i].max >= col as i64 || cols.col[i].max + 1 == col as i64 {
106 let new_max = cols.col[i].max + offset as i64;
107 cols.col[i].max = new_max.min(MAX_COLUMNS as i64);
108 }
109 i += 1;
110 continue;
111 }
112
113 if cols.col[i].min == col as i64 && cols.col[i].max == col as i64 {
114 cols.col.remove(i);
115 continue;
116 }
117 if cols.col[i].min > col as i64 {
118 cols.col[i].min += offset as i64;
119 }
120 if cols.col[i].max >= col as i64 {
121 cols.col[i].max += offset as i64;
122 }
123 i += 1;
124 }
125
126 if cols.col.is_empty() {
127 ws.cols = None;
128 }
129 Ok(())
130 }
131
132 fn adjust_col_dimensions(
133 &self,
134 sheet: &str,
135 ws: &mut XlsxWorksheet,
136 col: i32,
137 offset: i32,
138 ) -> Result<()> {
139 for row in &ws.sheet_data.row {
140 for cell in &row.c {
141 if let Some(r) = &cell.r {
142 if let Ok((cell_col, _)) = cell_name_to_coordinates(r) {
143 if col <= cell_col {
144 let new_col = cell_col + offset;
145 if new_col > 0 && new_col > MAX_COLUMNS {
146 return Err(Box::new(ErrColumnNumber));
147 }
148 }
149 }
150 }
151 }
152 }
153
154 for sheet_n in self.get_sheet_list() {
155 let mut worksheet = match self.work_sheet_reader(&sheet_n) {
156 Ok(w) => w,
157 Err(e) => {
158 if e.to_string() == new_not_worksheet_error(&sheet_n) {
159 continue;
160 }
161 return Err(e);
162 }
163 };
164 let path = match self.get_sheet_xml_path(&sheet_n) {
165 Some(p) => p,
166 None => continue,
167 };
168
169 for row_idx in 0..worksheet.sheet_data.row.len() {
170 for col_idx in 0..worksheet.sheet_data.row[row_idx].c.len() {
171 let cell = &worksheet.sheet_data.row[row_idx].c[col_idx];
172 if let Some(r) = &cell.r {
173 if let Ok((cell_col, cell_row)) = cell_name_to_coordinates(r) {
174 if sheet_n.eq_ignore_ascii_case(sheet) && col <= cell_col {
175 let new_col = cell_col + offset;
176 if new_col > 0 {
177 worksheet.sheet_data.row[row_idx].c[col_idx].r =
178 Some(coordinates_to_cell_name(new_col, cell_row, false)?);
179 }
180 }
181 }
182 }
183 let cell_ref = &mut worksheet.sheet_data.row[row_idx].c[col_idx];
184 self.adjust_formula(
185 sheet,
186 &sheet_n,
187 cell_ref,
188 AdjustDirection::Columns,
189 col,
190 offset,
191 false,
192 )?;
193 }
194 }
195 self.sheet.insert(path, worksheet);
196 }
197
198 let path = self.get_sheet_xml_path(sheet).unwrap_or_default();
201 if let Some(updated) = self.sheet.get(&path) {
202 *ws = updated.clone();
203 }
204
205 self.adjust_cols(ws, col, offset)
206 }
207
208 fn adjust_row_dimensions(
209 &self,
210 sheet: &str,
211 ws: &mut XlsxWorksheet,
212 row: i32,
213 offset: i32,
214 ) -> Result<()> {
215 for sheet_n in self.get_sheet_list() {
216 if sheet_n.eq_ignore_ascii_case(sheet) {
217 continue;
218 }
219 let mut worksheet = match self.work_sheet_reader(&sheet_n) {
220 Ok(w) => w,
221 Err(e) => {
222 if e.to_string() == new_not_worksheet_error(&sheet_n) {
223 continue;
224 }
225 return Err(e);
226 }
227 };
228 let path = self.get_sheet_xml_path(&sheet_n).unwrap_or_default();
229 for i in 0..worksheet.sheet_data.row.len() {
230 let r = &mut worksheet.sheet_data.row[i];
231 self.adjust_single_row_formulas(sheet, &sheet_n, r, row, offset, false)?;
232 }
233 self.sheet.insert(path, worksheet);
234 }
235
236 let total_rows = ws.sheet_data.row.len();
237 if total_rows == 0 {
238 return Ok(());
239 }
240 if let Some(last_r) = ws.sheet_data.row[total_rows - 1].r {
241 let new_row = last_r + offset as i64;
242 if last_r >= row as i64 && new_row > 0 && new_row > TOTAL_ROWS as i64 {
243 return Err(Box::new(ErrMaxRows));
244 }
245 }
246
247 for i in 0..ws.sheet_data.row.len() {
248 if let Some(row_num) = ws.sheet_data.row[i].r {
249 let new_row = row_num + offset as i64;
250 if row_num >= row as i64 && new_row > 0 {
251 adjust_single_row_dimensions(&mut ws.sheet_data.row[i], offset);
252 }
253 }
254 let r = &mut ws.sheet_data.row[i];
255 self.adjust_single_row_formulas(sheet, sheet, r, row, offset, false)?;
256 }
257 Ok(())
258 }
259
260 pub(crate) fn adjust_single_row_formulas(
261 &self,
262 sheet: &str,
263 sheet_n: &str,
264 r: &mut XlsxRow,
265 num: i32,
266 offset: i32,
267 si: bool,
268 ) -> Result<()> {
269 for i in 0..r.c.len() {
270 self.adjust_formula(
271 sheet,
272 sheet_n,
273 &mut r.c[i],
274 AdjustDirection::Rows,
275 num,
276 offset,
277 si,
278 )?;
279 }
280 Ok(())
281 }
282
283 fn adjust_cell_ref(
288 &self,
289 cell_ref: &str,
290 dir: AdjustDirection,
291 num: i32,
292 offset: i32,
293 ) -> Result<String> {
294 let mut sq_ref = Vec::new();
295 for ref_ in cell_ref.split(' ').filter(|s| !s.is_empty()) {
296 let mut expanded = ref_.to_string();
297 if !expanded.contains(':') {
298 expanded.push(':');
299 expanded.push_str(ref_);
300 }
301 let mut coordinates = range_ref_to_coordinates(&expanded)?;
302 if dir == AdjustDirection::Columns {
303 if offset < 0 && coordinates[0] == coordinates[2] && num == coordinates[0] {
304 continue;
305 }
306 apply_offset(&mut coordinates, 0, 2, MAX_COLUMNS, num, offset);
307 } else {
308 if offset < 0 && coordinates[1] == coordinates[3] && num == coordinates[1] {
309 continue;
310 }
311 apply_offset(&mut coordinates, 1, 3, TOTAL_ROWS, num, offset);
312 }
313 sq_ref.push(coordinates_to_range_ref(&coordinates, false)?);
314 }
315 Ok(sq_ref.join(" "))
316 }
317
318 fn adjust_formula(
319 &self,
320 sheet: &str,
321 sheet_n: &str,
322 cell: &mut XlsxC,
323 dir: AdjustDirection,
324 num: i32,
325 offset: i32,
326 si: bool,
327 ) -> Result<()> {
328 if let Some(f) = &mut cell.f {
329 if !f.content.is_empty() {
330 f.content =
331 self.adjust_formula_ref(sheet, sheet_n, &f.content, false, dir, num, offset)?;
332 }
333 if f.r#ref.is_some() && sheet == sheet_n {
334 if let Some(ref_) = &mut f.r#ref {
335 *ref_ = self.adjust_cell_ref(ref_, dir, num, offset)?;
336 }
337 if si {
338 if let Some(si_val) = f.si.as_mut() {
339 *si_val += 1;
340 }
341 }
342 }
343 }
344 Ok(())
345 }
346
347 fn adjust_formula_ref(
356 &self,
357 sheet: &str,
358 sheet_n: &str,
359 formula: &str,
360 keep_relative: bool,
361 dir: AdjustDirection,
362 num: i32,
363 offset: i32,
364 ) -> Result<String> {
365 if formula.trim().is_empty() {
366 return Ok(formula.to_string());
367 }
368 let had_leading_eq = formula.starts_with('=');
369 let expr = match crate::calc::parse_formula(formula) {
370 Ok(e) => e,
371 Err(_) => return Ok(formula.to_string()),
372 };
373 let mut expr = expr;
374 adjust_expr(&mut expr, dir, num, offset, keep_relative, sheet, sheet_n);
375 let mut result = format_expr(&expr)?;
376 if had_leading_eq {
377 result.insert(0, '=');
378 }
379 Ok(result)
380 }
381
382 fn adjust_hyperlinks(
387 &self,
388 ws: &mut XlsxWorksheet,
389 sheet: &str,
390 dir: AdjustDirection,
391 num: i32,
392 offset: i32,
393 ) {
394 if ws.hyperlinks.is_none() || ws.hyperlinks.as_ref().unwrap().hyperlink.is_empty() {
395 return;
396 }
397
398 if offset < 0 {
399 let mut i = ws.hyperlinks.as_ref().unwrap().hyperlink.len();
400 while i > 0 {
401 i -= 1;
402 let link_data = ws.hyperlinks.as_ref().unwrap().hyperlink[i].clone();
403 if let Ok((col_num, row_num)) = cell_name_to_coordinates(&link_data.r#ref) {
404 if (dir == AdjustDirection::Rows && num == row_num)
405 || (dir == AdjustDirection::Columns && num == col_num)
406 {
407 if let Some(rid) = &link_data.rid {
408 self.delete_sheet_relationships(sheet, rid);
409 }
410 if ws.hyperlinks.as_ref().unwrap().hyperlink.len() > 1 {
411 ws.hyperlinks.as_mut().unwrap().hyperlink.remove(i);
412 } else {
413 ws.hyperlinks = None;
414 }
415 }
416 }
417 }
418 }
419
420 if ws.hyperlinks.is_none() {
421 return;
422 }
423 for link in ws.hyperlinks.as_mut().unwrap().hyperlink.iter_mut() {
424 if let Ok(new_ref) =
425 self.adjust_formula_ref(sheet, sheet, &link.r#ref, false, dir, num, offset)
426 {
427 link.r#ref = new_ref;
428 }
429 }
430 }
431
432 fn adjust_table(
437 &self,
438 ws: &mut XlsxWorksheet,
439 sheet: &str,
440 dir: AdjustDirection,
441 num: i32,
442 offset: i32,
443 _sheet_id: i32,
444 ) -> Result<()> {
445 if ws.table_parts.is_none() || ws.table_parts.as_ref().unwrap().table_part.is_empty() {
446 return Ok(());
447 }
448
449 let mut idx = 0;
450 while idx < ws.table_parts.as_ref().unwrap().table_part.len() {
451 let tbl = ws.table_parts.as_ref().unwrap().table_part[idx].clone();
452 let target =
453 self.get_sheet_relationships_target_by_id(sheet, tbl.rid.as_deref().unwrap_or(""));
454 if target.is_empty() {
455 idx += 1;
456 continue;
457 }
458 let table_xml = target.replace("..", "xl");
459 let content = self.read_xml(&table_xml);
460 if content.is_empty() {
461 idx += 1;
462 continue;
463 }
464 let mut t: XlsxTable =
465 xml_from_reader(namespace_strict_to_transitional(&content).as_slice())?;
466 let mut coordinates = range_ref_to_coordinates(&t.r#ref)?;
467
468 if dir == AdjustDirection::Rows && num == coordinates[1] && offset == -1 {
469 ws.table_parts.as_mut().unwrap().table_part.remove(idx);
470 continue;
471 }
472
473 coordinates = self.adjust_auto_filter_helper(dir, coordinates, num, offset);
474 let (x1, y1, x2, y2) = (
475 coordinates[0],
476 coordinates[1],
477 coordinates[2],
478 coordinates[3],
479 );
480 if y2 - y1 < 1 || x2 - x1 < 0 {
481 ws.table_parts.as_mut().unwrap().table_part.remove(idx);
482 continue;
483 }
484
485 t.r#ref = coordinates_to_range_ref(&coordinates, false)?;
486 if let Some(af) = &mut t.auto_filter {
487 af.r#ref = t.r#ref.clone();
488 }
489 self.set_table_columns(sheet, true, x1, y1, x2, &mut t)?;
490 t.table_type = None;
491 t.totals_row_count = 0;
492 t.connection_id = 0;
493
494 let table = xml_to_string(&t)?.into_bytes();
495 self.save_file_list(&table_xml, &table);
496 idx += 1;
497 }
498
499 if let Some(parts) = ws.table_parts.as_mut() {
500 parts.count = Some(parts.table_part.len() as i64);
501 if parts.table_part.is_empty() {
502 ws.table_parts = None;
503 }
504 }
505 Ok(())
506 }
507
508 fn adjust_auto_filter(
513 &self,
514 ws: &mut XlsxWorksheet,
515 _sheet: &str,
516 dir: AdjustDirection,
517 num: i32,
518 offset: i32,
519 _sheet_id: i32,
520 ) -> Result<()> {
521 if ws.auto_filter.is_none() {
522 return Ok(());
523 }
524 let af = ws.auto_filter.as_mut().unwrap();
525 let mut coordinates = range_ref_to_coordinates(&af.r#ref)?;
526 let (x1, y1, x2, y2) = (
527 coordinates[0],
528 coordinates[1],
529 coordinates[2],
530 coordinates[3],
531 );
532
533 if (dir == AdjustDirection::Rows && y1 == num && offset < 0)
534 || (dir == AdjustDirection::Columns && x1 == num && x2 == num)
535 {
536 ws.auto_filter = None;
537 for row in &mut ws.sheet_data.row {
538 if let Some(r) = row.r {
539 if r > y1 as i64 && r <= y2 as i64 {
540 row.hidden = Some(false);
541 }
542 }
543 }
544 return Ok(());
545 }
546
547 coordinates = self.adjust_auto_filter_helper(dir, coordinates, num, offset);
548 af.r#ref = coordinates_to_range_ref(&coordinates, false)?;
549 Ok(())
550 }
551
552 fn adjust_auto_filter_helper(
553 &self,
554 dir: AdjustDirection,
555 mut coordinates: Vec<i32>,
556 num: i32,
557 offset: i32,
558 ) -> Vec<i32> {
559 if dir == AdjustDirection::Rows {
560 if coordinates[1] >= num {
561 coordinates[1] += offset;
562 }
563 if coordinates[3] >= num {
564 coordinates[3] += offset;
565 }
566 } else {
567 if coordinates[0] >= num {
568 coordinates[0] += offset;
569 }
570 if coordinates[2] >= num {
571 coordinates[2] += offset;
572 }
573 }
574 coordinates
575 }
576
577 fn adjust_merge_cells(
582 &self,
583 ws: &mut XlsxWorksheet,
584 _sheet: &str,
585 dir: AdjustDirection,
586 num: i32,
587 offset: i32,
588 _sheet_id: i32,
589 ) -> Result<()> {
590 if ws.merge_cells.is_none() {
591 return Ok(());
592 }
593
594 let mut i = 0;
595 while i < ws.merge_cells.as_ref().unwrap().cells.len() {
596 let merged_cells_ref = ws.merge_cells.as_ref().unwrap().cells[i]
597 .r#ref
598 .clone()
599 .unwrap_or_default();
600 let mut merged_ref = merged_cells_ref.clone();
601 if !merged_ref.contains(':') {
602 merged_ref.push(':');
603 merged_ref.push_str(&merged_cells_ref);
604 }
605 let mut coordinates = range_ref_to_coordinates(&merged_ref)?;
606 let (mut x1, mut y1, mut x2, mut y2) = (
607 coordinates[0],
608 coordinates[1],
609 coordinates[2],
610 coordinates[3],
611 );
612
613 if dir == AdjustDirection::Rows {
614 if y1 == num && y2 == num && offset < 0 {
615 delete_merge_cell(ws, i);
616 continue;
617 }
618 (y1, y2) = adjust_merge_cells_helper(y1, y2, num, offset);
619 } else {
620 if x1 == num && x2 == num && offset < 0 {
621 delete_merge_cell(ws, i);
622 continue;
623 }
624 (x1, x2) = adjust_merge_cells_helper(x1, x2, num, offset);
625 }
626
627 if x1 == x2 && y1 == y2 {
628 delete_merge_cell(ws, i);
629 continue;
630 }
631
632 coordinates = vec![x1, y1, x2, y2];
633 if let Some(cell) = ws.merge_cells.as_mut().unwrap().cells.get_mut(i) {
634 cell.r#ref = Some(coordinates_to_range_ref(&coordinates, false)?);
635 }
636 i += 1;
637 }
638 Ok(())
639 }
640
641 fn adjust_calc_chain(
646 &self,
647 _ws: &mut XlsxWorksheet,
648 _sheet: &str,
649 dir: AdjustDirection,
650 num: i32,
651 offset: i32,
652 sheet_id: i32,
653 ) -> Result<()> {
654 let mut guard = self.calc_chain.lock().unwrap();
655 let cc = match guard.as_mut() {
656 Some(c) => c,
657 None => return Ok(()),
658 };
659
660 let mut prev_sheet_id = 0;
661 let mut i = 0;
662 while i < cc.c.len() {
663 let mut ci = cc.c[i].clone();
664 if ci.i == 0 {
665 ci.i = prev_sheet_id;
666 }
667 prev_sheet_id = ci.i;
668 if ci.i != sheet_id {
669 i += 1;
670 continue;
671 }
672
673 let (col_num, row_num) = cell_name_to_coordinates(&ci.r)?;
674 let mut updated = false;
675 if dir == AdjustDirection::Rows && num <= row_num {
676 if num == row_num && offset == -1 {
677 cc.c.remove(i);
678 continue;
679 }
680 cc.c[i].r = adjust_cell_name(&ci.r, dir, col_num, row_num, offset)?;
681 updated = true;
682 }
683 if !updated && dir == AdjustDirection::Columns && num <= col_num {
684 if num == col_num && offset == -1 {
685 cc.c.remove(i);
686 continue;
687 }
688 cc.c[i].r = adjust_cell_name(&ci.r, dir, col_num, row_num, offset)?;
689 }
690 i += 1;
691 }
692 Ok(())
693 }
694
695 fn adjust_conditional_formats(
700 &self,
701 ws: &mut XlsxWorksheet,
702 _sheet: &str,
703 dir: AdjustDirection,
704 num: i32,
705 offset: i32,
706 _sheet_id: i32,
707 ) -> Result<()> {
708 let mut i = 0;
709 while i < ws.conditional_formatting.len() {
710 let sqref = match &ws.conditional_formatting[i].sqref {
711 Some(s) => s.clone(),
712 None => {
713 i += 1;
714 continue;
715 }
716 };
717 let ref_ = self.adjust_cell_ref(&sqref, dir, num, offset)?;
718 if ref_.is_empty() {
719 ws.conditional_formatting.remove(i);
720 continue;
721 }
722 ws.conditional_formatting[i].sqref = Some(ref_);
723 i += 1;
724 }
725 Ok(())
726 }
727
728 fn adjust_data_validations(
733 &self,
734 _ws: &mut XlsxWorksheet,
735 sheet: &str,
736 dir: AdjustDirection,
737 num: i32,
738 offset: i32,
739 _sheet_id: i32,
740 ) -> Result<()> {
741 for sheet_n in self.get_sheet_list() {
742 let mut worksheet = match self.work_sheet_reader(&sheet_n) {
743 Ok(w) => w,
744 Err(e) => {
745 if e.to_string() == new_not_worksheet_error(&sheet_n) {
746 continue;
747 }
748 return Err(e);
749 }
750 };
751 let path = match self.get_sheet_xml_path(&sheet_n) {
752 Some(p) => p,
753 None => continue,
754 };
755
756 if worksheet.data_validations.is_none() {
757 continue;
758 }
759
760 let dvs = worksheet.data_validations.as_mut().unwrap();
761 let mut i = 0;
762 while i < dvs.data_validation.len() {
763 if sheet.eq_ignore_ascii_case(&sheet_n) {
764 let ref_ =
765 self.adjust_cell_ref(&dvs.data_validation[i].sqref, dir, num, offset)?;
766 if ref_.is_empty() {
767 dvs.data_validation.remove(i);
768 continue;
769 }
770 dvs.data_validation[i].sqref = ref_;
771 }
772
773 if let Some(f1) = dvs.data_validation[i].formula1.as_mut() {
774 if inner_xml_is_formula(&f1.content) {
775 let formula = formula_unescaper_replace(&f1.content);
776 let adjusted = self.adjust_formula_ref(
777 sheet, &sheet_n, &formula, false, dir, num, offset,
778 )?;
779 f1.content = formula_escaper_replace(&adjusted);
780 }
781 }
782 if let Some(f2) = dvs.data_validation[i].formula2.as_mut() {
783 if inner_xml_is_formula(&f2.content) {
784 let formula = formula_unescaper_replace(&f2.content);
785 let adjusted = self.adjust_formula_ref(
786 sheet, &sheet_n, &formula, false, dir, num, offset,
787 )?;
788 f2.content = formula_escaper_replace(&adjusted);
789 }
790 }
791 i += 1;
792 }
793 dvs.count = Some(dvs.data_validation.len() as i64);
794 if dvs.data_validation.is_empty() {
795 worksheet.data_validations = None;
796 }
797 self.sheet.insert(path, worksheet);
798 }
799 Ok(())
800 }
801
802 fn adjust_drawings(
807 &self,
808 ws: &mut XlsxWorksheet,
809 sheet: &str,
810 dir: AdjustDirection,
811 num: i32,
812 offset: i32,
813 ) -> Result<()> {
814 let drawing_rid = match ws.drawing.as_ref().and_then(|d| d.rid.clone()) {
815 Some(rid) => rid,
816 None => return Ok(()),
817 };
818 let target = self.get_sheet_relationships_target_by_id(sheet, &drawing_rid);
819 if target.is_empty() {
820 return Ok(());
821 }
822 let drawing_xml = target
823 .replace("..", "xl")
824 .trim_start_matches('/')
825 .to_string();
826 let (mut ws_dr, _) = self.drawing_parser(&drawing_xml)?;
827
828 for anchor in ws_dr.two_cell_anchor.iter_mut() {
829 adjust_cell_anchor(anchor, dir, num, offset)?;
830 }
831 for anchor in ws_dr.one_cell_anchor.iter_mut() {
832 adjust_cell_anchor(anchor, dir, num, offset)?;
833 }
834
835 self.drawings.insert(drawing_xml, ws_dr);
836 Ok(())
837 }
838
839 fn adjust_defined_names(
844 &self,
845 sheet: &str,
846 dir: AdjustDirection,
847 num: i32,
848 offset: i32,
849 ) -> Result<()> {
850 let mut wb = self.workbook_reader()?;
851 if let Some(dns) = wb.defined_names.as_mut() {
852 for i in 0..dns.defined_name.len() {
853 let data = dns.defined_name[i].data.clone();
854 if let Ok(adjusted) =
855 self.adjust_formula_ref(sheet, "", &data, true, dir, num, offset)
856 {
857 dns.defined_name[i].data = adjusted;
858 }
859 }
860 }
861 *self.workbook.lock().unwrap() = Some(wb);
862 Ok(())
863 }
864
865 fn adjust_volatile_deps(
870 &self,
871 _ws: &mut XlsxWorksheet,
872 _sheet: &str,
873 dir: AdjustDirection,
874 num: i32,
875 offset: i32,
876 sheet_id: i32,
877 ) -> Result<()> {
878 let mut vol_types = match self.volatile_deps_reader()? {
879 Some(vt) => vt,
880 None => return Ok(()),
881 };
882
883 let mut i1 = 0;
884 while i1 < vol_types.vol_type.len() {
885 let mut i2 = 0;
886 while i2 < vol_types.vol_type[i1].main.len() {
887 let mut i3 = 0;
888 while i3 < vol_types.vol_type[i1].main[i2].tp.len() {
889 let mut i4 = 0;
890 while i4 < vol_types.vol_type[i1].main[i2].tp[i3].tr.len() {
891 let tr = &vol_types.vol_type[i1].main[i2].tp[i3].tr[i4];
892 if tr.s != sheet_id {
893 i4 += 1;
894 continue;
895 }
896 let (col, row) = cell_name_to_coordinates(&tr.r)?;
897 let should_delete = match dir {
898 AdjustDirection::Rows => num <= row && num == row && offset == -1,
899 AdjustDirection::Columns => num <= col && num == col && offset == -1,
900 };
901 if should_delete {
902 crate::calc_chain::delete_vol_topic_ref(&mut vol_types, i1, i2, i3, i4);
903 continue;
904 }
905 let adjusted = match dir {
906 AdjustDirection::Rows if num <= row => {
907 adjust_cell_name(&tr.r, dir, col, row, offset)?
908 }
909 AdjustDirection::Columns if num <= col => {
910 adjust_cell_name(&tr.r, dir, col, row, offset)?
911 }
912 _ => tr.r.clone(),
913 };
914 vol_types.vol_type[i1].main[i2].tp[i3].tr[i4].r = adjusted;
915 i4 += 1;
916 }
917 i3 += 1;
918 }
919 i2 += 1;
920 }
921 i1 += 1;
922 }
923
924 *self.volatile_deps.lock().unwrap() = Some(vol_types);
925 Ok(())
926 }
927
928 pub(crate) fn check_sheet(&self, ws: &mut XlsxWorksheet) -> Result<()> {
938 for (row_idx, row) in ws.sheet_data.row.iter_mut().enumerate() {
939 if row.r.is_none() {
940 row.r = Some(row_idx as i64 + 1);
941 }
942 }
943 self.check_row(ws)
944 }
945
946 pub(crate) fn check_row(&self, ws: &mut XlsxWorksheet) -> Result<()> {
951 for (row_idx, row) in ws.sheet_data.row.iter_mut().enumerate() {
952 let row_num = row.r.unwrap_or((row_idx as i64) + 1);
953 let col_count = row.c.len();
954 if col_count == 0 {
955 continue;
956 }
957
958 let mut r_count = 0;
960 for cell in &mut row.c {
961 r_count += 1;
962 if let Some(ref name) = cell.r {
963 if let Ok((col, _)) = cell_name_to_coordinates(name) {
964 if col > r_count {
965 r_count = col;
966 }
967 }
968 continue;
969 }
970 cell.r = coordinates_to_cell_name(r_count, row_num as i32, false).ok();
971 }
972
973 let last_col = if let Some(ref name) = row.c.last().and_then(|c| c.r.as_ref()) {
974 cell_name_to_coordinates(name)?.0
975 } else {
976 continue;
977 };
978
979 if col_count < last_col as usize {
980 let mut target = Vec::with_capacity(last_col as usize);
981 for col_idx in 0..last_col {
982 let cell_name = coordinates_to_cell_name(col_idx + 1, row_num as i32, false)?;
983 target.push(XlsxC {
984 r: Some(cell_name),
985 ..Default::default()
986 });
987 }
988 for cell in row.c.drain(..) {
989 if let Some(ref name) = cell.r {
990 if let Ok((col, _)) = cell_name_to_coordinates(name) {
991 target[(col - 1) as usize] = cell;
992 }
993 }
994 }
995 row.c = target;
996 }
997 }
998 Ok(())
999 }
1000}
1001
1002fn adjust_single_row_dimensions(row: &mut XlsxRow, offset: i32) {
1007 if let Some(r) = row.r.as_mut() {
1008 *r += offset as i64;
1009 }
1010 let new_row = row.r.unwrap_or(0);
1011 for cell in &mut row.c {
1012 if let Some(name) = &cell.r {
1013 if let Ok((col, _)) = split_cell_name(name) {
1014 if let Ok(new_name) = join_cell_name(&col, new_row as i32) {
1015 cell.r = Some(new_name);
1016 }
1017 }
1018 }
1019 }
1020}
1021
1022fn adjust_expr(
1023 expr: &mut crate::calc::Expr,
1024 dir: AdjustDirection,
1025 num: i32,
1026 offset: i32,
1027 keep_relative: bool,
1028 sheet: &str,
1029 active_sheet: &str,
1030) {
1031 use crate::calc::Expr;
1032 match expr {
1033 Expr::Cell(r) => {
1034 if should_adjust_ref(r, sheet, active_sheet) {
1035 adjust_cell_reference(r, dir, num, offset, keep_relative);
1036 }
1037 }
1038 Expr::Range(start, end) => {
1039 if should_adjust_ref(start, sheet, active_sheet) {
1040 adjust_cell_reference(start, dir, num, offset, keep_relative);
1041 }
1042 if should_adjust_ref(end, sheet, active_sheet) {
1043 adjust_cell_reference(end, dir, num, offset, keep_relative);
1044 }
1045 }
1046 Expr::Call(_, args) => {
1047 for arg in args {
1048 adjust_expr(arg, dir, num, offset, keep_relative, sheet, active_sheet);
1049 }
1050 }
1051 Expr::Unary(_, e) => {
1052 adjust_expr(e, dir, num, offset, keep_relative, sheet, active_sheet);
1053 }
1054 Expr::Binary(_, l, r) => {
1055 adjust_expr(l, dir, num, offset, keep_relative, sheet, active_sheet);
1056 adjust_expr(r, dir, num, offset, keep_relative, sheet, active_sheet);
1057 }
1058 _ => {}
1059 }
1060}
1061
1062fn should_adjust_ref(r: &crate::calc::CellRef, sheet: &str, active_sheet: &str) -> bool {
1063 match &r.sheet {
1064 Some(s) => s.eq_ignore_ascii_case(sheet),
1065 None => active_sheet.eq_ignore_ascii_case(sheet),
1066 }
1067}
1068
1069fn adjust_cell_reference(
1070 r: &mut crate::calc::CellRef,
1071 dir: AdjustDirection,
1072 num: i32,
1073 offset: i32,
1074 keep_relative: bool,
1075) {
1076 match dir {
1077 AdjustDirection::Columns => {
1078 if (!keep_relative || r.col_abs) && r.col >= num {
1079 r.col += offset;
1080 if r.col < 1 {
1081 r.col = 1;
1082 }
1083 if r.col > MAX_COLUMNS {
1084 r.col = MAX_COLUMNS;
1085 }
1086 }
1087 }
1088 AdjustDirection::Rows => {
1089 if (!keep_relative || r.row_abs) && r.row >= num {
1090 r.row += offset;
1091 if r.row < 1 {
1092 r.row = 1;
1093 }
1094 if r.row > TOTAL_ROWS {
1095 r.row = TOTAL_ROWS;
1096 }
1097 }
1098 }
1099 }
1100}
1101
1102fn format_expr(expr: &crate::calc::Expr) -> Result<String> {
1103 use crate::calc::Expr;
1104 Ok(match expr {
1105 Expr::Number(n) => {
1106 if n.fract() == 0.0 {
1107 format!("{}", *n as i64)
1108 } else {
1109 format!("{}", n)
1110 }
1111 }
1112 Expr::String(s) => format!("\"{}\"", s.replace('"', "\"\"")),
1113 Expr::Bool(true) => "TRUE".to_string(),
1114 Expr::Bool(false) => "FALSE".to_string(),
1115 Expr::Cell(r) => format_cell_ref(r),
1116 Expr::Range(start, end) => {
1117 if start.sheet.is_some() && start.sheet == end.sheet {
1118 let sheet = start.sheet.as_deref().unwrap();
1119 format!(
1120 "{}!{}:{}",
1121 escape_sheet_name(sheet),
1122 format_cell_ref_without_sheet(start),
1123 format_cell_ref_without_sheet(end)
1124 )
1125 } else {
1126 format!("{}:{}", format_cell_ref(start), format_cell_ref(end))
1127 }
1128 }
1129 Expr::Call(name, args) => {
1130 let args_str = args
1131 .iter()
1132 .map(format_expr)
1133 .collect::<Result<Vec<_>>>()?
1134 .join(",");
1135 format!("{}({})", name, args_str)
1136 }
1137 Expr::Unary(op, e) => format!("{}{}", op, format_expr(e)?),
1138 Expr::Binary(op, l, r) => {
1139 format!("{}{}{}", format_expr(l)?, op, format_expr(r)?)
1140 }
1141 Expr::Range3D(s1, s2, start, end) => {
1142 format!(
1143 "{}:{}!{}:{}",
1144 escape_sheet_name(s1),
1145 escape_sheet_name(s2),
1146 format_cell_ref_without_sheet(start),
1147 format_cell_ref_without_sheet(end)
1148 )
1149 }
1150 Expr::Name(name) => name.clone(),
1151 Expr::Array(rows) => {
1152 let rows_str = rows
1153 .iter()
1154 .map(|row| {
1155 row.iter()
1156 .map(format_expr)
1157 .collect::<Result<Vec<_>>>()
1158 .map(|v| v.join(","))
1159 })
1160 .collect::<Result<Vec<_>>>()?
1161 .join(";");
1162 format!("{{{}}}", rows_str)
1163 }
1164 })
1165}
1166
1167fn format_cell_ref(r: &crate::calc::CellRef) -> String {
1168 let sheet_prefix = r
1169 .sheet
1170 .as_deref()
1171 .map(|s| format!("{}!", escape_sheet_name(s)))
1172 .unwrap_or_default();
1173 format!("{}{}", sheet_prefix, format_cell_ref_without_sheet(r))
1174}
1175
1176fn format_cell_ref_without_sheet(r: &crate::calc::CellRef) -> String {
1177 let col_name = column_number_to_name(r.col).unwrap_or_default();
1178 let col_part = if r.col_abs {
1179 format!("${}", col_name)
1180 } else {
1181 col_name
1182 };
1183 let row_part = if r.row_abs {
1184 format!("${}", r.row)
1185 } else {
1186 r.row.to_string()
1187 };
1188 format!("{}{}", col_part, row_part)
1189}
1190
1191fn apply_offset(
1192 coordinates: &mut [i32],
1193 idx1: usize,
1194 idx2: usize,
1195 max_val: i32,
1196 num: i32,
1197 offset: i32,
1198) {
1199 if coordinates[idx1] >= num {
1200 coordinates[idx1] += offset;
1201 }
1202 if coordinates[idx2] >= num {
1203 coordinates[idx2] += offset;
1204 if coordinates[idx2] > max_val {
1205 coordinates[idx2] = max_val;
1206 }
1207 }
1208}
1209
1210fn adjust_merge_cells_helper(mut p1: i32, mut p2: i32, num: i32, offset: i32) -> (i32, i32) {
1211 if p2 < p1 {
1212 std::mem::swap(&mut p1, &mut p2);
1213 }
1214 if offset >= 0 {
1215 if num <= p1 {
1216 p1 += offset;
1217 p2 += offset;
1218 } else if num <= p2 {
1219 p2 += offset;
1220 }
1221 } else {
1222 if num < p1 || (num == p1 && num == p2) {
1223 p1 += offset;
1224 p2 += offset;
1225 } else if num <= p2 {
1226 p2 += offset;
1227 }
1228 }
1229 (p1, p2)
1230}
1231
1232fn delete_merge_cell(ws: &mut XlsxWorksheet, idx: usize) {
1233 if ws.merge_cells.is_none() {
1234 return;
1235 }
1236 let merges = ws.merge_cells.as_mut().unwrap();
1237 if idx >= merges.cells.len() {
1238 return;
1239 }
1240 merges.cells.remove(idx);
1241 merges.count = Some(merges.cells.len() as i64);
1242}
1243
1244fn adjust_cell_name(
1245 _cell: &str,
1246 dir: AdjustDirection,
1247 c: i32,
1248 r: i32,
1249 offset: i32,
1250) -> Result<String> {
1251 if dir == AdjustDirection::Rows {
1252 let rn = r + offset;
1253 if rn > 0 {
1254 return coordinates_to_cell_name(c, rn, false).map_err(|e| e.into());
1255 }
1256 }
1257 coordinates_to_cell_name(c + offset, r, false).map_err(|e| e.into())
1258}
1259
1260fn inner_xml_is_formula(content: &str) -> bool {
1261 let trimmed = content.trim_start();
1262 !trimmed.is_empty() && (trimmed.starts_with('=') || trimmed.contains("<formula"))
1263}
1264
1265fn formula_unescaper_replace(s: &str) -> String {
1266 s.to_string()
1267}
1268
1269fn formula_escaper_replace(s: &str) -> String {
1270 s.to_string()
1271}
1272
1273fn escape_sheet_name(name: &str) -> String {
1274 if name.chars().any(|r| !r.is_alphanumeric()) {
1275 format!("'{}'", name.replace('\'', "''"))
1276 } else {
1277 name.to_string()
1278 }
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283 use super::*;
1284 use crate::options::Options;
1285
1286 #[test]
1287 fn adjust_helper_shifts_on_row_insert() {
1288 let f = File::new_with_options(Options::default());
1289 f.merge_cell("Sheet1", "A1", "B2").unwrap();
1290 f.set_cell_value("Sheet1", "C5", "value").unwrap();
1291 f.adjust_helper("Sheet1", AdjustDirection::Rows, 2, 1)
1292 .unwrap();
1293
1294 let merges = f.get_merge_cells("Sheet1").unwrap();
1295 assert!(merges.contains(&"A1:B3".to_string()));
1296 assert!(f.get_cell_value("Sheet1", "C6").unwrap().contains("value"));
1297 }
1298
1299 fn get_cell_formula(f: &File, sheet: &str, cell: &str) -> String {
1300 let ws = f.work_sheet_reader(sheet).unwrap();
1301 for row in &ws.sheet_data.row {
1302 for c in &row.c {
1303 if c.r
1304 .as_deref()
1305 .map(|r| r.eq_ignore_ascii_case(cell))
1306 .unwrap_or(false)
1307 {
1308 return c.f.as_ref().map(|f| f.content.clone()).unwrap_or_default();
1309 }
1310 }
1311 }
1312 String::new()
1313 }
1314
1315 #[test]
1316 fn adjust_formula_ref_shifts_relative_row_refs() {
1317 let f = File::new_with_options(Options::default());
1318 assert_eq!(
1319 f.adjust_formula_ref("Sheet1", "Sheet1", "A1", false, AdjustDirection::Rows, 1, 1)
1320 .unwrap(),
1321 "A2"
1322 );
1323 }
1324
1325 #[test]
1326 fn adjust_formula_ref_keeps_refs_below_row_insert() {
1327 let f = File::new_with_options(Options::default());
1328 assert_eq!(
1329 f.adjust_formula_ref("Sheet1", "Sheet1", "A1", false, AdjustDirection::Rows, 2, 1)
1330 .unwrap(),
1331 "A1"
1332 );
1333 }
1334
1335 #[test]
1336 fn adjust_formula_ref_shifts_absolute_row_refs() {
1337 let f = File::new_with_options(Options::default());
1338 assert_eq!(
1339 f.adjust_formula_ref(
1340 "Sheet1",
1341 "Sheet1",
1342 "$A$1",
1343 false,
1344 AdjustDirection::Rows,
1345 1,
1346 1
1347 )
1348 .unwrap(),
1349 "$A$2"
1350 );
1351 }
1352
1353 #[test]
1354 fn adjust_formula_ref_shifts_relative_col_refs() {
1355 let f = File::new_with_options(Options::default());
1356 assert_eq!(
1357 f.adjust_formula_ref(
1358 "Sheet1",
1359 "Sheet1",
1360 "A1",
1361 false,
1362 AdjustDirection::Columns,
1363 1,
1364 1
1365 )
1366 .unwrap(),
1367 "B1"
1368 );
1369 }
1370
1371 #[test]
1372 fn adjust_formula_ref_keeps_other_sheet_refs() {
1373 let f = File::new_with_options(Options::default());
1374 assert_eq!(
1375 f.adjust_formula_ref(
1376 "Sheet1",
1377 "Sheet1",
1378 "Sheet2!A1",
1379 false,
1380 AdjustDirection::Rows,
1381 1,
1382 1
1383 )
1384 .unwrap(),
1385 "Sheet2!A1"
1386 );
1387 }
1388
1389 #[test]
1390 fn adjust_formula_ref_adjusts_same_sheet_qualified_refs() {
1391 let f = File::new_with_options(Options::default());
1392 assert_eq!(
1393 f.adjust_formula_ref(
1394 "Sheet1",
1395 "Sheet1",
1396 "Sheet1!A1",
1397 false,
1398 AdjustDirection::Rows,
1399 1,
1400 1
1401 )
1402 .unwrap(),
1403 "Sheet1!A2"
1404 );
1405 }
1406
1407 #[test]
1408 fn adjust_formula_ref_adjusts_ranges() {
1409 let f = File::new_with_options(Options::default());
1410 assert_eq!(
1411 f.adjust_formula_ref(
1412 "Sheet1",
1413 "Sheet1",
1414 "SUM(A1:B3)",
1415 false,
1416 AdjustDirection::Rows,
1417 2,
1418 1
1419 )
1420 .unwrap(),
1421 "SUM(A1:B4)"
1422 );
1423 }
1424
1425 #[test]
1426 fn adjust_formula_ref_keep_relative_only_adjusts_absolute() {
1427 let f = File::new_with_options(Options::default());
1428 assert_eq!(
1429 f.adjust_formula_ref(
1430 "Sheet1",
1431 "Sheet1",
1432 "$A$1+A1",
1433 true,
1434 AdjustDirection::Rows,
1435 1,
1436 1
1437 )
1438 .unwrap(),
1439 "$A$2+A1"
1440 );
1441 }
1442
1443 #[test]
1444 fn adjust_formula_ref_preserves_defined_names() {
1445 let f = File::new_with_options(Options::default());
1446 assert_eq!(
1447 f.adjust_formula_ref(
1448 "Sheet1",
1449 "Sheet1",
1450 "=MYRANGE+A1",
1451 false,
1452 AdjustDirection::Rows,
1453 1,
1454 1
1455 )
1456 .unwrap(),
1457 "=MYRANGE+A2"
1458 );
1459 }
1460
1461 #[test]
1462 fn adjust_helper_updates_cell_formula_on_row_insert() {
1463 let f = File::new_with_options(Options::default());
1464 f.set_cell_formula("Sheet1", "C5", "A5+B5").unwrap();
1465 f.adjust_helper("Sheet1", AdjustDirection::Rows, 3, 1)
1466 .unwrap();
1467 assert_eq!(get_cell_formula(&f, "Sheet1", "C6"), "A6+B6");
1468 }
1469
1470 #[test]
1471 fn adjust_helper_updates_cell_formula_on_col_insert() {
1472 let f = File::new_with_options(Options::default());
1473 f.set_cell_formula("Sheet1", "D1", "A1+B1").unwrap();
1474 f.adjust_helper("Sheet1", AdjustDirection::Columns, 2, 1)
1475 .unwrap();
1476 assert_eq!(get_cell_formula(&f, "Sheet1", "E1"), "A1+C1");
1477 }
1478}
1479
1480fn adjust_cell_anchor(
1481 anchor: &mut XdrCellAnchor,
1482 dir: AdjustDirection,
1483 num: i32,
1484 offset: i32,
1485) -> Result<()> {
1486 let edit_as = anchor.edit_as.as_deref().unwrap_or("");
1487 if (anchor.from.is_none() && (anchor.to.is_none() || anchor.ext.is_none()))
1488 || edit_as == "absolute"
1489 {
1490 return Ok(());
1491 }
1492 let ok = adjust_from(anchor.from.as_mut().unwrap(), dir, num, offset, edit_as)?;
1493 if let Some(to) = anchor.to.as_mut() {
1494 adjust_to(to, dir, num, offset, ok || edit_as.is_empty())?;
1495 }
1496 Ok(())
1497}
1498
1499fn adjust_from(
1500 from: &mut XlsxFrom,
1501 dir: AdjustDirection,
1502 num: i32,
1503 offset: i32,
1504 edit_as: &str,
1505) -> Result<bool> {
1506 let mut ok = false;
1507 if dir == AdjustDirection::Columns
1508 && from.col + 1 >= num as i64
1509 && from.col + offset as i64 >= 0
1510 {
1511 if from.col + offset as i64 >= MAX_COLUMNS as i64 {
1512 return Err(Box::new(ErrColumnNumber));
1513 }
1514 from.col += offset as i64;
1515 ok = edit_as == "oneCell";
1516 }
1517 if dir == AdjustDirection::Rows && from.row + 1 >= num as i64 && from.row + offset as i64 >= 0 {
1518 if from.row + offset as i64 >= TOTAL_ROWS as i64 {
1519 return Err(Box::new(ErrMaxRows));
1520 }
1521 from.row += offset as i64;
1522 ok = edit_as == "oneCell";
1523 }
1524 Ok(ok)
1525}
1526
1527fn adjust_to(to: &mut XlsxTo, dir: AdjustDirection, num: i32, offset: i32, ok: bool) -> Result<()> {
1528 if !ok {
1529 return Ok(());
1530 }
1531 if dir == AdjustDirection::Columns && to.col + 1 >= num as i64 && to.col + offset as i64 >= 0 {
1532 if to.col + offset as i64 >= MAX_COLUMNS as i64 {
1533 return Err(Box::new(ErrColumnNumber));
1534 }
1535 to.col += offset as i64;
1536 }
1537 if dir == AdjustDirection::Rows && to.row + 1 >= num as i64 && to.row + offset as i64 >= 0 {
1538 if to.row + offset as i64 >= TOTAL_ROWS as i64 {
1539 return Err(Box::new(ErrMaxRows));
1540 }
1541 to.row += offset as i64;
1542 }
1543 Ok(())
1544}