#![cfg_attr(docsrs, feature(doc_cfg))]
#[macro_use]
mod utils;
mod auto;
mod cfb;
mod datatype;
mod formats;
mod ods;
mod style;
mod xls;
mod xlsb;
mod xlsx;
mod de;
mod errors;
pub mod changelog;
pub mod vba;
use serde::de::{Deserialize, DeserializeOwned, Deserializer};
use std::cmp::{max, min};
use std::fmt;
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::ops::{Index, IndexMut};
use std::path::Path;
pub use crate::auto::{open_workbook_auto, open_workbook_auto_from_rs, Sheets};
pub use crate::datatype::{CellData, Data, DataRef, DataType, ExcelDateTime, ExcelDateTimeType};
pub use crate::de::{DeError, RangeDeserializer, RangeDeserializerBuilder, ToCellDeserializer};
pub use crate::errors::Error;
pub use crate::ods::{Ods, OdsError};
pub use crate::style::{
Alignment, Border, BorderStyle, Borders, Color, ColumnWidth, Fill, FillPattern, Font,
FontStyle, FontWeight, HorizontalAlignment, NumberFormat, Protection, RichText, RowHeight,
Style, StyleRange, TextRotation, TextRun, UnderlineStyle, VerticalAlignment, WorksheetLayout,
};
pub use crate::xls::{Xls, XlsError, XlsOptions};
pub use crate::xlsb::{Xlsb, XlsbError};
pub use crate::xlsx::{Xlsx, XlsxError};
use crate::vba::VbaProject;
#[derive(Debug, Clone, PartialEq)]
pub enum CellErrorType {
Div0,
NA,
Name,
Null,
Num,
Ref,
Value,
GettingData,
}
impl fmt::Display for CellErrorType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
CellErrorType::Div0 => write!(f, "#DIV/0!"),
CellErrorType::NA => write!(f, "#N/A"),
CellErrorType::Name => write!(f, "#NAME?"),
CellErrorType::Null => write!(f, "#NULL!"),
CellErrorType::Num => write!(f, "#NUM!"),
CellErrorType::Ref => write!(f, "#REF!"),
CellErrorType::Value => write!(f, "#VALUE!"),
CellErrorType::GettingData => write!(f, "#DATA!"),
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Hash, Ord, PartialOrd, Copy, Clone)]
pub struct Dimensions {
pub start: (u32, u32),
pub end: (u32, u32),
}
#[allow(clippy::len_without_is_empty)]
impl Dimensions {
pub fn new(start: (u32, u32), end: (u32, u32)) -> Self {
Self { start, end }
}
pub fn contains(&self, row: u32, col: u32) -> bool {
row >= self.start.0 && row <= self.end.0 && col >= self.start.1 && col <= self.end.1
}
pub fn len(&self) -> u64 {
(self.end.0 - self.start.0 + 1) as u64 * (self.end.1 - self.start.1 + 1) as u64
}
}
#[derive(Debug, Default)]
pub struct Metadata {
sheets: Vec<Sheet>,
names: Vec<(String, String)>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SheetType {
WorkSheet,
DialogSheet,
MacroSheet,
ChartSheet,
Vba,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SheetVisible {
Visible,
Hidden,
VeryHidden,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Sheet {
pub name: String,
pub typ: SheetType,
pub visible: SheetVisible,
}
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub enum HeaderRow {
#[default]
FirstNonEmptyRow,
Row(u32),
}
pub trait Reader<RS>: Sized
where
RS: Read + Seek,
{
type Error: std::fmt::Debug + From<std::io::Error>;
fn new(reader: RS) -> Result<Self, Self::Error>;
fn with_header_row(&mut self, header_row: HeaderRow) -> &mut Self;
fn vba_project(&mut self) -> Result<Option<VbaProject>, Self::Error>;
fn metadata(&self) -> &Metadata;
fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, Self::Error>;
fn worksheets(&mut self) -> Vec<(String, Range<Data>)>;
fn worksheet_formula(&mut self, _: &str) -> Result<Range<String>, Self::Error>;
fn worksheet_style(&mut self, name: &str) -> Result<StyleRange, Self::Error>;
fn worksheet_layout(&mut self, name: &str) -> Result<WorksheetLayout, Self::Error>;
fn sheet_names(&self) -> Vec<String> {
self.metadata()
.sheets
.iter()
.map(|s| s.name.to_owned())
.collect()
}
fn sheets_metadata(&self) -> &[Sheet] {
&self.metadata().sheets
}
fn defined_names(&self) -> &[(String, String)] {
&self.metadata().names
}
fn worksheet_range_at(&mut self, n: usize) -> Option<Result<Range<Data>, Self::Error>> {
let name = self.sheet_names().get(n)?.to_string();
Some(self.worksheet_range(&name))
}
#[cfg(feature = "picture")]
#[cfg_attr(docsrs, doc(cfg(feature = "picture")))]
fn pictures(&self) -> Option<Vec<(String, Vec<u8>)>>;
}
pub trait ReaderRef<RS>: Reader<RS>
where
RS: Read + Seek,
{
fn worksheet_range_ref<'a>(&'a mut self, name: &str)
-> Result<Range<DataRef<'a>>, Self::Error>;
fn worksheet_range_at_ref(
&mut self,
n: usize,
) -> Option<Result<Range<DataRef<'_>>, Self::Error>> {
let name = self.sheet_names().get(n)?.to_string();
Some(self.worksheet_range_ref(&name))
}
}
pub fn open_workbook<R, P>(path: P) -> Result<R, R::Error>
where
R: Reader<BufReader<File>>,
P: AsRef<Path>,
{
let file = BufReader::new(File::open(path)?);
R::new(file)
}
pub fn open_workbook_from_rs<R, RS>(rs: RS) -> Result<R, R::Error>
where
RS: Read + Seek,
R: Reader<RS>,
{
R::new(rs)
}
pub trait CellType: Default + Clone + PartialEq {}
impl CellType for Data {}
impl<'a> CellType for DataRef<'a> {}
impl CellType for String {}
impl CellType for usize {} impl CellType for CellData {}
impl CellType for Style {}
#[derive(Debug, Clone)]
pub struct Cell<T: CellType> {
pos: (u32, u32),
val: T,
style: Option<Style>,
}
impl<T: CellType> Cell<T> {
pub fn new(position: (u32, u32), value: T) -> Cell<T> {
Cell {
pos: position,
val: value,
style: None,
}
}
pub fn with_style(position: (u32, u32), value: T, style: Style) -> Cell<T> {
Cell {
pos: position,
val: value,
style: Some(style),
}
}
pub fn get_position(&self) -> (u32, u32) {
self.pos
}
pub fn get_value(&self) -> &T {
&self.val
}
pub fn get_style(&self) -> Option<&Style> {
self.style.as_ref()
}
pub fn has_style(&self) -> bool {
self.style.is_some()
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Range<T> {
start: (u32, u32),
end: (u32, u32),
inner: Vec<T>,
}
impl<T: CellType> Range<T> {
#[inline]
pub fn new(start: (u32, u32), end: (u32, u32)) -> Range<T> {
assert!(start <= end, "invalid range bounds");
Range {
start,
end,
inner: vec![T::default(); ((end.0 - start.0 + 1) * (end.1 - start.1 + 1)) as usize],
}
}
#[inline]
pub fn empty() -> Range<T> {
Range {
start: (0, 0),
end: (0, 0),
inner: Vec::new(),
}
}
#[inline]
pub fn start(&self) -> Option<(u32, u32)> {
if self.is_empty() {
None
} else {
Some(self.start)
}
}
#[inline]
pub fn end(&self) -> Option<(u32, u32)> {
if self.is_empty() {
None
} else {
Some(self.end)
}
}
#[inline]
pub fn width(&self) -> usize {
if self.is_empty() {
0
} else {
(self.end.1 - self.start.1 + 1) as usize
}
}
#[inline]
pub fn height(&self) -> usize {
if self.is_empty() {
0
} else {
(self.end.0 - self.start.0 + 1) as usize
}
}
#[inline]
pub fn get_size(&self) -> (usize, usize) {
(self.height(), self.width())
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn from_sparse(cells: Vec<Cell<T>>) -> Range<T> {
if cells.is_empty() {
return Range::empty();
}
let mut row_start = u32::MAX;
let mut row_end = 0;
let mut col_start = u32::MAX;
let mut col_end = 0;
for (r, c) in cells.iter().map(|c| c.pos) {
row_start = min(r, row_start);
row_end = max(r, row_end);
col_start = min(c, col_start);
col_end = max(c, col_end);
}
let cols = (col_end - col_start + 1) as usize;
let rows = (row_end - row_start + 1) as usize;
let len = cols.saturating_mul(rows);
let mut v = vec![T::default(); len];
v.shrink_to_fit();
for c in cells {
let row = (c.pos.0 - row_start) as usize;
let col = (c.pos.1 - col_start) as usize;
let idx = row.saturating_mul(cols) + col;
if let Some(v) = v.get_mut(idx) {
*v = c.val;
}
}
Range {
start: (row_start, col_start),
end: (row_end, col_end),
inner: v,
}
}
pub fn set_value(&mut self, absolute_position: (u32, u32), value: T) {
assert!(
self.start.0 <= absolute_position.0 && self.start.1 <= absolute_position.1,
"absolute_position out of bounds"
);
match (
self.end.0 < absolute_position.0,
self.end.1 < absolute_position.1,
) {
(false, false) => (), (true, false) => {
let len = (absolute_position.0 - self.end.0 + 1) as usize * self.width();
self.inner.extend_from_slice(&vec![T::default(); len]);
self.end.0 = absolute_position.0;
}
(e, true) => {
let height = if e {
(absolute_position.0 - self.start.0 + 1) as usize
} else {
self.height()
};
let width = (absolute_position.1 - self.start.1 + 1) as usize;
let old_width = self.width();
let mut data = Vec::with_capacity(width * height);
let empty = vec![T::default(); width - old_width];
for sce in self.inner.chunks(old_width) {
data.extend_from_slice(sce);
data.extend_from_slice(&empty);
}
data.extend_from_slice(&vec![T::default(); width * (height - self.height())]);
if e {
self.end = absolute_position;
} else {
self.end.1 = absolute_position.1;
}
self.inner = data;
} }
let pos = (
absolute_position.0 - self.start.0,
absolute_position.1 - self.start.1,
);
let idx = pos.0 as usize * self.width() + pos.1 as usize;
self.inner[idx] = value;
}
pub fn get_value(&self, absolute_position: (u32, u32)) -> Option<&T> {
let p = absolute_position;
if p.0 >= self.start.0 && p.0 <= self.end.0 && p.1 >= self.start.1 && p.1 <= self.end.1 {
return self.get((
(absolute_position.0 - self.start.0) as usize,
(absolute_position.1 - self.start.1) as usize,
));
}
None
}
pub fn get(&self, relative_position: (usize, usize)) -> Option<&T> {
let (row, col) = relative_position;
let width = self.width();
if row >= self.height() || col >= width {
None
} else {
self.inner.get(row * width + col)
}
}
pub fn rows(&self) -> Rows<'_, T> {
if self.inner.is_empty() {
Rows { inner: None }
} else {
let width = self.width();
Rows {
inner: Some(self.inner.chunks(width)),
}
}
}
pub fn used_cells(&self) -> UsedCells<'_, T> {
UsedCells {
width: self.width(),
inner: self.inner.iter().enumerate(),
}
}
pub fn cells(&self) -> Cells<'_, T> {
Cells {
width: self.width(),
inner: self.inner.iter().enumerate(),
}
}
pub fn deserialize<'a, D>(&'a self) -> Result<RangeDeserializer<'a, T, D>, DeError>
where
T: ToCellDeserializer<'a>,
D: DeserializeOwned,
{
RangeDeserializerBuilder::new().from_range(self)
}
pub fn range(&self, start: (u32, u32), end: (u32, u32)) -> Range<T> {
let mut other = Range::new(start, end);
let (self_start_row, self_start_col) = self.start;
let (self_end_row, self_end_col) = self.end;
let (other_start_row, other_start_col) = other.start;
let (other_end_row, other_end_col) = other.end;
let start_row = max(self_start_row, other_start_row);
let end_row = min(self_end_row, other_end_row);
let start_col = max(self_start_col, other_start_col);
let end_col = min(self_end_col, other_end_col);
if start_row > end_row || start_col > end_col {
return other;
}
let self_width = self.width();
let other_width = other.width();
let self_row_start = (start_row - self_start_row) as usize;
let self_row_end = (end_row + 1 - self_start_row) as usize;
let self_col_start = (start_col - self_start_col) as usize;
let self_col_end = (end_col + 1 - self_start_col) as usize;
let other_row_start = (start_row - other_start_row) as usize;
let other_row_end = (end_row + 1 - other_start_row) as usize;
let other_col_start = (start_col - other_start_col) as usize;
let other_col_end = (end_col + 1 - other_start_col) as usize;
{
let self_rows = self
.inner
.chunks(self_width)
.take(self_row_end)
.skip(self_row_start);
let other_rows = other
.inner
.chunks_mut(other_width)
.take(other_row_end)
.skip(other_row_start);
for (self_row, other_row) in self_rows.zip(other_rows) {
let self_cols = &self_row[self_col_start..self_col_end];
let other_cols = &mut other_row[other_col_start..other_col_end];
other_cols.clone_from_slice(self_cols);
}
}
other
}
}
impl<T: CellType + fmt::Display> Range<T> {
pub fn headers(&self) -> Option<Vec<String>> {
self.rows()
.next()
.map(|row| row.iter().map(ToString::to_string).collect())
}
}
impl<T: CellType> Index<usize> for Range<T> {
type Output = [T];
fn index(&self, index: usize) -> &[T] {
let width = self.width();
&self.inner[index * width..(index + 1) * width]
}
}
impl<T: CellType> Index<(usize, usize)> for Range<T> {
type Output = T;
fn index(&self, index: (usize, usize)) -> &T {
let (height, width) = self.get_size();
assert!(index.1 < width && index.0 < height, "index out of bounds");
&self.inner[index.0 * width + index.1]
}
}
impl<T: CellType> IndexMut<usize> for Range<T> {
fn index_mut(&mut self, index: usize) -> &mut [T] {
let width = self.width();
&mut self.inner[index * width..(index + 1) * width]
}
}
impl<T: CellType> IndexMut<(usize, usize)> for Range<T> {
fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
let (height, width) = self.get_size();
assert!(index.1 < width && index.0 < height, "index out of bounds");
&mut self.inner[index.0 * width + index.1]
}
}
#[derive(Clone, Debug)]
pub struct Cells<'a, T: CellType> {
width: usize,
inner: std::iter::Enumerate<std::slice::Iter<'a, T>>,
}
impl<'a, T: 'a + CellType> Iterator for Cells<'a, T> {
type Item = (usize, usize, &'a T);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(i, v)| {
let row = i / self.width;
let col = i % self.width;
(row, col, v)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, T: 'a + CellType> DoubleEndedIterator for Cells<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back().map(|(i, v)| {
let row = i / self.width;
let col = i % self.width;
(row, col, v)
})
}
}
impl<'a, T: 'a + CellType> ExactSizeIterator for Cells<'a, T> {}
#[derive(Clone, Debug)]
pub struct UsedCells<'a, T: CellType> {
width: usize,
inner: std::iter::Enumerate<std::slice::Iter<'a, T>>,
}
impl<'a, T: 'a + CellType> Iterator for UsedCells<'a, T> {
type Item = (usize, usize, &'a T);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.by_ref()
.find(|&(_, v)| v != &T::default())
.map(|(i, v)| {
let row = i / self.width;
let col = i % self.width;
(row, col, v)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, up) = self.inner.size_hint();
(0, up)
}
}
impl<'a, T: 'a + CellType> DoubleEndedIterator for UsedCells<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner
.by_ref()
.rfind(|&(_, v)| v != &T::default())
.map(|(i, v)| {
let row = i / self.width;
let col = i % self.width;
(row, col, v)
})
}
}
#[derive(Clone, Debug)]
pub struct Rows<'a, T: CellType> {
inner: Option<std::slice::Chunks<'a, T>>,
}
impl<'a, T: 'a + CellType> Iterator for Rows<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
self.inner.as_mut().and_then(std::iter::Iterator::next)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner
.as_ref()
.map_or((0, Some(0)), std::iter::Iterator::size_hint)
}
}
impl<'a, T: 'a + CellType> DoubleEndedIterator for Rows<'a, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner
.as_mut()
.and_then(std::iter::DoubleEndedIterator::next_back)
}
}
impl<'a, T: 'a + CellType> ExactSizeIterator for Rows<'a, T> {}
#[derive(Debug, Clone)]
pub struct Table<T> {
pub(crate) name: String,
pub(crate) sheet_name: String,
pub(crate) columns: Vec<String>,
pub(crate) data: Range<T>,
}
impl<T> Table<T> {
pub fn name(&self) -> &str {
&self.name
}
pub fn sheet_name(&self) -> &str {
&self.sheet_name
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn data(&self) -> &Range<T> {
&self.data
}
}
impl<T: CellType> From<Table<T>> for Range<T> {
fn from(table: Table<T>) -> Range<T> {
table.data
}
}
pub fn deserialize_as_i64_or_none<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_i64())
}
pub fn deserialize_as_i64_or_string<'de, D>(
deserializer: D,
) -> Result<Result<i64, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_i64().ok_or_else(|| data.to_string()))
}
pub fn deserialize_as_f64_or_none<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_f64())
}
pub fn deserialize_as_f64_or_string<'de, D>(
deserializer: D,
) -> Result<Result<f64, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_f64().ok_or_else(|| data.to_string()))
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_date_or_none<'de, D>(
deserializer: D,
) -> Result<Option<chrono::NaiveDate>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_date())
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_date_or_string<'de, D>(
deserializer: D,
) -> Result<Result<chrono::NaiveDate, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_date().ok_or_else(|| data.to_string()))
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_time_or_none<'de, D>(
deserializer: D,
) -> Result<Option<chrono::NaiveTime>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_time())
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_time_or_string<'de, D>(
deserializer: D,
) -> Result<Result<chrono::NaiveTime, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_time().ok_or_else(|| data.to_string()))
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_duration_or_none<'de, D>(
deserializer: D,
) -> Result<Option<chrono::Duration>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_duration())
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_duration_or_string<'de, D>(
deserializer: D,
) -> Result<Result<chrono::Duration, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_duration().ok_or_else(|| data.to_string()))
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_datetime_or_none<'de, D>(
deserializer: D,
) -> Result<Option<chrono::NaiveDateTime>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_datetime())
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
pub fn deserialize_as_datetime_or_string<'de, D>(
deserializer: D,
) -> Result<Result<chrono::NaiveDateTime, String>, D::Error>
where
D: Deserializer<'de>,
{
let data = Data::deserialize(deserializer)?;
Ok(data.as_datetime().ok_or_else(|| data.to_string()))
}