1use std::collections::BTreeMap;
8
9use sheetkit_xml::worksheet::{Col, Cols, WorksheetXml};
10
11use crate::cell::CellValue;
12use crate::error::{Error, Result};
13use crate::row::get_rows;
14use crate::sst::SharedStringTable;
15use crate::utils::cell_ref::{
16 cell_name_to_coordinates, column_name_to_number, column_number_to_name,
17 coordinates_to_cell_name,
18};
19use crate::utils::constants::{MAX_COLUMNS, MAX_COLUMN_WIDTH};
20
21#[allow(clippy::type_complexity)]
27pub fn get_cols(
28 ws: &WorksheetXml,
29 sst: &SharedStringTable,
30) -> Result<Vec<(String, Vec<(u32, CellValue)>)>> {
31 let rows = get_rows(ws, sst)?;
32
33 let mut col_map: BTreeMap<u32, Vec<(u32, CellValue)>> = BTreeMap::new();
36
37 for (row_num, cells) in rows {
38 for (col_num, value) in cells {
39 col_map.entry(col_num).or_default().push((row_num, value));
40 }
41 }
42
43 let mut result = Vec::with_capacity(col_map.len());
45 for (col_num, cells) in col_map {
46 let col_name = column_number_to_name(col_num)?;
47 result.push((col_name, cells));
48 }
49
50 Ok(result)
51}
52
53pub fn set_col_width(ws: &mut WorksheetXml, col: &str, width: f64) -> Result<()> {
58 let col_num = column_name_to_number(col)?;
59 if !(0.0..=MAX_COLUMN_WIDTH).contains(&width) {
60 return Err(Error::ColumnWidthExceeded {
61 width,
62 max: MAX_COLUMN_WIDTH,
63 });
64 }
65
66 let col_entry = find_or_create_col(ws, col_num);
67 col_entry.width = Some(width);
68 col_entry.custom_width = Some(true);
69 Ok(())
70}
71
72pub fn get_col_width(ws: &WorksheetXml, col: &str) -> Option<f64> {
75 let col_num = column_name_to_number(col).ok()?;
76 ws.cols
77 .as_ref()
78 .and_then(|cols| {
79 cols.cols
80 .iter()
81 .find(|c| col_num >= c.min && col_num <= c.max)
82 })
83 .and_then(|c| c.width)
84}
85
86pub fn set_col_visible(ws: &mut WorksheetXml, col: &str, visible: bool) -> Result<()> {
88 let col_num = column_name_to_number(col)?;
89 let col_entry = find_or_create_col(ws, col_num);
90 col_entry.hidden = if visible { None } else { Some(true) };
91 Ok(())
92}
93
94pub fn get_col_visible(ws: &WorksheetXml, col: &str) -> Result<bool> {
99 let col_num = column_name_to_number(col)?;
100 let hidden = ws
101 .cols
102 .as_ref()
103 .and_then(|cols| {
104 cols.cols
105 .iter()
106 .find(|c| col_num >= c.min && col_num <= c.max)
107 })
108 .and_then(|c| c.hidden)
109 .unwrap_or(false);
110 Ok(!hidden)
111}
112
113pub fn set_col_outline_level(ws: &mut WorksheetXml, col: &str, level: u8) -> Result<()> {
117 let col_num = column_name_to_number(col)?;
118 if level > 7 {
119 return Err(Error::OutlineLevelExceeded { level, max: 7 });
120 }
121
122 let col_entry = find_or_create_col(ws, col_num);
123 col_entry.outline_level = if level == 0 { None } else { Some(level) };
124 Ok(())
125}
126
127pub fn get_col_outline_level(ws: &WorksheetXml, col: &str) -> Result<u8> {
129 let col_num = column_name_to_number(col)?;
130 let level = ws
131 .cols
132 .as_ref()
133 .and_then(|cols| {
134 cols.cols
135 .iter()
136 .find(|c| col_num >= c.min && col_num <= c.max)
137 })
138 .and_then(|c| c.outline_level)
139 .unwrap_or(0);
140 Ok(level)
141}
142
143pub fn insert_cols(ws: &mut WorksheetXml, col: &str, count: u32) -> Result<()> {
149 let start_col = column_name_to_number(col)?;
150 if count == 0 {
151 return Ok(());
152 }
153
154 let max_existing = ws
156 .sheet_data
157 .rows
158 .iter()
159 .flat_map(|r| r.cells.iter())
160 .filter_map(|c| cell_name_to_coordinates(c.r.as_str()).ok())
161 .map(|(col_n, _)| col_n)
162 .max()
163 .unwrap_or(0);
164 let furthest = max_existing.max(start_col);
165 if furthest.checked_add(count).is_none_or(|v| v > MAX_COLUMNS) {
166 return Err(Error::InvalidColumnNumber(furthest + count));
167 }
168
169 for row in ws.sheet_data.rows.iter_mut() {
171 for cell in row.cells.iter_mut() {
172 let (c, r) = cell_name_to_coordinates(cell.r.as_str())?;
173 if c >= start_col {
174 let new_col = c + count;
175 cell.r = coordinates_to_cell_name(new_col, r)?.into();
176 cell.col = new_col;
177 }
178 }
179 }
180
181 if let Some(ref mut cols) = ws.cols {
183 for c in cols.cols.iter_mut() {
184 if c.min >= start_col {
185 c.min += count;
186 }
187 if c.max >= start_col {
188 c.max += count;
189 }
190 }
191 }
192
193 Ok(())
194}
195
196pub fn remove_col(ws: &mut WorksheetXml, col: &str) -> Result<()> {
198 let col_num = column_name_to_number(col)?;
199
200 for row in ws.sheet_data.rows.iter_mut() {
202 row.cells.retain(|cell| {
204 cell_name_to_coordinates(cell.r.as_str())
205 .map(|(c, _)| c != col_num)
206 .unwrap_or(true)
207 });
208
209 for cell in row.cells.iter_mut() {
211 let (c, r) = cell_name_to_coordinates(cell.r.as_str())?;
212 if c > col_num {
213 let new_col = c - 1;
214 cell.r = coordinates_to_cell_name(new_col, r)?.into();
215 cell.col = new_col;
216 }
217 }
218 }
219
220 if let Some(ref mut cols) = ws.cols {
222 cols.cols
224 .retain(|c| !(c.min == col_num && c.max == col_num));
225
226 for c in cols.cols.iter_mut() {
227 if c.min > col_num {
228 c.min -= 1;
229 }
230 if c.max >= col_num {
231 c.max -= 1;
232 }
233 }
234
235 if cols.cols.is_empty() {
237 ws.cols = None;
238 }
239 }
240
241 Ok(())
242}
243
244pub fn set_col_style(ws: &mut WorksheetXml, col: &str, style_id: u32) -> Result<()> {
247 let col_num = column_name_to_number(col)?;
248 let col_entry = find_or_create_col(ws, col_num);
249 col_entry.style = Some(style_id);
250 Ok(())
251}
252
253pub fn get_col_style(ws: &WorksheetXml, col: &str) -> Result<u32> {
256 let col_num = column_name_to_number(col)?;
257 let style = ws
258 .cols
259 .as_ref()
260 .and_then(|cols| {
261 cols.cols
262 .iter()
263 .find(|c| col_num >= c.min && col_num <= c.max)
264 })
265 .and_then(|c| c.style)
266 .unwrap_or(0);
267 Ok(style)
268}
269
270fn find_or_create_col(ws: &mut WorksheetXml, col_num: u32) -> &mut Col {
273 if ws.cols.is_none() {
275 ws.cols = Some(Cols { cols: vec![] });
276 }
277 let cols = ws.cols.as_mut().unwrap();
278
279 let existing = cols
281 .cols
282 .iter()
283 .position(|c| c.min == col_num && c.max == col_num);
284
285 if let Some(idx) = existing {
286 return &mut cols.cols[idx];
287 }
288
289 cols.cols.push(Col {
291 min: col_num,
292 max: col_num,
293 width: None,
294 style: None,
295 hidden: None,
296 custom_width: None,
297 outline_level: None,
298 });
299 let last = cols.cols.len() - 1;
300 &mut cols.cols[last]
301}
302
303#[cfg(test)]
304#[allow(clippy::field_reassign_with_default)]
305mod tests {
306 use super::*;
307 use sheetkit_xml::worksheet::{Cell, CellTypeTag, Row, SheetData};
308
309 fn sample_ws() -> WorksheetXml {
311 let mut ws = WorksheetXml::default();
312 ws.sheet_data = SheetData {
313 rows: vec![
314 Row {
315 r: 1,
316 spans: None,
317 s: None,
318 custom_format: None,
319 ht: None,
320 hidden: None,
321 custom_height: None,
322 outline_level: None,
323 cells: vec![
324 Cell {
325 r: "A1".into(),
326 col: 1,
327 s: None,
328 t: CellTypeTag::None,
329 v: Some("10".to_string()),
330 f: None,
331 is: None,
332 },
333 Cell {
334 r: "B1".into(),
335 col: 2,
336 s: None,
337 t: CellTypeTag::None,
338 v: Some("20".to_string()),
339 f: None,
340 is: None,
341 },
342 Cell {
343 r: "D1".into(),
344 col: 4,
345 s: None,
346 t: CellTypeTag::None,
347 v: Some("40".to_string()),
348 f: None,
349 is: None,
350 },
351 ],
352 },
353 Row {
354 r: 2,
355 spans: None,
356 s: None,
357 custom_format: None,
358 ht: None,
359 hidden: None,
360 custom_height: None,
361 outline_level: None,
362 cells: vec![
363 Cell {
364 r: "A2".into(),
365 col: 1,
366 s: None,
367 t: CellTypeTag::None,
368 v: Some("100".to_string()),
369 f: None,
370 is: None,
371 },
372 Cell {
373 r: "C2".into(),
374 col: 3,
375 s: None,
376 t: CellTypeTag::None,
377 v: Some("300".to_string()),
378 f: None,
379 is: None,
380 },
381 ],
382 },
383 ],
384 };
385 ws
386 }
387
388 #[test]
389 fn test_set_and_get_col_width() {
390 let mut ws = WorksheetXml::default();
391 set_col_width(&mut ws, "A", 15.0).unwrap();
392 assert_eq!(get_col_width(&ws, "A"), Some(15.0));
393 }
394
395 #[test]
396 fn test_set_col_width_creates_cols_container() {
397 let mut ws = WorksheetXml::default();
398 assert!(ws.cols.is_none());
399 set_col_width(&mut ws, "B", 20.0).unwrap();
400 assert!(ws.cols.is_some());
401 let cols = ws.cols.as_ref().unwrap();
402 assert_eq!(cols.cols.len(), 1);
403 assert_eq!(cols.cols[0].min, 2);
404 assert_eq!(cols.cols[0].max, 2);
405 assert_eq!(cols.cols[0].custom_width, Some(true));
406 }
407
408 #[test]
409 fn test_set_col_width_zero_is_valid() {
410 let mut ws = WorksheetXml::default();
411 set_col_width(&mut ws, "A", 0.0).unwrap();
412 assert_eq!(get_col_width(&ws, "A"), Some(0.0));
413 }
414
415 #[test]
416 fn test_set_col_width_max_is_valid() {
417 let mut ws = WorksheetXml::default();
418 set_col_width(&mut ws, "A", 255.0).unwrap();
419 assert_eq!(get_col_width(&ws, "A"), Some(255.0));
420 }
421
422 #[test]
423 fn test_set_col_width_exceeds_max_returns_error() {
424 let mut ws = WorksheetXml::default();
425 let result = set_col_width(&mut ws, "A", 256.0);
426 assert!(result.is_err());
427 assert!(matches!(
428 result.unwrap_err(),
429 Error::ColumnWidthExceeded { .. }
430 ));
431 }
432
433 #[test]
434 fn test_set_col_width_negative_returns_error() {
435 let mut ws = WorksheetXml::default();
436 let result = set_col_width(&mut ws, "A", -1.0);
437 assert!(result.is_err());
438 }
439
440 #[test]
441 fn test_get_col_width_nonexistent_returns_none() {
442 let ws = WorksheetXml::default();
443 assert_eq!(get_col_width(&ws, "Z"), None);
444 }
445
446 #[test]
447 fn test_set_col_width_invalid_column_returns_error() {
448 let mut ws = WorksheetXml::default();
449 let result = set_col_width(&mut ws, "XFE", 10.0);
450 assert!(result.is_err());
451 }
452
453 #[test]
454 fn test_set_col_hidden() {
455 let mut ws = WorksheetXml::default();
456 set_col_visible(&mut ws, "A", false).unwrap();
457
458 let col = &ws.cols.as_ref().unwrap().cols[0];
459 assert_eq!(col.hidden, Some(true));
460 }
461
462 #[test]
463 fn test_set_col_visible_clears_hidden() {
464 let mut ws = WorksheetXml::default();
465 set_col_visible(&mut ws, "A", false).unwrap();
466 set_col_visible(&mut ws, "A", true).unwrap();
467
468 let col = &ws.cols.as_ref().unwrap().cols[0];
469 assert_eq!(col.hidden, None);
470 }
471
472 #[test]
473 fn test_insert_cols_shifts_cells_right() {
474 let mut ws = sample_ws();
475 insert_cols(&mut ws, "B", 2).unwrap();
476
477 let r1 = &ws.sheet_data.rows[0];
479 assert_eq!(r1.cells[0].r, "A1");
480 assert_eq!(r1.cells[1].r, "D1"); assert_eq!(r1.cells[2].r, "F1"); let r2 = &ws.sheet_data.rows[1];
485 assert_eq!(r2.cells[0].r, "A2");
486 assert_eq!(r2.cells[1].r, "E2"); }
488
489 #[test]
490 fn test_insert_cols_at_column_a() {
491 let mut ws = sample_ws();
492 insert_cols(&mut ws, "A", 1).unwrap();
493
494 let r1 = &ws.sheet_data.rows[0];
496 assert_eq!(r1.cells[0].r, "B1"); assert_eq!(r1.cells[1].r, "C1"); assert_eq!(r1.cells[2].r, "E1"); }
500
501 #[test]
502 fn test_insert_cols_count_zero_is_noop() {
503 let mut ws = sample_ws();
504 insert_cols(&mut ws, "B", 0).unwrap();
505 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
506 assert_eq!(ws.sheet_data.rows[0].cells[1].r, "B1");
507 }
508
509 #[test]
510 fn test_insert_cols_on_empty_sheet() {
511 let mut ws = WorksheetXml::default();
512 insert_cols(&mut ws, "A", 5).unwrap();
513 assert!(ws.sheet_data.rows.is_empty());
514 }
515
516 #[test]
517 fn test_insert_cols_shifts_col_definitions() {
518 let mut ws = WorksheetXml::default();
519 set_col_width(&mut ws, "C", 20.0).unwrap();
520 insert_cols(&mut ws, "B", 2).unwrap();
521
522 let col = &ws.cols.as_ref().unwrap().cols[0];
524 assert_eq!(col.min, 5);
525 assert_eq!(col.max, 5);
526 }
527
528 #[test]
529 fn test_remove_col_shifts_cells_left() {
530 let mut ws = sample_ws();
531 remove_col(&mut ws, "B").unwrap();
532
533 let r1 = &ws.sheet_data.rows[0];
535 assert_eq!(r1.cells.len(), 2);
536 assert_eq!(r1.cells[0].r, "A1");
537 assert_eq!(r1.cells[1].r, "C1"); assert_eq!(r1.cells[1].v, Some("40".to_string()));
539
540 let r2 = &ws.sheet_data.rows[1];
542 assert_eq!(r2.cells[0].r, "A2");
543 assert_eq!(r2.cells[1].r, "B2"); }
545
546 #[test]
547 fn test_remove_first_col() {
548 let mut ws = sample_ws();
549 remove_col(&mut ws, "A").unwrap();
550
551 let r1 = &ws.sheet_data.rows[0];
553 assert_eq!(r1.cells.len(), 2);
554 assert_eq!(r1.cells[0].r, "A1");
555 assert_eq!(r1.cells[0].v, Some("20".to_string())); assert_eq!(r1.cells[1].r, "C1");
557 assert_eq!(r1.cells[1].v, Some("40".to_string())); }
559
560 #[test]
561 fn test_remove_col_with_col_definitions() {
562 let mut ws = WorksheetXml::default();
563 set_col_width(&mut ws, "B", 20.0).unwrap();
564 remove_col(&mut ws, "B").unwrap();
565
566 assert!(ws.cols.is_none());
568 }
569
570 #[test]
571 fn test_remove_col_shrinks_range_ending_at_removed_column() {
572 let mut ws = WorksheetXml::default();
574 set_col_width(&mut ws, "B", 15.0).unwrap();
575 ws.cols.as_mut().unwrap().cols[0].max = 3;
577 remove_col(&mut ws, "C").unwrap();
578 let col = &ws.cols.as_ref().unwrap().cols[0];
579 assert_eq!(col.min, 2);
580 assert_eq!(col.max, 2);
581 }
582
583 #[test]
584 fn test_remove_col_shrinks_range_spanning_removed_column() {
585 let mut ws = WorksheetXml::default();
587 set_col_width(&mut ws, "B", 15.0).unwrap();
588 ws.cols.as_mut().unwrap().cols[0].max = 5;
589 remove_col(&mut ws, "C").unwrap();
590 let col = &ws.cols.as_ref().unwrap().cols[0];
591 assert_eq!(col.min, 2);
592 assert_eq!(col.max, 4);
593 }
594
595 #[test]
596 fn test_remove_col_invalid_column_returns_error() {
597 let mut ws = WorksheetXml::default();
598 let result = remove_col(&mut ws, "XFE");
599 assert!(result.is_err());
600 }
601
602 #[test]
603 fn test_remove_col_invalid_cell_reference_returns_error() {
604 let mut ws = sample_ws();
605 ws.sheet_data.rows[0].cells.push(Cell {
606 r: "INVALID".into(),
607 col: 0,
608 s: None,
609 t: CellTypeTag::None,
610 v: Some("1".to_string()),
611 f: None,
612 is: None,
613 });
614 let result = remove_col(&mut ws, "A");
615 assert!(result.is_err());
616 }
617
618 #[test]
619 fn test_set_multiple_col_widths() {
620 let mut ws = WorksheetXml::default();
621 set_col_width(&mut ws, "A", 10.0).unwrap();
622 set_col_width(&mut ws, "C", 30.0).unwrap();
623
624 assert_eq!(get_col_width(&ws, "A"), Some(10.0));
625 assert_eq!(get_col_width(&ws, "B"), None);
626 assert_eq!(get_col_width(&ws, "C"), Some(30.0));
627 }
628
629 #[test]
630 fn test_overwrite_col_width() {
631 let mut ws = WorksheetXml::default();
632 set_col_width(&mut ws, "A", 10.0).unwrap();
633 set_col_width(&mut ws, "A", 25.0).unwrap();
634
635 assert_eq!(get_col_width(&ws, "A"), Some(25.0));
636 }
637
638 #[test]
639 fn test_get_col_visible_default_is_true() {
640 let ws = WorksheetXml::default();
641 assert!(get_col_visible(&ws, "A").unwrap());
642 }
643
644 #[test]
645 fn test_get_col_visible_after_hide() {
646 let mut ws = WorksheetXml::default();
647 set_col_visible(&mut ws, "B", false).unwrap();
648 assert!(!get_col_visible(&ws, "B").unwrap());
649 }
650
651 #[test]
652 fn test_get_col_visible_after_hide_then_show() {
653 let mut ws = WorksheetXml::default();
654 set_col_visible(&mut ws, "A", false).unwrap();
655 set_col_visible(&mut ws, "A", true).unwrap();
656 assert!(get_col_visible(&ws, "A").unwrap());
657 }
658
659 #[test]
660 fn test_get_col_visible_invalid_column_returns_error() {
661 let ws = WorksheetXml::default();
662 let result = get_col_visible(&ws, "XFE");
663 assert!(result.is_err());
664 }
665
666 #[test]
667 fn test_set_col_outline_level() {
668 let mut ws = WorksheetXml::default();
669 set_col_outline_level(&mut ws, "A", 3).unwrap();
670
671 let col = &ws.cols.as_ref().unwrap().cols[0];
672 assert_eq!(col.outline_level, Some(3));
673 }
674
675 #[test]
676 fn test_set_col_outline_level_zero_clears() {
677 let mut ws = WorksheetXml::default();
678 set_col_outline_level(&mut ws, "A", 3).unwrap();
679 set_col_outline_level(&mut ws, "A", 0).unwrap();
680
681 let col = &ws.cols.as_ref().unwrap().cols[0];
682 assert_eq!(col.outline_level, None);
683 }
684
685 #[test]
686 fn test_set_col_outline_level_exceeds_max_returns_error() {
687 let mut ws = WorksheetXml::default();
688 let result = set_col_outline_level(&mut ws, "A", 8);
689 assert!(result.is_err());
690 }
691
692 #[test]
693 fn test_set_col_outline_level_max_valid() {
694 let mut ws = WorksheetXml::default();
695 set_col_outline_level(&mut ws, "A", 7).unwrap();
696
697 let col = &ws.cols.as_ref().unwrap().cols[0];
698 assert_eq!(col.outline_level, Some(7));
699 }
700
701 #[test]
702 fn test_get_col_outline_level_default_is_zero() {
703 let ws = WorksheetXml::default();
704 assert_eq!(get_col_outline_level(&ws, "A").unwrap(), 0);
705 }
706
707 #[test]
708 fn test_get_col_outline_level_after_set() {
709 let mut ws = WorksheetXml::default();
710 set_col_outline_level(&mut ws, "B", 5).unwrap();
711 assert_eq!(get_col_outline_level(&ws, "B").unwrap(), 5);
712 }
713
714 #[test]
715 fn test_get_col_outline_level_after_clear() {
716 let mut ws = WorksheetXml::default();
717 set_col_outline_level(&mut ws, "C", 4).unwrap();
718 set_col_outline_level(&mut ws, "C", 0).unwrap();
719 assert_eq!(get_col_outline_level(&ws, "C").unwrap(), 0);
720 }
721
722 #[test]
723 fn test_get_col_outline_level_invalid_column_returns_error() {
724 let ws = WorksheetXml::default();
725 let result = get_col_outline_level(&ws, "XFE");
726 assert!(result.is_err());
727 }
728
729 #[test]
732 fn test_get_cols_empty_sheet() {
733 let ws = WorksheetXml::default();
734 let sst = SharedStringTable::new();
735 let cols = get_cols(&ws, &sst).unwrap();
736 assert!(cols.is_empty());
737 }
738
739 #[test]
740 fn test_get_cols_transposes_row_data() {
741 let ws = sample_ws();
742 let sst = SharedStringTable::new();
743 let cols = get_cols(&ws, &sst).unwrap();
744
745 assert_eq!(cols.len(), 4);
751
752 assert_eq!(cols[0].0, "A");
754 assert_eq!(cols[0].1.len(), 2);
755 assert_eq!(cols[0].1[0], (1, CellValue::Number(10.0)));
756 assert_eq!(cols[0].1[1], (2, CellValue::Number(100.0)));
757
758 assert_eq!(cols[1].0, "B");
760 assert_eq!(cols[1].1.len(), 1);
761 assert_eq!(cols[1].1[0], (1, CellValue::Number(20.0)));
762
763 assert_eq!(cols[2].0, "C");
765 assert_eq!(cols[2].1.len(), 1);
766 assert_eq!(cols[2].1[0], (2, CellValue::Number(300.0)));
767
768 assert_eq!(cols[3].0, "D");
770 assert_eq!(cols[3].1.len(), 1);
771 assert_eq!(cols[3].1[0], (1, CellValue::Number(40.0)));
772 }
773
774 #[test]
775 fn test_get_cols_with_shared_strings() {
776 let mut sst = SharedStringTable::new();
777 sst.add("Name");
778 sst.add("Age");
779 sst.add("Alice");
780
781 let mut ws = WorksheetXml::default();
782 ws.sheet_data = SheetData {
783 rows: vec![
784 Row {
785 r: 1,
786 spans: None,
787 s: None,
788 custom_format: None,
789 ht: None,
790 hidden: None,
791 custom_height: None,
792 outline_level: None,
793 cells: vec![
794 Cell {
795 r: "A1".into(),
796 col: 1,
797 s: None,
798 t: CellTypeTag::SharedString,
799 v: Some("0".to_string()),
800 f: None,
801 is: None,
802 },
803 Cell {
804 r: "B1".into(),
805 col: 2,
806 s: None,
807 t: CellTypeTag::SharedString,
808 v: Some("1".to_string()),
809 f: None,
810 is: None,
811 },
812 ],
813 },
814 Row {
815 r: 2,
816 spans: None,
817 s: None,
818 custom_format: None,
819 ht: None,
820 hidden: None,
821 custom_height: None,
822 outline_level: None,
823 cells: vec![
824 Cell {
825 r: "A2".into(),
826 col: 1,
827 s: None,
828 t: CellTypeTag::SharedString,
829 v: Some("2".to_string()),
830 f: None,
831 is: None,
832 },
833 Cell {
834 r: "B2".into(),
835 col: 2,
836 s: None,
837 t: CellTypeTag::None,
838 v: Some("30".to_string()),
839 f: None,
840 is: None,
841 },
842 ],
843 },
844 ],
845 };
846
847 let cols = get_cols(&ws, &sst).unwrap();
848 assert_eq!(cols.len(), 2);
849
850 assert_eq!(cols[0].0, "A");
852 assert_eq!(cols[0].1[0].1, CellValue::String("Name".to_string()));
853 assert_eq!(cols[0].1[1].1, CellValue::String("Alice".to_string()));
854
855 assert_eq!(cols[1].0, "B");
857 assert_eq!(cols[1].1[0].1, CellValue::String("Age".to_string()));
858 assert_eq!(cols[1].1[1].1, CellValue::Number(30.0));
859 }
860
861 #[test]
862 fn test_get_cols_sorted_correctly() {
863 let mut ws = WorksheetXml::default();
866 ws.sheet_data = SheetData {
867 rows: vec![Row {
868 r: 1,
869 spans: None,
870 s: None,
871 custom_format: None,
872 ht: None,
873 hidden: None,
874 custom_height: None,
875 outline_level: None,
876 cells: vec![
877 Cell {
878 r: "AA1".into(),
879 col: 27,
880 s: None,
881 t: CellTypeTag::None,
882 v: Some("1".to_string()),
883 f: None,
884 is: None,
885 },
886 Cell {
887 r: "B1".into(),
888 col: 2,
889 s: None,
890 t: CellTypeTag::None,
891 v: Some("2".to_string()),
892 f: None,
893 is: None,
894 },
895 Cell {
896 r: "A1".into(),
897 col: 1,
898 s: None,
899 t: CellTypeTag::None,
900 v: Some("3".to_string()),
901 f: None,
902 is: None,
903 },
904 ],
905 }],
906 };
907
908 let sst = SharedStringTable::new();
909 let cols = get_cols(&ws, &sst).unwrap();
910
911 assert_eq!(cols.len(), 3);
912 assert_eq!(cols[0].0, "A");
913 assert_eq!(cols[1].0, "B");
914 assert_eq!(cols[2].0, "AA");
915 }
916
917 #[test]
920 fn test_get_col_style_default_is_zero() {
921 let ws = WorksheetXml::default();
922 assert_eq!(get_col_style(&ws, "A").unwrap(), 0);
923 }
924
925 #[test]
926 fn test_set_col_style_applies_style() {
927 let mut ws = WorksheetXml::default();
928 set_col_style(&mut ws, "B", 4).unwrap();
929
930 let col = &ws.cols.as_ref().unwrap().cols[0];
931 assert_eq!(col.style, Some(4));
932 }
933
934 #[test]
935 fn test_get_col_style_after_set() {
936 let mut ws = WorksheetXml::default();
937 set_col_style(&mut ws, "C", 10).unwrap();
938 assert_eq!(get_col_style(&ws, "C").unwrap(), 10);
939 }
940
941 #[test]
942 fn test_set_col_style_creates_cols_container() {
943 let mut ws = WorksheetXml::default();
944 assert!(ws.cols.is_none());
945 set_col_style(&mut ws, "A", 2).unwrap();
946 assert!(ws.cols.is_some());
947 }
948
949 #[test]
950 fn test_set_col_style_overwrite() {
951 let mut ws = WorksheetXml::default();
952 set_col_style(&mut ws, "A", 3).unwrap();
953 set_col_style(&mut ws, "A", 7).unwrap();
954 assert_eq!(get_col_style(&ws, "A").unwrap(), 7);
955 }
956
957 #[test]
958 fn test_get_col_style_invalid_column_returns_error() {
959 let ws = WorksheetXml::default();
960 let result = get_col_style(&ws, "XFE");
961 assert!(result.is_err());
962 }
963
964 #[test]
965 fn test_set_col_style_invalid_column_returns_error() {
966 let mut ws = WorksheetXml::default();
967 let result = set_col_style(&mut ws, "XFE", 1);
968 assert!(result.is_err());
969 }
970}