xlsx_diff/core/
parse.rs

1extern crate calamine;
2extern crate serde;
3extern crate serde_json;
4use calamine::{
5    open_workbook, open_workbook_from_rs, Data, Range, RangeDeserializerBuilder, Reader, Xlsx,
6};
7use std::io::Cursor;
8// use serde::Serialize;
9use serde::{Serialize, Serializer};
10use std::path::PathBuf;
11use std::{collections::HashMap, error::Error};
12
13#[derive(Debug, Clone, PartialEq, Default)]
14pub struct SerializableData(Data);
15impl Eq for SerializableData {}
16impl Serialize for SerializableData {
17    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: Serializer,
20    {
21        match &self.0 {
22            Data::Empty => serializer.serialize_none(),
23            Data::String(s) => serializer.serialize_str(s),
24            Data::Float(f) => serializer.serialize_f64(*f),
25            Data::Int(i) => serializer.serialize_i64(*i),
26            Data::Bool(b) => serializer.serialize_bool(*b),
27            Data::DateTime(dt) => serializer.serialize_str(&dt.to_string()),
28            Data::Error(e) => serializer.serialize_str(&format!("{:?}", e)),
29            Data::DateTimeIso(dt) => serializer.serialize_str(&dt.to_string()),
30            Data::DurationIso(d) => serializer.serialize_str(&d.to_string()),
31        }
32    }
33}
34
35type RowData = Vec<SerializableData>;
36pub type SheetData = Vec<RowData>;
37pub type WorkbookData = HashMap<String, SheetData>;
38#[derive(serde::Serialize)]
39pub struct CalamineWorkbook {
40    pub sheet_names: Vec<String>,
41    pub data: WorkbookData,
42}
43
44pub enum FileLike {
45    Path(PathBuf),
46    Bytes(Vec<u8>),
47}
48
49pub fn load_workbook(file: &FileLike) -> Result<CalamineWorkbook, Box<dyn Error>> {
50    match file {
51        FileLike::Path(path) => load_by_path(path),
52        FileLike::Bytes(bytes) => load_by_rs(bytes),
53    }
54}
55
56fn load_by_path(file: &PathBuf) -> Result<CalamineWorkbook, Box<dyn Error>> {
57    let mut workbook: Xlsx<_> = open_workbook(file)?;
58    Ok(build_workbook(&mut workbook)?)
59}
60
61pub fn load_by_rs(file: &[u8]) -> Result<CalamineWorkbook, Box<dyn Error>> {
62    let mut workbook: Xlsx<_> = open_workbook_from_rs(Cursor::new(file))?;
63    Ok(build_workbook(&mut workbook)?)
64}
65
66fn build_workbook<T: std::io::Read + std::io::Seek>(workbook: &mut Xlsx<T>) -> Result<CalamineWorkbook, Box<dyn Error>> {
67    let mut calamine_workbook = CalamineWorkbook {
68        sheet_names: workbook.sheet_names().to_vec(),
69        data: HashMap::new(),
70    };
71    for sheet_name in workbook.sheet_names() {
72        if let Ok(sheet) = workbook.worksheet_range(&sheet_name) {
73            let sheet_data = deserialize_sheet(&sheet)?;
74            calamine_workbook.data.insert(sheet_name, sheet_data);
75        }
76    }
77    Ok(calamine_workbook)
78}
79
80fn deserialize_sheet(sheet: &Range<Data>) -> Result<SheetData, Box<dyn Error>> {
81    let mut iter = RangeDeserializerBuilder::new()
82        .has_headers(false)
83        .from_range(&sheet)?;
84    let mut sheet_data: SheetData = Vec::new();
85    while let Some(result) = iter.next() {
86        let row: Vec<Data> = result?;
87        let mut row_data: RowData = Vec::new();
88        for (index, cell) in row.into_iter().enumerate() {
89            row_data.push(SerializableData(cell));
90        }
91        sheet_data.push(row_data);
92    }
93    Ok(sheet_data)
94}