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