1use sheetkit_xml::worksheet::{cell_types, Row, WorksheetXml};
8
9use crate::cell::CellValue;
10use crate::error::{Error, Result};
11use crate::sst::SharedStringTable;
12use crate::utils::cell_ref::{
13 cell_name_to_coordinates, column_number_to_name, coordinates_to_cell_name,
14};
15use crate::utils::constants::{MAX_ROWS, MAX_ROW_HEIGHT};
16
17#[allow(clippy::type_complexity)]
23pub fn get_rows(
24 ws: &WorksheetXml,
25 sst: &SharedStringTable,
26) -> Result<Vec<(u32, Vec<(String, CellValue)>)>> {
27 let mut result = Vec::new();
28
29 for row in &ws.sheet_data.rows {
30 if row.cells.is_empty() {
31 continue;
32 }
33
34 let mut cells = Vec::new();
35 for cell in &row.cells {
36 let (col_num, _) = cell_name_to_coordinates(&cell.r)?;
37 let col_name = column_number_to_name(col_num)?;
38 let value = resolve_cell_value(cell, sst);
39 cells.push((col_name, value));
40 }
41
42 result.push((row.r, cells));
43 }
44
45 Ok(result)
46}
47
48fn resolve_cell_value(cell: &sheetkit_xml::worksheet::Cell, sst: &SharedStringTable) -> CellValue {
51 if let Some(ref formula) = cell.f {
53 let expr = formula.value.clone().unwrap_or_default();
54 let cached = match (cell.t.as_deref(), &cell.v) {
55 (Some("b"), Some(v)) => Some(Box::new(CellValue::Bool(v == "1"))),
56 (Some("e"), Some(v)) => Some(Box::new(CellValue::Error(v.clone()))),
57 (_, Some(v)) => v
58 .parse::<f64>()
59 .ok()
60 .map(|n| Box::new(CellValue::Number(n))),
61 _ => None,
62 };
63 return CellValue::Formula {
64 expr,
65 result: cached,
66 };
67 }
68
69 let cell_type = cell.t.as_deref();
70 let cell_value = cell.v.as_deref();
71
72 match (cell_type, cell_value) {
73 (Some(cell_types::SHARED_STRING), Some(v)) => {
74 let idx: usize = match v.parse() {
75 Ok(i) => i,
76 Err(_) => return CellValue::Empty,
77 };
78 let s = sst.get(idx).unwrap_or("").to_string();
79 CellValue::String(s)
80 }
81 (Some(cell_types::BOOLEAN), Some(v)) => CellValue::Bool(v == "1"),
82 (Some(cell_types::ERROR), Some(v)) => CellValue::Error(v.to_string()),
83 (Some(cell_types::INLINE_STRING), _) => {
84 let s = cell
85 .is
86 .as_ref()
87 .and_then(|is| is.t.clone())
88 .unwrap_or_default();
89 CellValue::String(s)
90 }
91 (Some(cell_types::FORMULA_STRING), Some(v)) => CellValue::String(v.to_string()),
92 (None | Some(cell_types::NUMBER), Some(v)) => match v.parse::<f64>() {
93 Ok(n) => CellValue::Number(n),
94 Err(_) => CellValue::Empty,
95 },
96 _ => CellValue::Empty,
97 }
98}
99
100pub fn insert_rows(ws: &mut WorksheetXml, start_row: u32, count: u32) -> Result<()> {
106 if start_row == 0 {
107 return Err(Error::InvalidRowNumber(0));
108 }
109 if count == 0 {
110 return Ok(());
111 }
112 let max_existing = ws.sheet_data.rows.iter().map(|r| r.r).max().unwrap_or(0);
114 let furthest = max_existing.max(start_row);
115 if furthest.checked_add(count).is_none_or(|v| v > MAX_ROWS) {
116 return Err(Error::InvalidRowNumber(furthest + count));
117 }
118
119 for row in ws.sheet_data.rows.iter_mut().rev() {
122 if row.r >= start_row {
123 let new_row_num = row.r + count;
124 shift_row_cells(row, new_row_num)?;
125 row.r = new_row_num;
126 }
127 }
128
129 Ok(())
130}
131
132pub fn remove_row(ws: &mut WorksheetXml, row: u32) -> Result<()> {
134 if row == 0 {
135 return Err(Error::InvalidRowNumber(0));
136 }
137
138 ws.sheet_data.rows.retain(|r| r.r != row);
140
141 for r in ws.sheet_data.rows.iter_mut() {
143 if r.r > row {
144 let new_row_num = r.r - 1;
145 shift_row_cells(r, new_row_num)?;
146 r.r = new_row_num;
147 }
148 }
149
150 Ok(())
151}
152
153pub fn duplicate_row(ws: &mut WorksheetXml, row: u32) -> Result<()> {
155 duplicate_row_to(ws, row, row + 1)
156}
157
158pub fn duplicate_row_to(ws: &mut WorksheetXml, row: u32, target: u32) -> Result<()> {
161 if row == 0 {
162 return Err(Error::InvalidRowNumber(0));
163 }
164 if target == 0 {
165 return Err(Error::InvalidRowNumber(0));
166 }
167 if target > MAX_ROWS {
168 return Err(Error::InvalidRowNumber(target));
169 }
170
171 let source = ws
173 .sheet_data
174 .rows
175 .iter()
176 .find(|r| r.r == row)
177 .cloned()
178 .ok_or(Error::InvalidRowNumber(row))?;
179
180 insert_rows(ws, target, 1)?;
182
183 let mut new_row = source;
185 shift_row_cells(&mut new_row, target)?;
186 new_row.r = target;
187
188 let pos = ws
190 .sheet_data
191 .rows
192 .iter()
193 .position(|r| r.r > target)
194 .unwrap_or(ws.sheet_data.rows.len());
195 if let Some(existing) = ws.sheet_data.rows.iter().position(|r| r.r == target) {
197 ws.sheet_data.rows[existing] = new_row;
198 } else {
199 ws.sheet_data.rows.insert(pos, new_row);
200 }
201
202 Ok(())
203}
204
205pub fn set_row_height(ws: &mut WorksheetXml, row: u32, height: f64) -> Result<()> {
209 if row == 0 || row > MAX_ROWS {
210 return Err(Error::InvalidRowNumber(row));
211 }
212 if !(0.0..=MAX_ROW_HEIGHT).contains(&height) {
213 return Err(Error::RowHeightExceeded {
214 height,
215 max: MAX_ROW_HEIGHT,
216 });
217 }
218
219 let r = find_or_create_row(ws, row);
220 r.ht = Some(height);
221 r.custom_height = Some(true);
222 Ok(())
223}
224
225pub fn get_row_height(ws: &WorksheetXml, row: u32) -> Option<f64> {
228 ws.sheet_data
229 .rows
230 .iter()
231 .find(|r| r.r == row)
232 .and_then(|r| r.ht)
233}
234
235pub fn set_row_visible(ws: &mut WorksheetXml, row: u32, visible: bool) -> Result<()> {
237 if row == 0 || row > MAX_ROWS {
238 return Err(Error::InvalidRowNumber(row));
239 }
240
241 let r = find_or_create_row(ws, row);
242 r.hidden = if visible { None } else { Some(true) };
243 Ok(())
244}
245
246pub fn get_row_visible(ws: &WorksheetXml, row: u32) -> bool {
251 ws.sheet_data
252 .rows
253 .iter()
254 .find(|r| r.r == row)
255 .and_then(|r| r.hidden)
256 .map(|h| !h)
257 .unwrap_or(true)
258}
259
260pub fn get_row_outline_level(ws: &WorksheetXml, row: u32) -> u8 {
262 ws.sheet_data
263 .rows
264 .iter()
265 .find(|r| r.r == row)
266 .and_then(|r| r.outline_level)
267 .unwrap_or(0)
268}
269
270pub fn set_row_outline_level(ws: &mut WorksheetXml, row: u32, level: u8) -> Result<()> {
274 if row == 0 || row > MAX_ROWS {
275 return Err(Error::InvalidRowNumber(row));
276 }
277 if level > 7 {
278 return Err(Error::OutlineLevelExceeded { level, max: 7 });
279 }
280
281 let r = find_or_create_row(ws, row);
282 r.outline_level = if level == 0 { None } else { Some(level) };
283 Ok(())
284}
285
286pub fn set_row_style(ws: &mut WorksheetXml, row: u32, style_id: u32) -> Result<()> {
291 if row == 0 || row > MAX_ROWS {
292 return Err(Error::InvalidRowNumber(row));
293 }
294
295 let r = find_or_create_row(ws, row);
296 r.s = Some(style_id);
297 r.custom_format = if style_id == 0 { None } else { Some(true) };
298
299 for cell in r.cells.iter_mut() {
301 cell.s = Some(style_id);
302 }
303 Ok(())
304}
305
306pub fn get_row_style(ws: &WorksheetXml, row: u32) -> u32 {
309 ws.sheet_data
310 .rows
311 .iter()
312 .find(|r| r.r == row)
313 .and_then(|r| r.s)
314 .unwrap_or(0)
315}
316
317fn shift_row_cells(row: &mut Row, new_row_num: u32) -> Result<()> {
319 for cell in row.cells.iter_mut() {
320 let (col, _) = cell_name_to_coordinates(&cell.r)?;
321 cell.r = coordinates_to_cell_name(col, new_row_num)?;
322 }
323 Ok(())
324}
325
326fn find_or_create_row(ws: &mut WorksheetXml, row: u32) -> &mut Row {
328 let exists = ws.sheet_data.rows.iter().position(|r| r.r == row);
330 if let Some(idx) = exists {
331 return &mut ws.sheet_data.rows[idx];
332 }
333
334 let pos = ws
336 .sheet_data
337 .rows
338 .iter()
339 .position(|r| r.r > row)
340 .unwrap_or(ws.sheet_data.rows.len());
341 ws.sheet_data.rows.insert(
342 pos,
343 Row {
344 r: row,
345 spans: None,
346 s: None,
347 custom_format: None,
348 ht: None,
349 hidden: None,
350 custom_height: None,
351 outline_level: None,
352 cells: vec![],
353 },
354 );
355 &mut ws.sheet_data.rows[pos]
356}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361 use sheetkit_xml::worksheet::{Cell, SheetData};
362
363 fn sample_ws() -> WorksheetXml {
365 let mut ws = WorksheetXml::default();
366 ws.sheet_data = SheetData {
367 rows: vec![
368 Row {
369 r: 1,
370 spans: None,
371 s: None,
372 custom_format: None,
373 ht: None,
374 hidden: None,
375 custom_height: None,
376 outline_level: None,
377 cells: vec![
378 Cell {
379 r: "A1".to_string(),
380 s: None,
381 t: None,
382 v: Some("10".to_string()),
383 f: None,
384 is: None,
385 },
386 Cell {
387 r: "B1".to_string(),
388 s: None,
389 t: None,
390 v: Some("20".to_string()),
391 f: None,
392 is: None,
393 },
394 ],
395 },
396 Row {
397 r: 2,
398 spans: None,
399 s: None,
400 custom_format: None,
401 ht: None,
402 hidden: None,
403 custom_height: None,
404 outline_level: None,
405 cells: vec![Cell {
406 r: "A2".to_string(),
407 s: None,
408 t: None,
409 v: Some("30".to_string()),
410 f: None,
411 is: None,
412 }],
413 },
414 Row {
415 r: 5,
416 spans: None,
417 s: None,
418 custom_format: None,
419 ht: None,
420 hidden: None,
421 custom_height: None,
422 outline_level: None,
423 cells: vec![Cell {
424 r: "C5".to_string(),
425 s: None,
426 t: None,
427 v: Some("50".to_string()),
428 f: None,
429 is: None,
430 }],
431 },
432 ],
433 };
434 ws
435 }
436
437 #[test]
438 fn test_insert_rows_shifts_cells_down() {
439 let mut ws = sample_ws();
440 insert_rows(&mut ws, 2, 3).unwrap();
441
442 assert_eq!(ws.sheet_data.rows[0].r, 1);
444 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
445
446 assert_eq!(ws.sheet_data.rows[1].r, 5);
448 assert_eq!(ws.sheet_data.rows[1].cells[0].r, "A5");
449
450 assert_eq!(ws.sheet_data.rows[2].r, 8);
452 assert_eq!(ws.sheet_data.rows[2].cells[0].r, "C8");
453 }
454
455 #[test]
456 fn test_insert_rows_at_row_1() {
457 let mut ws = sample_ws();
458 insert_rows(&mut ws, 1, 2).unwrap();
459
460 assert_eq!(ws.sheet_data.rows[0].r, 3);
462 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A3");
463 assert_eq!(ws.sheet_data.rows[1].r, 4);
464 assert_eq!(ws.sheet_data.rows[2].r, 7);
465 }
466
467 #[test]
468 fn test_insert_rows_count_zero_is_noop() {
469 let mut ws = sample_ws();
470 insert_rows(&mut ws, 1, 0).unwrap();
471 assert_eq!(ws.sheet_data.rows[0].r, 1);
472 assert_eq!(ws.sheet_data.rows[1].r, 2);
473 assert_eq!(ws.sheet_data.rows[2].r, 5);
474 }
475
476 #[test]
477 fn test_insert_rows_row_zero_returns_error() {
478 let mut ws = sample_ws();
479 let result = insert_rows(&mut ws, 0, 1);
480 assert!(result.is_err());
481 }
482
483 #[test]
484 fn test_insert_rows_beyond_max_returns_error() {
485 let mut ws = WorksheetXml::default();
486 ws.sheet_data.rows.push(Row {
487 r: MAX_ROWS,
488 spans: None,
489 s: None,
490 custom_format: None,
491 ht: None,
492 hidden: None,
493 custom_height: None,
494 outline_level: None,
495 cells: vec![],
496 });
497 let result = insert_rows(&mut ws, 1, 1);
498 assert!(result.is_err());
499 }
500
501 #[test]
502 fn test_insert_rows_on_empty_sheet() {
503 let mut ws = WorksheetXml::default();
504 insert_rows(&mut ws, 1, 5).unwrap();
505 assert!(ws.sheet_data.rows.is_empty());
506 }
507
508 #[test]
509 fn test_remove_row_shifts_up() {
510 let mut ws = sample_ws();
511 remove_row(&mut ws, 2).unwrap();
512
513 assert_eq!(ws.sheet_data.rows[0].r, 1);
515 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
516
517 assert_eq!(ws.sheet_data.rows.len(), 2);
519 assert_eq!(ws.sheet_data.rows[1].r, 4);
520 assert_eq!(ws.sheet_data.rows[1].cells[0].r, "C4");
521 }
522
523 #[test]
524 fn test_remove_first_row() {
525 let mut ws = sample_ws();
526 remove_row(&mut ws, 1).unwrap();
527
528 assert_eq!(ws.sheet_data.rows[0].r, 1);
530 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
531 assert_eq!(ws.sheet_data.rows[1].r, 4);
532 }
533
534 #[test]
535 fn test_remove_nonexistent_row_still_shifts() {
536 let mut ws = sample_ws();
537 remove_row(&mut ws, 3).unwrap();
539 assert_eq!(ws.sheet_data.rows.len(), 3); assert_eq!(ws.sheet_data.rows[2].r, 4); }
542
543 #[test]
544 fn test_remove_row_zero_returns_error() {
545 let mut ws = sample_ws();
546 let result = remove_row(&mut ws, 0);
547 assert!(result.is_err());
548 }
549
550 #[test]
551 fn test_duplicate_row_inserts_copy_below() {
552 let mut ws = sample_ws();
553 duplicate_row(&mut ws, 1).unwrap();
554
555 assert_eq!(ws.sheet_data.rows[0].r, 1);
557 assert_eq!(ws.sheet_data.rows[0].cells[0].r, "A1");
558 assert_eq!(ws.sheet_data.rows[0].cells[0].v, Some("10".to_string()));
559
560 assert_eq!(ws.sheet_data.rows[1].r, 2);
562 assert_eq!(ws.sheet_data.rows[1].cells[0].r, "A2");
563 assert_eq!(ws.sheet_data.rows[1].cells[0].v, Some("10".to_string()));
564 assert_eq!(ws.sheet_data.rows[1].cells.len(), 2);
565
566 assert_eq!(ws.sheet_data.rows[2].r, 3);
568 assert_eq!(ws.sheet_data.rows[2].cells[0].r, "A3");
569 }
570
571 #[test]
572 fn test_duplicate_row_to_specific_target() {
573 let mut ws = sample_ws();
574 duplicate_row_to(&mut ws, 1, 5).unwrap();
575
576 assert_eq!(ws.sheet_data.rows[0].r, 1);
578
579 let row5 = ws.sheet_data.rows.iter().find(|r| r.r == 5).unwrap();
581 assert_eq!(row5.cells[0].r, "A5");
582 assert_eq!(row5.cells[0].v, Some("10".to_string()));
583
584 let row6 = ws.sheet_data.rows.iter().find(|r| r.r == 6).unwrap();
586 assert_eq!(row6.cells[0].r, "C6");
587 }
588
589 #[test]
590 fn test_duplicate_nonexistent_row_returns_error() {
591 let mut ws = sample_ws();
592 let result = duplicate_row(&mut ws, 99);
593 assert!(result.is_err());
594 }
595
596 #[test]
597 fn test_set_and_get_row_height() {
598 let mut ws = sample_ws();
599 set_row_height(&mut ws, 1, 25.5).unwrap();
600
601 assert_eq!(get_row_height(&ws, 1), Some(25.5));
602 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
603 assert_eq!(row.custom_height, Some(true));
604 }
605
606 #[test]
607 fn test_set_row_height_creates_row_if_missing() {
608 let mut ws = WorksheetXml::default();
609 set_row_height(&mut ws, 10, 30.0).unwrap();
610
611 assert_eq!(get_row_height(&ws, 10), Some(30.0));
612 assert_eq!(ws.sheet_data.rows.len(), 1);
613 assert_eq!(ws.sheet_data.rows[0].r, 10);
614 }
615
616 #[test]
617 fn test_set_row_height_zero_is_valid() {
618 let mut ws = WorksheetXml::default();
619 set_row_height(&mut ws, 1, 0.0).unwrap();
620 assert_eq!(get_row_height(&ws, 1), Some(0.0));
621 }
622
623 #[test]
624 fn test_set_row_height_max_is_valid() {
625 let mut ws = WorksheetXml::default();
626 set_row_height(&mut ws, 1, 409.0).unwrap();
627 assert_eq!(get_row_height(&ws, 1), Some(409.0));
628 }
629
630 #[test]
631 fn test_set_row_height_exceeds_max_returns_error() {
632 let mut ws = WorksheetXml::default();
633 let result = set_row_height(&mut ws, 1, 410.0);
634 assert!(result.is_err());
635 assert!(matches!(
636 result.unwrap_err(),
637 Error::RowHeightExceeded { .. }
638 ));
639 }
640
641 #[test]
642 fn test_set_row_height_negative_returns_error() {
643 let mut ws = WorksheetXml::default();
644 let result = set_row_height(&mut ws, 1, -1.0);
645 assert!(result.is_err());
646 }
647
648 #[test]
649 fn test_set_row_height_row_zero_returns_error() {
650 let mut ws = WorksheetXml::default();
651 let result = set_row_height(&mut ws, 0, 15.0);
652 assert!(result.is_err());
653 }
654
655 #[test]
656 fn test_get_row_height_nonexistent_returns_none() {
657 let ws = WorksheetXml::default();
658 assert_eq!(get_row_height(&ws, 99), None);
659 }
660
661 #[test]
662 fn test_set_row_hidden() {
663 let mut ws = sample_ws();
664 set_row_visible(&mut ws, 1, false).unwrap();
665
666 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
667 assert_eq!(row.hidden, Some(true));
668 }
669
670 #[test]
671 fn test_set_row_visible_clears_hidden() {
672 let mut ws = sample_ws();
673 set_row_visible(&mut ws, 1, false).unwrap();
674 set_row_visible(&mut ws, 1, true).unwrap();
675
676 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
677 assert_eq!(row.hidden, None);
678 }
679
680 #[test]
681 fn test_set_row_visible_creates_row_if_missing() {
682 let mut ws = WorksheetXml::default();
683 set_row_visible(&mut ws, 3, false).unwrap();
684 assert_eq!(ws.sheet_data.rows.len(), 1);
685 assert_eq!(ws.sheet_data.rows[0].r, 3);
686 assert_eq!(ws.sheet_data.rows[0].hidden, Some(true));
687 }
688
689 #[test]
690 fn test_set_row_visible_row_zero_returns_error() {
691 let mut ws = WorksheetXml::default();
692 let result = set_row_visible(&mut ws, 0, true);
693 assert!(result.is_err());
694 }
695
696 #[test]
697 fn test_set_row_outline_level() {
698 let mut ws = sample_ws();
699 set_row_outline_level(&mut ws, 1, 3).unwrap();
700
701 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
702 assert_eq!(row.outline_level, Some(3));
703 }
704
705 #[test]
706 fn test_set_row_outline_level_zero_clears() {
707 let mut ws = sample_ws();
708 set_row_outline_level(&mut ws, 1, 3).unwrap();
709 set_row_outline_level(&mut ws, 1, 0).unwrap();
710
711 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
712 assert_eq!(row.outline_level, None);
713 }
714
715 #[test]
716 fn test_set_row_outline_level_exceeds_max_returns_error() {
717 let mut ws = sample_ws();
718 let result = set_row_outline_level(&mut ws, 1, 8);
719 assert!(result.is_err());
720 }
721
722 #[test]
723 fn test_set_row_outline_level_row_zero_returns_error() {
724 let mut ws = WorksheetXml::default();
725 let result = set_row_outline_level(&mut ws, 0, 1);
726 assert!(result.is_err());
727 }
728
729 #[test]
730 fn test_get_row_visible_default_is_true() {
731 let ws = sample_ws();
732 assert!(get_row_visible(&ws, 1));
733 }
734
735 #[test]
736 fn test_get_row_visible_nonexistent_row_is_true() {
737 let ws = WorksheetXml::default();
738 assert!(get_row_visible(&ws, 99));
739 }
740
741 #[test]
742 fn test_get_row_visible_after_hide() {
743 let mut ws = sample_ws();
744 set_row_visible(&mut ws, 1, false).unwrap();
745 assert!(!get_row_visible(&ws, 1));
746 }
747
748 #[test]
749 fn test_get_row_visible_after_hide_then_show() {
750 let mut ws = sample_ws();
751 set_row_visible(&mut ws, 1, false).unwrap();
752 set_row_visible(&mut ws, 1, true).unwrap();
753 assert!(get_row_visible(&ws, 1));
754 }
755
756 #[test]
757 fn test_get_row_outline_level_default_is_zero() {
758 let ws = sample_ws();
759 assert_eq!(get_row_outline_level(&ws, 1), 0);
760 }
761
762 #[test]
763 fn test_get_row_outline_level_nonexistent_row() {
764 let ws = WorksheetXml::default();
765 assert_eq!(get_row_outline_level(&ws, 99), 0);
766 }
767
768 #[test]
769 fn test_get_row_outline_level_after_set() {
770 let mut ws = sample_ws();
771 set_row_outline_level(&mut ws, 1, 5).unwrap();
772 assert_eq!(get_row_outline_level(&ws, 1), 5);
773 }
774
775 #[test]
776 fn test_get_row_outline_level_after_clear() {
777 let mut ws = sample_ws();
778 set_row_outline_level(&mut ws, 1, 3).unwrap();
779 set_row_outline_level(&mut ws, 1, 0).unwrap();
780 assert_eq!(get_row_outline_level(&ws, 1), 0);
781 }
782
783 #[test]
786 fn test_get_rows_empty_sheet() {
787 let ws = WorksheetXml::default();
788 let sst = SharedStringTable::new();
789 let rows = get_rows(&ws, &sst).unwrap();
790 assert!(rows.is_empty());
791 }
792
793 #[test]
794 fn test_get_rows_returns_numeric_values() {
795 let ws = sample_ws();
796 let sst = SharedStringTable::new();
797 let rows = get_rows(&ws, &sst).unwrap();
798
799 assert_eq!(rows.len(), 3);
800
801 assert_eq!(rows[0].0, 1);
803 assert_eq!(rows[0].1.len(), 2);
804 assert_eq!(rows[0].1[0].0, "A");
805 assert_eq!(rows[0].1[0].1, CellValue::Number(10.0));
806 assert_eq!(rows[0].1[1].0, "B");
807 assert_eq!(rows[0].1[1].1, CellValue::Number(20.0));
808
809 assert_eq!(rows[1].0, 2);
811 assert_eq!(rows[1].1.len(), 1);
812 assert_eq!(rows[1].1[0].0, "A");
813 assert_eq!(rows[1].1[0].1, CellValue::Number(30.0));
814
815 assert_eq!(rows[2].0, 5);
817 assert_eq!(rows[2].1.len(), 1);
818 assert_eq!(rows[2].1[0].0, "C");
819 assert_eq!(rows[2].1[0].1, CellValue::Number(50.0));
820 }
821
822 #[test]
823 fn test_get_rows_shared_strings() {
824 let mut sst = SharedStringTable::new();
825 sst.add("hello");
826 sst.add("world");
827
828 let mut ws = WorksheetXml::default();
829 ws.sheet_data = SheetData {
830 rows: vec![Row {
831 r: 1,
832 spans: None,
833 s: None,
834 custom_format: None,
835 ht: None,
836 hidden: None,
837 custom_height: None,
838 outline_level: None,
839 cells: vec![
840 Cell {
841 r: "A1".to_string(),
842 s: None,
843 t: Some("s".to_string()),
844 v: Some("0".to_string()),
845 f: None,
846 is: None,
847 },
848 Cell {
849 r: "B1".to_string(),
850 s: None,
851 t: Some("s".to_string()),
852 v: Some("1".to_string()),
853 f: None,
854 is: None,
855 },
856 ],
857 }],
858 };
859
860 let rows = get_rows(&ws, &sst).unwrap();
861 assert_eq!(rows.len(), 1);
862 assert_eq!(rows[0].1[0].1, CellValue::String("hello".to_string()));
863 assert_eq!(rows[0].1[1].1, CellValue::String("world".to_string()));
864 }
865
866 #[test]
867 fn test_get_rows_mixed_types() {
868 let mut sst = SharedStringTable::new();
869 sst.add("text");
870
871 let mut ws = WorksheetXml::default();
872 ws.sheet_data = SheetData {
873 rows: vec![Row {
874 r: 1,
875 spans: None,
876 s: None,
877 custom_format: None,
878 ht: None,
879 hidden: None,
880 custom_height: None,
881 outline_level: None,
882 cells: vec![
883 Cell {
884 r: "A1".to_string(),
885 s: None,
886 t: Some("s".to_string()),
887 v: Some("0".to_string()),
888 f: None,
889 is: None,
890 },
891 Cell {
892 r: "B1".to_string(),
893 s: None,
894 t: None,
895 v: Some("42.5".to_string()),
896 f: None,
897 is: None,
898 },
899 Cell {
900 r: "C1".to_string(),
901 s: None,
902 t: Some("b".to_string()),
903 v: Some("1".to_string()),
904 f: None,
905 is: None,
906 },
907 Cell {
908 r: "D1".to_string(),
909 s: None,
910 t: Some("e".to_string()),
911 v: Some("#DIV/0!".to_string()),
912 f: None,
913 is: None,
914 },
915 ],
916 }],
917 };
918
919 let rows = get_rows(&ws, &sst).unwrap();
920 assert_eq!(rows.len(), 1);
921 assert_eq!(rows[0].1[0].1, CellValue::String("text".to_string()));
922 assert_eq!(rows[0].1[1].1, CellValue::Number(42.5));
923 assert_eq!(rows[0].1[2].1, CellValue::Bool(true));
924 assert_eq!(rows[0].1[3].1, CellValue::Error("#DIV/0!".to_string()));
925 }
926
927 #[test]
928 fn test_get_rows_skips_rows_with_no_cells() {
929 let mut ws = WorksheetXml::default();
930 ws.sheet_data = SheetData {
931 rows: vec![
932 Row {
933 r: 1,
934 spans: None,
935 s: None,
936 custom_format: None,
937 ht: None,
938 hidden: None,
939 custom_height: None,
940 outline_level: None,
941 cells: vec![Cell {
942 r: "A1".to_string(),
943 s: None,
944 t: None,
945 v: Some("1".to_string()),
946 f: None,
947 is: None,
948 }],
949 },
950 Row {
952 r: 2,
953 spans: None,
954 s: None,
955 custom_format: None,
956 ht: Some(30.0),
957 hidden: None,
958 custom_height: Some(true),
959 outline_level: None,
960 cells: vec![],
961 },
962 Row {
963 r: 3,
964 spans: None,
965 s: None,
966 custom_format: None,
967 ht: None,
968 hidden: None,
969 custom_height: None,
970 outline_level: None,
971 cells: vec![Cell {
972 r: "A3".to_string(),
973 s: None,
974 t: None,
975 v: Some("3".to_string()),
976 f: None,
977 is: None,
978 }],
979 },
980 ],
981 };
982
983 let sst = SharedStringTable::new();
984 let rows = get_rows(&ws, &sst).unwrap();
985 assert_eq!(rows.len(), 2);
987 assert_eq!(rows[0].0, 1);
988 assert_eq!(rows[1].0, 3);
989 }
990
991 #[test]
992 fn test_get_rows_with_formula() {
993 let mut ws = WorksheetXml::default();
994 ws.sheet_data = SheetData {
995 rows: vec![Row {
996 r: 1,
997 spans: None,
998 s: None,
999 custom_format: None,
1000 ht: None,
1001 hidden: None,
1002 custom_height: None,
1003 outline_level: None,
1004 cells: vec![Cell {
1005 r: "A1".to_string(),
1006 s: None,
1007 t: None,
1008 v: Some("42".to_string()),
1009 f: Some(sheetkit_xml::worksheet::CellFormula {
1010 t: None,
1011 reference: None,
1012 si: None,
1013 value: Some("B1+C1".to_string()),
1014 }),
1015 is: None,
1016 }],
1017 }],
1018 };
1019
1020 let sst = SharedStringTable::new();
1021 let rows = get_rows(&ws, &sst).unwrap();
1022 assert_eq!(rows.len(), 1);
1023 match &rows[0].1[0].1 {
1024 CellValue::Formula { expr, result } => {
1025 assert_eq!(expr, "B1+C1");
1026 assert_eq!(*result, Some(Box::new(CellValue::Number(42.0))));
1027 }
1028 _ => panic!("expected Formula"),
1029 }
1030 }
1031
1032 #[test]
1033 fn test_get_rows_with_inline_string() {
1034 let mut ws = WorksheetXml::default();
1035 ws.sheet_data = SheetData {
1036 rows: vec![Row {
1037 r: 1,
1038 spans: None,
1039 s: None,
1040 custom_format: None,
1041 ht: None,
1042 hidden: None,
1043 custom_height: None,
1044 outline_level: None,
1045 cells: vec![Cell {
1046 r: "A1".to_string(),
1047 s: None,
1048 t: Some("inlineStr".to_string()),
1049 v: None,
1050 f: None,
1051 is: Some(sheetkit_xml::worksheet::InlineString {
1052 t: Some("inline text".to_string()),
1053 }),
1054 }],
1055 }],
1056 };
1057
1058 let sst = SharedStringTable::new();
1059 let rows = get_rows(&ws, &sst).unwrap();
1060 assert_eq!(rows.len(), 1);
1061 assert_eq!(rows[0].1[0].1, CellValue::String("inline text".to_string()));
1062 }
1063
1064 #[test]
1067 fn test_get_row_style_default_is_zero() {
1068 let ws = WorksheetXml::default();
1069 assert_eq!(get_row_style(&ws, 1), 0);
1070 }
1071
1072 #[test]
1073 fn test_get_row_style_nonexistent_row_is_zero() {
1074 let ws = sample_ws();
1075 assert_eq!(get_row_style(&ws, 99), 0);
1076 }
1077
1078 #[test]
1079 fn test_set_row_style_applies_style() {
1080 let mut ws = sample_ws();
1081 set_row_style(&mut ws, 1, 5).unwrap();
1082
1083 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
1084 assert_eq!(row.s, Some(5));
1085 assert_eq!(row.custom_format, Some(true));
1086 }
1087
1088 #[test]
1089 fn test_set_row_style_applies_to_existing_cells() {
1090 let mut ws = sample_ws();
1091 set_row_style(&mut ws, 1, 3).unwrap();
1092
1093 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
1094 for cell in &row.cells {
1095 assert_eq!(cell.s, Some(3));
1096 }
1097 }
1098
1099 #[test]
1100 fn test_get_row_style_after_set() {
1101 let mut ws = sample_ws();
1102 set_row_style(&mut ws, 2, 7).unwrap();
1103 assert_eq!(get_row_style(&ws, 2), 7);
1104 }
1105
1106 #[test]
1107 fn test_set_row_style_creates_row_if_missing() {
1108 let mut ws = WorksheetXml::default();
1109 set_row_style(&mut ws, 5, 2).unwrap();
1110
1111 assert_eq!(ws.sheet_data.rows.len(), 1);
1112 assert_eq!(ws.sheet_data.rows[0].r, 5);
1113 assert_eq!(ws.sheet_data.rows[0].s, Some(2));
1114 }
1115
1116 #[test]
1117 fn test_set_row_style_zero_clears_custom_format() {
1118 let mut ws = sample_ws();
1119 set_row_style(&mut ws, 1, 5).unwrap();
1120 set_row_style(&mut ws, 1, 0).unwrap();
1121
1122 let row = ws.sheet_data.rows.iter().find(|r| r.r == 1).unwrap();
1123 assert_eq!(row.s, Some(0));
1124 assert_eq!(row.custom_format, None);
1125 }
1126
1127 #[test]
1128 fn test_set_row_style_row_zero_returns_error() {
1129 let mut ws = WorksheetXml::default();
1130 let result = set_row_style(&mut ws, 0, 1);
1131 assert!(result.is_err());
1132 }
1133}