Skip to content

Commit

Permalink
feat: add header_row to ods file
Browse files Browse the repository at this point in the history
  • Loading branch information
PrettyWood committed Sep 18, 2024
1 parent dba5b60 commit 8b1a77f
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub use crate::auto::{open_workbook_auto, open_workbook_auto_from_rs, Sheets};
pub use crate::datatype::{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::ods::{Ods, OdsError, OdsOptions};
pub use crate::xls::{Xls, XlsError, XlsOptions};
pub use crate::xlsb::{Xlsb, XlsbError};
pub use crate::xlsx::{Xlsx, XlsxError, XlsxOptions};
Expand Down
49 changes: 46 additions & 3 deletions src/ods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,34 @@ pub enum OdsError {
WorksheetNotFound(String),
}

/// Ods reader options
#[derive(Debug, Default)]
pub struct OdsOptions {
/// Index of the header row
/// If not set, the first non-empty row is considered the header row
pub header_row: Option<u32>,
}

impl OdsOptions {
/// Create a new XlsxOptions
pub fn new() -> Self {
Self::default()
}

/// Set the header row index
pub fn with_header_row(self, header_row: Option<u32>) -> Self {
Self { header_row }
}
}

impl<RS> Ods<RS> {
/// Set reader options
pub fn with_options(mut self, options: OdsOptions) -> Self {
self.options = options;
self
}
}

from_err!(std::io::Error, OdsError, Io);
from_err!(zip::result::ZipError, OdsError, Zip);
from_err!(quick_xml::Error, OdsError, Xml);
Expand Down Expand Up @@ -116,6 +144,8 @@ pub struct Ods<RS> {
marker: PhantomData<RS>,
#[cfg(feature = "picture")]
pictures: Option<Vec<(String, Vec<u8>)>>,
/// Reader options
options: OdsOptions,
}

impl<RS> Reader<RS> for Ods<RS>
Expand Down Expand Up @@ -161,6 +191,7 @@ where
sheets,
#[cfg(feature = "picture")]
pictures,
options: OdsOptions::default(),
})
}

Expand All @@ -176,10 +207,22 @@ where

/// Read worksheet data in corresponding worksheet path
fn worksheet_range(&mut self, name: &str) -> Result<Range<Data>, OdsError> {
self.sheets
let sheet = self
.sheets
.get(name)
.ok_or_else(|| OdsError::WorksheetNotFound(name.into()))
.map(|r| r.0.to_owned())
.ok_or_else(|| OdsError::WorksheetNotFound(name.into()))?
.0
.to_owned();

// If a header_row is defined, adjust the range
if let Some(header_row) = self.options.header_row {
if let (Some(start), Some(end)) = (sheet.start(), sheet.end()) {
return Ok(sheet.range((header_row, start.1), end));
}
}

// Return the original range if no header row is set
Ok(sheet)
}

fn worksheets(&mut self) -> Vec<(String, Range<Data>)> {
Expand Down
39 changes: 35 additions & 4 deletions tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use calamine::Data::{Bool, DateTime, DateTimeIso, DurationIso, Empty, Error, Float, String};
use calamine::{
open_workbook, open_workbook_auto, DataRef, DataType, Dimensions, ExcelDateTime,
ExcelDateTimeType, Ods, Range, Reader, ReaderRef, Sheet, SheetType, SheetVisible, Xls, Xlsb,
Xlsx, XlsxOptions,
ExcelDateTimeType, Ods, OdsOptions, Range, Reader, ReaderRef, Sheet, SheetType, SheetVisible,
Xls, Xlsb, Xlsx, XlsxOptions,
};
use calamine::{CellErrorType::*, Data};
use rstest::rstest;
Expand Down Expand Up @@ -1805,7 +1805,7 @@ fn test_ref_xlsb() {
#[case("temperature.xlsx", Some(0), (0, 0), (2, 1), &[String("label".to_string()), String("value".to_string())], 6)]
#[case("temperature-in-middle.xlsx", None, (3, 1), (5, 2), &[String("label".to_string()), String("value".to_string())], 6)]
#[case("temperature-in-middle.xlsx", Some(0), (0, 1), (5, 2), &[Empty, Empty], 12)]
fn header_row_xlsx(
fn test_header_row_xlsx(
#[case] fixture_path: &str,
#[case] header_row: Option<u32>,
#[case] expected_start: (u32, u32),
Expand All @@ -1823,7 +1823,6 @@ fn header_row_xlsx(
},]
);

// By default empty cells are skipped so the first row is skipped
let range = excel
.with_options(XlsxOptions::new().with_header_row(header_row))
.worksheet_range("Sheet1")
Expand All @@ -1834,6 +1833,38 @@ fn header_row_xlsx(
assert_eq!(range.cells().count(), expected_total_cells);
}

#[rstest]
fn test_header_row_ods() {
let mut ods: Ods<_> = wb("date.ods");
assert_eq!(
ods.sheets_metadata(),
&[Sheet {
name: "Sheet1".to_string(),
typ: SheetType::WorkSheet,
visible: SheetVisible::Visible
},]
);

let range = ods.worksheet_range("Sheet1").unwrap();
assert_eq!(range.start(), Some((0, 0)));
assert_eq!(range.end(), Some((3, 1)));
assert_eq!(
range.rows().next().unwrap(),
&[DateTimeIso("2021-01-01".to_string()), Float(15.0)]
);

let range = ods
.with_options(OdsOptions::new().with_header_row(Some(2)))
.worksheet_range("Sheet1")
.unwrap();
assert_eq!(range.start(), Some((2, 0)));
assert_eq!(range.end(), Some((3, 1)));
assert_eq!(
range.rows().next().unwrap(),
&[DurationIso("PT10H10M10S".to_string()), Float(17.0)]
);
}

#[rstest]
#[case("single-empty.ods")]
#[case("multi-empty.ods")]
Expand Down

0 comments on commit 8b1a77f

Please sign in to comment.