Skip to main content

excelize_rs/
excelize.rs

1//! Core spreadsheet methods ported from `excelize.go`.
2//!
3//! These methods deal with workbook-level operations, lazy worksheet access,
4//! and content-type / VBA bookkeeping.
5
6use crate::errors::Result;
7use crate::file::File;
8use crate::xml::workbook::{CalcPropsOptions, WorkbookPropsOptions, WorkbookProtectionOptions};
9use crate::xml::worksheet::XlsxWorksheet;
10
11/// Validate a worksheet name.
12pub fn check_sheet_name(name: &str) -> Result<()> {
13    <File as ExcelizeCore>::check_sheet_name(name)
14}
15
16/// Core methods that mirror the Go `File` API.
17pub trait ExcelizeCore {
18    /// Validate that a worksheet name is legal.
19    fn check_sheet_name(name: &str) -> Result<()>;
20
21    /// Validate options used when opening a reader.
22    fn check_open_reader_options(&self) -> Result<()>;
23
24    /// Deserialize and return a worksheet by name.
25    fn work_sheet_reader(&self, sheet: &str) -> Result<XlsxWorksheet>;
26
27    /// Update linked cell values so Excel recalculates them on open.
28    fn update_linked_value(&self) -> Result<()>;
29
30    /// Add a VBA project binary to the workbook.
31    fn add_vba_project(&self, data: &[u8]) -> Result<()>;
32
33    /// Set the workbook content type and `.bin` default for macro workbooks.
34    fn set_content_type_part_project_extensions(&self, content_type: &str) -> Result<()>;
35
36    /// Set workbook properties.
37    fn set_workbook_props(&self, opts: &WorkbookPropsOptions) -> Result<()>;
38
39    /// Get workbook properties.
40    fn get_workbook_props(&self) -> Result<WorkbookPropsOptions>;
41
42    /// Set calculation properties.
43    fn set_calc_props(&self, opts: &CalcPropsOptions) -> Result<()>;
44
45    /// Get calculation properties.
46    fn get_calc_props(&self) -> Result<CalcPropsOptions>;
47
48    /// Protect the workbook.
49    fn protect_workbook(&self, opts: &WorkbookProtectionOptions) -> Result<()>;
50
51    /// Remove workbook protection, optionally verifying the password.
52    fn unprotect_workbook(&self, password: Option<&str>) -> Result<()>;
53}
54
55impl ExcelizeCore for File {
56    fn check_sheet_name(name: &str) -> Result<()> {
57        if name.is_empty() {
58            return Err(Box::new(crate::errors::ErrSheetNameBlank));
59        }
60        if crate::lib_util::count_utf16_string(name) > crate::constants::MAX_SHEET_NAME_LENGTH {
61            return Err(Box::new(crate::errors::ErrSheetNameLength));
62        }
63        if name.starts_with('\'') || name.ends_with('\'') {
64            return Err(Box::new(crate::errors::ErrSheetNameSingleQuote));
65        }
66        if name.contains(|c: char| matches!(c, ':'))
67            || name.contains('\\')
68            || name.contains('/')
69            || name.contains('?')
70            || name.contains('*')
71            || name.contains('[')
72            || name.contains(']')
73        {
74            return Err(Box::new(crate::errors::ErrSheetNameInvalid));
75        }
76        Ok(())
77    }
78
79    fn check_open_reader_options(&self) -> Result<()> {
80        self.check_open_reader_options()
81    }
82
83    fn work_sheet_reader(&self, sheet: &str) -> Result<XlsxWorksheet> {
84        File::work_sheet_reader(self, sheet)
85    }
86
87    fn update_linked_value(&self) -> Result<()> {
88        let mut wb = self.workbook_reader()?;
89        // Drop the calcPr so Excel recalculates on open.
90        wb.calc_pr = None;
91        *self.workbook.lock().unwrap() = Some(wb);
92        for name in self.get_sheet_list() {
93            let mut ws = self.work_sheet_reader(&name)?;
94            for row in &mut ws.sheet_data.row {
95                for cell in &mut row.c {
96                    if cell.f.is_some() && cell.v.is_some() {
97                        cell.v = None;
98                        cell.t = None;
99                    }
100                }
101            }
102            if let Some(path) = self.get_sheet_xml_path(&name) {
103                self.sheet.insert(path, ws);
104            }
105        }
106        Ok(())
107    }
108
109    fn add_vba_project(&self, data: &[u8]) -> Result<()> {
110        crate::file::add_vba_project(self, data)
111    }
112
113    fn set_content_type_part_project_extensions(&self, content_type: &str) -> Result<()> {
114        crate::file::set_content_type_part_project_extensions(self, content_type)
115    }
116
117    fn set_workbook_props(&self, opts: &WorkbookPropsOptions) -> Result<()> {
118        File::set_workbook_props(self, opts)
119    }
120
121    fn get_workbook_props(&self) -> Result<WorkbookPropsOptions> {
122        File::get_workbook_props(self)
123    }
124
125    fn set_calc_props(&self, opts: &CalcPropsOptions) -> Result<()> {
126        File::set_calc_props(self, opts)
127    }
128
129    fn get_calc_props(&self) -> Result<CalcPropsOptions> {
130        File::get_calc_props(self)
131    }
132
133    fn protect_workbook(&self, opts: &WorkbookProtectionOptions) -> Result<()> {
134        File::protect_workbook(self, opts)
135    }
136
137    fn unprotect_workbook(&self, password: Option<&str>) -> Result<()> {
138        File::unprotect_workbook(self, password)
139    }
140}