sheetkit_core/workbook/
mod.rs1use std::collections::{HashMap, HashSet};
8use std::io::{Read as _, Write as _};
9use std::path::Path;
10
11use serde::Serialize;
12use sheetkit_xml::chart::ChartSpace;
13use sheetkit_xml::comments::Comments;
14use sheetkit_xml::content_types::{
15 mime_types, ContentTypeDefault, ContentTypeOverride, ContentTypes,
16};
17use sheetkit_xml::drawing::{MarkerType, WsDr};
18use sheetkit_xml::relationships::{self, rel_types, Relationship, Relationships};
19use sheetkit_xml::shared_strings::Sst;
20use sheetkit_xml::styles::StyleSheet;
21use sheetkit_xml::workbook::{WorkbookProtection, WorkbookXml};
22use sheetkit_xml::worksheet::{Cell, CellFormula, DrawingRef, Row, WorksheetXml};
23use zip::write::SimpleFileOptions;
24use zip::CompressionMethod;
25
26use crate::cell::CellValue;
27use crate::cell_ref_shift::shift_cell_references_in_text;
28use crate::chart::ChartConfig;
29use crate::comment::CommentConfig;
30use crate::conditional::ConditionalFormatRule;
31use crate::error::{Error, Result};
32use crate::image::ImageConfig;
33use crate::pivot::{PivotTableConfig, PivotTableInfo};
34use crate::protection::WorkbookProtectionConfig;
35use crate::sst::SharedStringTable;
36use crate::utils::cell_ref::{cell_name_to_coordinates, column_name_to_number};
37use crate::utils::constants::MAX_CELL_CHARS;
38use crate::validation::DataValidationConfig;
39use crate::workbook_paths::{
40 default_relationships, relationship_part_path, relative_relationship_target,
41 resolve_relationship_target,
42};
43
44mod cell_ops;
45mod data;
46mod drawing;
47mod features;
48mod io;
49mod sheet_ops;
50
51const XML_DECLARATION: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#;
53
54pub struct Workbook {
56 content_types: ContentTypes,
57 package_rels: Relationships,
58 workbook_xml: WorkbookXml,
59 workbook_rels: Relationships,
60 worksheets: Vec<(String, WorksheetXml)>,
61 stylesheet: StyleSheet,
62 #[allow(dead_code)]
63 shared_strings: Sst,
64 sst_runtime: SharedStringTable,
65 sheet_comments: Vec<Option<Comments>>,
67 charts: Vec<(String, ChartSpace)>,
69 raw_charts: Vec<(String, Vec<u8>)>,
71 drawings: Vec<(String, WsDr)>,
73 images: Vec<(String, Vec<u8>)>,
75 #[allow(dead_code)]
77 worksheet_drawings: HashMap<usize, usize>,
78 worksheet_rels: HashMap<usize, Relationships>,
80 drawing_rels: HashMap<usize, Relationships>,
82 core_properties: Option<sheetkit_xml::doc_props::CoreProperties>,
84 app_properties: Option<sheetkit_xml::doc_props::ExtendedProperties>,
86 custom_properties: Option<sheetkit_xml::doc_props::CustomProperties>,
88 pivot_tables: Vec<(String, sheetkit_xml::pivot_table::PivotTableDefinition)>,
90 pivot_cache_defs: Vec<(String, sheetkit_xml::pivot_cache::PivotCacheDefinition)>,
92 pivot_cache_records: Vec<(String, sheetkit_xml::pivot_cache::PivotCacheRecords)>,
94 theme_xml: Option<Vec<u8>>,
96 theme_colors: sheetkit_xml::theme::ThemeColors,
98 sheet_sparklines: Vec<Vec<crate::sparkline::SparklineConfig>>,
100 sheet_vml: Vec<Option<Vec<u8>>>,
103}
104
105impl Workbook {
106 pub(crate) fn sheet_index(&self, sheet: &str) -> Result<usize> {
108 self.worksheets
109 .iter()
110 .position(|(name, _)| name == sheet)
111 .ok_or_else(|| Error::SheetNotFound {
112 name: sheet.to_string(),
113 })
114 }
115
116 pub(crate) fn worksheet_mut(&mut self, sheet: &str) -> Result<&mut WorksheetXml> {
118 self.worksheets
119 .iter_mut()
120 .find(|(name, _)| name == sheet)
121 .map(|(_, ws)| ws)
122 .ok_or_else(|| Error::SheetNotFound {
123 name: sheet.to_string(),
124 })
125 }
126
127 pub(crate) fn worksheet_ref(&self, sheet: &str) -> Result<&WorksheetXml> {
129 self.worksheets
130 .iter()
131 .find(|(name, _)| name == sheet)
132 .map(|(_, ws)| ws)
133 .ok_or_else(|| Error::SheetNotFound {
134 name: sheet.to_string(),
135 })
136 }
137
138 pub(crate) fn sheet_part_path(&self, sheet_idx: usize) -> String {
141 if let Some(sheet_entry) = self.workbook_xml.sheets.sheets.get(sheet_idx) {
142 if let Some(rel) = self
143 .workbook_rels
144 .relationships
145 .iter()
146 .find(|r| r.id == sheet_entry.r_id && r.rel_type == rel_types::WORKSHEET)
147 {
148 return resolve_relationship_target("xl/workbook.xml", &rel.target);
149 }
150 }
151 format!("xl/worksheets/sheet{}.xml", sheet_idx + 1)
152 }
153}