Skip to main content

rs_xsheet2jsonl/
lib.rs

1use std::io;
2use std::path::Path;
3
4use io::BufWriter;
5use io::Write;
6
7use serde_json::Map;
8use serde_json::Value;
9use serde_json::json;
10
11use chrono::NaiveDateTime;
12
13use calamine::CellType;
14use calamine::Data;
15use calamine::ExcelDateTime;
16use calamine::Range;
17use calamine::Reader;
18use calamine::Rows;
19use calamine::Xlsx;
20
21pub struct XlRange<T>(pub Range<T>);
22
23impl<T> XlRange<T>
24where
25    T: CellType,
26{
27    pub fn cell_pos_end(&self) -> Option<(u32, u32)> {
28        self.0.end()
29    }
30
31    pub fn cell_pos_start(&self) -> Option<(u32, u32)> {
32        self.0.start()
33    }
34}
35
36impl<T> XlRange<T>
37where
38    T: CellType,
39{
40    pub fn rows(&self) -> Rows<'_, T> {
41        self.0.rows()
42    }
43}
44
45impl XlRange<Data> {
46    pub fn rows2writer<W>(&self, wtr: &mut W) -> Result<(), io::Error>
47    where
48        W: FnMut(usize, &[Data]) -> Result<(), io::Error>,
49    {
50        let irow = self.rows();
51        for (rno, row) in irow.enumerate() {
52            wtr(rno, row)?;
53        }
54        Ok(())
55    }
56}
57
58pub fn date2val(dt: ExcelDateTime) -> Value {
59    let ondt: Option<NaiveDateTime> = dt.as_datetime();
60    match ondt {
61        Some(ndt) => json!(ndt.to_string()),
62        None => json!(dt.to_string()),
63    }
64}
65
66pub fn dat2val(dat: &Data) -> Value {
67    match dat {
68        Data::Int(v) => json!(v),
69        Data::Float(v) => json!(v),
70        Data::String(v) => json!(v),
71        Data::Bool(v) => json!(v),
72        Data::DateTime(v) => date2val(*v),
73        Data::DateTimeIso(v) => json!(v),
74        Data::DurationIso(v) => json!(v),
75        Data::Error(v) => json!(v.to_string()),
76        Data::Empty => json!(null),
77    }
78}
79
80impl XlRange<Data> {
81    pub fn rows2io_writer<W>(&self, mut wtr: W) -> Result<(), io::Error>
82    where
83        W: Write,
84    {
85        let mut row: Map<String, Value> = Map::default();
86        self.rows2writer(&mut move |rowno: usize, cols: &[Data]| {
87            row.clear();
88            for (cno, col) in cols.iter().enumerate() {
89                row.insert(format!("{cno}"), dat2val(col));
90            }
91            row.insert("row_number".into(), json!(rowno));
92            serde_json::to_writer(&mut wtr, &row)?;
93            writeln!(&mut wtr)?;
94            Ok(())
95        })?;
96        Ok(())
97    }
98}
99
100pub fn xpath2sheet2rows2writer<P, W>(xpath: P, sheet: &str, mut wtr: W) -> Result<(), io::Error>
101where
102    W: Write,
103    P: AsRef<Path>,
104{
105    let mut wkbk: Xlsx<_> = calamine::open_workbook(xpath).map_err(io::Error::other)?;
106    let rng: Range<Data> = wkbk.worksheet_range(sheet).map_err(io::Error::other)?;
107    XlRange(rng).rows2io_writer(&mut wtr)?;
108    wtr.flush()
109}
110
111pub fn xpath2sheet2rows2stdout<P>(xpath: P, sheet: &str) -> Result<(), io::Error>
112where
113    P: AsRef<Path>,
114{
115    let o = io::stdout();
116    let mut ol = o.lock();
117    xpath2sheet2rows2writer(xpath, sheet, BufWriter::new(&mut ol))?;
118    ol.flush()
119}