simple_xlsx_writer/
lib.rs

1//! # simple_xlsx_writer
2//! This is a very simple XLSX writer library.
3//!
4//! This is not feature rich and it is not supposed to be. A lot of the design was based on the work of [simple_excel_writer](https://docs.rs/simple_excel_writer/latest/simple_excel_writer/) and I recomend you to check that crate.
5//!
6//! The main idea of this create is to help you build XLSX files using very little RAM.
7//! I created it to use in my web assembly site [csv2xlsx](https://csv2xlsx.com).
8//!
9//! Basically, you just need to pass an output that implements [Write](std::io::Write) and [Seek](std::io::Seek) to the [WorkBook](crate::WorkBook). And while you are writing the file, it wil be written directly to the output already compressed. So, you could stream directly into a file using very little RAM. Or even write to the memory and still not use that much memory because the file will be already compressed.
10//!
11//! ## Example
12//! ```rust
13//! use simple_xlsx_writer::{row, Row, WorkBook};
14//! use std::fs::File;
15//! use std::io::Write;
16//!
17//! fn main() -> std::io::Result<()> {
18//!     let mut files = File::create("example.xlsx")?;
19//!     let mut workbook = WorkBook::new(&mut files)?;
20//!     let header_style = workbook.create_cell_style((255, 255, 255), (0, 0, 0));
21//!     workbook.get_new_sheet().write_sheet(|sheet_writer| {
22//!         sheet_writer.write_row(row![("My", &header_style), ("Sample", &header_style), ("Header", &header_style)])?;
23//!         sheet_writer.write_row(row![1, 2, 3])?;
24//!         Ok(())
25//!     })?;
26//!     workbook.get_new_sheet().write_sheet(|sheet_writer| {
27//!         sheet_writer.write_row(row![("Another", &header_style), ("Sheet", &header_style), ("Header", &header_style)])?;
28//!         sheet_writer.write_row(row![1.32, 2.43, 3.54])?;
29//!         Ok(())
30//!     })?;
31//!     workbook.finish()?;
32//!     files.flush()?;
33//!     Ok(())
34//! }
35//! ```
36mod row;
37mod sheet;
38mod workbook;
39
40pub use row::{Cell, CellValue, Row};
41pub use sheet::{Sheet, SheetWriter};
42pub use workbook::{CellStyle, WorkBook};
43
44#[macro_export]
45macro_rules! row {
46    ($( $x:expr ),*) => {
47        {
48            let mut row = Row::new();
49            $(row.add_cell($x.into());)*
50            row
51        }
52    };
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use calamine::{open_workbook_from_rs, Reader, Xlsx};
59    use std::io::{Cursor, Result as IoResult};
60
61    // Very simple smoke test.
62    #[test]
63    fn it_works() -> IoResult<()> {
64        let mut cursor = Cursor::new(Vec::new());
65        let mut workbook = WorkBook::new(&mut cursor)?;
66        let cell_style = workbook.create_cell_style((255, 255, 255), (0, 0, 0));
67        let sheet_1 = workbook.get_new_sheet();
68        sheet_1.write_sheet(|sheet_writer| {
69            sheet_writer.write_row(row!(
70                (1, &cell_style),
71                (10.3, &cell_style),
72                (54.3, &cell_style)
73            ))?;
74            sheet_writer.write_row(row!("ola", "text", "tree"))?;
75            sheet_writer.write_row(row!(true, false, false, false))?;
76            Ok(())
77        })?;
78        let sheet_2 = workbook.get_new_sheet();
79        sheet_2.write_sheet(|sheet_writer| {
80            sheet_writer.write_row(row!(1, 2, 3, 4, 4))?;
81            sheet_writer.write_row(row!("one", "two", "three"))?;
82            sheet_writer.write_row(row!("Another row"))?;
83            Ok(())
84        })?;
85        workbook.finish()?;
86        Ok(())
87    }
88
89    #[test]
90    fn test_is_valid_report() -> IoResult<()> {
91        let mut cursor = Cursor::new(Vec::new());
92        let mut workbook = WorkBook::new(&mut cursor)?;
93        let sheet_1 = workbook.get_new_sheet();
94        sheet_1.write_sheet(|sheet_writer| {
95            sheet_writer.write_row(row!(1, 10.3, 54.3))?;
96            sheet_writer.write_row(row!("ola", "text", "tree"))?;
97            sheet_writer.write_row(row!(true, false, false, false))?;
98            Ok(())
99        })?;
100        let sheet_2 = workbook.get_new_sheet();
101        sheet_2.write_sheet(|sheet_writer| {
102            sheet_writer.write_row(row!(1, 2, 3, 4, 4))?;
103            sheet_writer.write_row(row!("one", "two", "three"))?;
104            sheet_writer.write_row(row!("Another row"))?;
105            Ok(())
106        })?;
107        workbook.finish()?;
108        let result = xlsx_to_vec(cursor);
109        assert_eq!(
110            vec![
111                vec![
112                    vec!["1", "10.3", "54.3", ""],
113                    vec!["ola", "text", "tree", ""],
114                    vec!["true", "false", "false", "false"]
115                ],
116                vec![
117                    vec!["1", "2", "3", "4", "4"],
118                    vec!["one", "two", "three", "", ""],
119                    vec!["Another row", "", "", "", ""],
120                ]
121            ],
122            result
123        );
124        Ok(())
125    }
126
127    fn xlsx_to_vec(cursor: Cursor<Vec<u8>>) -> Vec<Vec<Vec<String>>> {
128        let mut xlsx_reader: Xlsx<_> = open_workbook_from_rs(cursor).unwrap();
129        let mut result = (1..5)
130            .into_iter()
131            .map(
132                |sheet_n| match xlsx_reader.worksheet_range(&format!("Sheet {}", sheet_n)) {
133                    Some(result_calamine) => result_calamine
134                        .unwrap()
135                        .rows()
136                        .map(|row| row.into_iter().map(|column| column.to_string()).collect())
137                        .collect::<Vec<Vec<String>>>(),
138                    None => Vec::new(),
139                },
140            )
141            .collect::<Vec<Vec<Vec<String>>>>();
142
143        result.reverse();
144        result = result
145            .into_iter()
146            .skip_while(|row| row.is_empty())
147            .collect::<Vec<Vec<Vec<String>>>>();
148        result.reverse();
149        result
150    }
151}