1use super::*;
2
3impl Workbook {
4 pub fn sheet_names(&self) -> Vec<&str> {
6 self.worksheets
7 .iter()
8 .map(|(name, _)| name.as_str())
9 .collect()
10 }
11
12 pub fn new_sheet(&mut self, name: &str) -> Result<usize> {
14 let idx = crate::sheet::add_sheet(
15 &mut self.workbook_xml,
16 &mut self.workbook_rels,
17 &mut self.content_types,
18 &mut self.worksheets,
19 name,
20 WorksheetXml::default(),
21 )?;
22 if self.sheet_comments.len() < self.worksheets.len() {
23 self.sheet_comments.push(None);
24 }
25 if self.sheet_sparklines.len() < self.worksheets.len() {
26 self.sheet_sparklines.push(vec![]);
27 }
28 if self.sheet_vml.len() < self.worksheets.len() {
29 self.sheet_vml.push(None);
30 }
31 if self.raw_sheet_xml.len() < self.worksheets.len() {
32 self.raw_sheet_xml.push(None);
33 }
34 if self.sheet_threaded_comments.len() < self.worksheets.len() {
35 self.sheet_threaded_comments.push(None);
36 }
37 if self.sheet_form_controls.len() < self.worksheets.len() {
38 self.sheet_form_controls.push(vec![]);
39 }
40 self.rebuild_sheet_index();
41 Ok(idx)
42 }
43
44 pub fn delete_sheet(&mut self, name: &str) -> Result<()> {
46 let idx = self.sheet_index(name)?;
47 self.assert_parallel_vecs_in_sync();
48
49 crate::sheet::delete_sheet(
50 &mut self.workbook_xml,
51 &mut self.workbook_rels,
52 &mut self.content_types,
53 &mut self.worksheets,
54 name,
55 )?;
56
57 self.sheet_comments.remove(idx);
61 self.sheet_sparklines.remove(idx);
62 self.sheet_vml.remove(idx);
63 self.raw_sheet_xml.remove(idx);
64 self.sheet_threaded_comments.remove(idx);
65 self.sheet_form_controls.remove(idx);
66
67 self.tables.retain(|(_, _, si)| *si != idx);
69 for (_, _, si) in &mut self.tables {
70 if *si > idx {
71 *si -= 1;
72 }
73 }
74 self.reindex_sheet_maps_after_delete(idx);
75 self.rebuild_sheet_index();
76 Ok(())
77 }
78
79 fn assert_parallel_vecs_in_sync(&self) {
83 let n = self.worksheets.len();
84 debug_assert_eq!(self.sheet_comments.len(), n, "sheet_comments desync");
85 debug_assert_eq!(self.sheet_sparklines.len(), n, "sheet_sparklines desync");
86 debug_assert_eq!(self.sheet_vml.len(), n, "sheet_vml desync");
87 debug_assert_eq!(self.raw_sheet_xml.len(), n, "raw_sheet_xml desync");
88 debug_assert_eq!(
89 self.sheet_threaded_comments.len(),
90 n,
91 "sheet_threaded_comments desync"
92 );
93 debug_assert_eq!(
94 self.sheet_form_controls.len(),
95 n,
96 "sheet_form_controls desync"
97 );
98 }
99
100 pub fn set_sheet_name(&mut self, old_name: &str, new_name: &str) -> Result<()> {
102 crate::sheet::rename_sheet(
103 &mut self.workbook_xml,
104 &mut self.worksheets,
105 old_name,
106 new_name,
107 )?;
108 self.rebuild_sheet_index();
109 Ok(())
110 }
111
112 pub fn copy_sheet(&mut self, source: &str, target: &str) -> Result<usize> {
114 let idx = crate::sheet::copy_sheet(
115 &mut self.workbook_xml,
116 &mut self.workbook_rels,
117 &mut self.content_types,
118 &mut self.worksheets,
119 source,
120 target,
121 )?;
122 if self.sheet_comments.len() < self.worksheets.len() {
123 self.sheet_comments.push(None);
124 }
125 let source_sparklines = {
126 let src_idx = self.sheet_index(source).unwrap_or(0);
127 self.sheet_sparklines
128 .get(src_idx)
129 .cloned()
130 .unwrap_or_default()
131 };
132 if self.sheet_sparklines.len() < self.worksheets.len() {
133 self.sheet_sparklines.push(source_sparklines);
134 }
135 if self.sheet_vml.len() < self.worksheets.len() {
136 self.sheet_vml.push(None);
137 }
138 if self.raw_sheet_xml.len() < self.worksheets.len() {
139 self.raw_sheet_xml.push(None);
140 }
141 if self.sheet_threaded_comments.len() < self.worksheets.len() {
142 self.sheet_threaded_comments.push(None);
143 }
144 if self.sheet_form_controls.len() < self.worksheets.len() {
145 self.sheet_form_controls.push(vec![]);
146 }
147 self.rebuild_sheet_index();
148 Ok(idx)
149 }
150
151 pub fn get_sheet_index(&self, name: &str) -> Option<usize> {
153 crate::sheet::find_sheet_index(&self.worksheets, name)
154 }
155
156 pub fn get_active_sheet(&self) -> &str {
158 let idx = crate::sheet::active_sheet_index(&self.workbook_xml);
159 self.worksheets
160 .get(idx)
161 .map(|(n, _)| n.as_str())
162 .unwrap_or_else(|| self.worksheets[0].0.as_str())
163 }
164
165 pub fn set_active_sheet(&mut self, name: &str) -> Result<()> {
167 let idx = crate::sheet::find_sheet_index(&self.worksheets, name).ok_or_else(|| {
168 Error::SheetNotFound {
169 name: name.to_string(),
170 }
171 })?;
172 crate::sheet::set_active_sheet_index(&mut self.workbook_xml, idx as u32);
173 Ok(())
174 }
175
176 pub fn new_stream_writer(&self, sheet_name: &str) -> Result<crate::stream::StreamWriter> {
181 crate::sheet::validate_sheet_name(sheet_name)?;
182 if self.worksheets.iter().any(|(n, _)| n == sheet_name) {
183 return Err(Error::SheetAlreadyExists {
184 name: sheet_name.to_string(),
185 });
186 }
187 Ok(crate::stream::StreamWriter::new(sheet_name))
188 }
189
190 pub fn apply_stream_writer(&mut self, writer: crate::stream::StreamWriter) -> Result<usize> {
195 let sheet_name = writer.sheet_name().to_string();
196 let (mut ws, sst) = writer.into_worksheet_parts()?;
197
198 let mut sst_remap: Vec<usize> = Vec::with_capacity(sst.len());
200 for i in 0..sst.len() {
201 if let Some(s) = sst.get(i) {
202 let new_idx = self.sst_runtime.add(s);
203 sst_remap.push(new_idx);
204 }
205 }
206
207 for row in &mut ws.sheet_data.rows {
209 for cell in &mut row.cells {
210 if cell.t == CellTypeTag::SharedString {
211 if let Some(ref v) = cell.v {
212 if let Ok(old_idx) = v.parse::<usize>() {
213 if let Some(&new_idx) = sst_remap.get(old_idx) {
214 cell.v = Some(new_idx.to_string());
215 }
216 }
217 }
218 }
219 }
220 }
221
222 let idx = crate::sheet::add_sheet(
226 &mut self.workbook_xml,
227 &mut self.workbook_rels,
228 &mut self.content_types,
229 &mut self.worksheets,
230 &sheet_name,
231 ws,
232 )?;
233 if self.sheet_comments.len() < self.worksheets.len() {
234 self.sheet_comments.push(None);
235 }
236 if self.sheet_sparklines.len() < self.worksheets.len() {
237 self.sheet_sparklines.push(vec![]);
238 }
239 if self.sheet_vml.len() < self.worksheets.len() {
240 self.sheet_vml.push(None);
241 }
242 if self.raw_sheet_xml.len() < self.worksheets.len() {
243 self.raw_sheet_xml.push(None);
244 }
245 if self.sheet_threaded_comments.len() < self.worksheets.len() {
246 self.sheet_threaded_comments.push(None);
247 }
248 if self.sheet_form_controls.len() < self.worksheets.len() {
249 self.sheet_form_controls.push(vec![]);
250 }
251 self.rebuild_sheet_index();
252 Ok(idx)
253 }
254
255 pub fn insert_rows(&mut self, sheet: &str, start_row: u32, count: u32) -> Result<()> {
257 let sheet_idx = self.sheet_index(sheet)?;
258 {
259 let ws = &mut self.worksheets[sheet_idx].1;
260 crate::row::insert_rows(ws, start_row, count)?;
261 }
262 self.apply_reference_shift_for_sheet(sheet_idx, |col, row| {
263 if row >= start_row {
264 (col, row + count)
265 } else {
266 (col, row)
267 }
268 })
269 }
270
271 pub fn remove_row(&mut self, sheet: &str, row: u32) -> Result<()> {
273 let sheet_idx = self.sheet_index(sheet)?;
274 {
275 let ws = &mut self.worksheets[sheet_idx].1;
276 crate::row::remove_row(ws, row)?;
277 }
278 self.apply_reference_shift_for_sheet(sheet_idx, |col, r| {
279 if r > row {
280 (col, r - 1)
281 } else {
282 (col, r)
283 }
284 })
285 }
286
287 pub fn duplicate_row(&mut self, sheet: &str, row: u32) -> Result<()> {
289 let ws = self.worksheet_mut(sheet)?;
290 crate::row::duplicate_row(ws, row)
291 }
292
293 pub fn set_row_height(&mut self, sheet: &str, row: u32, height: f64) -> Result<()> {
295 let ws = self.worksheet_mut(sheet)?;
296 crate::row::set_row_height(ws, row, height)
297 }
298
299 pub fn get_row_height(&self, sheet: &str, row: u32) -> Result<Option<f64>> {
301 let ws = self.worksheet_ref(sheet)?;
302 Ok(crate::row::get_row_height(ws, row))
303 }
304
305 pub fn set_row_visible(&mut self, sheet: &str, row: u32, visible: bool) -> Result<()> {
307 let ws = self.worksheet_mut(sheet)?;
308 crate::row::set_row_visible(ws, row, visible)
309 }
310
311 pub fn get_row_visible(&self, sheet: &str, row: u32) -> Result<bool> {
313 let ws = self.worksheet_ref(sheet)?;
314 Ok(crate::row::get_row_visible(ws, row))
315 }
316
317 pub fn set_row_outline_level(&mut self, sheet: &str, row: u32, level: u8) -> Result<()> {
319 let ws = self.worksheet_mut(sheet)?;
320 crate::row::set_row_outline_level(ws, row, level)
321 }
322
323 pub fn get_row_outline_level(&self, sheet: &str, row: u32) -> Result<u8> {
325 let ws = self.worksheet_ref(sheet)?;
326 Ok(crate::row::get_row_outline_level(ws, row))
327 }
328
329 pub fn set_row_style(&mut self, sheet: &str, row: u32, style_id: u32) -> Result<()> {
333 if style_id as usize >= self.stylesheet.cell_xfs.xfs.len() {
334 return Err(Error::StyleNotFound { id: style_id });
335 }
336 let ws = self.worksheet_mut(sheet)?;
337 crate::row::set_row_style(ws, row, style_id)
338 }
339
340 pub fn get_row_style(&self, sheet: &str, row: u32) -> Result<u32> {
342 let ws = self.worksheet_ref(sheet)?;
343 Ok(crate::row::get_row_style(ws, row))
344 }
345
346 #[allow(clippy::type_complexity)]
352 pub fn get_rows(&self, sheet: &str) -> Result<Vec<(u32, Vec<(u32, CellValue)>)>> {
353 let ws = self.worksheet_ref(sheet)?;
354 crate::row::get_rows(ws, &self.sst_runtime)
355 }
356
357 #[allow(clippy::type_complexity)]
362 pub fn get_cols(&self, sheet: &str) -> Result<Vec<(String, Vec<(u32, CellValue)>)>> {
363 let ws = self.worksheet_ref(sheet)?;
364 crate::col::get_cols(ws, &self.sst_runtime)
365 }
366
367 pub fn set_col_width(&mut self, sheet: &str, col: &str, width: f64) -> Result<()> {
369 let ws = self.worksheet_mut(sheet)?;
370 crate::col::set_col_width(ws, col, width)
371 }
372
373 pub fn get_col_width(&self, sheet: &str, col: &str) -> Result<Option<f64>> {
375 let ws = self.worksheet_ref(sheet)?;
376 Ok(crate::col::get_col_width(ws, col))
377 }
378
379 pub fn set_col_visible(&mut self, sheet: &str, col: &str, visible: bool) -> Result<()> {
381 let ws = self.worksheet_mut(sheet)?;
382 crate::col::set_col_visible(ws, col, visible)
383 }
384
385 pub fn get_col_visible(&self, sheet: &str, col: &str) -> Result<bool> {
387 let ws = self.worksheet_ref(sheet)?;
388 crate::col::get_col_visible(ws, col)
389 }
390
391 pub fn set_col_outline_level(&mut self, sheet: &str, col: &str, level: u8) -> Result<()> {
393 let ws = self.worksheet_mut(sheet)?;
394 crate::col::set_col_outline_level(ws, col, level)
395 }
396
397 pub fn get_col_outline_level(&self, sheet: &str, col: &str) -> Result<u8> {
399 let ws = self.worksheet_ref(sheet)?;
400 crate::col::get_col_outline_level(ws, col)
401 }
402
403 pub fn set_col_style(&mut self, sheet: &str, col: &str, style_id: u32) -> Result<()> {
407 if style_id as usize >= self.stylesheet.cell_xfs.xfs.len() {
408 return Err(Error::StyleNotFound { id: style_id });
409 }
410 let ws = self.worksheet_mut(sheet)?;
411 crate::col::set_col_style(ws, col, style_id)
412 }
413
414 pub fn get_col_style(&self, sheet: &str, col: &str) -> Result<u32> {
416 let ws = self.worksheet_ref(sheet)?;
417 crate::col::get_col_style(ws, col)
418 }
419
420 pub fn insert_cols(&mut self, sheet: &str, col: &str, count: u32) -> Result<()> {
422 let sheet_idx = self.sheet_index(sheet)?;
423 let start_col = column_name_to_number(col)?;
424 {
425 let ws = &mut self.worksheets[sheet_idx].1;
426 crate::col::insert_cols(ws, col, count)?;
427 }
428 self.apply_reference_shift_for_sheet(sheet_idx, |c, row| {
429 if c >= start_col {
430 (c + count, row)
431 } else {
432 (c, row)
433 }
434 })
435 }
436
437 pub fn remove_col(&mut self, sheet: &str, col: &str) -> Result<()> {
439 let sheet_idx = self.sheet_index(sheet)?;
440 let col_num = column_name_to_number(col)?;
441 {
442 let ws = &mut self.worksheets[sheet_idx].1;
443 crate::col::remove_col(ws, col)?;
444 }
445 self.apply_reference_shift_for_sheet(sheet_idx, |c, row| {
446 if c > col_num {
447 (c - 1, row)
448 } else {
449 (c, row)
450 }
451 })
452 }
453
454 pub(crate) fn reindex_sheet_maps_after_delete(&mut self, removed_idx: usize) {
456 self.worksheet_rels = self
457 .worksheet_rels
458 .iter()
459 .filter_map(|(idx, rels)| {
460 if *idx == removed_idx {
461 None
462 } else if *idx > removed_idx {
463 Some((idx - 1, rels.clone()))
464 } else {
465 Some((*idx, rels.clone()))
466 }
467 })
468 .collect();
469
470 self.worksheet_drawings = self
471 .worksheet_drawings
472 .iter()
473 .filter_map(|(idx, drawing_idx)| {
474 if *idx == removed_idx {
475 None
476 } else if *idx > removed_idx {
477 Some((idx - 1, *drawing_idx))
478 } else {
479 Some((*idx, *drawing_idx))
480 }
481 })
482 .collect();
483 }
484
485 pub(crate) fn apply_reference_shift_for_sheet<F>(
487 &mut self,
488 sheet_idx: usize,
489 shift_cell: F,
490 ) -> Result<()>
491 where
492 F: Fn(u32, u32) -> (u32, u32) + Copy,
493 {
494 {
495 let ws = &mut self.worksheets[sheet_idx].1;
496
497 for row in &mut ws.sheet_data.rows {
499 for cell in &mut row.cells {
500 if let Some(ref mut f) = cell.f {
501 if let Some(ref mut expr) = f.value {
502 *expr = shift_cell_references_in_text(expr, shift_cell)?;
503 }
504 }
505 }
506 }
507
508 if let Some(ref mut merges) = ws.merge_cells {
510 for mc in &mut merges.merge_cells {
511 mc.reference = shift_cell_references_in_text(&mc.reference, shift_cell)?;
512 }
513 }
514
515 if let Some(ref mut af) = ws.auto_filter {
517 af.reference = shift_cell_references_in_text(&af.reference, shift_cell)?;
518 }
519
520 if let Some(ref mut dvs) = ws.data_validations {
522 for dv in &mut dvs.data_validations {
523 dv.sqref = shift_cell_references_in_text(&dv.sqref, shift_cell)?;
524 if let Some(ref mut f1) = dv.formula1 {
525 *f1 = shift_cell_references_in_text(f1, shift_cell)?;
526 }
527 if let Some(ref mut f2) = dv.formula2 {
528 *f2 = shift_cell_references_in_text(f2, shift_cell)?;
529 }
530 }
531 }
532
533 for cf in &mut ws.conditional_formatting {
535 cf.sqref = shift_cell_references_in_text(&cf.sqref, shift_cell)?;
536 for rule in &mut cf.cf_rules {
537 for f in &mut rule.formulas {
538 *f = shift_cell_references_in_text(f, shift_cell)?;
539 }
540 }
541 }
542
543 if let Some(ref mut hyperlinks) = ws.hyperlinks {
545 for hl in &mut hyperlinks.hyperlinks {
546 hl.reference = shift_cell_references_in_text(&hl.reference, shift_cell)?;
547 if let Some(ref mut loc) = hl.location {
548 *loc = shift_cell_references_in_text(loc, shift_cell)?;
549 }
550 }
551 }
552
553 if let Some(ref mut views) = ws.sheet_views {
555 for view in &mut views.sheet_views {
556 if let Some(ref mut pane) = view.pane {
557 if let Some(ref mut top_left) = pane.top_left_cell {
558 *top_left = shift_cell_references_in_text(top_left, shift_cell)?;
559 }
560 }
561 for sel in &mut view.selection {
562 if let Some(ref mut ac) = sel.active_cell {
563 *ac = shift_cell_references_in_text(ac, shift_cell)?;
564 }
565 if let Some(ref mut sqref) = sel.sqref {
566 *sqref = shift_cell_references_in_text(sqref, shift_cell)?;
567 }
568 }
569 }
570 }
571 }
572
573 if let Some(&drawing_idx) = self.worksheet_drawings.get(&sheet_idx) {
575 if let Some((_, drawing)) = self.drawings.get_mut(drawing_idx) {
576 for anchor in &mut drawing.one_cell_anchors {
577 let (new_col, new_row) = shift_cell(anchor.from.col + 1, anchor.from.row + 1);
578 anchor.from.col = new_col - 1;
579 anchor.from.row = new_row - 1;
580 }
581 for anchor in &mut drawing.two_cell_anchors {
582 let (from_col, from_row) = shift_cell(anchor.from.col + 1, anchor.from.row + 1);
583 anchor.from.col = from_col - 1;
584 anchor.from.row = from_row - 1;
585 let (to_col, to_row) = shift_cell(anchor.to.col + 1, anchor.to.row + 1);
586 anchor.to.col = to_col - 1;
587 anchor.to.row = to_row - 1;
588 }
589 }
590 }
591
592 Ok(())
593 }
594
595 pub(crate) fn ensure_drawing_for_sheet(&mut self, sheet_idx: usize) -> usize {
598 if let Some(&idx) = self.worksheet_drawings.get(&sheet_idx) {
599 return idx;
600 }
601
602 let idx = self.drawings.len();
603 let drawing_path = format!("xl/drawings/drawing{}.xml", idx + 1);
604 self.drawings.push((drawing_path, WsDr::default()));
605 self.worksheet_drawings.insert(sheet_idx, idx);
606
607 let ws_rid = self.next_worksheet_rid(sheet_idx);
609 self.worksheets[sheet_idx].1.drawing = Some(DrawingRef {
610 r_id: ws_rid.clone(),
611 });
612
613 let drawing_rel_target = format!("../drawings/drawing{}.xml", idx + 1);
615 let ws_rels = self
616 .worksheet_rels
617 .entry(sheet_idx)
618 .or_insert_with(|| Relationships {
619 xmlns: sheetkit_xml::namespaces::PACKAGE_RELATIONSHIPS.to_string(),
620 relationships: vec![],
621 });
622 ws_rels.relationships.push(Relationship {
623 id: ws_rid,
624 rel_type: rel_types::DRAWING.to_string(),
625 target: drawing_rel_target,
626 target_mode: None,
627 });
628
629 self.content_types.overrides.push(ContentTypeOverride {
631 part_name: format!("/xl/drawings/drawing{}.xml", idx + 1),
632 content_type: mime_types::DRAWING.to_string(),
633 });
634
635 idx
636 }
637
638 pub(crate) fn next_worksheet_rid(&self, sheet_idx: usize) -> String {
640 let existing = self
641 .worksheet_rels
642 .get(&sheet_idx)
643 .map(|r| r.relationships.as_slice())
644 .unwrap_or(&[]);
645 crate::sheet::next_rid(existing)
646 }
647
648 pub(crate) fn next_drawing_rid(&self, drawing_idx: usize) -> String {
650 let existing = self
651 .drawing_rels
652 .get(&drawing_idx)
653 .map(|r| r.relationships.as_slice())
654 .unwrap_or(&[]);
655 crate::sheet::next_rid(existing)
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662 use tempfile::TempDir;
663
664 #[test]
665 fn test_new_sheet_basic() {
666 let mut wb = Workbook::new();
667 let idx = wb.new_sheet("Sheet2").unwrap();
668 assert_eq!(idx, 1);
669 assert_eq!(wb.sheet_names(), vec!["Sheet1", "Sheet2"]);
670 }
671
672 #[test]
673 fn test_new_sheet_duplicate_returns_error() {
674 let mut wb = Workbook::new();
675 let result = wb.new_sheet("Sheet1");
676 assert!(result.is_err());
677 assert!(matches!(
678 result.unwrap_err(),
679 Error::SheetAlreadyExists { .. }
680 ));
681 }
682
683 #[test]
684 fn test_new_sheet_invalid_name_returns_error() {
685 let mut wb = Workbook::new();
686 let result = wb.new_sheet("Bad/Name");
687 assert!(result.is_err());
688 assert!(matches!(result.unwrap_err(), Error::InvalidSheetName(_)));
689 }
690
691 #[test]
692 fn test_delete_sheet_basic() {
693 let mut wb = Workbook::new();
694 wb.new_sheet("Sheet2").unwrap();
695 wb.delete_sheet("Sheet1").unwrap();
696 assert_eq!(wb.sheet_names(), vec!["Sheet2"]);
697 }
698
699 #[test]
700 fn test_delete_sheet_keeps_parallel_vecs_in_sync() {
701 let mut wb = Workbook::new();
702 wb.new_sheet("Sheet2").unwrap();
703 wb.new_sheet("Sheet3").unwrap();
704
705 wb.add_comment(
707 "Sheet2",
708 &crate::comment::CommentConfig {
709 cell: "A1".to_string(),
710 author: "Test".to_string(),
711 text: "note".to_string(),
712 },
713 )
714 .unwrap();
715
716 wb.delete_sheet("Sheet2").unwrap();
718 assert_eq!(wb.sheet_names(), vec!["Sheet1", "Sheet3"]);
719
720 wb.add_comment(
723 "Sheet3",
724 &crate::comment::CommentConfig {
725 cell: "B2".to_string(),
726 author: "Test".to_string(),
727 text: "note2".to_string(),
728 },
729 )
730 .unwrap();
731 }
732
733 #[test]
734 fn test_delete_last_sheet_returns_error() {
735 let mut wb = Workbook::new();
736 let result = wb.delete_sheet("Sheet1");
737 assert!(result.is_err());
738 }
739
740 #[test]
741 fn test_delete_nonexistent_sheet_returns_error() {
742 let mut wb = Workbook::new();
743 let result = wb.delete_sheet("NoSuchSheet");
744 assert!(result.is_err());
745 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
746 }
747
748 #[test]
749 fn test_set_sheet_name_basic() {
750 let mut wb = Workbook::new();
751 wb.set_sheet_name("Sheet1", "Renamed").unwrap();
752 assert_eq!(wb.sheet_names(), vec!["Renamed"]);
753 }
754
755 #[test]
756 fn test_set_sheet_name_to_existing_returns_error() {
757 let mut wb = Workbook::new();
758 wb.new_sheet("Sheet2").unwrap();
759 let result = wb.set_sheet_name("Sheet1", "Sheet2");
760 assert!(result.is_err());
761 assert!(matches!(
762 result.unwrap_err(),
763 Error::SheetAlreadyExists { .. }
764 ));
765 }
766
767 #[test]
768 fn test_copy_sheet_basic() {
769 let mut wb = Workbook::new();
770 let idx = wb.copy_sheet("Sheet1", "Sheet1 Copy").unwrap();
771 assert_eq!(idx, 1);
772 assert_eq!(wb.sheet_names(), vec!["Sheet1", "Sheet1 Copy"]);
773 }
774
775 #[test]
776 fn test_get_sheet_index() {
777 let mut wb = Workbook::new();
778 wb.new_sheet("Sheet2").unwrap();
779 assert_eq!(wb.get_sheet_index("Sheet1"), Some(0));
780 assert_eq!(wb.get_sheet_index("Sheet2"), Some(1));
781 assert_eq!(wb.get_sheet_index("Nonexistent"), None);
782 }
783
784 #[test]
785 fn test_get_active_sheet_default() {
786 let wb = Workbook::new();
787 assert_eq!(wb.get_active_sheet(), "Sheet1");
788 }
789
790 #[test]
791 fn test_set_active_sheet() {
792 let mut wb = Workbook::new();
793 wb.new_sheet("Sheet2").unwrap();
794 wb.set_active_sheet("Sheet2").unwrap();
795 assert_eq!(wb.get_active_sheet(), "Sheet2");
796 }
797
798 #[test]
799 fn test_set_active_sheet_not_found() {
800 let mut wb = Workbook::new();
801 let result = wb.set_active_sheet("NoSuchSheet");
802 assert!(result.is_err());
803 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
804 }
805
806 #[test]
807 fn test_sheet_management_roundtrip_save_open() {
808 let dir = TempDir::new().unwrap();
809 let path = dir.path().join("sheet_mgmt.xlsx");
810
811 let mut wb = Workbook::new();
812 wb.new_sheet("Data").unwrap();
813 wb.new_sheet("Summary").unwrap();
814 wb.set_sheet_name("Sheet1", "Overview").unwrap();
815 wb.save(&path).unwrap();
816
817 let wb2 = Workbook::open(&path).unwrap();
818 assert_eq!(wb2.sheet_names(), vec!["Overview", "Data", "Summary"]);
819 }
820
821 #[test]
822 fn test_workbook_insert_rows() {
823 let mut wb = Workbook::new();
824 wb.set_cell_value("Sheet1", "A1", "stay").unwrap();
825 wb.set_cell_value("Sheet1", "A2", "shift").unwrap();
826 wb.insert_rows("Sheet1", 2, 1).unwrap();
827
828 assert_eq!(
829 wb.get_cell_value("Sheet1", "A1").unwrap(),
830 CellValue::String("stay".to_string())
831 );
832 assert_eq!(
833 wb.get_cell_value("Sheet1", "A3").unwrap(),
834 CellValue::String("shift".to_string())
835 );
836 assert_eq!(wb.get_cell_value("Sheet1", "A2").unwrap(), CellValue::Empty);
837 }
838
839 #[test]
840 fn test_workbook_insert_rows_updates_formula_and_ranges() {
841 let mut wb = Workbook::new();
842 wb.set_cell_value(
843 "Sheet1",
844 "C1",
845 CellValue::Formula {
846 expr: "SUM(A2:B2)".to_string(),
847 result: None,
848 },
849 )
850 .unwrap();
851 wb.add_data_validation(
852 "Sheet1",
853 &crate::validation::DataValidationConfig::whole_number("A2:A5", 1, 9),
854 )
855 .unwrap();
856 wb.set_auto_filter("Sheet1", "A2:B10").unwrap();
857 wb.merge_cells("Sheet1", "A2", "B3").unwrap();
858
859 wb.insert_rows("Sheet1", 2, 1).unwrap();
860
861 match wb.get_cell_value("Sheet1", "C1").unwrap() {
862 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A3:B3)"),
863 other => panic!("expected formula, got {other:?}"),
864 }
865
866 let validations = wb.get_data_validations("Sheet1").unwrap();
867 assert_eq!(validations.len(), 1);
868 assert_eq!(validations[0].sqref, "A3:A6");
869
870 let merges = wb.get_merge_cells("Sheet1").unwrap();
871 assert_eq!(merges, vec!["A3:B4".to_string()]);
872
873 let ws = wb.worksheet_ref("Sheet1").unwrap();
874 assert_eq!(ws.auto_filter.as_ref().unwrap().reference, "A3:B11");
875 }
876
877 #[test]
878 fn test_workbook_insert_rows_sheet_not_found() {
879 let mut wb = Workbook::new();
880 let result = wb.insert_rows("NoSheet", 1, 1);
881 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
882 }
883
884 #[test]
885 fn test_workbook_remove_row() {
886 let mut wb = Workbook::new();
887 wb.set_cell_value("Sheet1", "A1", "first").unwrap();
888 wb.set_cell_value("Sheet1", "A2", "second").unwrap();
889 wb.set_cell_value("Sheet1", "A3", "third").unwrap();
890 wb.remove_row("Sheet1", 2).unwrap();
891
892 assert_eq!(
893 wb.get_cell_value("Sheet1", "A1").unwrap(),
894 CellValue::String("first".to_string())
895 );
896 assert_eq!(
897 wb.get_cell_value("Sheet1", "A2").unwrap(),
898 CellValue::String("third".to_string())
899 );
900 }
901
902 #[test]
903 fn test_workbook_remove_row_sheet_not_found() {
904 let mut wb = Workbook::new();
905 let result = wb.remove_row("NoSheet", 1);
906 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
907 }
908
909 #[test]
910 fn test_workbook_duplicate_row() {
911 let mut wb = Workbook::new();
912 wb.set_cell_value("Sheet1", "A1", "original").unwrap();
913 wb.duplicate_row("Sheet1", 1).unwrap();
914
915 assert_eq!(
916 wb.get_cell_value("Sheet1", "A1").unwrap(),
917 CellValue::String("original".to_string())
918 );
919 assert_eq!(
921 wb.get_cell_value("Sheet1", "A2").unwrap(),
922 CellValue::String("original".to_string())
923 );
924 }
925
926 #[test]
927 fn test_workbook_set_and_get_row_height() {
928 let mut wb = Workbook::new();
929 wb.set_row_height("Sheet1", 3, 25.0).unwrap();
930 assert_eq!(wb.get_row_height("Sheet1", 3).unwrap(), Some(25.0));
931 }
932
933 #[test]
934 fn test_workbook_get_row_height_sheet_not_found() {
935 let wb = Workbook::new();
936 let result = wb.get_row_height("NoSheet", 1);
937 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
938 }
939
940 #[test]
941 fn test_workbook_set_row_visible() {
942 let mut wb = Workbook::new();
943 wb.set_row_visible("Sheet1", 1, false).unwrap();
944 }
945
946 #[test]
947 fn test_workbook_set_row_visible_sheet_not_found() {
948 let mut wb = Workbook::new();
949 let result = wb.set_row_visible("NoSheet", 1, false);
950 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
951 }
952
953 #[test]
954 fn test_workbook_set_and_get_col_width() {
955 let mut wb = Workbook::new();
956 wb.set_col_width("Sheet1", "A", 18.0).unwrap();
957 assert_eq!(wb.get_col_width("Sheet1", "A").unwrap(), Some(18.0));
958 }
959
960 #[test]
961 fn test_workbook_get_col_width_sheet_not_found() {
962 let wb = Workbook::new();
963 let result = wb.get_col_width("NoSheet", "A");
964 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
965 }
966
967 #[test]
968 fn test_workbook_set_col_visible() {
969 let mut wb = Workbook::new();
970 wb.set_col_visible("Sheet1", "B", false).unwrap();
971 }
972
973 #[test]
974 fn test_workbook_set_col_visible_sheet_not_found() {
975 let mut wb = Workbook::new();
976 let result = wb.set_col_visible("NoSheet", "A", false);
977 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
978 }
979
980 #[test]
981 fn test_workbook_insert_cols() {
982 let mut wb = Workbook::new();
983 wb.set_cell_value("Sheet1", "A1", "a").unwrap();
984 wb.set_cell_value("Sheet1", "B1", "b").unwrap();
985 wb.insert_cols("Sheet1", "B", 1).unwrap();
986
987 assert_eq!(
988 wb.get_cell_value("Sheet1", "A1").unwrap(),
989 CellValue::String("a".to_string())
990 );
991 assert_eq!(wb.get_cell_value("Sheet1", "B1").unwrap(), CellValue::Empty);
992 assert_eq!(
993 wb.get_cell_value("Sheet1", "C1").unwrap(),
994 CellValue::String("b".to_string())
995 );
996 }
997
998 #[test]
999 fn test_workbook_insert_cols_updates_formula_and_ranges() {
1000 let mut wb = Workbook::new();
1001 wb.set_cell_value(
1002 "Sheet1",
1003 "D1",
1004 CellValue::Formula {
1005 expr: "SUM(A1:B1)".to_string(),
1006 result: None,
1007 },
1008 )
1009 .unwrap();
1010 wb.add_data_validation(
1011 "Sheet1",
1012 &crate::validation::DataValidationConfig::whole_number("B2:C3", 1, 9),
1013 )
1014 .unwrap();
1015 wb.set_auto_filter("Sheet1", "A1:C10").unwrap();
1016 wb.merge_cells("Sheet1", "B3", "C4").unwrap();
1017
1018 wb.insert_cols("Sheet1", "B", 2).unwrap();
1019
1020 match wb.get_cell_value("Sheet1", "F1").unwrap() {
1021 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(A1:D1)"),
1022 other => panic!("expected formula, got {other:?}"),
1023 }
1024
1025 let validations = wb.get_data_validations("Sheet1").unwrap();
1026 assert_eq!(validations.len(), 1);
1027 assert_eq!(validations[0].sqref, "D2:E3");
1028
1029 let merges = wb.get_merge_cells("Sheet1").unwrap();
1030 assert_eq!(merges, vec!["D3:E4".to_string()]);
1031
1032 let ws = wb.worksheet_ref("Sheet1").unwrap();
1033 assert_eq!(ws.auto_filter.as_ref().unwrap().reference, "A1:E10");
1034 }
1035
1036 #[test]
1037 fn test_workbook_insert_cols_sheet_not_found() {
1038 let mut wb = Workbook::new();
1039 let result = wb.insert_cols("NoSheet", "A", 1);
1040 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
1041 }
1042
1043 #[test]
1044 fn test_workbook_remove_col() {
1045 let mut wb = Workbook::new();
1046 wb.set_cell_value("Sheet1", "A1", "a").unwrap();
1047 wb.set_cell_value("Sheet1", "B1", "b").unwrap();
1048 wb.set_cell_value("Sheet1", "C1", "c").unwrap();
1049 wb.remove_col("Sheet1", "B").unwrap();
1050
1051 assert_eq!(
1052 wb.get_cell_value("Sheet1", "A1").unwrap(),
1053 CellValue::String("a".to_string())
1054 );
1055 assert_eq!(
1056 wb.get_cell_value("Sheet1", "B1").unwrap(),
1057 CellValue::String("c".to_string())
1058 );
1059 }
1060
1061 #[test]
1062 fn test_workbook_remove_col_sheet_not_found() {
1063 let mut wb = Workbook::new();
1064 let result = wb.remove_col("NoSheet", "A");
1065 assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
1066 }
1067
1068 #[test]
1069 fn test_new_stream_writer_validates_name() {
1070 let wb = Workbook::new();
1071 let result = wb.new_stream_writer("Bad[Name");
1072 assert!(result.is_err());
1073 assert!(matches!(result.unwrap_err(), Error::InvalidSheetName(_)));
1074 }
1075
1076 #[test]
1077 fn test_new_stream_writer_rejects_duplicate() {
1078 let wb = Workbook::new();
1079 let result = wb.new_stream_writer("Sheet1");
1080 assert!(result.is_err());
1081 assert!(matches!(
1082 result.unwrap_err(),
1083 Error::SheetAlreadyExists { .. }
1084 ));
1085 }
1086
1087 #[test]
1088 fn test_new_stream_writer_valid_name() {
1089 let wb = Workbook::new();
1090 let sw = wb.new_stream_writer("StreamSheet").unwrap();
1091 assert_eq!(sw.sheet_name(), "StreamSheet");
1092 }
1093
1094 #[test]
1095 fn test_apply_stream_writer_adds_sheet() {
1096 let mut wb = Workbook::new();
1097 let mut sw = wb.new_stream_writer("StreamSheet").unwrap();
1098 sw.write_row(1, &[CellValue::from("Hello"), CellValue::from(42)])
1099 .unwrap();
1100 let idx = wb.apply_stream_writer(sw).unwrap();
1101 assert_eq!(idx, 1);
1102 assert_eq!(wb.sheet_names(), vec!["Sheet1", "StreamSheet"]);
1103 }
1104
1105 #[test]
1106 fn test_apply_stream_writer_merges_sst() {
1107 let mut wb = Workbook::new();
1108 wb.set_cell_value("Sheet1", "A1", "Existing").unwrap();
1109
1110 let mut sw = wb.new_stream_writer("StreamSheet").unwrap();
1111 sw.write_row(1, &[CellValue::from("New"), CellValue::from("Existing")])
1112 .unwrap();
1113 wb.apply_stream_writer(sw).unwrap();
1114
1115 assert!(wb.sst_runtime.len() >= 2);
1116 }
1117
1118 #[test]
1119 fn test_stream_writer_save_and_reopen() {
1120 let dir = TempDir::new().unwrap();
1121 let path = dir.path().join("stream_test.xlsx");
1122
1123 let mut wb = Workbook::new();
1124 wb.set_cell_value("Sheet1", "A1", "Normal").unwrap();
1125
1126 let mut sw = wb.new_stream_writer("Streamed").unwrap();
1127 sw.write_row(1, &[CellValue::from("Name"), CellValue::from("Value")])
1128 .unwrap();
1129 sw.write_row(2, &[CellValue::from("Alice"), CellValue::from(100)])
1130 .unwrap();
1131 sw.write_row(3, &[CellValue::from("Bob"), CellValue::from(200)])
1132 .unwrap();
1133 wb.apply_stream_writer(sw).unwrap();
1134
1135 wb.save(&path).unwrap();
1136
1137 let wb2 = Workbook::open(&path).unwrap();
1138 assert_eq!(wb2.sheet_names(), vec!["Sheet1", "Streamed"]);
1139 assert_eq!(
1140 wb2.get_cell_value("Sheet1", "A1").unwrap(),
1141 CellValue::String("Normal".to_string())
1142 );
1143 assert_eq!(
1144 wb2.get_cell_value("Streamed", "A1").unwrap(),
1145 CellValue::String("Name".to_string())
1146 );
1147 assert_eq!(
1148 wb2.get_cell_value("Streamed", "B2").unwrap(),
1149 CellValue::Number(100.0)
1150 );
1151 assert_eq!(
1152 wb2.get_cell_value("Streamed", "A3").unwrap(),
1153 CellValue::String("Bob".to_string())
1154 );
1155 }
1156
1157 #[test]
1158 fn test_workbook_get_rows_empty_sheet() {
1159 let wb = Workbook::new();
1160 let rows = wb.get_rows("Sheet1").unwrap();
1161 assert!(rows.is_empty());
1162 }
1163
1164 #[test]
1165 fn test_workbook_get_rows_with_data() {
1166 let mut wb = Workbook::new();
1167 wb.set_cell_value("Sheet1", "A1", "Name").unwrap();
1168 wb.set_cell_value("Sheet1", "B1", 42.0).unwrap();
1169 wb.set_cell_value("Sheet1", "A2", "Alice").unwrap();
1170 wb.set_cell_value("Sheet1", "B2", true).unwrap();
1171
1172 let rows = wb.get_rows("Sheet1").unwrap();
1173 assert_eq!(rows.len(), 2);
1174 assert_eq!(rows[0].0, 1);
1175 assert_eq!(rows[0].1.len(), 2);
1176 assert_eq!(rows[0].1[0].0, 1);
1177 assert_eq!(rows[0].1[0].1, CellValue::String("Name".to_string()));
1178 assert_eq!(rows[0].1[1].0, 2);
1179 assert_eq!(rows[0].1[1].1, CellValue::Number(42.0));
1180 assert_eq!(rows[1].0, 2);
1181 assert_eq!(rows[1].1[0].1, CellValue::String("Alice".to_string()));
1182 assert_eq!(rows[1].1[1].1, CellValue::Bool(true));
1183 }
1184
1185 #[test]
1186 fn test_workbook_get_rows_sheet_not_found() {
1187 let wb = Workbook::new();
1188 assert!(wb.get_rows("NoSheet").is_err());
1189 }
1190
1191 #[test]
1192 fn test_workbook_get_cols_empty_sheet() {
1193 let wb = Workbook::new();
1194 let cols = wb.get_cols("Sheet1").unwrap();
1195 assert!(cols.is_empty());
1196 }
1197
1198 #[test]
1199 fn test_workbook_get_cols_with_data() {
1200 let mut wb = Workbook::new();
1201 wb.set_cell_value("Sheet1", "A1", "Name").unwrap();
1202 wb.set_cell_value("Sheet1", "B1", 42.0).unwrap();
1203 wb.set_cell_value("Sheet1", "A2", "Alice").unwrap();
1204 wb.set_cell_value("Sheet1", "B2", 30.0).unwrap();
1205
1206 let cols = wb.get_cols("Sheet1").unwrap();
1207 assert_eq!(cols.len(), 2);
1208 assert_eq!(cols[0].0, "A");
1209 assert_eq!(cols[0].1.len(), 2);
1210 assert_eq!(cols[0].1[0], (1, CellValue::String("Name".to_string())));
1211 assert_eq!(cols[0].1[1], (2, CellValue::String("Alice".to_string())));
1212 assert_eq!(cols[1].0, "B");
1213 assert_eq!(cols[1].1[0], (1, CellValue::Number(42.0)));
1214 assert_eq!(cols[1].1[1], (2, CellValue::Number(30.0)));
1215 }
1216
1217 #[test]
1218 fn test_workbook_get_cols_sheet_not_found() {
1219 let wb = Workbook::new();
1220 assert!(wb.get_cols("NoSheet").is_err());
1221 }
1222
1223 #[test]
1224 fn test_apply_stream_writer_cells_readable_without_reopen() {
1225 let mut wb = Workbook::new();
1226 let mut sw = wb.new_stream_writer("Streamed").unwrap();
1227 sw.write_row(1, &[CellValue::from("Name"), CellValue::from("Age")])
1228 .unwrap();
1229 sw.write_row(2, &[CellValue::from("Alice"), CellValue::from(30)])
1230 .unwrap();
1231 wb.apply_stream_writer(sw).unwrap();
1232
1233 assert_eq!(
1236 wb.get_cell_value("Streamed", "A1").unwrap(),
1237 CellValue::String("Name".to_string())
1238 );
1239 assert_eq!(
1240 wb.get_cell_value("Streamed", "B1").unwrap(),
1241 CellValue::String("Age".to_string())
1242 );
1243 assert_eq!(
1244 wb.get_cell_value("Streamed", "A2").unwrap(),
1245 CellValue::String("Alice".to_string())
1246 );
1247 assert_eq!(
1248 wb.get_cell_value("Streamed", "B2").unwrap(),
1249 CellValue::Number(30.0)
1250 );
1251 }
1252
1253 #[test]
1254 fn test_workbook_get_rows_roundtrip_save_open() {
1255 let mut wb = Workbook::new();
1256 wb.set_cell_value("Sheet1", "A1", "hello").unwrap();
1257 wb.set_cell_value("Sheet1", "B1", 99.0).unwrap();
1258 wb.set_cell_value("Sheet1", "A2", true).unwrap();
1259
1260 let tmp = std::env::temp_dir().join("test_get_rows_roundtrip.xlsx");
1261 wb.save(&tmp).unwrap();
1262
1263 let wb2 = Workbook::open(&tmp).unwrap();
1264 let rows = wb2.get_rows("Sheet1").unwrap();
1265 assert_eq!(rows.len(), 2);
1266 assert_eq!(rows[0].1[0].1, CellValue::String("hello".to_string()));
1267 assert_eq!(rows[0].1[1].1, CellValue::Number(99.0));
1268 assert_eq!(rows[1].1[0].1, CellValue::Bool(true));
1269
1270 let _ = std::fs::remove_file(&tmp);
1271 }
1272
1273 #[test]
1274 fn test_apply_stream_optimized_basic() {
1275 let mut wb = Workbook::new();
1276 let mut sw = wb.new_stream_writer("Optimized").unwrap();
1277 sw.write_row(1, &[CellValue::from("Hello"), CellValue::from(42)])
1278 .unwrap();
1279 sw.write_row(2, &[CellValue::from("World"), CellValue::from(99)])
1280 .unwrap();
1281 let idx = wb.apply_stream_writer(sw).unwrap();
1282 assert_eq!(idx, 1);
1283
1284 assert_eq!(
1285 wb.get_cell_value("Optimized", "A1").unwrap(),
1286 CellValue::String("Hello".to_string())
1287 );
1288 assert_eq!(
1289 wb.get_cell_value("Optimized", "B1").unwrap(),
1290 CellValue::Number(42.0)
1291 );
1292 assert_eq!(
1293 wb.get_cell_value("Optimized", "A2").unwrap(),
1294 CellValue::String("World".to_string())
1295 );
1296 assert_eq!(
1297 wb.get_cell_value("Optimized", "B2").unwrap(),
1298 CellValue::Number(99.0)
1299 );
1300 }
1301
1302 #[test]
1303 fn test_apply_stream_optimized_sst_merge() {
1304 let mut wb = Workbook::new();
1305 wb.set_cell_value("Sheet1", "A1", "Existing").unwrap();
1306
1307 let mut sw = wb.new_stream_writer("Streamed").unwrap();
1308 sw.write_row(
1309 1,
1310 &[
1311 CellValue::from("New"),
1312 CellValue::from("Existing"),
1313 CellValue::from("New"),
1314 ],
1315 )
1316 .unwrap();
1317 wb.apply_stream_writer(sw).unwrap();
1318
1319 assert_eq!(
1320 wb.get_cell_value("Streamed", "A1").unwrap(),
1321 CellValue::String("New".to_string())
1322 );
1323 assert_eq!(
1324 wb.get_cell_value("Streamed", "B1").unwrap(),
1325 CellValue::String("Existing".to_string())
1326 );
1327 assert!(wb.sst_runtime.len() >= 2);
1328 }
1329
1330 #[test]
1331 fn test_apply_stream_optimized_all_types() {
1332 let mut wb = Workbook::new();
1333 let mut sw = wb.new_stream_writer("Types").unwrap();
1334 sw.write_row(
1335 1,
1336 &[
1337 CellValue::from("text"),
1338 CellValue::from(42),
1339 CellValue::from(3.14),
1340 CellValue::from(true),
1341 CellValue::Formula {
1342 expr: "SUM(B1:C1)".to_string(),
1343 result: None,
1344 },
1345 CellValue::Error("#N/A".to_string()),
1346 CellValue::Empty,
1347 ],
1348 )
1349 .unwrap();
1350 wb.apply_stream_writer(sw).unwrap();
1351
1352 assert_eq!(
1353 wb.get_cell_value("Types", "A1").unwrap(),
1354 CellValue::String("text".to_string())
1355 );
1356 assert_eq!(
1357 wb.get_cell_value("Types", "B1").unwrap(),
1358 CellValue::Number(42.0)
1359 );
1360 assert_eq!(
1361 wb.get_cell_value("Types", "D1").unwrap(),
1362 CellValue::Bool(true)
1363 );
1364 match wb.get_cell_value("Types", "E1").unwrap() {
1365 CellValue::Formula { expr, .. } => assert_eq!(expr, "SUM(B1:C1)"),
1366 other => panic!("expected formula, got {other:?}"),
1367 }
1368 assert_eq!(
1369 wb.get_cell_value("Types", "F1").unwrap(),
1370 CellValue::Error("#N/A".to_string())
1371 );
1372 assert_eq!(wb.get_cell_value("Types", "G1").unwrap(), CellValue::Empty);
1373 }
1374
1375 #[test]
1376 fn test_apply_stream_optimized_save_reopen() {
1377 let dir = TempDir::new().unwrap();
1378 let path = dir.path().join("stream_optimized.xlsx");
1379
1380 let mut wb = Workbook::new();
1381 wb.set_cell_value("Sheet1", "A1", "Normal").unwrap();
1382
1383 let mut sw = wb.new_stream_writer("Fast").unwrap();
1384 sw.write_row(1, &[CellValue::from("Name"), CellValue::from("Value")])
1385 .unwrap();
1386 sw.write_row(2, &[CellValue::from("Alice"), CellValue::from(100)])
1387 .unwrap();
1388 sw.write_row(3, &[CellValue::from("Bob"), CellValue::from(200)])
1389 .unwrap();
1390 wb.apply_stream_writer(sw).unwrap();
1391
1392 wb.save(&path).unwrap();
1393
1394 let wb2 = Workbook::open(&path).unwrap();
1395 assert_eq!(wb2.sheet_names(), vec!["Sheet1", "Fast"]);
1396 assert_eq!(
1397 wb2.get_cell_value("Fast", "A1").unwrap(),
1398 CellValue::String("Name".to_string())
1399 );
1400 assert_eq!(
1401 wb2.get_cell_value("Fast", "B2").unwrap(),
1402 CellValue::Number(100.0)
1403 );
1404 assert_eq!(
1405 wb2.get_cell_value("Fast", "A3").unwrap(),
1406 CellValue::String("Bob".to_string())
1407 );
1408 }
1409}