1use std::error::Error;
2use std::path::PathBuf;
3extern crate serde_json;
4pub mod diff;
5pub mod parse;
6use diff::DiffItem;
7use parse::{CalamineWorkbook, FileLike, SerializableData,WorkbookData};
8use serde::Serialize;
9
10#[derive(Serialize)]
11struct ModifiedSheet {
12 sheet_name: String,
13 diff: Vec<DiffItem<Vec<SerializableData>>>,
14}
15#[derive(Serialize)]
16struct OriginWorkbookData {
17 sheet_names: Vec<String>,
18 data: Option<WorkbookData>
19}
20
21#[derive(Serialize)]
22struct OriginData {
23 old: OriginWorkbookData,
24 new: OriginWorkbookData,
25}
26#[derive(Serialize)]
27pub struct DiffResult {
28 added_sheets: Vec<String>,
29 removed_sheets: Vec<String>,
30 no_change_sheets: Vec<String>,
31 modified_sheets: Vec<ModifiedSheet>,
32 data: OriginData
33}
34pub fn diff_xlsx(
43 old_file_path: PathBuf,
44 new_file_path: PathBuf,
45 raw_data: bool,
46 ) -> Result<DiffResult, Box<dyn Error>> {
48 let wb_old: parse::CalamineWorkbook = parse::load_workbook(&FileLike::Path(old_file_path))?;
49 let wb_new = parse::load_workbook(&FileLike::Path(new_file_path))?;
50 return build_diff(wb_old, wb_new, raw_data);
51}
52pub fn diff_xlsx_from_u8(
53 old_file: &[u8],
54 new_file: &[u8],
55 raw_data: bool,
56) -> Result<DiffResult, Box<dyn Error>> {
57 let wb_old: CalamineWorkbook = parse::load_workbook(&FileLike::Bytes(old_file.to_vec()))?;
58 let wb_new = parse::load_workbook(&FileLike::Bytes(new_file.to_vec()))?;
59 return build_diff(wb_old, wb_new, raw_data);
60}
61
62pub fn build_diff(
63 wb_old: CalamineWorkbook,
64 wb_new: CalamineWorkbook,
65 raw_data: bool,
66) -> Result<DiffResult, Box<dyn Error>> {
67 let mut modified_sheets: Vec<ModifiedSheet> = Vec::new();
68 let mut no_change_sheets: Vec<String> = Vec::new();
69 let added_sheets: Vec<String> = wb_new
71 .sheet_names
72 .iter()
73 .filter(|sheet_name| !wb_old.sheet_names.contains(*sheet_name))
74 .map(|sheet_name| sheet_name.to_string())
75 .collect();
76 let removed_sheets: Vec<String> = wb_old
77 .sheet_names
78 .iter()
79 .filter(|sheet_name| !wb_new.sheet_names.contains(*sheet_name))
80 .map(|sheet_name| sheet_name.to_string())
81 .collect();
82 for sheet_name in wb_old.sheet_names.iter() {
83 if wb_new.sheet_names.contains(sheet_name) {
84 let wb_old_first_sheet_data: &Vec<Vec<SerializableData>> =
85 wb_old.data.get(&sheet_name.to_string()).unwrap();
86 let wb_new_first_sheet_data = wb_new.data.get(&sheet_name.to_string()).unwrap();
87 let res = diff::myers_diff(&wb_old_first_sheet_data, &wb_new_first_sheet_data);
88 if res.len() == 0 {
89 no_change_sheets.push(sheet_name.to_string());
90 } else {
91 modified_sheets.push(ModifiedSheet {
92 sheet_name: sheet_name.to_string(),
93 diff: res,
94 });
95 }
96 }
97 }
98 return Ok(DiffResult {
99 added_sheets,
100 removed_sheets,
101 modified_sheets,
102 no_change_sheets,
103 data: OriginData{
104 old: OriginWorkbookData{
105 sheet_names: wb_old.sheet_names,
106 data: if raw_data {Some(wb_old.data)} else {None}
107 },
108 new: OriginWorkbookData{
109 sheet_names: wb_new.sheet_names,
110 data: if raw_data {Some(wb_new.data)} else {None}
111 }
112 }
113 });
114}