xlsx_group_write 1.2.2

excel xlsx分组输出器
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
use std::path::Path;

#[cfg(feature = "date")]
use crate::date_util;
use crate::xlsx_util;
use anyhow::{anyhow, Result};
use umya_spreadsheet::{reader, writer, Spreadsheet, Worksheet};

// =======================================================
//  xlsx每行数据的写入方法相关定义 {
// =======================================================

/// xlsx行写入后的后续处理指令
pub enum XlsxLineAdvanceWriterResult {
    /// 中断写入循环
    Break,
    /// 跳过当前行,继续写入下一行
    Continue,
}

/// xlsx每一行数据的自定义写入方法
/// 通过返回不同值,控制数据循环的后续处理方法。
/// 1. 当前要写入的xlsx表格
/// 2. 当前行号
/// 3. T 为实际需要写入的数据
///
pub type XlsxLineAdvanceWriter<T> =
    fn(&mut Worksheet, u32, T) -> Option<XlsxLineAdvanceWriterResult>;

/// 原始待数据到XLSX行实际写入单元格数据的转换器
///
/// 如果设置了非None值,则使用快速模式写入数据,即使LINE_WRITER非None也不调用
///
/// 大部分时候excel写入时都是一个数据写入一列
/// 这时候如果还得通过LINE_WRITER自己通过xlsx相关api写入单元格数据
/// 起始没有必要。
///
/// 对于如上情况,可通过该数据转换器,直接根据返回的数据,自动写入xlsx单元格中
/// 终端用户不需要了解xlsx写入的API,进一步降低编码难度和智能化水平
pub type XlsxLineConverter<T> = fn(u32, T) -> Vec<XlsxColValue>;
/// 待写入到xlsx单元格中的数据
#[derive(Debug)]
pub struct XlsxColValue {
    ///    pub value: String,
    /// 值类型
    pub value_type: XlsxColValueType,
}
impl XlsxColValue {
    pub fn new(value: impl ToString, value_type: XlsxColValueType) -> Self {
        Self {
            value: value.to_string(),
            value_type,
        }
    }
}
/// 待写入xlsx单元格中的值类型
#[derive(Debug, PartialEq, Eq)]
pub enum XlsxColValueType {
    /// 字符类型
    StringValue,
    /// 数字类型
    NumberValue,
}
/// xlsx 行各类数据的写入方法
pub enum XlsxLineWriterModel {
    /// 完全自由的写入方法
    Advance,
    /// 仅需要提供数据到待写入数据转换的简便方法
    /// 无需了解xlsx写入api
    /// 推荐使用此模式
    Simple,
}

impl XlsxColValue {
    /// 将数据写入到xlsx表格的某一行中
    ///
    /// # 参数说明
    ///
    /// * sheet 要写入的表格
    /// * line_index 要写入的行序号,第一行序号为1
    /// * line_data 要写入的数据
    ///
    /// 内部方法,不建议外部调用
    pub fn write_line_data_simple(
        sheet: &mut Worksheet,
        line_index: u32,
        line_data: &[XlsxColValue],
    ) -> Result<()> {
        for (col_index, XlsxColValue { value, value_type }) in line_data.iter().enumerate() {
            match value_type {
                XlsxColValueType::NumberValue => {
                    let value: f64 = value.parse()?;
                    xlsx_util::XlsxWriterTool::set_excel_cell_value_number(
                        sheet,
                        (col_index + 1) as u32,
                        line_index,
                        value,
                    );
                }
                XlsxColValueType::StringValue => {
                    xlsx_util::XlsxWriterTool::set_excel_cell_value_str(
                        sheet,
                        (col_index + 1) as u32,
                        line_index,
                        value,
                    )
                }
            }
        }
        Ok(())
    }
}
// =======================================================
//  xlsx每行数据的写入方法相关定义 }
// =======================================================

// =======================================================
// 数据分组相关定义 {
// =======================================================

/// 数据分组输出的组id生成器
///
/// 如根据数据中某一字段分别生成不同的分组id,以便写入不同xlsx文件
///
/// 参数:
/// 1.当前要处理的数据
/// 返回当前要写入的分组id,
/// 后续根据该id决定写入那个sheet
/// 如果为新id,则自动从模板创建初始xlsx表格
pub type GroupMaker<T> = fn(T) -> String;

// =======================================================
// 数据分组相关定义  }
// =======================================================

/// 要输出的excel xlsx 文件信息
#[derive(Clone)]
pub struct OutputXlsxFile {
    /// 写入行索引
    pub index: u32,
    /// 对应excel文件
    pub excel_book: Spreadsheet,
    /// 输出文件名称
    pub output_file_name: String,
    /// 关键字,用于与分类数据关联
    pub id: String,
    /// 是否汇总组
    pub is_sum_group:bool,
    /// 实际写入数据的行数
    pub data_lines:u32,
}
impl OutputXlsxFile {
    /// 保存xlsx文件
    ///
    /// # 参数说明
    ///
    /// * passwrod 加密密码,None则为不加密
    pub fn save(&self, #[cfg(feature = "encrypt")] passwrod: Option<&str>) -> Result<()> {
        writer::xlsx::write(&self.excel_book, Path::new(&self.output_file_name))?;
        #[cfg(feature = "encrypt")]
        if let Some(passwrod) = passwrod {
            msoffice_crypt::encrypt(&self.output_file_name, passwrod, None)?;
        }
        Ok(())
    }
    /// 复制excel数据,并更新id和文件名称
    /// 
    /// # 参数说明
    /// 
    /// * new_id 新创建的id
    /// * new_file_name 新创建的文件名称
    /// * is_sum_group 是否汇总文件
    pub fn clone_to_new(&self,new_id:&str,new_file_name:&str,is_sum_group:bool) -> Self{
        OutputXlsxFile {
            index: self.index,
            excel_book: self.excel_book.clone(),
            output_file_name: new_file_name.into(),
            id: new_id.into(),
            is_sum_group,
            data_lines:0,
        }
    }
}

// =======================================================
// 初始xlsx相关模板定义 {
// =======================================================

/// 新增分组的xlsx文件初始化模板
pub enum XlsxInitTemplet {
    /// 自动生成表头
    /// 各列表头以逗号分隔
    Header(String),
    /// 从模板文件创建
    Advanced(XlsxAdvanceTemplate),
}

impl XlsxInitTemplet {
    /// 新增xlsx文件初始化
    ///
    /// # 参数说明
    ///
    /// * id 新增xlsx对应的数据类别关键字,实际数据根据该关键字进行分割写入对应xlsx文件中
    /// * file_name 新增的xlsx文件的存放位置
    /// * is_sum_group 是否汇总文件
    pub fn init_new_xlsx(&self, id: &str, file_name: &str,is_sum_group:bool) -> Result<OutputXlsxFile> {
        match self {
            XlsxInitTemplet::Header(header_str) => {
                // 无模板,直接创建xlsx文件
                let mut book = umya_spreadsheet::new_file();
                let sheet = book
                    .get_sheet_mut(&0)
                    .map_err(|err| anyhow!("读取待写入excel表格失败:{}", err))?;
                // 有表头,写入表头
                for (col, value) in header_str.split(',').enumerate() {
                    xlsx_util::XlsxWriterTool::set_excel_cell_value_str(
                        sheet,
                        (col + 1) as u32,
                        1,
                        value,
                    );
                }
                Ok(OutputXlsxFile {
                    index: 2,
                    excel_book: book,
                    output_file_name: file_name.into(),
                    id: id.into(),
                    is_sum_group,
                    data_lines:0,
                })
            }
            XlsxInitTemplet::Advanced(template) => template.new_from_template(id, file_name,is_sum_group),
        }
    }


    /// 自动生成表头模式
    ///
    /// # 参数说明
    ///
    /// * header 表头,各列表头以逗号分隔
    pub fn new_header(header: &str) -> Self {
        Self::Header(header.into())
    }

    /// 从模板文件创建模式
    ///
    /// # 参数说明
    ///
    /// * file_path 模板文件路径
    /// * start_index 数据写入起始行
    pub fn new_advance(file_path: &str, start_line: u32) -> Self {
        Self::Advanced(XlsxAdvanceTemplate {
            file_path: file_path.into(),
            start_line,
        })
    }
}

/// xlsx高级模板文件定义
///
/// 用于从模板xlsx文件初始化xlsx文件
pub struct XlsxAdvanceTemplate {
    /// 文件存储位置
    file_path: String,
    /// 数据起始写入行
    start_line: u32,
}

impl XlsxAdvanceTemplate {
    /// 从模板文件创建新的xlsx文件
    ///
    /// # 参数说明
    ///
    /// * id 该xlsx的编号,用于后续数据分组写入是匹配
    /// * file_name 实际存放xlsx完整文件名称
    /// * is_sum_group 是否汇总文件
    pub fn new_from_template(&self, id: &str, file_name: &str,is_sum_group:bool) -> Result<OutputXlsxFile> {
        // 读取模板文件
        let book = reader::xlsx::read(&self.file_path)?;
        Ok(OutputXlsxFile {
            index: self.start_line,
            excel_book: book,
            output_file_name: file_name.into(),
            id: id.into(),
            is_sum_group,
            data_lines:0,
        })
    }
}

// =======================================================
// 初始xlsx相关模板定义 }
// =======================================================

// =======================================================
// 输出文件名称相关定义 {
// =======================================================

/// 完全自定义的输出文件名称名称生成器
///
/// 参数1:当期数据的分组id
///
pub type OutputFileNameAdvanceGetter = fn(&str) -> String;

/// 固定模式的简单文件名称生成
///
/// 生成文件名称格式:特定前缀-分组id-输出日期
pub struct OutputFileNameSimpleGetter {
    /// 默认文件名前缀
    /// 还有文件完整路径
    /// 如/a/b/c/file_name_pref
    ///
    /// 实际生成文件名称为
    /// default_file_name_pref-分组id-日期.xlsx
    file_name_pref: &'static str,
    #[cfg(feature = "date")]
    /// 是否自动在文件名称末尾添加导出日期
    is_append_date: bool,
}
impl OutputFileNameSimpleGetter {
    pub const fn new(
        file_name_pref: &'static str,
        #[cfg(feature = "date")] is_append_date: bool,
    ) -> Self {
        Self {
            file_name_pref,
            #[cfg(feature = "date")]
            is_append_date,
        }
    }
    /// 获取保持文件完整名称(含路径)
    ///
    /// # 参数说明
    ///
    /// * group_id 分组id
    pub fn get_save_file_name(&self, group_id: &str) -> String {
        // 自动生成文件名称
        #[cfg(feature = "date")]
        if self.is_append_date {
            // 在文件名称中加入输出日期
            format!(
                "{}-{}-{}.xlsx",
                self.file_name_pref,
                group_id,
                date_util::get_now_date()
            )
        } else {
            format!("{}-{}.xlsx", self.file_name_pref, group_id)
        }
        #[cfg(not(feature = "date"))]
        format!("{}-{}.xlsx", self.file_name_pref, group_id)
    }
}

// =======================================================
// 输出文件名相关定义 }
// =======================================================

/// 分组输出完毕后的结果信息
#[derive(Debug)]
pub struct XlsxGroupWriteResp {
    /// 该文件名对应的分组id
    pub group_id: String,
    /// 成功保存的文件名
    pub file_name: String,
    /// 是否为汇总分组
    pub is_sum_group: bool,
    /// 包含的数据行数
    pub data_lines:u32,
}
impl XlsxGroupWriteResp {
    pub fn new(group_id: &str, file_name: &str, is_sum_group:bool, data_lines:u32) -> Self {
        Self {
            file_name: file_name.into(),
            group_id: group_id.into(),
            is_sum_group,
            data_lines
        }
    }
}

impl From<OutputXlsxFile> for XlsxGroupWriteResp {
    /// OutputXlsxFile到XlsxGroupWriteResp的转换
    fn from(output_file: OutputXlsxFile) -> Self {
        Self::new(&output_file.id, &output_file.output_file_name,output_file.is_sum_group,output_file.data_lines)
    }
}
impl From<&OutputXlsxFile> for XlsxGroupWriteResp {
    /// OutputXlsxFile索引到XlsxGroupWriteResp的转换
    fn from(output_file: &OutputXlsxFile) -> Self {
        Self::new(&output_file.id, &output_file.output_file_name,output_file.is_sum_group,output_file.data_lines)
    }
}

/// 文件输出方式
#[derive(Debug, PartialEq, Eq)]
pub enum XlsxWriteModel {
    /// 同时导出合并和分组文件
    MergeAndGroup,
    /// 只导出分组文件
    GroupOnly,
    /// 只导出合并文件
    MergeOnly,
}