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;
8use 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}