1use std::{fs, slice};
2use std::cell::RefCell;
3use std::fs::File;
4use std::hash::{Hash, Hasher};
5use std::path::Path;
6use std::rc::Rc;
7use futures::executor::block_on;
8use futures::join;
9use zip::result::ZipError;
10use crate::api::worksheet::WorkSheet;
11use crate::file::XlsxFileType;
12use crate::utils::{id_util, zip_util};
13use crate::result::{WorkSheetError, WorkbookError, WorkbookResult};
14use crate::{Properties, xml};
15use crate::xml::content_types::ContentTypes;
16use crate::xml::core_properties::CoreProperties;
17use crate::xml::app_properties::AppProperties;
18use crate::xml::io::{Io, IoV2};
19use crate::xml::medias::Medias;
20use crate::xml::metadata::Metadata;
21use crate::xml::style::StyleSheet;
22use crate::xml::relationships::Relationships;
23use crate::xml::shared_string::SharedString;
24use crate::xml::theme::{Theme, Themes};
25
26#[derive(Debug)]
27pub struct Workbook {
28 pub sheets: Vec<WorkSheet>,
29 pub(crate) tmp_path: String,
30 pub(crate) file_path: String,
31 closed: bool,
32 pub(crate) workbook: Rc<RefCell<xml::workbook::Workbook>>,
33 pub(crate) style_sheet: Rc<RefCell<StyleSheet>>,
34 pub(crate) workbook_rel: Rc<RefCell<Relationships>>,
35 pub(crate) content_types: Rc<RefCell<ContentTypes>>,
36 pub(crate) medias: Rc<RefCell<Medias>>,
37 pub(crate) themes: Rc<RefCell<Themes>>,
38 pub(crate) metadata: Rc<RefCell<Metadata>>,
39 pub(crate) core_properties: Option<CoreProperties>,
40 pub(crate) app_properties: Option<AppProperties>,
41 pub(crate) shared_string: Rc<SharedString>,
42}
43
44impl Workbook {
48 fn get_core_properties(&mut self) -> &mut CoreProperties {
49 self.core_properties.get_or_insert(CoreProperties::from_path(&self.file_path).unwrap())
50 }
51
52 fn get_app_properties(&mut self) -> &mut AppProperties {
53 self.app_properties.get_or_insert(AppProperties::from_path(&self.file_path).unwrap())
54 }
55}
56
57impl Workbook {
58 pub fn new() -> Workbook {
59 let file_path = concat!(env!("CARGO_MANIFEST_DIR"), "/resources/new.xlsx");
60 let mut wb = Self::from_path(file_path).unwrap();
61 wb.file_path = String::from(file_path);
62 wb
63 }
64
65 pub fn get_worksheet_mut(&mut self, id: u32) -> WorkbookResult<&mut WorkSheet> {
66 let sheet = self.sheets
67 .iter_mut()
68 .find(|sheet| sheet.id == id).ok_or(WorkSheetError::FileNotFound)?;
69 Ok(sheet)
70 }
71
72 pub fn get_worksheet(&self, id: u32) -> WorkbookResult<&WorkSheet> {
73 let sheet = self.sheets
74 .iter()
75 .find(|sheet| sheet.id == id).ok_or(WorkSheetError::FileNotFound)?;
76 Ok(sheet)
77 }
78
79 pub fn get_worksheet_by_name(&self, name: &str) -> WorkbookResult<&WorkSheet> {
80 let sheet = self.sheets
81 .iter()
82 .find(|sheet| sheet.name == name);
83 match sheet {
84 Some(sheet) => Ok(sheet),
85 None => Err(WorkbookError::SheetError(WorkSheetError::FileNotFound))
86 }
87 }
88
89 pub fn get_worksheet_mut_by_name(&mut self, name: &str) -> WorkbookResult<& mut WorkSheet> {
90 let sheet = self.sheets
91 .iter_mut()
92 .find(|sheet| sheet.name == name);
93 match sheet {
94 Some(sheet) => Ok(sheet),
95 None => Err(WorkbookError::SheetError(WorkSheetError::FileNotFound))
96 }
97 }
98
99 pub fn add_worksheet(&mut self) -> WorkbookResult<&mut WorkSheet> {
100 let (r_id, target_id) = self.workbook_rel.borrow_mut().add_worksheet_v2();
101 let (sheet_id, name) = self.workbook.borrow_mut().add_worksheet_v2(r_id, None)?;
102 let worksheet = WorkSheet::add_worksheet(sheet_id, &name, target_id, self);
103 self.sheets.push(worksheet);
104 self.get_worksheet_mut(sheet_id)
105 }
106
107 pub fn add_worksheet_by_name(&mut self, name: &str) -> WorkbookResult<&mut WorkSheet> {
108 let (r_id, target_id) = self.workbook_rel.borrow_mut().add_worksheet_v2();
109 let (sheet_id, name) = self.workbook.borrow_mut().add_worksheet_v2(r_id, Some(name))?;
110 let worksheet = WorkSheet::add_worksheet(sheet_id, &name, target_id, self);
111 self.sheets.push(worksheet);
112 self.get_worksheet_mut(sheet_id)
113 }
114
115 pub fn duplicate_worksheet(&mut self, id: u32) -> WorkbookResult<&mut WorkSheet> {
116 let copy_worksheet = self.sheets
117 .iter()
118 .find(|sheet| sheet.id == id).ok_or(WorkSheetError::FileNotFound)?;
119 let (r_id, target_id) = self.workbook_rel.borrow_mut().add_worksheet_v2();
120 let (sheet_id, new_name) = self.workbook.borrow_mut().add_worksheet_v2(r_id, None)?;
121 let worksheet = WorkSheet::from_worksheet(sheet_id, &new_name, target_id, copy_worksheet);
122 self.sheets.push(worksheet);
123 self.get_worksheet_mut(sheet_id)
124 }
125
126 pub fn duplicate_worksheet_by_name(&mut self, name: &str) -> WorkbookResult<&mut WorkSheet> {
127 let copy_worksheet = self.sheets
128 .iter()
129 .find(|sheet| sheet.name == name).ok_or(WorkSheetError::FileNotFound)?;
130 let new_name = format!("{} Duplicated", name);
131 let (r_id, target_id) = self.workbook_rel.borrow_mut().add_worksheet_v2();
132 let (sheet_id, _) = self.workbook.borrow_mut().add_worksheet_v2(r_id, Some(&new_name))?;
133 let worksheet = WorkSheet::from_worksheet(sheet_id, &new_name, target_id, copy_worksheet);
134 self.sheets.push(worksheet);
135 self.get_worksheet_mut(sheet_id)
136 }
137
138 pub fn set_size(&mut self, width: u32, height: u32) -> WorkbookResult<()> {
139 let workbook = &mut self.workbook.borrow_mut();
140 let book_view = workbook.book_views.book_views.get_mut(0).unwrap();
141 book_view.window_width = width;
142 book_view.window_height = height;
143 Ok(())
144 }
145
146 pub fn set_tab_ratio(&mut self, tab_ratio: f64) -> WorkbookResult<()> {
147 let tab_ratio = (tab_ratio * 10.0).round() as u32;
148 let workbook = &mut self.workbook.borrow_mut();
149 let book_view = workbook.book_views.book_views.get_mut(0).unwrap();
150 book_view.tab_ratio = Some(tab_ratio);
151 Ok(())
152 }
153
154 pub fn define_name(&mut self, name: &str, value: &str) -> WorkbookResult<()> {
155 self.workbook.borrow_mut().defined_names.add_define_name(name, value, None);
156 Ok(())
157 }
158
159 pub fn define_local_name(&mut self, name: &str, value: &str, sheet_id: u32) -> WorkbookResult<()> {
160 if sheet_id > self.sheets.len() as u32 {
161 return Err(WorkbookError::SheetError(WorkSheetError::FileNotFound));
162 }
163 self.workbook.borrow_mut().defined_names.add_define_name(name, value, Some(sheet_id - 1));
164 Ok(())
165 }
166
167 pub fn get_defined_name(&self, name: &str) -> WorkbookResult<String> {
169 let book = self.workbook.borrow();
170 book.defined_names
171 .get_defined_name(name, None)
172 .map(String::from)
173 .ok_or(WorkbookError::SheetError(WorkSheetError::FileNotFound))
174 }
175 pub fn get_defined_local_name(&self, name: &str, sheet_id: u32) -> WorkbookResult<String> {
177 if sheet_id > self.sheets.len() as u32 {
178 return Err(WorkbookError::SheetError(WorkSheetError::FileNotFound));
179 }
180 let book = self.workbook.borrow();
181 book.defined_names
182 .get_defined_name(name, Some(sheet_id - 1))
183 .map(String::from)
184 .ok_or(WorkbookError::SheetError(WorkSheetError::FileNotFound))
185 }
186
187 pub fn worksheets_mut(&mut self) -> slice::IterMut<WorkSheet> {
188 self.sheets.iter_mut()
189 }
190
191 pub fn worksheets(&self) -> slice::Iter<WorkSheet> {
192 self.sheets.iter()
193 }
194
195 pub fn read_only_recommended(&mut self) -> WorkbookResult<()> {
196 let workbook = &mut self.workbook.borrow_mut();
197 let mut file_sharing = workbook.file_sharing.take().unwrap_or_default();
198 file_sharing.read_only_recommended = 1;
199 workbook.file_sharing = Some(file_sharing);
200 Ok(())
201 }
202
203 pub fn set_properties(&mut self, properties: &Properties) -> WorkbookResult<()> {
204 let core_properties = self.get_core_properties();
205 core_properties.update_by_properties(properties);
206 let app_properties = self.get_app_properties();
207 app_properties.update_by_properties(properties);
208 Ok(())
209 }
210}
211
212impl Workbook {
213 fn from_path_v2<P: AsRef<Path>>(file_path: P) -> WorkbookResult<Workbook> {
214 let file_name = file_path.as_ref().file_name().ok_or(ZipError::FileNotFound)?;
215 let tmp_path = format!("./~${}_{:X}", file_name.to_str().ok_or(ZipError::FileNotFound)?, id_util::new_id());
216 let file = File::open(&file_path)?;
217 let mut archive = zip::ZipArchive::new(file)?;
218 let mut medias = Medias::default();
219 let mut themes = Themes::default();
220 let workbook_xml = xml::workbook::Workbook::from_zip_file(&mut archive, "xl/workbook.xml");
221 let workbook_rel = Relationships::from_zip_file(&mut archive, "xl/_rels/workbook.xml.rels");
222 let content_types = ContentTypes::from_zip_file(&mut archive, "[Content_Types].xml");
223 let style_sheet = StyleSheet::from_zip_file(&mut archive, "xl/styles.xml");
224 let metadata = Metadata::from_zip_file(&mut archive, "xl/metadata.xml");
225 let shared_string = SharedString::from_zip_file(&mut archive, "xl/sharedStrings.xml");
226 let mut theme_paths = Vec::new();
227 for i in 0..archive.len() {
228 let mut file = archive.by_index(i)?;
229 if file.is_file() {
230 let file_name = file.name();
231 if file_name.starts_with("xl/media/") {
232 medias.add_existed_media(&file_name);
233 }
234 else if file_name.starts_with("xl/theme/") {
235 theme_paths.push(file_name.to_string());
236 }
237 }
238 }
239 theme_paths.iter().for_each(|file_name| {
240 let theme = Theme::from_zip_file(&mut archive, &file_name).unwrap();
241 themes.add_theme(theme);
242 });
243 let workbook = Rc::new(RefCell::new(workbook_xml.unwrap_or_default()));
244 let workbook_rel = Rc::new(RefCell::new(workbook_rel.unwrap_or_default()));
245 let content_types = Rc::new(RefCell::new(content_types.unwrap_or_default()));
246 let style_sheet = Rc::new(RefCell::new(style_sheet.unwrap_or_default()));
247 let metadata = Rc::new(RefCell::new(metadata.unwrap_or_default()));
248 let shared_string = Rc::new(shared_string.unwrap_or_default());
249 let medias = Rc::new(RefCell::new(medias));
250 let themes = Rc::new(RefCell::new(themes));
251 let sheets = workbook.borrow().sheets.sheets.iter().map(
252 |sheet_xml| {
253 let binding = workbook_rel.borrow();
254 let (target, target_id) = binding.get_target(&sheet_xml.r_id);
255 WorkSheet::from_archive(
256 sheet_xml.sheet_id,
257 &sheet_xml.name,
258 target,
259 target_id,
260 &file_path,
261 &mut archive,
262 Rc::clone(&workbook),
263 Rc::clone(&workbook_rel),
264 Rc::clone(&style_sheet),
265 Rc::clone(&content_types),
266 Rc::clone(&medias),
267 Rc::clone(&themes),
268 Rc::clone(&metadata),
269 Rc::clone(&shared_string),
270 )
271 }).collect::<Vec<WorkSheet>>();
272 let api_workbook = Workbook {
273 sheets,
274 tmp_path,
275 file_path: file_path.as_ref().to_str().unwrap().to_string(),
276 closed: false,
277 workbook: Rc::clone(&workbook),
278 workbook_rel: Rc::clone(&workbook_rel),
279 style_sheet: Rc::clone(&style_sheet),
280 content_types: Rc::clone(&content_types),
281 medias: Rc::clone(&medias),
282 themes: Rc::clone(&themes),
283 metadata,
284 core_properties: None,
285 app_properties: None,
286 shared_string,
287 };
288 Ok(api_workbook)
289 }
290
291 pub fn from_path<P: AsRef<Path>>(file_path: P) -> WorkbookResult<Workbook> {
292 let workbook = Self::from_path_v2(file_path);
293 workbook
295 }
296
297 async fn save_async(&self) -> WorkbookResult<()> {
298 let workbook = self.workbook.borrow();
299 let workbook = workbook.save_async(&self.tmp_path);
300 let style_sheet = self.style_sheet.borrow();
301 let style_sheet = style_sheet.save_async(&self.tmp_path);
302 let workbook_rel = self.workbook_rel.borrow();
303 let workbook_rel = workbook_rel.save_async(&self.tmp_path, XlsxFileType::WorkbookRels);
304 let content_types = self.content_types.borrow();
305 let content_types = content_types.save_async(&self.tmp_path);
306 let medias = self.medias.borrow();
307 let medias = medias.save_async(&self.tmp_path);
308 let metadata = self.metadata.borrow();
309 let metadata = metadata.save_async(&self.tmp_path);
310 join!(workbook, style_sheet, workbook_rel, content_types, medias, metadata);
311 Ok(())
312 }
313
314 pub fn save_as<P: AsRef<Path>>(&self, file_path: P) -> WorkbookResult<()> {
315 if self.closed {
316 return Err(WorkbookError::FileNotFound);
317 }
318 zip_util::extract_dir(&self.file_path, &self.tmp_path)?;
320 self.sheets.iter().for_each(|s| s.save_as(&self.tmp_path).unwrap());
322 block_on(self.save_async()).unwrap();
323 if let Some(core_propertises) = &self.core_properties {
325 core_propertises.save(&self.tmp_path);
326 }
327 if let Some(app_properties) = &self.app_properties {
328 app_properties.save(&self.tmp_path);
329 }
330 zip_util::zip_dir(&self.tmp_path, file_path)?;
332 fs::remove_dir_all(&self.tmp_path).unwrap();
334 Ok(())
335 }
336
337 pub fn save(&mut self) -> WorkbookResult<()> {
338 self.save_as(&self.file_path.clone())
339 }
340
341 pub fn finish(&mut self) {
342 if !self.closed {
343 fs::remove_dir_all(&self.tmp_path);
344 self.closed = true;
345 }
346 }
347}
348
349impl Drop for Workbook {
350 fn drop(&mut self) {
351 self.finish();
352 }
353}