Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/extension/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
use crate::database::column::Column;
use crate::database::column::ColumnType;
use crate::error::RustySheetError;
use crate::spreadsheet::SpreadsheetError;
use crate::spreadsheet::cell::Cell;
use crate::spreadsheet::cell::CellType;
use crate::spreadsheet::resolve_loaded_shared_string;
use crate::spreadsheet::sheet::Sheet;
use duckdb::core::FlatVector;
use duckdb::core::Inserter;
use libduckdb_sys::duckdb_date;
use libduckdb_sys::duckdb_time;
use libduckdb_sys::duckdb_timestamp;
use crate::spreadsheet::sheet::Sheet;
use crate::spreadsheet::SpreadsheetError;

/// Writes a cell value to a DuckDB vector based on column type.
/// Handles type conversion and error mapping for different data types.
Expand All @@ -25,8 +26,9 @@ pub(super) fn write_to_vector(sheet: &Sheet, column: &Column, cell: &Cell, vecto
)
};
let cell = if cell.kind == CellType::SharedString {
let index = cell.value.parse::<usize>()?;
if let Some(shared_string) = &shared_strings[index] {
if let Some(shared_string) =
resolve_loaded_shared_string(shared_strings, &sheet.file_name, &sheet.name, cell)?
{
&Cell {
row: cell.row,
col: cell.col,
Expand Down
168 changes: 131 additions & 37 deletions src/spreadsheet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,77 @@ pub(crate) mod xlsx;
pub(crate) mod criteria;
pub(crate) mod sheet;

pub(crate) fn resolve_number_format(
number_formats: &[CellType],
file_name: &str,
sheet_name: &str,
row: usize,
col: usize,
style_index: usize,
) -> Result<CellType, SpreadsheetError> {
number_formats.get(style_index).copied().ok_or_else(|| {
SpreadsheetError::CellStyleIndexError(
file_name.to_owned(),
sheet_name.to_owned(),
crate::spreadsheet::reference::index_to_reference(row, col),
style_index,
number_formats.len(),
)
})
}

pub(crate) fn resolve_shared_string<'a>(
shared_strings: &'a [String],
mappings: &HashMap<usize, usize>,
file_name: &str,
sheet_name: &str,
cell: &Cell,
) -> Result<&'a str, RustySheetError> {
let shared_string_index = cell.value.parse::<usize>()?;
let mapped_index = mappings.get(&shared_string_index).copied().ok_or_else(|| {
SpreadsheetError::CellSharedStringIndexError(
file_name.to_owned(),
sheet_name.to_owned(),
cell.reference(),
shared_string_index,
)
})?;

shared_strings
.get(mapped_index)
.map(String::as_str)
.ok_or_else(|| {
SpreadsheetError::CellSharedStringIndexError(
file_name.to_owned(),
sheet_name.to_owned(),
cell.reference(),
shared_string_index,
)
.into()
})
}

pub(crate) fn resolve_loaded_shared_string<'a>(
shared_strings: &'a [Option<String>],
file_name: &str,
sheet_name: &str,
cell: &Cell,
) -> Result<Option<&'a str>, RustySheetError> {
let shared_string_index = cell.value.parse::<usize>()?;
shared_strings
.get(shared_string_index)
.map(Option::as_deref)
.ok_or_else(|| {
SpreadsheetError::CellSharedStringIndexError(
file_name.to_owned(),
sheet_name.to_owned(),
cell.reference(),
shared_string_index,
)
.into()
})
}

#[derive(Error, Debug)]
pub(crate) enum SpreadsheetError {
/// Error indicating the spreadsheet format is not supported
Expand All @@ -48,6 +119,14 @@ pub(crate) enum SpreadsheetError {
/// Error indicating a specific cell value is invalid
#[error("Cell '[{0}]{1}!{2}': {3}")]
CellValueError(String, String, String, String),

/// Error indicating a cell style index is outside the workbook's style table.
#[error("Cell '[{0}]{1}!{2}': invalid style index {3}; workbook defines {4} styles")]
CellStyleIndexError(String, String, String, usize, usize),

/// Error indicating a shared string index is outside the workbook's shared string table.
#[error("Cell '[{0}]{1}!{2}': invalid shared string index {3}")]
CellSharedStringIndexError(String, String, String, usize),
}

pub(crate) trait Spreadsheet {
Expand Down Expand Up @@ -109,56 +188,71 @@ pub(crate) trait Spreadsheet {
let (shared_strings, mappings) = self.load_shared_strings(Some(shared_indexes))?;

let mut tables = Vec::<Table>::new();
for (name, header, data, row_lower_bound, col_lower_bound, col_upper_bound) in sheets.into_iter() {
let names = (col_lower_bound..=col_upper_bound).map(|col| {
let index = col - col_lower_bound;
if let Some(cell) = &header[index] {
let value = if cell.kind == CellType::SharedString {
let id = cell.value.parse::<usize>().expect("Shared string index");
let index = mappings[&id];
shared_strings[index].to_owned()
} else {
cell.to_string()
};
if !criteria.nulls.contains(&value) {
value
let file_name = self.name();
for (sheet_name, header, data, row_lower_bound, col_lower_bound, col_upper_bound) in sheets.into_iter() {
let names = (col_lower_bound..=col_upper_bound)
.map(|col| -> Result<String, RustySheetError> {
let index = col - col_lower_bound;
if let Some(cell) = &header[index] {
let value = if cell.kind == CellType::SharedString {
resolve_shared_string(
&shared_strings,
&mappings,
&file_name,
&sheet_name,
cell,
)?.to_owned()
} else {
cell.to_string()
};
Ok(if !criteria.nulls.contains(&value) {
value
} else {
index_to_col(col).to_owned()
})
} else {
index_to_col(col).to_owned()
Ok(index_to_col(col).to_owned())
}
} else {
index_to_col(col).to_owned()
}
}).collect::<Vec<_>>();
})
.collect::<Result<Vec<_>, _>>()?;

let columns = names.iter().zip(data)
.map(|(name, cells)| {
.map(|(column_name, cells)| -> Result<Column, RustySheetError> {
let types = cells.iter()
.map(|cell| {
.map(|cell| -> Result<Option<ColumnType>, RustySheetError> {
if cell.kind == CellType::SharedString {
let id = cell.value.parse::<usize>().expect("Shared string index");
let index = mappings[&id];
let value = shared_strings[index].as_str();
ColumnType::from(if criteria.nulls.contains(value) {
&CellType::Empty
} else {
&cell.kind
}, value)
let value = resolve_shared_string(
&shared_strings,
&mappings,
&file_name,
&sheet_name,
cell,
)?;
Ok(ColumnType::from(
if criteria.nulls.contains(value) {
&CellType::Empty
} else {
&cell.kind
},
value,
))
} else {
ColumnType::from(&cell.kind, &cell.value)
Ok(ColumnType::from(&cell.kind, &cell.value))
}
})
.collect::<Vec<_>>();
Column {
name: name.to_owned(),
kind: presets.iter()
.find(|(pattern, _)| pattern.matches(name))
.collect::<Result<Vec<_>, _>>()?;
Ok(Column {
name: column_name.to_owned(),
kind: presets
.iter()
.find(|(pattern, _)| pattern.matches(column_name))
.map(|(_, kind)| kind.to_owned())
.unwrap_or(ColumnType::detect(types)),
}
})
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, _>>()?;
tables.push(Table {
name,
name: sheet_name,
columns,
row_lower_bound,
col_lower_bound,
Expand Down
19 changes: 17 additions & 2 deletions src/spreadsheet/xls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::spreadsheet::cell::CellType;
use crate::spreadsheet::criteria::Criteria;
use crate::spreadsheet::excel::load_number_formats;
use crate::spreadsheet::reference::index_to_reference;
use crate::spreadsheet::resolve_number_format;
use crate::spreadsheet::sheet::Sheet;
use crate::spreadsheet::Spreadsheet;
use crate::spreadsheet::SpreadsheetError;
Expand Down Expand Up @@ -201,7 +202,14 @@ impl Spreadsheet for XlsSpreadsheet {
}
last_row = Some(row);
let index = self.reader.read_u16()? as usize;
let kind = self.number_formats[index];
let kind = resolve_number_format(
&self.number_formats,
&sheet.file_name,
&sheet.name,
row,
col,
index,
)?;
let value = self.reader.read_rk_number()?;
sheet.push(Cell {
row,
Expand Down Expand Up @@ -234,7 +242,14 @@ impl Spreadsheet for XlsSpreadsheet {
};
let kind = match either {
Either::Left(kind) => kind,
Either::Right(index) => self.number_formats[index],
Either::Right(index) => resolve_number_format(
&self.number_formats,
&sheet.file_name,
&sheet.name,
row,
col,
index,
)?,
};
if kind != CellType::Error {
if !criteria.nulls.contains(&value) {
Expand Down
12 changes: 10 additions & 2 deletions src/spreadsheet/xlsb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::spreadsheet::criteria::Criteria;
use crate::spreadsheet::excel;
use crate::spreadsheet::excel::load_relationships;
use crate::spreadsheet::reference::index_to_reference;
use crate::spreadsheet::resolve_number_format;
use crate::spreadsheet::sheet::Sheet;
use crate::spreadsheet::Spreadsheet;
use crate::spreadsheet::SpreadsheetError;
Expand Down Expand Up @@ -221,7 +222,14 @@ impl Spreadsheet for XlsbSpreadsheet {
};
let kind = match either {
Either::Left(kind) => kind,
Either::Right(index) => (self.number_formats)[index],
Either::Right(index) => resolve_number_format(
&self.number_formats,
&sheet.file_name,
&sheet.name,
row,
col,
index,
)?,
};
if kind != CellType::Error {
if !criteria.nulls.contains(&value) {
Expand Down Expand Up @@ -451,4 +459,4 @@ fn read_rk_cell(reader: &mut Biff12Reader<BufReader<ZipFile<UnifiedReader>>>) ->
};

(Either::Right(index), value)
}
}
Loading