use excelreader::workbook::Workbook;
use excelreader::writer_handle::WriterHandle;
use excelreader::{CellType, XL_FORMAT_AUTO, XL_FORMAT_XLSX};
fn fixture(name: &str) -> String {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.expect("repo root is two levels above the crate");
root.join(name).to_string_lossy().into_owned()
}
fn empty_sheet_workbook() -> Workbook {
let mut writer = WriterHandle::open_memory(XL_FORMAT_XLSX, None).expect("open writer");
writer.start_sheet("Empty").expect("start sheet");
writer.end_sheet().expect("end sheet");
let bytes = writer.bytes().expect("bytes");
Workbook::open_memory(&bytes, XL_FORMAT_AUTO, None).expect("open")
}
#[test]
fn cursor_reads_the_header_row() {
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let mut cursor = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
let row = cursor.next_row().expect("a first row").expect("no error");
let names: Vec<String> = row.iter().map(|cell| cell.as_str().unwrap().to_string()).collect();
assert!(names.contains(&"Coluna1".to_string()), "got {names:?}");
assert!(names.contains(&"Coluna3".to_string()), "got {names:?}");
}
#[test]
fn cursor_terminates_cleanly() {
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let mut counted = 0usize;
{
let mut cursor = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
while let Some(row) = cursor.next_row() {
row.expect("no error mid-sheet");
counted += 1;
}
assert!(cursor.next_row().is_none());
}
assert!(counted > 1, "fixture should have a header plus data rows");
let mut again = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
let mut second = 0usize;
while let Some(row) = again.next_row() {
row.expect("no error mid-sheet");
second += 1;
}
assert_eq!(second, counted, "a new cursor starts from the top");
}
#[test]
fn grows_its_buffer_for_an_oversized_row() {
let big = "x".repeat(200_000);
let dir = std::env::temp_dir().join("excelreader-rust-rows-test");
std::fs::create_dir_all(&dir).expect("temp dir");
let path = dir.join("wide.csv");
std::fs::write(&path, format!("a\n{big}\n")).expect("write fixture");
let workbook =
Workbook::open_with(&path.to_string_lossy(), excelreader::XL_FORMAT_CSV, None).expect("open csv");
let mut cursor = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
let header = cursor.next_row().expect("header").expect("ok");
assert_eq!(header.get(0).unwrap().as_str().unwrap(), "a");
let row = cursor.next_row().expect("data row").expect("ok");
let cell = row.get(0).expect("one cell");
assert_eq!(cell.cell_type, CellType::String);
assert_eq!(cell.as_str().unwrap().len(), 200_000);
drop(cursor);
drop(workbook);
std::fs::remove_file(&path).ok();
}
#[test]
fn decoded_rows_agree_with_the_cursor() {
let from_cursor: Vec<Vec<String>> = {
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let mut cursor = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
let mut all = Vec::new();
while let Some(row) = cursor.next_row() {
let row = row.expect("ok");
all.push(row.iter().map(|c| c.as_str().unwrap().to_string()).collect());
}
all
};
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let decoded = workbook.sheet(0).expect("sheet 0").read_all_decoded().expect("decode");
assert_eq!(decoded.len(), from_cursor.len());
for (index, expected) in from_cursor.iter().enumerate() {
let row = decoded.get(index).expect("row in range");
let actual: Vec<String> = row.iter().map(|c| c.as_str().unwrap().to_string()).collect();
assert_eq!(&actual, expected, "row {index} differs");
}
}
#[test]
fn decoded_rows_on_an_empty_sheet_is_empty_not_an_error() {
let workbook = empty_sheet_workbook();
let decoded = workbook.sheet(0).expect("sheet 0").read_all_decoded().expect("an empty sheet is not an error");
assert_eq!(decoded.len(), 0);
assert!(decoded.is_empty());
assert_eq!(decoded.iter().count(), 0);
}
#[test]
fn all_rows_blob_agrees_with_the_cursor() {
let from_cursor: Vec<Vec<String>> = {
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let mut cursor = workbook.sheet(0).expect("sheet 0").rows().expect("cursor");
let mut all = Vec::new();
while let Some(row) = cursor.next_row() {
let row = row.expect("ok");
all.push(row.iter().map(|c| c.as_str().unwrap().to_string()).collect());
}
all
};
let workbook = Workbook::open(&fixture("RealExcel.xlsx")).expect("open");
let all_rows = workbook.sheet(0).expect("sheet 0").read_all_blob().expect("read_all_blob");
assert_eq!(all_rows.len(), from_cursor.len());
for (index, expected) in from_cursor.iter().enumerate() {
let row = all_rows.get(index).expect("row in range");
let actual: Vec<String> = row.iter().map(|c| c.as_str().unwrap().to_string()).collect();
assert_eq!(&actual, expected, "row {index} differs");
}
}
#[test]
fn all_rows_blob_on_an_empty_sheet_is_empty_not_an_error() {
let workbook = empty_sheet_workbook();
let all_rows = workbook.sheet(0).expect("sheet 0").read_all_blob().expect("an empty sheet is not an error");
assert_eq!(all_rows.len(), 0);
assert!(all_rows.is_empty());
assert_eq!(all_rows.iter().count(), 0);
}
#[test]
fn all_rows_blob_grows_its_buffer_past_the_initial_size() {
let big = "x".repeat(2_000_000);
let dir = std::env::temp_dir().join("excelreader-rust-all-rows-blob-test");
std::fs::create_dir_all(&dir).expect("temp dir");
let path = dir.join("wide.csv");
std::fs::write(&path, format!("a\n{big}\n")).expect("write fixture");
let workbook =
Workbook::open_with(&path.to_string_lossy(), excelreader::XL_FORMAT_CSV, None).expect("open csv");
let all_rows = workbook.sheet(0).expect("sheet 0").read_all_blob().expect("read_all_blob");
assert_eq!(all_rows.len(), 2);
assert_eq!(all_rows.get(0).unwrap().get(0).unwrap().as_str().unwrap(), "a");
let data_cell = all_rows.get(1).unwrap().get(0).unwrap();
assert_eq!(data_cell.cell_type, CellType::String);
assert_eq!(data_cell.as_str().unwrap().len(), 2_000_000);
drop(all_rows);
drop(workbook);
std::fs::remove_file(&path).ok();
}