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)]
304mod tests {
305 use super::*;
306 use sheetkit_xml::worksheet::{Cell, CellTypeTag, Row, SheetData};
307
308 fn sample_ws() -> WorksheetXml {
310 let mut ws = WorksheetXml::default();
311 ws.sheet_data = SheetData {
312 rows: vec![
313 Row {
314 r: 1,
315 spans: None,
316 s: None,
317 custom_format: None,
318 ht: None,
319 hidden: None,
320 custom_height: None,
321 outline_level: None,
322 cells: vec![
323 Cell {
324 r: "A1".into(),
325 col: 1,
326 s: None,
327 t: CellTypeTag::None,
328 v: Some("10".to_string()),
329 f: None,
330 is: None,
331 },
332 Cell {
333 r: "B1".into(),
334 col: 2,
335 s: None,
336 t: CellTypeTag::None,
337 v: Some("20".to_string()),
338 f: None,
339 is: None,
340 },
341 Cell {
342 r: "D1".into(),
343 col: 4,
344 s: None,
345 t: CellTypeTag::None,
346 v: Some("40".to_string()),
347 f: None,
348 is: None,
349 },
350 ],
351 },
352 Row {
353 r: 2,
354 spans: None,
355 s: None,
356 custom_format: None,
357 ht: None,
358 hidden: None,
359 custom_height: None,
360 outline_level: None,
361 cells: vec![
362 Cell {
363 r: "A2".into(),
364 col: 1,
365 s: None,
366 t: CellTypeTag::None,
367 v: Some("100".to_string()),
368 f: None,
369 is: None,
370 },
371 Cell {
372 r: "C2".into(),
373 col: 3,
374 s: None,
375 t: CellTypeTag::None,
376 v: Some("300".to_string()),
377 f: None,
378 is: None,
379 },
380 ],
381 },
382 ],
383 };
384 ws
385 }
386
387 #[test]
388 fn test_set_and_get_col_width() {
389 let mut ws = WorksheetXml::default();
390 set_col_width(&mut ws, "A", 15.0).unwrap();
391 assert_eq!(get_col_width(&ws, "A"), Some(15.0));
392 }
393
394 #[test]
395 fn test_set_col_width_creates_cols_container() {
396 let mut ws = WorksheetXml::default();
397 assert!(ws.cols.is_none());
398 set_col_width(&mut ws, "B", 20.0).unwrap();
399 assert!(ws.cols.is_some());
400 let cols = ws.cols.as_ref().unwrap();
401 assert_eq!(cols.cols.len(), 1);
402 assert_eq!(cols.cols[0].min, 2);
403 assert_eq!(cols.cols[0].max, 2);
404 assert_eq!(cols.cols[0].custom_width, Some(true));
405 }
406
407 #[test]
408 fn test_set_col_width_zero_is_valid() {
409 let mut ws = WorksheetXml::default();
410 set_col_width(&mut ws, "A", 0.0).unwrap();
411 assert_eq!(get_col_width(&ws, "A"), Some(0.0));
412 }
413
414 #[test]
415 fn test_set_col_width_max_is_valid() {
416 let mut ws = WorksheetXml::default();
417 set_col_width(&mut ws, "A", 255.0).unwrap();
418 assert_eq!(get_col_width(&ws, "A"), Some(255.0));
419 }
420
421 #[test]
422 fn test_set_col_width_exceeds_max_returns_error() {
423 let mut ws = WorksheetXml::default();
424 let result = set_col_width(&mut ws, "A", 256.0);
425 assert!(result.is_err());
426 assert!(matches!(
427 result.unwrap_err(),
428 Error::ColumnWidthExceeded { .. }
429 ));
430 }
431
432 #[test]
433 fn test_set_col_width_negative_returns_error() {
434 let mut ws = WorksheetXml::default();
435 let result = set_col_width(&mut ws, "A", -1.0);
436 assert!(result.is_err());
437 }
438
439 #[test]
440 fn test_get_col_width_nonexistent_returns_none() {
441 let ws = WorksheetXml::default();
442 assert_eq!(get_col_width(&ws, "Z"), None);
443 }
444
445 #[test]
446 fn test_set_col_width_invalid_column_returns_error() {
447 let mut ws = WorksheetXml::default();
448 let result = set_col_width(&mut ws, "XFE", 10.0);
449 assert!(result.is_err());
450 }
451
452 #[test]
453 fn test_set_col_hidden() {
454 let mut ws = WorksheetXml::default();
455 set_col_visible(&mut ws, "A", false).unwrap();
456
457 let col = &ws.cols.as_ref().unwrap().cols[0];
458 assert_eq!(col.hidden, Some(true));
459 }
460
461 #[test]
462 fn test_set_col_visible_clears_hidden() {
463 let mut ws = WorksheetXml::default();
464 set_col_visible(&mut ws, "A", false).unwrap();
465 set_col_visible(&mut ws, "A", true).unwrap();
466
467 let col = &ws.cols.as_ref().unwrap().cols[0];
468 assert_eq!(col.hidden, None);
469 }
470
471 #[test]
472 fn test_insert_cols_shifts_cells_right() {
473 let mut ws = sample_ws();
474 insert_cols(&mut ws, "B", 2).unwrap();
475
476 let r1 = &ws.sheet_data.rows[0];
478 assert_eq!(r1.cells[0].r, "A1");
479 assert_eq!(r1.cells[1].r, "D1"); assert_eq!(r1.cells[2].r, "F1"); let r2 = &ws.sheet_data.rows[1];
484 assert_eq!(r2.cells[0].r, "A2");
485 assert_eq!(r2.cells[1].r, "E2"); }
487
488 #[test]
489 fn test_insert_cols_at_column_a() {
490 let mut ws = sample_ws();
491 insert_cols(&mut ws, "A", 1).unwrap();
492
493 let r1 = &ws.sheet_data.rows[0];
495 assert_eq!(r1.cells[0].r, "B1"); assert_eq!(r1.cells[1].r, "C1"); assert_eq!(r1.cells[2].r, "E1"); }
499
500 #[test]
501 fn test_insert_cols_count_zero_is_noop() {
502 let mut ws = sample_ws();
503 insert_cols(&mut ws, "B", 0).unwrap();
504 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
505 assert_eq!(ws.sheet_data.rows[0].cells[1].r, "B1");
506 }
507
508 #[test]
509 fn test_insert_cols_on_empty_sheet() {
510 let mut ws = WorksheetXml::default();
511 insert_cols(&mut ws, "A", 5).unwrap();
512 assert!(ws.sheet_data.rows.is_empty());
513 }
514
515 #[test]
516 fn test_insert_cols_shifts_col_definitions() {
517 let mut ws = WorksheetXml::default();
518 set_col_width(&mut ws, "C", 20.0).unwrap();
519 insert_cols(&mut ws, "B", 2).unwrap();
520
521 let col = &ws.cols.as_ref().unwrap().cols[0];
523 assert_eq!(col.min, 5);
524 assert_eq!(col.max, 5);
525 }
526
527 #[test]
528 fn test_remove_col_shifts_cells_left() {
529 let mut ws = sample_ws();
530 remove_col(&mut ws, "B").unwrap();
531
532 let r1 = &ws.sheet_data.rows[0];
534 assert_eq!(r1.cells.len(), 2);
535 assert_eq!(r1.cells[0].r, "A1");
536 assert_eq!(r1.cells[1].r, "C1"); assert_eq!(r1.cells[1].v, Some("40".to_string()));
538
539 let r2 = &ws.sheet_data.rows[1];
541 assert_eq!(r2.cells[0].r, "A2");
542 assert_eq!(r2.cells[1].r, "B2"); }
544
545 #[test]
546 fn test_remove_first_col() {
547 let mut ws = sample_ws();
548 remove_col(&mut ws, "A").unwrap();
549
550 let r1 = &ws.sheet_data.rows[0];
552 assert_eq!(r1.cells.len(), 2);
553 assert_eq!(r1.cells[0].r, "A1");
554 assert_eq!(r1.cells[0].v, Some("20".to_string())); assert_eq!(r1.cells[1].r, "C1");
556 assert_eq!(r1.cells[1].v, Some("40".to_string())); }
558
559 #[test]
560 fn test_remove_col_with_col_definitions() {
561 let mut ws = WorksheetXml::default();
562 set_col_width(&mut ws, "B", 20.0).unwrap();
563 remove_col(&mut ws, "B").unwrap();
564
565 assert!(ws.cols.is_none());
567 }
568
569 #[test]
570 fn test_remove_col_shrinks_range_ending_at_removed_column() {
571 let mut ws = WorksheetXml::default();
573 set_col_width(&mut ws, "B", 15.0).unwrap();
574 ws.cols.as_mut().unwrap().cols[0].max = 3;
576 remove_col(&mut ws, "C").unwrap();
577 let col = &ws.cols.as_ref().unwrap().cols[0];
578 assert_eq!(col.min, 2);
579 assert_eq!(col.max, 2);
580 }
581
582 #[test]
583 fn test_remove_col_shrinks_range_spanning_removed_column() {
584 let mut ws = WorksheetXml::default();
586 set_col_width(&mut ws, "B", 15.0).unwrap();
587 ws.cols.as_mut().unwrap().cols[0].max = 5;
588 remove_col(&mut ws, "C").unwrap();
589 let col = &ws.cols.as_ref().unwrap().cols[0];
590 assert_eq!(col.min, 2);
591 assert_eq!(col.max, 4);
592 }
593
594 #[test]
595 fn test_remove_col_invalid_column_returns_error() {
596 let mut ws = WorksheetXml::default();
597 let result = remove_col(&mut ws, "XFE");
598 assert!(result.is_err());
599 }
600
601 #[test]
602 fn test_remove_col_invalid_cell_reference_returns_error() {
603 let mut ws = sample_ws();
604 ws.sheet_data.rows[0].cells.push(Cell {
605 r: "INVALID".into(),
606 col: 0,
607 s: None,
608 t: CellTypeTag::None,
609 v: Some("1".to_string()),
610 f: None,
611 is: None,
612 });
613 let result = remove_col(&mut ws, "A");
614 assert!(result.is_err());
615 }
616
617 #[test]
618 fn test_set_multiple_col_widths() {
619 let mut ws = WorksheetXml::default();
620 set_col_width(&mut ws, "A", 10.0).unwrap();
621 set_col_width(&mut ws, "C", 30.0).unwrap();
622
623 assert_eq!(get_col_width(&ws, "A"), Some(10.0));
624 assert_eq!(get_col_width(&ws, "B"), None);
625 assert_eq!(get_col_width(&ws, "C"), Some(30.0));
626 }
627
628 #[test]
629 fn test_overwrite_col_width() {
630 let mut ws = WorksheetXml::default();
631 set_col_width(&mut ws, "A", 10.0).unwrap();
632 set_col_width(&mut ws, "A", 25.0).unwrap();
633
634 assert_eq!(get_col_width(&ws, "A"), Some(25.0));
635 }
636
637 #[test]
638 fn test_get_col_visible_default_is_true() {
639 let ws = WorksheetXml::default();
640 assert!(get_col_visible(&ws, "A").unwrap());
641 }
642
643 #[test]
644 fn test_get_col_visible_after_hide() {
645 let mut ws = WorksheetXml::default();
646 set_col_visible(&mut ws, "B", false).unwrap();
647 assert!(!get_col_visible(&ws, "B").unwrap());
648 }
649
650 #[test]
651 fn test_get_col_visible_after_hide_then_show() {
652 let mut ws = WorksheetXml::default();
653 set_col_visible(&mut ws, "A", false).unwrap();
654 set_col_visible(&mut ws, "A", true).unwrap();
655 assert!(get_col_visible(&ws, "A").unwrap());
656 }
657
658 #[test]
659 fn test_get_col_visible_invalid_column_returns_error() {
660 let ws = WorksheetXml::default();
661 let result = get_col_visible(&ws, "XFE");
662 assert!(result.is_err());
663 }
664
665 #[test]
666 fn test_set_col_outline_level() {
667 let mut ws = WorksheetXml::default();
668 set_col_outline_level(&mut ws, "A", 3).unwrap();
669
670 let col = &ws.cols.as_ref().unwrap().cols[0];
671 assert_eq!(col.outline_level, Some(3));
672 }
673
674 #[test]
675 fn test_set_col_outline_level_zero_clears() {
676 let mut ws = WorksheetXml::default();
677 set_col_outline_level(&mut ws, "A", 3).unwrap();
678 set_col_outline_level(&mut ws, "A", 0).unwrap();
679
680 let col = &ws.cols.as_ref().unwrap().cols[0];
681 assert_eq!(col.outline_level, None);
682 }
683
684 #[test]
685 fn test_set_col_outline_level_exceeds_max_returns_error() {
686 let mut ws = WorksheetXml::default();
687 let result = set_col_outline_level(&mut ws, "A", 8);
688 assert!(result.is_err());
689 }
690
691 #[test]
692 fn test_set_col_outline_level_max_valid() {
693 let mut ws = WorksheetXml::default();
694 set_col_outline_level(&mut ws, "A", 7).unwrap();
695
696 let col = &ws.cols.as_ref().unwrap().cols[0];
697 assert_eq!(col.outline_level, Some(7));
698 }
699
700 #[test]
701 fn test_get_col_outline_level_default_is_zero() {
702 let ws = WorksheetXml::default();
703 assert_eq!(get_col_outline_level(&ws, "A").unwrap(), 0);
704 }
705
706 #[test]
707 fn test_get_col_outline_level_after_set() {
708 let mut ws = WorksheetXml::default();
709 set_col_outline_level(&mut ws, "B", 5).unwrap();
710 assert_eq!(get_col_outline_level(&ws, "B").unwrap(), 5);
711 }
712
713 #[test]
714 fn test_get_col_outline_level_after_clear() {
715 let mut ws = WorksheetXml::default();
716 set_col_outline_level(&mut ws, "C", 4).unwrap();
717 set_col_outline_level(&mut ws, "C", 0).unwrap();
718 assert_eq!(get_col_outline_level(&ws, "C").unwrap(), 0);
719 }
720
721 #[test]
722 fn test_get_col_outline_level_invalid_column_returns_error() {
723 let ws = WorksheetXml::default();
724 let result = get_col_outline_level(&ws, "XFE");
725 assert!(result.is_err());
726 }
727
728 #[test]
731 fn test_get_cols_empty_sheet() {
732 let ws = WorksheetXml::default();
733 let sst = SharedStringTable::new();
734 let cols = get_cols(&ws, &sst).unwrap();
735 assert!(cols.is_empty());
736 }
737
738 #[test]
739 fn test_get_cols_transposes_row_data() {
740 let ws = sample_ws();
741 let sst = SharedStringTable::new();
742 let cols = get_cols(&ws, &sst).unwrap();
743
744 assert_eq!(cols.len(), 4);
750
751 assert_eq!(cols[0].0, "A");
753 assert_eq!(cols[0].1.len(), 2);
754 assert_eq!(cols[0].1[0], (1, CellValue::Number(10.0)));
755 assert_eq!(cols[0].1[1], (2, CellValue::Number(100.0)));
756
757 assert_eq!(cols[1].0, "B");
759 assert_eq!(cols[1].1.len(), 1);
760 assert_eq!(cols[1].1[0], (1, CellValue::Number(20.0)));
761
762 assert_eq!(cols[2].0, "C");
764 assert_eq!(cols[2].1.len(), 1);
765 assert_eq!(cols[2].1[0], (2, CellValue::Number(300.0)));
766
767 assert_eq!(cols[3].0, "D");
769 assert_eq!(cols[3].1.len(), 1);
770 assert_eq!(cols[3].1[0], (1, CellValue::Number(40.0)));
771 }
772
773 #[test]
774 fn test_get_cols_with_shared_strings() {
775 let mut sst = SharedStringTable::new();
776 sst.add("Name");
777 sst.add("Age");
778 sst.add("Alice");
779
780 let mut ws = WorksheetXml::default();
781 ws.sheet_data = SheetData {
782 rows: vec![
783 Row {
784 r: 1,
785 spans: None,
786 s: None,
787 custom_format: None,
788 ht: None,
789 hidden: None,
790 custom_height: None,
791 outline_level: None,
792 cells: vec![
793 Cell {
794 r: "A1".into(),
795 col: 1,
796 s: None,
797 t: CellTypeTag::SharedString,
798 v: Some("0".to_string()),
799 f: None,
800 is: None,
801 },
802 Cell {
803 r: "B1".into(),
804 col: 2,
805 s: None,
806 t: CellTypeTag::SharedString,
807 v: Some("1".to_string()),
808 f: None,
809 is: None,
810 },
811 ],
812 },
813 Row {
814 r: 2,
815 spans: None,
816 s: None,
817 custom_format: None,
818 ht: None,
819 hidden: None,
820 custom_height: None,
821 outline_level: None,
822 cells: vec![
823 Cell {
824 r: "A2".into(),
825 col: 1,
826 s: None,
827 t: CellTypeTag::SharedString,
828 v: Some("2".to_string()),
829 f: None,
830 is: None,
831 },
832 Cell {
833 r: "B2".into(),
834 col: 2,
835 s: None,
836 t: CellTypeTag::None,
837 v: Some("30".to_string()),
838 f: None,
839 is: None,
840 },
841 ],
842 },
843 ],
844 };
845
846 let cols = get_cols(&ws, &sst).unwrap();
847 assert_eq!(cols.len(), 2);
848
849 assert_eq!(cols[0].0, "A");
851 assert_eq!(cols[0].1[0].1, CellValue::String("Name".to_string()));
852 assert_eq!(cols[0].1[1].1, CellValue::String("Alice".to_string()));
853
854 assert_eq!(cols[1].0, "B");
856 assert_eq!(cols[1].1[0].1, CellValue::String("Age".to_string()));
857 assert_eq!(cols[1].1[1].1, CellValue::Number(30.0));
858 }
859
860 #[test]
861 fn test_get_cols_sorted_correctly() {
862 let mut ws = WorksheetXml::default();
865 ws.sheet_data = SheetData {
866 rows: vec![Row {
867 r: 1,
868 spans: None,
869 s: None,
870 custom_format: None,
871 ht: None,
872 hidden: None,
873 custom_height: None,
874 outline_level: None,
875 cells: vec![
876 Cell {
877 r: "AA1".into(),
878 col: 27,
879 s: None,
880 t: CellTypeTag::None,
881 v: Some("1".to_string()),
882 f: None,
883 is: None,
884 },
885 Cell {
886 r: "B1".into(),
887 col: 2,
888 s: None,
889 t: CellTypeTag::None,
890 v: Some("2".to_string()),
891 f: None,
892 is: None,
893 },
894 Cell {
895 r: "A1".into(),
896 col: 1,
897 s: None,
898 t: CellTypeTag::None,
899 v: Some("3".to_string()),
900 f: None,
901 is: None,
902 },
903 ],
904 }],
905 };
906
907 let sst = SharedStringTable::new();
908 let cols = get_cols(&ws, &sst).unwrap();
909
910 assert_eq!(cols.len(), 3);
911 assert_eq!(cols[0].0, "A");
912 assert_eq!(cols[1].0, "B");
913 assert_eq!(cols[2].0, "AA");
914 }
915
916 #[test]
919 fn test_get_col_style_default_is_zero() {
920 let ws = WorksheetXml::default();
921 assert_eq!(get_col_style(&ws, "A").unwrap(), 0);
922 }
923
924 #[test]
925 fn test_set_col_style_applies_style() {
926 let mut ws = WorksheetXml::default();
927 set_col_style(&mut ws, "B", 4).unwrap();
928
929 let col = &ws.cols.as_ref().unwrap().cols[0];
930 assert_eq!(col.style, Some(4));
931 }
932
933 #[test]
934 fn test_get_col_style_after_set() {
935 let mut ws = WorksheetXml::default();
936 set_col_style(&mut ws, "C", 10).unwrap();
937 assert_eq!(get_col_style(&ws, "C").unwrap(), 10);
938 }
939
940 #[test]
941 fn test_set_col_style_creates_cols_container() {
942 let mut ws = WorksheetXml::default();
943 assert!(ws.cols.is_none());
944 set_col_style(&mut ws, "A", 2).unwrap();
945 assert!(ws.cols.is_some());
946 }
947
948 #[test]
949 fn test_set_col_style_overwrite() {
950 let mut ws = WorksheetXml::default();
951 set_col_style(&mut ws, "A", 3).unwrap();
952 set_col_style(&mut ws, "A", 7).unwrap();
953 assert_eq!(get_col_style(&ws, "A").unwrap(), 7);
954 }
955
956 #[test]
957 fn test_get_col_style_invalid_column_returns_error() {
958 let ws = WorksheetXml::default();
959 let result = get_col_style(&ws, "XFE");
960 assert!(result.is_err());
961 }
962
963 #[test]
964 fn test_set_col_style_invalid_column_returns_error() {
965 let mut ws = WorksheetXml::default();
966 let result = set_col_style(&mut ws, "XFE", 1);
967 assert!(result.is_err());
968 }
969}