1use std::collections::HashMap;
2
3use anyhow::{anyhow, Result};
4use rust_xlsxwriter::{Workbook, Worksheet, XlsxError, Format, IntoExcelData};
5
6use crate::{CellValue, ColNum, RowNum};
7
8impl IntoExcelData for CellValue<'_> {
9 fn write<'a>(
10 self,
11 worksheet: &'a mut Worksheet,
12 row: RowNum,
13 col: ColNum,
14 ) -> Result<&'a mut Worksheet, XlsxError> {
15 match self {
16 CellValue::Blank => {},
17 CellValue::Error(_) => {},
18 CellValue::Bool(v) => {
19 worksheet.write_boolean(row, col, v)?;
20 },
21 CellValue::Number(v) => {
22 worksheet.write_number(row, col, v)?;
23 },
24 CellValue::Date(v) => {
25 worksheet.write_number_with_format(row, col, v, &FMT_DEFAULT_DATE)?;
26 },
27 CellValue::Time(v) => {
28 worksheet.write_number_with_format(row, col, v, &FMT_DEFAULT_TIME)?;
29 },
30 CellValue::Datetime(v) => {
31 worksheet.write_number_with_format(row, col, v, &FMT_DEFAULT_DATETIME)?;
32 },
33 CellValue::Shared(v) => {
34 worksheet.write_string(row, col, v)?;
35 },
36 CellValue::String(v) => {
37 worksheet.write_string(row, col, v)?;
38 }
39 };
40 Ok(worksheet)
41 }
42
43 fn write_with_format<'a, 'b>(
44 self,
45 worksheet: &'a mut Worksheet,
46 row: RowNum,
47 col: ColNum,
48 format: &'b Format,
49 ) -> Result<&'a mut Worksheet, XlsxError> {
50 match self {
51 CellValue::Blank => {},
52 CellValue::Error(_) => {},
53 CellValue::Bool(v) => {
54 worksheet.write_boolean_with_format(row, col, v, format)?;
55 },
56 CellValue::Number(v) => {
57 worksheet.write_number_with_format(row, col, v, format)?;
58 },
59 CellValue::Date(v) => {
60 worksheet.write_number_with_format(row, col, v, format)?;
61 },
62 CellValue::Time(v) => {
63 worksheet.write_number_with_format(row, col, v, format)?;
64 },
65 CellValue::Datetime(v) => {
66 worksheet.write_number_with_format(row, col, v, format)?;
67 },
68 CellValue::Shared(v) => {
69 worksheet.write_string_with_format(row, col, v, format)?;
70 },
71 CellValue::String(v) => {
72 worksheet.write_string_with_format(row, col, v, format)?;
73 }
74 };
75 Ok(worksheet)
76 }
77}
78
79pub struct XlsxWriter {
80 book: Workbook,
81 rows: HashMap<String, RowNum>,
82 open: bool,
83 columns: HashMap<String, HashMap<String, ColNum>>,
84 prepends: HashMap<String, bool>
85}
86
87impl XlsxWriter {
88 pub fn new() -> Self {
90 Self {
91 open: true,
92 book: Workbook::new(),
93 rows: HashMap::new(),
94 columns: HashMap::new(),
95 prepends: HashMap::new()
96 }
97 }
98 #[inline]
100 pub fn has_sheet(&mut self, shname: &String) -> bool {
101 match self.book.worksheet_from_name(&shname) {
102 Ok(_) => true,
103 Err(_) => false
104 }
105 }
106 pub fn with_columns(&mut self, shname: String, columns: Vec<String>, add_to_top: bool) {
111 let mut maps = HashMap::with_capacity(columns.len());
112 for (i, colname) in columns.into_iter().enumerate() {
113 maps.insert(colname, i as ColNum);
114 }
115 self.columns.insert(shname.clone(), maps);
116 self.prepends.insert(shname, add_to_top);
117 }
118
119 #[inline]
123 pub fn get_sheet_mut<'a, 'b>(&'a mut self, shname: &'b str) -> Result<(&'a mut Worksheet, RowNum)> {
124 if self.open {
125 if !self.rows.contains_key(shname) {
126 let sheet = self.book.add_worksheet_with_constant_memory();
128 sheet.set_name(shname)?;
129 if self.prepends.get(shname) == Some(&true) {
130 if let Some(columns) = self.columns.get(shname) {
131 for (colval, colnum) in columns {
132 sheet.write(0, *colnum, colval)?;
133 }
134 self.rows.insert(shname.to_owned(), 1);
135 } else {
136 self.rows.insert(shname.to_owned(), 0);
137 }
138 } else {
139 self.rows.insert(shname.to_owned(), 0);
140 }
141 }
142 Ok((self.book.worksheet_from_name(shname)?, self.rows.get(shname).unwrap_or(&0).to_owned()))
143 } else {
144 Err(anyhow!("cannot write saved workbook"))
145 }
146 }
147 pub fn append_row<T: IntoExcelData, H: IntoExcelData+Clone>(&mut self, shname: &str, nrow: Option<&RowNum>, data: Vec<T>, pre_cells: &Vec<H>) -> Result<()> {
154 let (sheet, mut irow) = self.get_sheet_mut(shname)?;
155
156 let mut icol = 0;
157 let npre = pre_cells.len() as ColNum;
158 if npre > 0 {
159 sheet.write_row(irow, icol, pre_cells.clone())?;
160 icol += npre;
161 }
162 if let Some(nrow) = nrow {
163 sheet.write_number(irow, icol, *nrow)?;
164 icol += 1;
165 }
166 sheet.write_row(irow, icol, data)?;
167 irow += 1;
168 self.rows.insert(shname.to_owned(), irow);
169 Ok(())
170 }
171 pub fn append_rows<T: IntoExcelData, H: IntoExcelData+Clone>(&mut self, shname: &str, nrows: Vec<u32>, data: Vec<Vec<T>>, pre_cells: &Vec<H>) -> Result<()> {
177 let (sheet, mut irow) = self.get_sheet_mut(shname)?;
178 if nrows.len() > 0 && nrows.len() != data.len() {
180 return Err(anyhow!("the length of nrows is not equal to the length of data".to_string()));
181 }
182
183 let mut icol;
184 let npre = pre_cells.len() as ColNum;
185 for (i, rdata) in data.into_iter().enumerate() {
186 icol = 0;
187 if npre > 0 {
188 sheet.write_row(irow, icol, pre_cells.clone())?;
189 icol += npre;
190 }
191 if let Some(nrow) = nrows.get(i) {
192 sheet.write_number(irow, icol, *nrow)?;
193 icol += 1;
194 }
195 sheet.write_row(irow, icol, rdata)?;
196 irow += 1;
197 };
198 self.rows.insert(shname.to_owned(), irow);
199 Ok(())
200 }
201 pub fn append_row_by_name<T: IntoExcelData>(&mut self, shname: &str, data: HashMap<String, T>) -> Result<()> {
205 if let Some(columns) = self.columns.get(shname) {
206 let columns = columns.clone();
207 let (sheet, mut irow) = self.get_sheet_mut(shname)?;
208 for (colname, colval) in data.into_iter() {
209 if let Some(colnum) = columns.get(&colname) {
210 sheet.write(irow, *colnum, colval)?;
211 } else {
212 return Err(anyhow!("column name {} not found", colname));
213 }
214 }
215 irow += 1;
216 self.rows.insert(shname.to_owned(), irow);
217 Ok(())
218 } else {
219 Err(anyhow!("columns not set"))
220 }
221 }
222
223 pub fn append_rows_by_name<T: IntoExcelData>(&mut self, shname: &str, data: Vec<HashMap<String, T>>) -> Result<()> {
227 if let Some(columns) = self.columns.get(shname) {
228 let columns = columns.clone();
229 let (sheet, mut irow) = self.get_sheet_mut(shname)?;
230 for rdata in data.into_iter() {
231 for (colname, colval) in rdata.into_iter() {
232 if let Some(colnum) = columns.get(&colname) {
233 sheet.write(irow, *colnum, colval)?;
234 } else {
235 return Err(anyhow!("column name {} not found", colname));
236 }
237 }
238 irow += 1;
239 };
240 self.rows.insert(shname.to_owned(), irow);
241 Ok(())
242 } else {
243 Err(anyhow!("columns not set"))
244 }
245 }
246 pub fn save_as<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<()> {
248 if self.open {
249 self.book.save(path)?;
250 self.open = false;
251 Ok(())
252 } else {
253 Err(anyhow!("cannot save saved workbook"))
254 }
255 }
256}
257
258lazy_static::lazy_static! {
259 static ref FMT_DEFAULT_DATE: Format = Format::new().set_num_format_index(14);
260 static ref FMT_DEFAULT_TIME: Format = Format::new().set_num_format_index(21);
261 static ref FMT_DEFAULT_DATETIME: Format = Format::new().set_num_format_index(22);
262 static ref EMPTY_COLUMNS: HashMap<String, ColNum> = HashMap::new();
263}