1use super::*;
2
3impl Workbook {
4 pub fn get_cell_value(&self, sheet: &str, cell: &str) -> Result<CellValue> {
9 let ws = self.worksheet_ref(sheet)?;
10
11 let (col, row) = cell_name_to_coordinates(cell)?;
12
13 let xml_row = match ws.sheet_data.rows.binary_search_by_key(&row, |r| r.r) {
15 Ok(idx) => &ws.sheet_data.rows[idx],
16 Err(_) => return Ok(CellValue::Empty),
17 };
18
19 let xml_cell = match xml_row.cells.binary_search_by_key(&col, |c| c.col) {
21 Ok(idx) => &xml_row.cells[idx],
22 Err(_) => return Ok(CellValue::Empty),
23 };
24
25 self.xml_cell_to_value(xml_cell)
26 }
27
28 pub fn set_cell_value(
35 &mut self,
36 sheet: &str,
37 cell: &str,
38 value: impl Into<CellValue>,
39 ) -> Result<()> {
40 let value = value.into();
41
42 if let CellValue::String(ref s) = value {
44 if s.len() > MAX_CELL_CHARS {
45 return Err(Error::CellValueTooLong {
46 length: s.len(),
47 max: MAX_CELL_CHARS,
48 });
49 }
50 }
51
52 let sheet_idx = self.sheet_index(sheet)?;
53 let ws = &mut self.worksheets[sheet_idx].1;
54
55 let (col, row_num) = cell_name_to_coordinates(cell)?;
56 let cell_ref = crate::utils::cell_ref::coordinates_to_cell_name(col, row_num)?;
57
58 let row_idx = match ws.sheet_data.rows.binary_search_by_key(&row_num, |r| r.r) {
60 Ok(idx) => idx,
61 Err(idx) => {
62 ws.sheet_data.rows.insert(idx, new_row(row_num));
63 idx
64 }
65 };
66
67 let row = &mut ws.sheet_data.rows[row_idx];
68
69 if value == CellValue::Empty {
71 if let Ok(idx) = row.cells.binary_search_by_key(&col, |c| c.col) {
72 row.cells.remove(idx);
73 }
74 return Ok(());
75 }
76
77 let cell_idx = match row.cells.binary_search_by_key(&col, |c| c.col) {
79 Ok(idx) => idx,
80 Err(insert_pos) => {
81 row.cells.insert(
82 insert_pos,
83 Cell {
84 r: cell_ref.into(),
85 col,
86 s: None,
87 t: CellTypeTag::None,
88 v: None,
89 f: None,
90 is: None,
91 },
92 );
93 insert_pos
94 }
95 };
96
97 let xml_cell = &mut row.cells[cell_idx];
98 value_to_xml_cell(&mut self.sst_runtime, xml_cell, value);
99
100 Ok(())
101 }
102
103 pub(crate) fn xml_cell_to_value(&self, xml_cell: &Cell) -> Result<CellValue> {
105 if let Some(ref formula) = xml_cell.f {
107 let expr = formula.value.clone().unwrap_or_default();
108 let result = match (xml_cell.t, &xml_cell.v) {
109 (CellTypeTag::Boolean, Some(v)) => Some(Box::new(CellValue::Bool(v == "1"))),
110 (CellTypeTag::Error, Some(v)) => Some(Box::new(CellValue::Error(v.clone()))),
111 (_, Some(v)) => v
112 .parse::<f64>()
113 .ok()
114 .map(|n| Box::new(CellValue::Number(n))),
115 _ => None,
116 };
117 return Ok(CellValue::Formula { expr, result });
118 }
119
120 let cell_value = xml_cell.v.as_deref();
121
122 match (xml_cell.t, cell_value) {
123 (CellTypeTag::SharedString, Some(v)) => {
125 let idx: usize = v
126 .parse()
127 .map_err(|_| Error::Internal(format!("invalid SST index: {v}")))?;
128 let s = self.sst_runtime.get(idx).unwrap_or("").to_string();
129 Ok(CellValue::String(s))
130 }
131 (CellTypeTag::Boolean, Some(v)) => Ok(CellValue::Bool(v == "1")),
133 (CellTypeTag::Error, Some(v)) => Ok(CellValue::Error(v.to_string())),
135 (CellTypeTag::InlineString, _) => {
137 let s = xml_cell
138 .is
139 .as_ref()
140 .and_then(|is| is.t.clone())
141 .unwrap_or_default();
142 Ok(CellValue::String(s))
143 }
144 (CellTypeTag::FormulaString, Some(v)) => Ok(CellValue::String(v.to_string())),
146 (CellTypeTag::None | CellTypeTag::Number, Some(v)) => {
148 let n: f64 = v
149 .parse()
150 .map_err(|_| Error::Internal(format!("invalid number: {v}")))?;
151 if self.is_date_styled_cell(xml_cell) {
153 return Ok(CellValue::Date(n));
154 }
155 Ok(CellValue::Number(n))
156 }
157 _ => Ok(CellValue::Empty),
159 }
160 }
161
162 pub(crate) fn is_date_styled_cell(&self, xml_cell: &Cell) -> bool {
164 let style_idx = match xml_cell.s {
165 Some(idx) => idx as usize,
166 None => return false,
167 };
168 let xf = match self.stylesheet.cell_xfs.xfs.get(style_idx) {
169 Some(xf) => xf,
170 None => return false,
171 };
172 let num_fmt_id = xf.num_fmt_id.unwrap_or(0);
173 if crate::cell::is_date_num_fmt(num_fmt_id) {
175 return true;
176 }
177 if num_fmt_id >= 164 {
179 if let Some(ref num_fmts) = self.stylesheet.num_fmts {
180 if let Some(nf) = num_fmts
181 .num_fmts
182 .iter()
183 .find(|nf| nf.num_fmt_id == num_fmt_id)
184 {
185 return crate::cell::is_date_format_code(&nf.format_code);
186 }
187 }
188 }
189 false
190 }
191
192 pub fn add_style(&mut self, style: &crate::style::Style) -> Result<u32> {
197 crate::style::add_style(&mut self.stylesheet, style)
198 }
199
200 pub fn get_cell_style(&self, sheet: &str, cell: &str) -> Result<Option<u32>> {
205 let ws = self.worksheet_ref(sheet)?;
206
207 let (col, row) = cell_name_to_coordinates(cell)?;
208
209 let xml_row = match ws.sheet_data.rows.binary_search_by_key(&row, |r| r.r) {
211 Ok(idx) => &ws.sheet_data.rows[idx],
212 Err(_) => return Ok(None),
213 };
214
215 let xml_cell = match xml_row.cells.binary_search_by_key(&col, |c| c.col) {
217 Ok(idx) => &xml_row.cells[idx],
218 Err(_) => return Ok(None),
219 };
220
221 Ok(xml_cell.s)
222 }
223
224 pub fn set_cell_style(&mut self, sheet: &str, cell: &str, style_id: u32) -> Result<()> {
229 if style_id as usize >= self.stylesheet.cell_xfs.xfs.len() {
231 return Err(Error::StyleNotFound { id: style_id });
232 }
233
234 let sheet_idx = self.sheet_index(sheet)?;
235 let ws = &mut self.worksheets[sheet_idx].1;
236
237 let (col, row_num) = cell_name_to_coordinates(cell)?;
238 let cell_ref = crate::utils::cell_ref::coordinates_to_cell_name(col, row_num)?;
239
240 let row_idx = match ws.sheet_data.rows.binary_search_by_key(&row_num, |r| r.r) {
242 Ok(idx) => idx,
243 Err(idx) => {
244 ws.sheet_data.rows.insert(idx, new_row(row_num));
245 idx
246 }
247 };
248
249 let row = &mut ws.sheet_data.rows[row_idx];
250
251 let cell_idx = match row.cells.binary_search_by_key(&col, |c| c.col) {
253 Ok(idx) => idx,
254 Err(insert_pos) => {
255 row.cells.insert(
256 insert_pos,
257 Cell {
258 r: cell_ref.into(),
259 col,
260 s: None,
261 t: CellTypeTag::None,
262 v: None,
263 f: None,
264 is: None,
265 },
266 );
267 insert_pos
268 }
269 };
270
271 row.cells[cell_idx].s = Some(style_id);
272 Ok(())
273 }
274
275 pub fn merge_cells(&mut self, sheet: &str, top_left: &str, bottom_right: &str) -> Result<()> {
280 let ws = self.worksheet_mut(sheet)?;
281 crate::merge::merge_cells(ws, top_left, bottom_right)
282 }
283
284 pub fn unmerge_cell(&mut self, sheet: &str, reference: &str) -> Result<()> {
288 let ws = self.worksheet_mut(sheet)?;
289 crate::merge::unmerge_cell(ws, reference)
290 }
291
292 pub fn get_merge_cells(&self, sheet: &str) -> Result<Vec<String>> {
296 let ws = self.worksheet_ref(sheet)?;
297 Ok(crate::merge::get_merge_cells(ws))
298 }
299
300 pub fn set_cell_formula(&mut self, sheet: &str, cell: &str, formula: &str) -> Result<()> {
305 self.set_cell_value(
306 sheet,
307 cell,
308 CellValue::Formula {
309 expr: formula.to_string(),
310 result: None,
311 },
312 )
313 }
314
315 pub fn fill_formula(&mut self, sheet: &str, range: &str, formula: &str) -> Result<()> {
323 let parts: Vec<&str> = range.split(':').collect();
324 if parts.len() != 2 {
325 return Err(Error::InvalidCellReference(format!(
326 "invalid range: {range}"
327 )));
328 }
329 let (start_col, start_row) = cell_name_to_coordinates(parts[0])?;
330 let (end_col, end_row) = cell_name_to_coordinates(parts[1])?;
331
332 if start_col != end_col {
333 return Err(Error::InvalidCellReference(
334 "fill_formula only supports single-column ranges".to_string(),
335 ));
336 }
337
338 for row in start_row..=end_row {
339 let row_offset = row as i32 - start_row as i32;
340 let adjusted = if row_offset == 0 {
341 formula.to_string()
342 } else {
343 crate::cell_ref_shift::shift_cell_references_with_abs(
344 formula,
345 |col, r, _abs_col, abs_row| {
346 if abs_row {
347 (col, r)
348 } else {
349 (col, (r as i32 + row_offset) as u32)
350 }
351 },
352 )?
353 };
354 let cell_ref = crate::utils::cell_ref::coordinates_to_cell_name(start_col, row)?;
355 self.set_cell_formula(sheet, &cell_ref, &adjusted)?;
356 }
357 Ok(())
358 }
359
360 pub fn set_cell_rich_text(
362 &mut self,
363 sheet: &str,
364 cell: &str,
365 runs: Vec<crate::rich_text::RichTextRun>,
366 ) -> Result<()> {
367 self.set_cell_value(sheet, cell, CellValue::RichString(runs))
368 }
369
370 pub fn get_cell_rich_text(
375 &self,
376 sheet: &str,
377 cell: &str,
378 ) -> Result<Option<Vec<crate::rich_text::RichTextRun>>> {
379 let (col, row) = cell_name_to_coordinates(cell)?;
380 let ws = self.worksheet_ref(sheet)?;
381
382 let xml_row = match ws.sheet_data.rows.binary_search_by_key(&row, |r| r.r) {
384 Ok(idx) => &ws.sheet_data.rows[idx],
385 Err(_) => return Ok(None),
386 };
387
388 let xml_cell = match xml_row.cells.binary_search_by_key(&col, |c| c.col) {
390 Ok(idx) => &xml_row.cells[idx],
391 Err(_) => return Ok(None),
392 };
393
394 if xml_cell.t == CellTypeTag::SharedString {
395 if let Some(ref v) = xml_cell.v {
396 if let Ok(idx) = v.parse::<usize>() {
397 return Ok(self.sst_runtime.get_rich_text(idx));
398 }
399 }
400 }
401 Ok(None)
402 }
403
404 pub fn set_cell_values(
409 &mut self,
410 sheet: &str,
411 entries: Vec<(String, CellValue)>,
412 ) -> Result<()> {
413 let sheet_idx = self.sheet_index(sheet)?;
414
415 for (cell, value) in entries {
416 if let CellValue::String(ref s) = value {
417 if s.len() > MAX_CELL_CHARS {
418 return Err(Error::CellValueTooLong {
419 length: s.len(),
420 max: MAX_CELL_CHARS,
421 });
422 }
423 }
424
425 let (col, row_num) = cell_name_to_coordinates(&cell)?;
426 let cell_ref = crate::utils::cell_ref::coordinates_to_cell_name(col, row_num)?;
427
428 let row_idx = {
429 let ws = &mut self.worksheets[sheet_idx].1;
430 match ws.sheet_data.rows.binary_search_by_key(&row_num, |r| r.r) {
431 Ok(idx) => idx,
432 Err(idx) => {
433 ws.sheet_data.rows.insert(idx, new_row(row_num));
434 idx
435 }
436 }
437 };
438
439 if value == CellValue::Empty {
440 let row = &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx];
441 if let Ok(idx) = row.cells.binary_search_by_key(&col, |c| c.col) {
442 row.cells.remove(idx);
443 }
444 continue;
445 }
446
447 let cell_idx = {
448 let row = &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx];
449 match row.cells.binary_search_by_key(&col, |c| c.col) {
450 Ok(idx) => idx,
451 Err(pos) => {
452 row.cells.insert(
453 pos,
454 Cell {
455 r: cell_ref.into(),
456 col,
457 s: None,
458 t: CellTypeTag::None,
459 v: None,
460 f: None,
461 is: None,
462 },
463 );
464 pos
465 }
466 }
467 };
468
469 let xml_cell =
470 &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx].cells[cell_idx];
471 value_to_xml_cell(&mut self.sst_runtime, xml_cell, value);
472 }
473
474 Ok(())
475 }
476
477 pub fn set_sheet_data(
486 &mut self,
487 sheet: &str,
488 data: Vec<Vec<CellValue>>,
489 start_row: u32,
490 start_col: u32,
491 ) -> Result<()> {
492 let sheet_idx = self.sheet_index(sheet)?;
493
494 let max_cols = data.iter().map(|r| r.len()).max().unwrap_or(0) as u32;
496 let col_names: Vec<String> = (0..max_cols)
497 .map(|i| crate::utils::cell_ref::column_number_to_name(start_col + i))
498 .collect::<Result<Vec<_>>>()?;
499
500 for (row_offset, row_values) in data.into_iter().enumerate() {
501 let row_num = start_row + row_offset as u32;
502
503 let row_idx = {
504 let ws = &mut self.worksheets[sheet_idx].1;
505 match ws.sheet_data.rows.binary_search_by_key(&row_num, |r| r.r) {
506 Ok(idx) => idx,
507 Err(idx) => {
508 ws.sheet_data.rows.insert(idx, new_row(row_num));
509 idx
510 }
511 }
512 };
513
514 for (col_offset, value) in row_values.into_iter().enumerate() {
515 let col = start_col + col_offset as u32;
516
517 if let CellValue::String(ref s) = value {
518 if s.len() > MAX_CELL_CHARS {
519 return Err(Error::CellValueTooLong {
520 length: s.len(),
521 max: MAX_CELL_CHARS,
522 });
523 }
524 }
525
526 if value == CellValue::Empty {
527 let row = &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx];
528 if let Ok(idx) = row.cells.binary_search_by_key(&col, |c| c.col) {
529 row.cells.remove(idx);
530 }
531 continue;
532 }
533
534 let cell_ref = format!("{}{}", col_names[col_offset], row_num);
535
536 let cell_idx = {
537 let row = &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx];
538 match row.cells.binary_search_by_key(&col, |c| c.col) {
539 Ok(idx) => idx,
540 Err(pos) => {
541 row.cells.insert(
542 pos,
543 Cell {
544 r: cell_ref.into(),
545 col,
546 s: None,
547 t: CellTypeTag::None,
548 v: None,
549 f: None,
550 is: None,
551 },
552 );
553 pos
554 }
555 }
556 };
557
558 let xml_cell =
559 &mut self.worksheets[sheet_idx].1.sheet_data.rows[row_idx].cells[cell_idx];
560 value_to_xml_cell(&mut self.sst_runtime, xml_cell, value);
561 }
562 }
563
564 Ok(())
565 }
566
567 pub fn set_row_values(
572 &mut self,
573 sheet: &str,
574 row_num: u32,
575 start_col: u32,
576 values: Vec<CellValue>,
577 ) -> Result<()> {
578 self.set_sheet_data(sheet, vec![values], row_num, start_col)
579 }
580}
581
582pub(crate) fn value_to_xml_cell(
584 sst: &mut SharedStringTable,
585 xml_cell: &mut Cell,
586 value: CellValue,
587) {
588 xml_cell.t = CellTypeTag::None;
590 xml_cell.v = None;
591 xml_cell.f = None;
592 xml_cell.is = None;
593
594 match value {
595 CellValue::String(s) => {
596 let idx = sst.add_owned(s);
597 xml_cell.t = CellTypeTag::SharedString;
598 xml_cell.v = Some(idx.to_string());
599 }
600 CellValue::Number(n) => {
601 xml_cell.v = Some(n.to_string());
602 }
603 CellValue::Date(serial) => {
604 xml_cell.v = Some(serial.to_string());
607 }
608 CellValue::Bool(b) => {
609 xml_cell.t = CellTypeTag::Boolean;
610 xml_cell.v = Some(if b { "1" } else { "0" }.to_string());
611 }
612 CellValue::Formula { expr, .. } => {
613 xml_cell.f = Some(CellFormula {
614 t: None,
615 reference: None,
616 si: None,
617 value: Some(expr),
618 });
619 }
620 CellValue::Error(e) => {
621 xml_cell.t = CellTypeTag::Error;
622 xml_cell.v = Some(e);
623 }
624 CellValue::Empty => {
625 }
627 CellValue::RichString(runs) => {
628 let idx = sst.add_rich_text(&runs);
629 xml_cell.t = CellTypeTag::SharedString;
630 xml_cell.v = Some(idx.to_string());
631 }
632 }
633}
634
635pub(crate) fn new_row(row_num: u32) -> Row {
637 Row {
638 r: row_num,
639 spans: None,
640 s: None,
641 custom_format: None,
642 ht: None,
643 hidden: None,
644 custom_height: None,
645 outline_level: None,
646 cells: vec![],
647 }
648}
649
650#[cfg(test)]
651mod tests {
652 use super::*;
653 use tempfile::TempDir;
654
655 #[test]
656 fn test_set_and_get_string_value() {
657 let mut wb = Workbook::new();
658 wb.set_cell_value("Sheet1", "A1", "Hello").unwrap();
659 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
660 assert_eq!(val, CellValue::String("Hello".to_string()));
661 }
662
663 #[test]
664 fn test_set_and_get_number_value() {
665 let mut wb = Workbook::new();
666 wb.set_cell_value("Sheet1", "B2", 42.5f64).unwrap();
667 let val = wb.get_cell_value("Sheet1", "B2").unwrap();
668 assert_eq!(val, CellValue::Number(42.5));
669 }
670
671 #[test]
672 fn test_set_and_get_bool_value() {
673 let mut wb = Workbook::new();
674 wb.set_cell_value("Sheet1", "C3", true).unwrap();
675 let val = wb.get_cell_value("Sheet1", "C3").unwrap();
676 assert_eq!(val, CellValue::Bool(true));
677
678 wb.set_cell_value("Sheet1", "D4", false).unwrap();
679 let val = wb.get_cell_value("Sheet1", "D4").unwrap();
680 assert_eq!(val, CellValue::Bool(false));
681 }
682
683 #[test]
684 fn test_set_value_sheet_not_found() {
685 let mut wb = Workbook::new();
686 let result = wb.set_cell_value("NoSuchSheet", "A1", "test");
687 assert!(result.is_err());
688 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
689 }
690
691 #[test]
692 fn test_get_value_sheet_not_found() {
693 let wb = Workbook::new();
694 let result = wb.get_cell_value("NoSuchSheet", "A1");
695 assert!(result.is_err());
696 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
697 }
698
699 #[test]
700 fn test_get_empty_cell_returns_empty() {
701 let wb = Workbook::new();
702 let val = wb.get_cell_value("Sheet1", "Z99").unwrap();
703 assert_eq!(val, CellValue::Empty);
704 }
705
706 #[test]
707 fn test_cell_value_roundtrip_save_open() {
708 let dir = TempDir::new().unwrap();
709 let path = dir.path().join("cell_roundtrip.xlsx");
710
711 let mut wb = Workbook::new();
712 wb.set_cell_value("Sheet1", "A1", "Hello").unwrap();
713 wb.set_cell_value("Sheet1", "B1", 42.0f64).unwrap();
714 wb.set_cell_value("Sheet1", "C1", true).unwrap();
715 wb.save(&path).unwrap();
716
717 let wb2 = Workbook::open(&path).unwrap();
718 assert_eq!(
719 wb2.get_cell_value("Sheet1", "A1").unwrap(),
720 CellValue::String("Hello".to_string())
721 );
722 assert_eq!(
723 wb2.get_cell_value("Sheet1", "B1").unwrap(),
724 CellValue::Number(42.0)
725 );
726 assert_eq!(
727 wb2.get_cell_value("Sheet1", "C1").unwrap(),
728 CellValue::Bool(true)
729 );
730 }
731
732 #[test]
733 fn test_set_empty_value_clears_cell() {
734 let mut wb = Workbook::new();
735 wb.set_cell_value("Sheet1", "A1", "test").unwrap();
736 assert_eq!(
737 wb.get_cell_value("Sheet1", "A1").unwrap(),
738 CellValue::String("test".to_string())
739 );
740
741 wb.set_cell_value("Sheet1", "A1", CellValue::Empty).unwrap();
742 assert_eq!(wb.get_cell_value("Sheet1", "A1").unwrap(), CellValue::Empty);
743 }
744
745 #[test]
746 fn test_string_too_long_returns_error() {
747 let mut wb = Workbook::new();
748 let long_string = "x".repeat(MAX_CELL_CHARS + 1);
749 let result = wb.set_cell_value("Sheet1", "A1", long_string.as_str());
750 assert!(result.is_err());
751 assert!(matches!(
752 result.unwrap_err(),
753 Error::CellValueTooLong { .. }
754 ));
755 }
756
757 #[test]
758 fn test_set_multiple_cells_same_row() {
759 let mut wb = Workbook::new();
760 wb.set_cell_value("Sheet1", "A1", "first").unwrap();
761 wb.set_cell_value("Sheet1", "B1", "second").unwrap();
762 wb.set_cell_value("Sheet1", "C1", "third").unwrap();
763
764 assert_eq!(
765 wb.get_cell_value("Sheet1", "A1").unwrap(),
766 CellValue::String("first".to_string())
767 );
768 assert_eq!(
769 wb.get_cell_value("Sheet1", "B1").unwrap(),
770 CellValue::String("second".to_string())
771 );
772 assert_eq!(
773 wb.get_cell_value("Sheet1", "C1").unwrap(),
774 CellValue::String("third".to_string())
775 );
776 }
777
778 #[test]
779 fn test_overwrite_cell_value() {
780 let mut wb = Workbook::new();
781 wb.set_cell_value("Sheet1", "A1", "original").unwrap();
782 wb.set_cell_value("Sheet1", "A1", "updated").unwrap();
783
784 assert_eq!(
785 wb.get_cell_value("Sheet1", "A1").unwrap(),
786 CellValue::String("updated".to_string())
787 );
788 }
789
790 #[test]
791 fn test_set_and_get_error_value() {
792 let mut wb = Workbook::new();
793 wb.set_cell_value("Sheet1", "A1", CellValue::Error("#DIV/0!".to_string()))
794 .unwrap();
795 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
796 assert_eq!(val, CellValue::Error("#DIV/0!".to_string()));
797 }
798
799 #[test]
800 fn test_set_and_get_date_value() {
801 use crate::style::{builtin_num_fmts, NumFmtStyle, Style};
802
803 let mut wb = Workbook::new();
804 let style_id = wb
806 .add_style(&Style {
807 num_fmt: Some(NumFmtStyle::Builtin(builtin_num_fmts::DATE_MDY)),
808 ..Style::default()
809 })
810 .unwrap();
811
812 let date_serial =
814 crate::cell::date_to_serial(chrono::NaiveDate::from_ymd_opt(2024, 6, 15).unwrap());
815 wb.set_cell_value("Sheet1", "A1", CellValue::Date(date_serial))
816 .unwrap();
817 wb.set_cell_style("Sheet1", "A1", style_id).unwrap();
818
819 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
821 assert_eq!(val, CellValue::Date(date_serial));
822 }
823
824 #[test]
825 fn test_date_value_without_style_returns_number() {
826 let mut wb = Workbook::new();
827 let date_serial =
829 crate::cell::date_to_serial(chrono::NaiveDate::from_ymd_opt(2024, 6, 15).unwrap());
830 wb.set_cell_value("Sheet1", "A1", CellValue::Date(date_serial))
831 .unwrap();
832
833 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
835 assert_eq!(val, CellValue::Number(date_serial));
836 }
837
838 #[test]
839 fn test_date_value_roundtrip_through_save() {
840 use crate::style::{builtin_num_fmts, NumFmtStyle, Style};
841
842 let mut wb = Workbook::new();
843 let style_id = wb
844 .add_style(&Style {
845 num_fmt: Some(NumFmtStyle::Builtin(builtin_num_fmts::DATETIME)),
846 ..Style::default()
847 })
848 .unwrap();
849
850 let dt = chrono::NaiveDate::from_ymd_opt(2024, 3, 15)
851 .unwrap()
852 .and_hms_opt(14, 30, 0)
853 .unwrap();
854 let serial = crate::cell::datetime_to_serial(dt);
855 wb.set_cell_value("Sheet1", "A1", CellValue::Date(serial))
856 .unwrap();
857 wb.set_cell_style("Sheet1", "A1", style_id).unwrap();
858
859 let tmp = tempfile::NamedTempFile::new().unwrap();
860 let path = tmp.path().to_str().unwrap();
861 wb.save(path).unwrap();
862
863 let wb2 = Workbook::open(path).unwrap();
864 let val = wb2.get_cell_value("Sheet1", "A1").unwrap();
865 assert_eq!(val, CellValue::Date(serial));
866 }
867
868 #[test]
869 fn test_date_from_naive_date_conversion() {
870 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
871 let cv: CellValue = date.into();
872 match cv {
873 CellValue::Date(s) => {
874 let roundtripped = crate::cell::serial_to_date(s).unwrap();
875 assert_eq!(roundtripped, date);
876 }
877 _ => panic!("expected Date variant"),
878 }
879 }
880
881 #[test]
882 fn test_set_and_get_formula_value() {
883 let mut wb = Workbook::new();
884 wb.set_cell_value(
885 "Sheet1",
886 "A1",
887 CellValue::Formula {
888 expr: "SUM(B1:B10)".to_string(),
889 result: None,
890 },
891 )
892 .unwrap();
893 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
894 match val {
895 CellValue::Formula { expr, .. } => {
896 assert_eq!(expr, "SUM(B1:B10)");
897 }
898 other => panic!("expected Formula, got {:?}", other),
899 }
900 }
901
902 #[test]
903 fn test_set_i32_value() {
904 let mut wb = Workbook::new();
905 wb.set_cell_value("Sheet1", "A1", 100i32).unwrap();
906 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
907 assert_eq!(val, CellValue::Number(100.0));
908 }
909
910 #[test]
911 fn test_set_string_at_max_length() {
912 let mut wb = Workbook::new();
913 let max_string = "x".repeat(MAX_CELL_CHARS);
914 wb.set_cell_value("Sheet1", "A1", max_string.as_str())
915 .unwrap();
916 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
917 assert_eq!(val, CellValue::String(max_string));
918 }
919
920 #[test]
921 fn test_set_cells_different_rows() {
922 let mut wb = Workbook::new();
923 wb.set_cell_value("Sheet1", "A1", "row1").unwrap();
924 wb.set_cell_value("Sheet1", "A3", "row3").unwrap();
925 wb.set_cell_value("Sheet1", "A2", "row2").unwrap(); assert_eq!(
928 wb.get_cell_value("Sheet1", "A1").unwrap(),
929 CellValue::String("row1".to_string())
930 );
931 assert_eq!(
932 wb.get_cell_value("Sheet1", "A2").unwrap(),
933 CellValue::String("row2".to_string())
934 );
935 assert_eq!(
936 wb.get_cell_value("Sheet1", "A3").unwrap(),
937 CellValue::String("row3".to_string())
938 );
939 }
940
941 #[test]
942 fn test_string_deduplication_in_sst() {
943 let mut wb = Workbook::new();
944 wb.set_cell_value("Sheet1", "A1", "same").unwrap();
945 wb.set_cell_value("Sheet1", "A2", "same").unwrap();
946 wb.set_cell_value("Sheet1", "A3", "different").unwrap();
947
948 assert_eq!(wb.sst_runtime.len(), 2);
950 assert_eq!(
951 wb.get_cell_value("Sheet1", "A1").unwrap(),
952 CellValue::String("same".to_string())
953 );
954 assert_eq!(
955 wb.get_cell_value("Sheet1", "A2").unwrap(),
956 CellValue::String("same".to_string())
957 );
958 }
959
960 #[test]
961 fn test_add_style_returns_id() {
962 use crate::style::{FontStyle, Style};
963
964 let mut wb = Workbook::new();
965 let style = Style {
966 font: Some(FontStyle {
967 bold: true,
968 ..FontStyle::default()
969 }),
970 ..Style::default()
971 };
972 let id = wb.add_style(&style).unwrap();
973 assert!(id > 0);
974 }
975
976 #[test]
977 fn test_get_cell_style_unstyled_cell_returns_none() {
978 let wb = Workbook::new();
979 let result = wb.get_cell_style("Sheet1", "A1").unwrap();
980 assert!(result.is_none());
981 }
982
983 #[test]
984 fn test_set_cell_style_on_existing_value() {
985 use crate::style::{FontStyle, Style};
986
987 let mut wb = Workbook::new();
988 wb.set_cell_value("Sheet1", "A1", "Hello").unwrap();
989
990 let style = Style {
991 font: Some(FontStyle {
992 bold: true,
993 ..FontStyle::default()
994 }),
995 ..Style::default()
996 };
997 let style_id = wb.add_style(&style).unwrap();
998 wb.set_cell_style("Sheet1", "A1", style_id).unwrap();
999
1000 let retrieved_id = wb.get_cell_style("Sheet1", "A1").unwrap();
1001 assert_eq!(retrieved_id, Some(style_id));
1002
1003 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
1005 assert_eq!(val, CellValue::String("Hello".to_string()));
1006 }
1007
1008 #[test]
1009 fn test_set_cell_style_on_empty_cell_creates_cell() {
1010 use crate::style::{FontStyle, Style};
1011
1012 let mut wb = Workbook::new();
1013 let style = Style {
1014 font: Some(FontStyle {
1015 bold: true,
1016 ..FontStyle::default()
1017 }),
1018 ..Style::default()
1019 };
1020 let style_id = wb.add_style(&style).unwrap();
1021
1022 wb.set_cell_style("Sheet1", "B5", style_id).unwrap();
1024
1025 let retrieved_id = wb.get_cell_style("Sheet1", "B5").unwrap();
1026 assert_eq!(retrieved_id, Some(style_id));
1027
1028 let val = wb.get_cell_value("Sheet1", "B5").unwrap();
1030 assert_eq!(val, CellValue::Empty);
1031 }
1032
1033 #[test]
1034 fn test_set_cell_style_invalid_id() {
1035 let mut wb = Workbook::new();
1036 let result = wb.set_cell_style("Sheet1", "A1", 999);
1037 assert!(result.is_err());
1038 assert!(matches!(result.unwrap_err(), Error::StyleNotFound { .. }));
1039 }
1040
1041 #[test]
1042 fn test_set_cell_style_sheet_not_found() {
1043 let mut wb = Workbook::new();
1044 let style = crate::style::Style::default();
1045 let style_id = wb.add_style(&style).unwrap();
1046 let result = wb.set_cell_style("NoSuchSheet", "A1", style_id);
1047 assert!(result.is_err());
1048 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
1049 }
1050
1051 #[test]
1052 fn test_get_cell_style_sheet_not_found() {
1053 let wb = Workbook::new();
1054 let result = wb.get_cell_style("NoSuchSheet", "A1");
1055 assert!(result.is_err());
1056 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
1057 }
1058
1059 #[test]
1060 fn test_style_roundtrip_save_open() {
1061 use crate::style::{
1062 AlignmentStyle, BorderLineStyle, BorderSideStyle, BorderStyle, FillStyle, FontStyle,
1063 HorizontalAlign, NumFmtStyle, PatternType, Style, StyleColor, VerticalAlign,
1064 };
1065
1066 let dir = TempDir::new().unwrap();
1067 let path = dir.path().join("style_roundtrip.xlsx");
1068
1069 let mut wb = Workbook::new();
1070 wb.set_cell_value("Sheet1", "A1", "Styled").unwrap();
1071
1072 let style = Style {
1073 font: Some(FontStyle {
1074 name: Some("Arial".to_string()),
1075 size: Some(14.0),
1076 bold: true,
1077 italic: true,
1078 color: Some(StyleColor::Rgb("FFFF0000".to_string())),
1079 ..FontStyle::default()
1080 }),
1081 fill: Some(FillStyle {
1082 pattern: PatternType::Solid,
1083 fg_color: Some(StyleColor::Rgb("FFFFFF00".to_string())),
1084 bg_color: None,
1085 gradient: None,
1086 }),
1087 border: Some(BorderStyle {
1088 left: Some(BorderSideStyle {
1089 style: BorderLineStyle::Thin,
1090 color: None,
1091 }),
1092 right: Some(BorderSideStyle {
1093 style: BorderLineStyle::Thin,
1094 color: None,
1095 }),
1096 top: Some(BorderSideStyle {
1097 style: BorderLineStyle::Thin,
1098 color: None,
1099 }),
1100 bottom: Some(BorderSideStyle {
1101 style: BorderLineStyle::Thin,
1102 color: None,
1103 }),
1104 diagonal: None,
1105 }),
1106 alignment: Some(AlignmentStyle {
1107 horizontal: Some(HorizontalAlign::Center),
1108 vertical: Some(VerticalAlign::Center),
1109 wrap_text: true,
1110 ..AlignmentStyle::default()
1111 }),
1112 num_fmt: Some(NumFmtStyle::Custom("#,##0.00".to_string())),
1113 protection: None,
1114 };
1115 let style_id = wb.add_style(&style).unwrap();
1116 wb.set_cell_style("Sheet1", "A1", style_id).unwrap();
1117 wb.save(&path).unwrap();
1118
1119 let wb2 = Workbook::open(&path).unwrap();
1121 let retrieved_id = wb2.get_cell_style("Sheet1", "A1").unwrap();
1122 assert_eq!(retrieved_id, Some(style_id));
1123
1124 let val = wb2.get_cell_value("Sheet1", "A1").unwrap();
1126 assert_eq!(val, CellValue::String("Styled".to_string()));
1127
1128 let retrieved_style = crate::style::get_style(&wb2.stylesheet, style_id).unwrap();
1130 assert!(retrieved_style.font.is_some());
1131 let font = retrieved_style.font.unwrap();
1132 assert!(font.bold);
1133 assert!(font.italic);
1134 assert_eq!(font.name, Some("Arial".to_string()));
1135
1136 assert!(retrieved_style.fill.is_some());
1137 let fill = retrieved_style.fill.unwrap();
1138 assert_eq!(fill.pattern, PatternType::Solid);
1139
1140 assert!(retrieved_style.alignment.is_some());
1141 let align = retrieved_style.alignment.unwrap();
1142 assert_eq!(align.horizontal, Some(HorizontalAlign::Center));
1143 assert_eq!(align.vertical, Some(VerticalAlign::Center));
1144 assert!(align.wrap_text);
1145 }
1146
1147 #[test]
1148 fn test_set_and_get_cell_rich_text() {
1149 use crate::rich_text::RichTextRun;
1150
1151 let mut wb = Workbook::new();
1152 let runs = vec![
1153 RichTextRun {
1154 text: "Bold".to_string(),
1155 font: None,
1156 size: None,
1157 bold: true,
1158 italic: false,
1159 color: None,
1160 },
1161 RichTextRun {
1162 text: " Normal".to_string(),
1163 font: None,
1164 size: None,
1165 bold: false,
1166 italic: false,
1167 color: None,
1168 },
1169 ];
1170 wb.set_cell_rich_text("Sheet1", "A1", runs.clone()).unwrap();
1171
1172 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
1174 assert_eq!(val.to_string(), "Bold Normal");
1175
1176 let got = wb.get_cell_rich_text("Sheet1", "A1").unwrap();
1178 assert!(got.is_some());
1179 let got_runs = got.unwrap();
1180 assert_eq!(got_runs.len(), 2);
1181 assert_eq!(got_runs[0].text, "Bold");
1182 assert!(got_runs[0].bold);
1183 assert_eq!(got_runs[1].text, " Normal");
1184 assert!(!got_runs[1].bold);
1185 }
1186
1187 #[test]
1188 fn test_get_cell_rich_text_returns_none_for_plain() {
1189 let mut wb = Workbook::new();
1190 wb.set_cell_value("Sheet1", "A1", CellValue::String("plain".to_string()))
1191 .unwrap();
1192 let got = wb.get_cell_rich_text("Sheet1", "A1").unwrap();
1193 assert!(got.is_none());
1194 }
1195
1196 #[test]
1197 fn test_rich_text_roundtrip_save_open() {
1198 use crate::rich_text::RichTextRun;
1199
1200 let dir = TempDir::new().unwrap();
1201 let path = dir.path().join("rich_text.xlsx");
1202
1203 let mut wb = Workbook::new();
1207 let runs = vec![
1208 RichTextRun {
1209 text: "Hello".to_string(),
1210 font: Some("Arial".to_string()),
1211 size: Some(14.0),
1212 bold: true,
1213 italic: false,
1214 color: Some("#FF0000".to_string()),
1215 },
1216 RichTextRun {
1217 text: "World".to_string(),
1218 font: None,
1219 size: None,
1220 bold: false,
1221 italic: true,
1222 color: None,
1223 },
1224 ];
1225 wb.set_cell_rich_text("Sheet1", "B2", runs).unwrap();
1226 wb.save(&path).unwrap();
1227
1228 let wb2 = Workbook::open(&path).unwrap();
1229 let val = wb2.get_cell_value("Sheet1", "B2").unwrap();
1230 assert_eq!(val.to_string(), "HelloWorld");
1231
1232 let got = wb2.get_cell_rich_text("Sheet1", "B2").unwrap();
1233 assert!(got.is_some());
1234 let got_runs = got.unwrap();
1235 assert_eq!(got_runs.len(), 2);
1236 assert_eq!(got_runs[0].text, "Hello");
1237 assert!(got_runs[0].bold);
1238 assert_eq!(got_runs[0].font.as_deref(), Some("Arial"));
1239 assert_eq!(got_runs[0].size, Some(14.0));
1240 assert_eq!(got_runs[0].color.as_deref(), Some("#FF0000"));
1241 assert_eq!(got_runs[1].text, "World");
1242 assert!(got_runs[1].italic);
1243 assert!(!got_runs[1].bold);
1244 }
1245
1246 #[test]
1247 fn test_set_cell_formula() {
1248 let mut wb = Workbook::new();
1249 wb.set_cell_formula("Sheet1", "A1", "SUM(B1:B10)").unwrap();
1250 let val = wb.get_cell_value("Sheet1", "A1").unwrap();
1251 match val {
1252 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(B1:B10)"),
1253 other => panic!("expected Formula, got {:?}", other),
1254 }
1255 }
1256
1257 #[test]
1258 fn test_fill_formula_basic() {
1259 let mut wb = Workbook::new();
1260 wb.fill_formula("Sheet1", "D2:D5", "SUM(A2:C2)").unwrap();
1261
1262 match wb.get_cell_value("Sheet1", "D2").unwrap() {
1264 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A2:C2)"),
1265 other => panic!("D2: expected Formula, got {:?}", other),
1266 }
1267 match wb.get_cell_value("Sheet1", "D3").unwrap() {
1269 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A3:C3)"),
1270 other => panic!("D3: expected Formula, got {:?}", other),
1271 }
1272 match wb.get_cell_value("Sheet1", "D4").unwrap() {
1274 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A4:C4)"),
1275 other => panic!("D4: expected Formula, got {:?}", other),
1276 }
1277 match wb.get_cell_value("Sheet1", "D5").unwrap() {
1279 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A5:C5)"),
1280 other => panic!("D5: expected Formula, got {:?}", other),
1281 }
1282 }
1283
1284 #[test]
1285 fn test_fill_formula_preserves_absolute_refs() {
1286 let mut wb = Workbook::new();
1287 wb.fill_formula("Sheet1", "B1:B3", "$A$1*A1").unwrap();
1288
1289 match wb.get_cell_value("Sheet1", "B1").unwrap() {
1290 CellValue::Formula { expr, .. } => assert_eq!(expr, "$A$1*A1"),
1291 other => panic!("B1: expected Formula, got {:?}", other),
1292 }
1293 match wb.get_cell_value("Sheet1", "B2").unwrap() {
1294 CellValue::Formula { expr, .. } => assert_eq!(expr, "$A$1*A2"),
1295 other => panic!("B2: expected Formula, got {:?}", other),
1296 }
1297 match wb.get_cell_value("Sheet1", "B3").unwrap() {
1298 CellValue::Formula { expr, .. } => assert_eq!(expr, "$A$1*A3"),
1299 other => panic!("B3: expected Formula, got {:?}", other),
1300 }
1301 }
1302
1303 #[test]
1304 fn test_fill_formula_single_cell() {
1305 let mut wb = Workbook::new();
1306 wb.fill_formula("Sheet1", "A1:A1", "B1+C1").unwrap();
1307 match wb.get_cell_value("Sheet1", "A1").unwrap() {
1308 CellValue::Formula { expr, .. } => assert_eq!(expr, "B1+C1"),
1309 other => panic!("expected Formula, got {:?}", other),
1310 }
1311 }
1312
1313 #[test]
1314 fn test_fill_formula_invalid_range() {
1315 let mut wb = Workbook::new();
1316 assert!(wb.fill_formula("Sheet1", "INVALID", "A1").is_err());
1317 }
1318
1319 #[test]
1320 fn test_fill_formula_multi_column_range_rejected() {
1321 let mut wb = Workbook::new();
1322 assert!(wb.fill_formula("Sheet1", "A1:B5", "C1").is_err());
1323 }
1324
1325 #[test]
1326 fn test_set_cell_values_batch() {
1327 let mut wb = Workbook::new();
1328 wb.set_cell_values(
1329 "Sheet1",
1330 vec![
1331 ("A1".to_string(), CellValue::String("hello".to_string())),
1332 ("B1".to_string(), CellValue::Number(42.0)),
1333 ("C1".to_string(), CellValue::Bool(true)),
1334 ("A2".to_string(), CellValue::String("world".to_string())),
1335 ],
1336 )
1337 .unwrap();
1338
1339 assert_eq!(
1340 wb.get_cell_value("Sheet1", "A1").unwrap(),
1341 CellValue::String("hello".to_string())
1342 );
1343 assert_eq!(
1344 wb.get_cell_value("Sheet1", "B1").unwrap(),
1345 CellValue::Number(42.0)
1346 );
1347 assert_eq!(
1348 wb.get_cell_value("Sheet1", "C1").unwrap(),
1349 CellValue::Bool(true)
1350 );
1351 assert_eq!(
1352 wb.get_cell_value("Sheet1", "A2").unwrap(),
1353 CellValue::String("world".to_string())
1354 );
1355 }
1356
1357 #[test]
1358 fn test_set_cell_values_empty_removes_cell() {
1359 let mut wb = Workbook::new();
1360 wb.set_cell_value("Sheet1", "A1", "existing").unwrap();
1361 wb.set_cell_values("Sheet1", vec![("A1".to_string(), CellValue::Empty)])
1362 .unwrap();
1363 assert_eq!(wb.get_cell_value("Sheet1", "A1").unwrap(), CellValue::Empty);
1364 }
1365
1366 #[test]
1367 fn test_set_sheet_data_basic() {
1368 let mut wb = Workbook::new();
1369 wb.set_sheet_data(
1370 "Sheet1",
1371 vec![
1372 vec![
1373 CellValue::String("Name".to_string()),
1374 CellValue::String("Age".to_string()),
1375 ],
1376 vec![
1377 CellValue::String("Alice".to_string()),
1378 CellValue::Number(30.0),
1379 ],
1380 vec![
1381 CellValue::String("Bob".to_string()),
1382 CellValue::Number(25.0),
1383 ],
1384 ],
1385 1,
1386 1,
1387 )
1388 .unwrap();
1389
1390 assert_eq!(
1391 wb.get_cell_value("Sheet1", "A1").unwrap(),
1392 CellValue::String("Name".to_string())
1393 );
1394 assert_eq!(
1395 wb.get_cell_value("Sheet1", "B1").unwrap(),
1396 CellValue::String("Age".to_string())
1397 );
1398 assert_eq!(
1399 wb.get_cell_value("Sheet1", "A2").unwrap(),
1400 CellValue::String("Alice".to_string())
1401 );
1402 assert_eq!(
1403 wb.get_cell_value("Sheet1", "B2").unwrap(),
1404 CellValue::Number(30.0)
1405 );
1406 assert_eq!(
1407 wb.get_cell_value("Sheet1", "A3").unwrap(),
1408 CellValue::String("Bob".to_string())
1409 );
1410 assert_eq!(
1411 wb.get_cell_value("Sheet1", "B3").unwrap(),
1412 CellValue::Number(25.0)
1413 );
1414 }
1415
1416 #[test]
1417 fn test_set_sheet_data_with_offset() {
1418 let mut wb = Workbook::new();
1419 wb.set_sheet_data(
1421 "Sheet1",
1422 vec![
1423 vec![CellValue::Number(1.0), CellValue::Number(2.0)],
1424 vec![CellValue::Number(3.0), CellValue::Number(4.0)],
1425 ],
1426 3,
1427 3,
1428 )
1429 .unwrap();
1430
1431 assert_eq!(
1432 wb.get_cell_value("Sheet1", "C3").unwrap(),
1433 CellValue::Number(1.0)
1434 );
1435 assert_eq!(
1436 wb.get_cell_value("Sheet1", "D3").unwrap(),
1437 CellValue::Number(2.0)
1438 );
1439 assert_eq!(
1440 wb.get_cell_value("Sheet1", "C4").unwrap(),
1441 CellValue::Number(3.0)
1442 );
1443 assert_eq!(
1444 wb.get_cell_value("Sheet1", "D4").unwrap(),
1445 CellValue::Number(4.0)
1446 );
1447 assert_eq!(wb.get_cell_value("Sheet1", "A1").unwrap(), CellValue::Empty);
1449 }
1450
1451 #[test]
1452 fn test_set_sheet_data_roundtrip() {
1453 let dir = TempDir::new().unwrap();
1454 let path = dir.path().join("batch_roundtrip.xlsx");
1455
1456 let mut wb = Workbook::new();
1457 wb.set_sheet_data(
1458 "Sheet1",
1459 vec![
1460 vec![
1461 CellValue::String("Header1".to_string()),
1462 CellValue::String("Header2".to_string()),
1463 ],
1464 vec![CellValue::Number(100.0), CellValue::Bool(true)],
1465 ],
1466 1,
1467 1,
1468 )
1469 .unwrap();
1470 wb.save(&path).unwrap();
1471
1472 let wb2 = Workbook::open(&path).unwrap();
1473 assert_eq!(
1474 wb2.get_cell_value("Sheet1", "A1").unwrap(),
1475 CellValue::String("Header1".to_string())
1476 );
1477 assert_eq!(
1478 wb2.get_cell_value("Sheet1", "B1").unwrap(),
1479 CellValue::String("Header2".to_string())
1480 );
1481 assert_eq!(
1482 wb2.get_cell_value("Sheet1", "A2").unwrap(),
1483 CellValue::Number(100.0)
1484 );
1485 assert_eq!(
1486 wb2.get_cell_value("Sheet1", "B2").unwrap(),
1487 CellValue::Bool(true)
1488 );
1489 }
1490
1491 #[test]
1492 fn test_set_row_values() {
1493 let mut wb = Workbook::new();
1494 wb.set_row_values(
1495 "Sheet1",
1496 1,
1497 1,
1498 vec![
1499 CellValue::String("A".to_string()),
1500 CellValue::String("B".to_string()),
1501 CellValue::String("C".to_string()),
1502 ],
1503 )
1504 .unwrap();
1505
1506 assert_eq!(
1507 wb.get_cell_value("Sheet1", "A1").unwrap(),
1508 CellValue::String("A".to_string())
1509 );
1510 assert_eq!(
1511 wb.get_cell_value("Sheet1", "B1").unwrap(),
1512 CellValue::String("B".to_string())
1513 );
1514 assert_eq!(
1515 wb.get_cell_value("Sheet1", "C1").unwrap(),
1516 CellValue::String("C".to_string())
1517 );
1518 }
1519
1520 #[test]
1521 fn test_set_row_values_with_offset() {
1522 let mut wb = Workbook::new();
1523 wb.set_row_values(
1525 "Sheet1",
1526 2,
1527 4,
1528 vec![CellValue::Number(10.0), CellValue::Number(20.0)],
1529 )
1530 .unwrap();
1531
1532 assert_eq!(
1533 wb.get_cell_value("Sheet1", "D2").unwrap(),
1534 CellValue::Number(10.0)
1535 );
1536 assert_eq!(
1537 wb.get_cell_value("Sheet1", "E2").unwrap(),
1538 CellValue::Number(20.0)
1539 );
1540 }
1541
1542 #[test]
1543 fn test_set_sheet_data_merges_with_existing() {
1544 let mut wb = Workbook::new();
1545 wb.set_cell_value("Sheet1", "A1", "existing").unwrap();
1546 wb.set_sheet_data(
1547 "Sheet1",
1548 vec![vec![CellValue::Empty, CellValue::String("new".to_string())]],
1549 1,
1550 1,
1551 )
1552 .unwrap();
1553
1554 assert_eq!(wb.get_cell_value("Sheet1", "A1").unwrap(), CellValue::Empty);
1556 assert_eq!(
1558 wb.get_cell_value("Sheet1", "B1").unwrap(),
1559 CellValue::String("new".to_string())
1560 );
1561 }
1562}