use std::fs::File;
use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write};
use std::path::Path;
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
use std::sync::Arc;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::streaming::{
StreamingRows, StreamingStructuredRows, StreamingTableRows, StreamingTableTypedRows,
StreamingTypedRows,
};
use crate::writer::XlsxWriter;
#[cfg(not(target_arch = "wasm32"))]
use crate::writer::{
validate_dimensions, validate_insert_sheet_options, validate_schema,
validate_single_sheet_options,
};
use crate::{
AnalysisResult, ByteQuerySummary, CsvReadOptions, CsvWriteOptions, DynamicRow, ExcelRange,
QueryPlan, QuerySummary, RagChunk, RagExport, RagExportOptions, RagManifest, ReadOptions,
Result, SheetInfo, StructuredRow, TemplateOptions, WriteOptions,
};
#[cfg(not(target_arch = "wasm32"))]
use crate::{ExistingSheetPolicy, InsertOptions, SheetVisibility, TargetRelationshipPolicy};
pub struct MiniExcel;
impl MiniExcel {
pub fn get_sheet_names(path: impl AsRef<Path>) -> Result<Vec<String>> {
crate::streaming::sheet_names(path)
}
pub fn get_sheet_names_from_bytes(bytes: &[u8]) -> Result<Vec<String>> {
crate::streaming::sheet_names_from_bytes(bytes)
}
pub fn get_sheet_names_from_reader<R>(reader: &mut R) -> Result<Vec<String>>
where
R: Read + Seek,
{
crate::streaming::sheet_names_from_reader(reader)
}
pub fn get_sheet_info(path: impl AsRef<Path>) -> Result<Vec<SheetInfo>> {
crate::streaming::sheet_info(path)
}
pub fn get_sheet_info_from_bytes(bytes: &[u8]) -> Result<Vec<SheetInfo>> {
crate::streaming::sheet_info_from_bytes(bytes)
}
pub fn get_sheet_info_from_reader<R>(reader: &mut R) -> Result<Vec<SheetInfo>>
where
R: Read + Seek,
{
crate::streaming::sheet_info_from_reader(reader)
}
pub fn get_sheet_dimensions(path: impl AsRef<Path>) -> Result<Vec<ExcelRange>> {
crate::streaming::sheet_dimensions(path)
}
pub fn get_sheet_dimensions_from_bytes(bytes: &[u8]) -> Result<Vec<ExcelRange>> {
crate::streaming::sheet_dimensions_from_bytes(bytes)
}
pub fn get_sheet_dimensions_from_reader<R>(reader: &mut R) -> Result<Vec<ExcelRange>>
where
R: Read + Seek,
{
crate::streaming::sheet_dimensions_from_reader(reader)
}
pub fn get_comments(
path: impl AsRef<Path>,
sheet_name: Option<&str>,
) -> Result<crate::SheetComments> {
crate::streaming::comments(path, sheet_name)
}
pub fn get_comments_from_bytes(
bytes: &[u8],
sheet_name: Option<&str>,
) -> Result<crate::SheetComments> {
crate::streaming::comments_from_bytes(bytes, sheet_name)
}
pub fn get_comments_from_reader<R>(
reader: &mut R,
sheet_name: Option<&str>,
) -> Result<crate::SheetComments>
where
R: Read + Seek,
{
crate::streaming::comments_from_reader(reader, sheet_name)
}
pub fn query(
path: impl AsRef<Path>,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + Send>> {
Self::query_with_options(path, &ReadOptions::default())
}
pub fn query_with_options(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + Send>> {
Ok(Box::new(StreamingRows::open(path, options)?))
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_async(path: impl AsRef<Path>) -> Result<crate::AsyncQuery<DynamicRow>> {
Self::query_async_with_options(path, &ReadOptions::default())
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_async_with_options(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<crate::AsyncQuery<DynamicRow>> {
Self::query_async_with_options_and_cancellation(
path,
options,
crate::CancellationToken::new(),
)
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_async_with_options_and_cancellation(
path: impl AsRef<Path>,
options: &ReadOptions,
cancellation: crate::CancellationToken,
) -> Result<crate::AsyncQuery<DynamicRow>> {
let path = path.as_ref().to_owned();
let options = options.clone();
crate::streaming::spawn_async_query(
move || StreamingRows::open(path, &options),
cancellation,
)
}
pub fn query_table(
path: impl AsRef<Path>,
table_name: &str,
sheet_name: Option<&str>,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + Send>> {
Ok(Box::new(StreamingTableRows::open(path, table_name, sheet_name)?))
}
pub fn query_table_as<T>(
path: impl AsRef<Path>,
table_name: &str,
sheet_name: Option<&str>,
) -> Result<Box<dyn Iterator<Item = Result<T>> + Send>>
where
T: DeserializeOwned + 'static,
{
Ok(Box::new(StreamingTableTypedRows::open(path, table_name, sheet_name)?))
}
pub fn query_csv(
path: impl AsRef<Path>,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + Send>> {
Self::query_csv_with_options(path, &CsvReadOptions::default())
}
pub fn query_csv_with_options(
path: impl AsRef<Path>,
options: &CsvReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + Send>> {
Ok(Box::new(crate::csv_io::query_path(path, options)?))
}
pub fn query_csv_as<T>(
path: impl AsRef<Path>,
) -> Result<Box<dyn Iterator<Item = Result<T>> + Send>>
where
T: DeserializeOwned + 'static,
{
Self::query_csv_as_with_options(path, &CsvReadOptions::default())
}
pub fn query_csv_as_with_options<T>(
path: impl AsRef<Path>,
options: &CsvReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<T>> + Send>>
where
T: DeserializeOwned + 'static,
{
Ok(Box::new(crate::csv_io::query_path_as(path, options)?))
}
pub fn query_structured(
path: impl AsRef<Path>,
) -> Result<Box<dyn Iterator<Item = Result<StructuredRow>> + Send>> {
Self::query_structured_with_options(path, &ReadOptions::default())
}
pub fn query_structured_with_options(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<StructuredRow>> + Send>> {
Ok(Box::new(StreamingStructuredRows::open(path, options)?))
}
pub fn get_columns(path: impl AsRef<Path>, options: &ReadOptions) -> Result<Vec<String>> {
let mut rows = Self::query_with_options(path, options)?;
Ok(rows.next().transpose()?.map_or_else(Vec::new, |row| row.into_keys().collect()))
}
pub fn query_bytes(bytes: &[u8], options: &ReadOptions) -> Result<Vec<DynamicRow>> {
crate::streaming::query_bytes(bytes, options)
}
pub fn query_table_bytes(
bytes: &[u8],
table_name: &str,
sheet_name: Option<&str>,
) -> Result<Vec<DynamicRow>> {
crate::streaming::query_table_bytes(bytes, table_name, sheet_name)
}
pub fn query_csv_bytes(bytes: &[u8], options: &CsvReadOptions) -> Result<Vec<DynamicRow>> {
crate::csv_io::query_bytes(bytes, options)
}
pub fn query_csv_as_bytes<T>(bytes: &[u8], options: &CsvReadOptions) -> Result<Vec<T>>
where
T: DeserializeOwned,
{
crate::csv_io::query_bytes_as(bytes, options)
}
pub fn query_csv_from_reader<'a, R>(
reader: &'a mut R,
options: &CsvReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<DynamicRow>> + 'a>>
where
R: Read + 'a,
{
Ok(Box::new(crate::csv_io::CsvRows::new(reader, options, false)?))
}
pub fn query_csv_as_from_reader<'a, T, R>(
reader: &'a mut R,
options: &CsvReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<T>> + 'a>>
where
T: DeserializeOwned + 'a,
R: Read + 'a,
{
Ok(Box::new(crate::csv_io::CsvTypedRows::new(reader, options)?))
}
pub fn get_csv_columns(
path: impl AsRef<Path>,
options: &CsvReadOptions,
) -> Result<Vec<String>> {
crate::csv_io::get_columns(BufReader::new(File::open(path)?), options)
}
pub fn get_csv_columns_from_bytes(
bytes: &[u8],
options: &CsvReadOptions,
) -> Result<Vec<String>> {
crate::csv_io::get_columns(Cursor::new(bytes), options)
}
pub fn get_csv_columns_from_reader<R>(
reader: &mut R,
options: &CsvReadOptions,
) -> Result<Vec<String>>
where
R: Read,
{
crate::csv_io::get_columns(reader, options)
}
pub fn visit_rows_from_bytes<F>(
bytes: &[u8],
options: &ReadOptions,
mut visitor: F,
) -> Result<ByteQuerySummary>
where
F: FnMut(usize, &DynamicRow) -> Result<bool>,
{
crate::streaming::visit_dynamic_rows(bytes, options, |_, excel_row, row| {
visitor(excel_row, &row)
})
}
pub fn visit_rows_from_reader<R, F>(
reader: &mut R,
options: &ReadOptions,
mut visitor: F,
) -> Result<QuerySummary>
where
R: Read + Seek,
F: FnMut(usize, &DynamicRow) -> Result<bool>,
{
crate::streaming::visit_dynamic_rows_from_reader(reader, options, |_, excel_row, row| {
visitor(excel_row, &row)
})
}
pub fn visit_table_rows_from_reader<R, F>(
reader: &mut R,
table_name: &str,
sheet_name: Option<&str>,
mut visitor: F,
) -> Result<QuerySummary>
where
R: Read + Seek,
F: FnMut(usize, &DynamicRow) -> Result<bool>,
{
crate::streaming::visit_table_dynamic_rows_from_reader(
reader,
table_name,
sheet_name,
|_, excel_row, row| visitor(excel_row, &row),
)
}
pub fn visit_rows_as_from_reader<T, R, F>(
reader: &mut R,
options: &ReadOptions,
mut visitor: F,
) -> Result<QuerySummary>
where
T: DeserializeOwned,
R: Read + Seek,
F: FnMut(usize, &T) -> Result<bool>,
{
crate::streaming::visit_typed_rows_from_reader(reader, options, |_, excel_row, row| {
visitor(excel_row, &row)
})
}
pub fn visit_table_rows_as_from_reader<T, R, F>(
reader: &mut R,
table_name: &str,
sheet_name: Option<&str>,
mut visitor: F,
) -> Result<QuerySummary>
where
T: DeserializeOwned,
R: Read + Seek,
F: FnMut(usize, &T) -> Result<bool>,
{
crate::streaming::visit_table_typed_rows_from_reader(
reader,
table_name,
sheet_name,
|_, excel_row, row| visitor(excel_row, &row),
)
}
pub fn visit_structured_rows_from_reader<R, F>(
reader: &mut R,
options: &ReadOptions,
mut visitor: F,
) -> Result<String>
where
R: Read + Seek,
F: FnMut(&StructuredRow) -> Result<bool>,
{
crate::streaming::visit_structured_rows_from_reader(reader, options, |row| visitor(&row))
}
pub fn get_columns_from_reader<R>(reader: &mut R, options: &ReadOptions) -> Result<Vec<String>>
where
R: Read + Seek,
{
let summary =
crate::streaming::visit_dynamic_rows_from_reader(reader, options, |_, _, _| Ok(false))?;
Ok(summary.columns().to_vec())
}
pub fn analyze_with_options(
path: impl AsRef<Path>,
options: &ReadOptions,
plan: &QueryPlan,
) -> Result<AnalysisResult> {
crate::analytics::analyze_path(path, options, plan)
}
pub fn analyze_bytes(
bytes: &[u8],
options: &ReadOptions,
plan: &QueryPlan,
) -> Result<AnalysisResult> {
crate::analytics::analyze_bytes(bytes, options, plan)
}
pub fn export_rag(
path: impl AsRef<Path>,
options: &ReadOptions,
export_options: &RagExportOptions,
) -> Result<RagExport> {
crate::rag::export_path(path, options, export_options)
}
pub fn visit_rag_chunks_from_bytes<F>(
bytes: &[u8],
options: &ReadOptions,
export_options: &RagExportOptions,
visitor: F,
) -> Result<RagManifest>
where
F: FnMut(&RagChunk) -> Result<()>,
{
crate::rag::export_bytes(bytes, options, export_options, visitor)
}
pub fn query_as<T>(path: impl AsRef<Path>) -> Result<Box<dyn Iterator<Item = Result<T>> + Send>>
where
T: DeserializeOwned + 'static,
{
Self::query_as_with_options(path, &ReadOptions::default())
}
pub fn query_as_with_options<T>(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<Box<dyn Iterator<Item = Result<T>> + Send>>
where
T: DeserializeOwned + 'static,
{
Ok(Box::new(StreamingTypedRows::open(path, options)?))
}
pub fn read_mapped_as<T>(path: impl AsRef<Path>, mapping: &crate::CellMap) -> Result<T>
where
T: DeserializeOwned,
{
crate::mapping::read_path(path, mapping)
}
pub fn read_mapped_as_bytes<T>(bytes: &[u8], mapping: &crate::CellMap) -> Result<T>
where
T: DeserializeOwned,
{
crate::mapping::read_bytes(bytes, mapping)
}
pub fn read_mapped_as_from_reader<T, R>(reader: &mut R, mapping: &crate::CellMap) -> Result<T>
where
T: DeserializeOwned,
R: Read + Seek,
{
crate::mapping::read_from_reader(reader, mapping)
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_as_async<T>(path: impl AsRef<Path>) -> Result<crate::AsyncQuery<T>>
where
T: DeserializeOwned + Send + 'static,
{
Self::query_as_async_with_options(path, &ReadOptions::default())
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_as_async_with_options<T>(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<crate::AsyncQuery<T>>
where
T: DeserializeOwned + Send + 'static,
{
Self::query_as_async_with_options_and_cancellation(
path,
options,
crate::CancellationToken::new(),
)
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub fn query_as_async_with_options_and_cancellation<T>(
path: impl AsRef<Path>,
options: &ReadOptions,
cancellation: crate::CancellationToken,
) -> Result<crate::AsyncQuery<T>>
where
T: DeserializeOwned + Send + 'static,
{
let path = path.as_ref().to_owned();
let options = options.clone();
crate::streaming::spawn_async_query(
move || StreamingTypedRows::open(path, &options),
cancellation,
)
}
pub fn save_as(path: impl AsRef<Path>, rows: &[DynamicRow]) -> Result<()> {
Self::save_as_with_options(path, rows, &WriteOptions::default())
}
pub fn save_as_with_options(
path: impl AsRef<Path>,
rows: &[DynamicRow],
options: &WriteOptions,
) -> Result<()> {
let mut writer = XlsxWriter::new();
writer.add_rows(rows, options)?;
writer.save(path, options.overwrite_file())
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_with_schema_async<S>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &WriteOptions,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
{
Self::save_as_with_schema_async_with_cancellation(
path,
schema,
rows,
options,
crate::CancellationToken::new(),
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_with_schema_async_with_cancellation<S>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &WriteOptions,
cancellation: crate::CancellationToken,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.print_header())?;
validate_single_sheet_options(options)?;
crate::insert::async_export::save_with_schema_async(
path.as_ref().to_owned(),
schema.to_vec(),
rows,
options.clone(),
cancellation,
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_with_schema_async_with_progress<S, P>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &WriteOptions,
progress: P,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
P: Fn(usize) + Send + Sync + 'static,
{
Self::save_as_with_schema_async_with_cancellation_and_progress(
path,
schema,
rows,
options,
crate::CancellationToken::new(),
progress,
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_with_schema_async_with_cancellation_and_progress<S, P>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &WriteOptions,
cancellation: crate::CancellationToken,
progress: P,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
P: Fn(usize) + Send + Sync + 'static,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.print_header())?;
validate_single_sheet_options(options)?;
crate::insert::async_export::save_with_schema_async_with_progress(
path.as_ref().to_owned(),
schema.to_vec(),
rows,
options.clone(),
cancellation,
Arc::new(progress),
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_serialized_async<T, S>(
path: impl AsRef<Path>,
rows: S,
options: &WriteOptions,
) -> Result<usize>
where
T: Serialize,
S: futures_core::Stream<Item = Result<T>>,
{
Self::save_as_serialized_async_with_cancellation(
path,
rows,
options,
crate::CancellationToken::new(),
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_serialized_async_with_cancellation<T, S>(
path: impl AsRef<Path>,
rows: S,
options: &WriteOptions,
cancellation: crate::CancellationToken,
) -> Result<usize>
where
T: Serialize,
S: futures_core::Stream<Item = Result<T>>,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
validate_single_sheet_options(options)?;
crate::insert::async_export::save_serialized_async(
path.as_ref().to_owned(),
rows,
options.clone(),
cancellation,
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_serialized_async_with_progress<T, S, P>(
path: impl AsRef<Path>,
rows: S,
options: &WriteOptions,
progress: P,
) -> Result<usize>
where
T: Serialize,
S: futures_core::Stream<Item = Result<T>>,
P: Fn(usize) + Send + Sync + 'static,
{
Self::save_as_serialized_async_with_cancellation_and_progress(
path,
rows,
options,
crate::CancellationToken::new(),
progress,
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_serialized_async_with_cancellation_and_progress<T, S, P>(
path: impl AsRef<Path>,
rows: S,
options: &WriteOptions,
cancellation: crate::CancellationToken,
progress: P,
) -> Result<usize>
where
T: Serialize,
S: futures_core::Stream<Item = Result<T>>,
P: Fn(usize) + Send + Sync + 'static,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
validate_single_sheet_options(options)?;
crate::insert::async_export::save_serialized_async_with_progress(
path.as_ref().to_owned(),
rows,
options.clone(),
cancellation,
Arc::new(progress),
)
.await
}
pub fn save_as_sheets<'a, I, N>(
path: impl AsRef<Path>,
sheets: I,
options: &WriteOptions,
) -> Result<Vec<usize>>
where
I: IntoIterator<Item = (N, &'a [DynamicRow])>,
N: AsRef<str>,
{
let mut writer = XlsxWriter::new();
let mut row_counts = Vec::new();
for (sheet_name, rows) in sheets {
let sheet_options = options.clone().with_sheet_name(sheet_name.as_ref());
writer.add_rows(rows, &sheet_options)?;
row_counts.push(rows.len());
}
if row_counts.is_empty() {
return Err(crate::Error::no_worksheets());
}
writer.save(path, options.overwrite_file())?;
Ok(row_counts)
}
pub fn save_as_bytes(rows: &[DynamicRow], options: &WriteOptions) -> Result<Vec<u8>> {
let mut writer = XlsxWriter::new();
writer.add_rows(rows, options)?;
writer.save_to_bytes()
}
pub fn save_as_to_writer<W>(
writer: &mut W,
rows: &[DynamicRow],
options: &WriteOptions,
) -> Result<()>
where
W: Write + Send,
{
let mut xlsx_writer = XlsxWriter::new();
xlsx_writer.add_rows(rows, options)?;
xlsx_writer.save_to_writer(writer)
}
pub fn save_as_with_schema_to_writer<W>(
writer: &mut W,
schema: &[String],
rows: &[DynamicRow],
options: &WriteOptions,
) -> Result<()>
where
W: Write + Send,
{
let mut xlsx_writer = XlsxWriter::new();
xlsx_writer.add_rows_with_schema(schema, rows, options)?;
xlsx_writer.save_to_writer(writer)
}
pub fn save_as_sheets_to_writer<'a, W, I, N>(
writer: &mut W,
sheets: I,
options: &WriteOptions,
) -> Result<Vec<usize>>
where
W: Write + Send,
I: IntoIterator<Item = (N, &'a [DynamicRow])>,
N: AsRef<str>,
{
let mut xlsx_writer = XlsxWriter::new();
let mut row_counts = Vec::new();
for (sheet_name, rows) in sheets {
let sheet_options = options.clone().with_sheet_name(sheet_name.as_ref());
xlsx_writer.add_rows(rows, &sheet_options)?;
row_counts.push(rows.len());
}
if row_counts.is_empty() {
return Err(crate::Error::no_worksheets());
}
xlsx_writer.save_to_writer(writer)?;
Ok(row_counts)
}
pub fn save_as_with_schema(
path: impl AsRef<Path>,
schema: &[String],
rows: &[DynamicRow],
options: &WriteOptions,
) -> Result<()> {
let mut writer = XlsxWriter::new();
writer.add_rows_with_schema(schema, rows, options)?;
writer.save(path, options.overwrite_file())
}
pub fn save_as_serialized<T>(path: impl AsRef<Path>, rows: &[T]) -> Result<()>
where
T: Serialize,
{
Self::save_as_serialized_with_options(path, rows, &WriteOptions::default())
}
pub fn save_as_serialized_with_options<T>(
path: impl AsRef<Path>,
rows: &[T],
options: &WriteOptions,
) -> Result<()>
where
T: Serialize,
{
let mut writer = XlsxWriter::new();
writer.add_serialized(rows, options)?;
writer.save(path, options.overwrite_file())
}
pub fn save_as_serialized_to_writer<T, W>(
writer: &mut W,
rows: &[T],
options: &WriteOptions,
) -> Result<()>
where
T: Serialize,
W: Write + Send,
{
let mut xlsx_writer = XlsxWriter::new();
xlsx_writer.add_serialized(rows, options)?;
xlsx_writer.save_to_writer(writer)
}
pub fn save_as_serialized_sheets_to_writer<'a, T, W, I, N>(
writer: &mut W,
sheets: I,
options: &WriteOptions,
) -> Result<Vec<usize>>
where
T: Serialize + 'a,
W: Write + Send,
I: IntoIterator<Item = (N, &'a [T])>,
N: AsRef<str>,
{
let mut xlsx_writer = XlsxWriter::new();
let mut row_counts = Vec::new();
for (sheet_name, rows) in sheets {
let sheet_options = options.clone().with_sheet_name(sheet_name.as_ref());
xlsx_writer.add_serialized(rows, &sheet_options)?;
row_counts.push(rows.len());
}
if row_counts.is_empty() {
return Err(crate::Error::no_worksheets());
}
xlsx_writer.save_to_writer(writer)?;
Ok(row_counts)
}
pub fn save_as_serialized_sheets<'a, T, I, N>(
path: impl AsRef<Path>,
sheets: I,
options: &WriteOptions,
) -> Result<Vec<usize>>
where
T: Serialize + 'a,
I: IntoIterator<Item = (N, &'a [T])>,
N: AsRef<str>,
{
let mut writer = XlsxWriter::new();
let mut row_counts = Vec::new();
for (sheet_name, rows) in sheets {
let sheet_options = options.clone().with_sheet_name(sheet_name.as_ref());
writer.add_serialized(rows, &sheet_options)?;
row_counts.push(rows.len());
}
if row_counts.is_empty() {
return Err(crate::Error::no_worksheets());
}
writer.save(path, options.overwrite_file())?;
Ok(row_counts)
}
pub fn save_csv(
path: impl AsRef<Path>,
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize> {
crate::csv_io::save_dynamic(path, None, rows, options)
}
pub fn save_csv_with_schema(
path: impl AsRef<Path>,
schema: &[String],
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize> {
crate::csv_io::save_dynamic(path, Some(schema), rows, options)
}
pub fn save_csv_serialized<T>(
path: impl AsRef<Path>,
rows: &[T],
options: &CsvWriteOptions,
) -> Result<usize>
where
T: Serialize,
{
crate::csv_io::save_serialized(path, rows, options)
}
pub fn save_csv_to_writer<W>(
writer: &mut W,
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize>
where
W: Write,
{
crate::csv_io::write_dynamic(writer, None, rows, options, true)
}
pub fn save_csv_with_schema_to_writer<W>(
writer: &mut W,
schema: &[String],
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize>
where
W: Write,
{
crate::csv_io::write_dynamic(writer, Some(schema), rows, options, true)
}
pub fn save_csv_serialized_to_writer<T, W>(
writer: &mut W,
rows: &[T],
options: &CsvWriteOptions,
) -> Result<usize>
where
T: Serialize,
W: Write,
{
crate::csv_io::write_serialized(writer, rows, options, true)
}
pub fn save_csv_bytes(rows: &[DynamicRow], options: &CsvWriteOptions) -> Result<Vec<u8>> {
let mut output = Vec::new();
crate::csv_io::write_dynamic(&mut output, None, rows, options, true)?;
Ok(output)
}
pub fn save_csv_with_schema_bytes(
schema: &[String],
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<Vec<u8>> {
let mut output = Vec::new();
crate::csv_io::write_dynamic(&mut output, Some(schema), rows, options, true)?;
Ok(output)
}
pub fn append_csv(
path: impl AsRef<Path>,
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize> {
crate::csv_io::append_dynamic(path, None, rows, options)
}
pub fn append_csv_with_schema(
path: impl AsRef<Path>,
schema: &[String],
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize> {
crate::csv_io::append_dynamic(path, Some(schema), rows, options)
}
pub fn append_csv_serialized<T>(
path: impl AsRef<Path>,
rows: &[T],
options: &CsvWriteOptions,
) -> Result<usize>
where
T: Serialize,
{
crate::csv_io::append_serialized(path, rows, options)
}
pub fn append_csv_to_writer<W>(
writer: &mut W,
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize>
where
W: Write + Seek,
{
let empty = writer.seek(SeekFrom::End(0))? == 0;
crate::csv_io::write_dynamic(writer, None, rows, options, empty)
}
pub fn append_csv_with_schema_to_writer<W>(
writer: &mut W,
schema: &[String],
rows: &[DynamicRow],
options: &CsvWriteOptions,
) -> Result<usize>
where
W: Write + Seek,
{
let empty = writer.seek(SeekFrom::End(0))? == 0;
crate::csv_io::write_dynamic(writer, Some(schema), rows, options, empty)
}
pub fn append_csv_serialized_to_writer<T, W>(
writer: &mut W,
rows: &[T],
options: &CsvWriteOptions,
) -> Result<usize>
where
T: Serialize,
W: Write + Seek,
{
let empty = writer.seek(SeekFrom::End(0))? == 0;
crate::csv_io::write_serialized(writer, rows, options, empty)
}
pub fn save_csv_serialized_bytes<T>(rows: &[T], options: &CsvWriteOptions) -> Result<Vec<u8>>
where
T: Serialize,
{
let mut output = Cursor::new(Vec::new());
crate::csv_io::write_serialized(&mut output, rows, options, true)?;
Ok(output.into_inner())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn rename_sheet(
path: impl AsRef<Path>,
sheet_name: &str,
new_sheet_name: &str,
) -> Result<()> {
crate::insert::atomic::rename_sheet_to_path(path, sheet_name, new_sheet_name)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_sheet_visibility(
path: impl AsRef<Path>,
sheet_name: &str,
visibility: SheetVisibility,
) -> Result<()> {
crate::insert::atomic::set_sheet_visibility_to_path(path, sheet_name, visibility)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn reorder_sheet(
path: impl AsRef<Path>,
sheet_name: &str,
new_sheet_index: i32,
) -> Result<()> {
crate::insert::atomic::reorder_sheet_to_path(path, sheet_name, new_sheet_index)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn copy_and_add_sheet(
source: impl AsRef<Path>,
destination: impl AsRef<Path>,
rows: &[DynamicRow],
options: &InsertOptions,
) -> Result<usize> {
validate_copy_and_add_options(source.as_ref(), destination.as_ref(), options)?;
crate::insert::atomic::copy_and_add_to_path(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
options.write_options().overwrite_file(),
|| crate::insert::donor::DonorBuilder::from_dynamic(rows, options.write_options()),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn copy_and_add_sheet_with_schema<I>(
source: impl AsRef<Path>,
destination: impl AsRef<Path>,
schema: &[String],
rows: I,
options: &InsertOptions,
) -> Result<usize>
where
I: IntoIterator<Item = Result<DynamicRow>>,
{
validate_copy_and_add_options(source.as_ref(), destination.as_ref(), options)?;
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.write_options().print_header())?;
crate::insert::atomic::copy_and_add_to_path(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
options.write_options().overwrite_file(),
|| {
crate::insert::donor::DonorBuilder::from_dynamic_iter(
schema,
rows,
options.write_options(),
)
},
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn copy_and_add_sheet_serialized<T>(
source: impl AsRef<Path>,
destination: impl AsRef<Path>,
rows: &[T],
options: &InsertOptions,
) -> Result<usize>
where
T: Serialize,
{
validate_copy_and_add_options(source.as_ref(), destination.as_ref(), options)?;
crate::insert::atomic::copy_and_add_to_path(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
options.write_options().overwrite_file(),
|| crate::insert::donor::DonorBuilder::from_serialized(rows, options.write_options()),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert(
path: impl AsRef<Path>,
rows: &[DynamicRow],
options: &InsertOptions,
) -> Result<usize> {
let path = path.as_ref();
validate_insert_options(path, options)?;
if !path.exists() {
let mut writer = XlsxWriter::new();
writer.add_rows(rows, options.write_options())?;
writer.save(path, false)?;
return Ok(rows.len());
}
crate::insert::atomic::insert_to_path(
path,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| crate::insert::donor::DonorBuilder::from_dynamic(rows, options.write_options()),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert_with_schema<I>(
path: impl AsRef<Path>,
schema: &[String],
rows: I,
options: &InsertOptions,
) -> Result<usize>
where
I: IntoIterator<Item = Result<DynamicRow>>,
{
let path = path.as_ref();
validate_insert_options(path, options)?;
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.write_options().print_header())?;
if !path.exists() {
return crate::insert::donor::save_dynamic_iter_to_path(
path,
schema,
rows,
options.write_options(),
);
}
crate::insert::atomic::insert_to_path(
path,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| {
crate::insert::donor::DonorBuilder::from_dynamic_iter(
schema,
rows,
options.write_options(),
)
},
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert_serialized<T>(
path: impl AsRef<Path>,
rows: &[T],
options: &InsertOptions,
) -> Result<usize>
where
T: Serialize,
{
let path = path.as_ref();
validate_insert_options(path, options)?;
if !path.exists() {
let mut writer = XlsxWriter::new();
writer.add_serialized(rows, options.write_options())?;
writer.save(path, false)?;
return Ok(rows.len());
}
crate::insert::atomic::insert_to_path(
path,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| crate::insert::donor::DonorBuilder::from_serialized(rows, options.write_options()),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert_from_reader_to_writer<R, W>(
source: &mut R,
destination: &mut W,
rows: &[DynamicRow],
options: &InsertOptions,
) -> Result<usize>
where
R: Read + Seek,
W: Write + Seek,
{
validate_existing_insert_options(options)?;
crate::insert::rewrite::insert_worksheet_from_reader_to_writer(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| crate::insert::donor::DonorBuilder::from_dynamic(rows, options.write_options()),
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert_with_schema_from_reader_to_writer<R, W, I>(
source: &mut R,
destination: &mut W,
schema: &[String],
rows: I,
options: &InsertOptions,
) -> Result<usize>
where
R: Read + Seek,
W: Write + Seek,
I: IntoIterator<Item = Result<DynamicRow>>,
{
validate_existing_insert_options(options)?;
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.write_options().print_header())?;
crate::insert::rewrite::insert_worksheet_from_reader_to_writer(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| {
crate::insert::donor::DonorBuilder::from_dynamic_iter(
schema,
rows,
options.write_options(),
)
},
)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn insert_serialized_from_reader_to_writer<T, R, W>(
source: &mut R,
destination: &mut W,
rows: &[T],
options: &InsertOptions,
) -> Result<usize>
where
T: Serialize,
R: Read + Seek,
W: Write + Seek,
{
validate_existing_insert_options(options)?;
crate::insert::rewrite::insert_worksheet_from_reader_to_writer(
source,
destination,
options.write_options().sheet_name(),
options.existing_sheet_policy(),
options.target_relationship_policy(),
|| crate::insert::donor::DonorBuilder::from_serialized(rows, options.write_options()),
)
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn insert_with_schema_async<S>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &InsertOptions,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
{
Self::insert_with_schema_async_with_cancellation(
path,
schema,
rows,
options,
crate::CancellationToken::new(),
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn insert_with_schema_async_with_cancellation<S>(
path: impl AsRef<Path>,
schema: &[String],
rows: S,
options: &InsertOptions,
cancellation: crate::CancellationToken,
) -> Result<usize>
where
S: futures_core::Stream<Item = Result<DynamicRow>>,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
let path = path.as_ref();
validate_insert_options(path, options)?;
std::fs::metadata(path)?;
validate_schema(schema)?;
validate_dimensions(0, schema.len(), options.write_options().print_header())?;
crate::insert::async_insert::insert_with_schema_async(
path.to_owned(),
schema.to_vec(),
rows,
options.clone(),
cancellation,
)
.await
}
pub fn save_as_template<T>(
path: impl AsRef<Path>,
template_path: impl AsRef<Path>,
value: &T,
options: &TemplateOptions,
) -> Result<()>
where
T: Serialize,
{
crate::template::fill_path(path, template_path, value, options)
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_template_async<T>(
path: impl AsRef<Path>,
template_path: impl AsRef<Path>,
value: &T,
options: &TemplateOptions,
) -> Result<()>
where
T: Serialize,
{
Self::save_as_template_async_with_cancellation(
path,
template_path,
value,
options,
crate::CancellationToken::new(),
)
.await
}
#[cfg(all(feature = "async", not(target_arch = "wasm32")))]
pub async fn save_as_template_async_with_cancellation<T>(
path: impl AsRef<Path>,
template_path: impl AsRef<Path>,
value: &T,
options: &TemplateOptions,
cancellation: crate::CancellationToken,
) -> Result<()>
where
T: Serialize,
{
if cancellation.is_cancelled() {
return Err(crate::Error::cancelled());
}
let value = crate::template::serialize_value(value)?;
crate::insert::async_template::fill_path_async(
path.as_ref().to_owned(),
template_path.as_ref().to_owned(),
value,
options.clone(),
cancellation,
)
.await
}
pub fn save_as_template_bytes<T>(
template_bytes: &[u8],
value: &T,
options: &TemplateOptions,
) -> Result<Vec<u8>>
where
T: Serialize,
{
crate::template::fill_bytes(template_bytes, value, options)
}
pub fn merge_same_cells_bytes(workbook: &[u8]) -> Result<Vec<u8>> {
crate::merge::merge_same_cells_bytes(workbook)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn merge_same_cells(
source: impl AsRef<Path>,
destination: impl AsRef<Path>,
options: &crate::MergeSameCellsOptions,
) -> Result<()> {
crate::merge::merge_same_cells_path(source.as_ref(), destination.as_ref(), options)
}
}
#[cfg(not(target_arch = "wasm32"))]
fn validate_insert_options(path: &Path, options: &InsertOptions) -> Result<()> {
if path
.extension()
.and_then(std::ffi::OsStr::to_str)
.is_some_and(|extension| extension.eq_ignore_ascii_case("xlsm"))
{
return Err(crate::Error::unsupported_package_feature(
"Insert does not support macro-enabled .xlsm paths",
));
}
validate_insert_policies(options)?;
if path.exists() {
validate_insert_sheet_options(options.write_options())
} else {
validate_single_sheet_options(options.write_options())
}
}
#[cfg(not(target_arch = "wasm32"))]
fn validate_existing_insert_options(options: &InsertOptions) -> Result<()> {
validate_insert_policies(options)?;
validate_insert_sheet_options(options.write_options())
}
#[cfg(not(target_arch = "wasm32"))]
fn validate_insert_policies(options: &InsertOptions) -> Result<()> {
if options.write_options().overwrite_file() {
return Err(crate::Error::invalid_write_options(
"overwrite_file does not apply to Insert; use ExistingSheetPolicy",
));
}
validate_target_relationship_policy(options)
}
#[cfg(not(target_arch = "wasm32"))]
fn validate_copy_and_add_options(
source: &Path,
destination: &Path,
options: &InsertOptions,
) -> Result<()> {
if [source, destination].iter().any(|path| {
path.extension()
.and_then(std::ffi::OsStr::to_str)
.is_some_and(|extension| extension.eq_ignore_ascii_case("xlsm"))
}) {
return Err(crate::Error::unsupported_package_feature(
"copy-and-add does not support macro-enabled .xlsm paths",
));
}
validate_target_relationship_policy(options)?;
validate_insert_sheet_options(options.write_options())
}
#[cfg(not(target_arch = "wasm32"))]
fn validate_target_relationship_policy(options: &InsertOptions) -> Result<()> {
if options.existing_sheet_policy() == ExistingSheetPolicy::Reject
&& options.target_relationship_policy() != TargetRelationshipPolicy::Reject
{
return Err(crate::Error::invalid_write_options(
"target relationship removal requires ExistingSheetPolicy::Replace",
));
}
Ok(())
}