diff --git a/Cargo.toml b/Cargo.toml index 5107e5bc..460cfc88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -188,6 +188,21 @@ name = "doc_worksheet_serialize_datetime2" path = "examples/doc_worksheet_serialize_datetime2.rs" required-features = ["serde", "chrono"] +[[example]] +name = "doc_worksheet_serialize_datetime3" +path = "examples/doc_worksheet_serialize_datetime3.rs" +required-features = ["serde", "chrono"] + +[[example]] +name = "doc_worksheet_serialize_datetime4" +path = "examples/doc_worksheet_serialize_datetime4.rs" +required-features = ["serde", "chrono"] + + +[[example]] +name = "doc_worksheet_serialize_datetime5" +path = "examples/doc_worksheet_serialize_datetime5.rs" +required-features = ["serde", "chrono"] # Workaround to display feature specific docs. diff --git a/examples/doc_worksheet_serialize_datetime3.rs b/examples/doc_worksheet_serialize_datetime3.rs new file mode 100644 index 00000000..bf6860b7 --- /dev/null +++ b/examples/doc_worksheet_serialize_datetime3.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright 2022-2023, John McNamara, jmcnamara@cpan.org + +//! Example of a serializable struct with a Chrono Naive value with a helper +//! function. + +use chrono::NaiveDate; +use rust_xlsxwriter::utility::serialize_chrono_naive_to_excel; +use serde::Serialize; + +fn main() { + #[derive(Serialize)] + struct Student { + full_name: String, + + #[serde(serialize_with = "serialize_chrono_naive_to_excel")] + birth_date: NaiveDate, + + id_number: u32, + } +} diff --git a/examples/doc_worksheet_serialize_datetime4.rs b/examples/doc_worksheet_serialize_datetime4.rs new file mode 100644 index 00000000..5a7a4656 --- /dev/null +++ b/examples/doc_worksheet_serialize_datetime4.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright 2022-2023, John McNamara, jmcnamara@cpan.org + +//! The following example demonstrates serializing instances of a Serde derived +//! data structure, including `Option` chrono datetimes, to a worksheet. + +use chrono::NaiveDate; +use rust_xlsxwriter::{CustomSerializeHeader, Format, FormatBorder, Workbook, XlsxError}; +use serde::Serialize; + +use rust_xlsxwriter::utility::serialize_chrono_option_naive_to_excel; + +fn main() -> Result<(), XlsxError> { + let mut workbook = Workbook::new(); + + // Add a worksheet to the workbook. + let worksheet = workbook.add_worksheet(); + + // Widen the date column for clarity. + worksheet.set_column_width(1, 12)?; + + // Add some formats to use with the serialization data. + let header_format = Format::new() + .set_bold() + .set_border(FormatBorder::Thin) + .set_background_color("C6E0B4"); + + let date_format = Format::new().set_num_format("yyyy/mm/dd"); + + // Create a serializable test struct. + #[derive(Serialize)] + struct Student<'a> { + name: &'a str, + + // Note, we add a `rust_xlsxwriter` function to serialize the date. + #[serde(serialize_with = "serialize_chrono_option_naive_to_excel")] + dob: Option, + + id: u32, + } + + let students = [ + Student { + name: "Aoife", + dob: NaiveDate::from_ymd_opt(1998, 1, 12), + id: 564351, + }, + Student { + name: "Caoimhe", + dob: NaiveDate::from_ymd_opt(2000, 5, 1), + id: 443287, + }, + ]; + + // Set up the start location and headers of the data to be serialized. Note, + // we need to add a cell format for the datetime data. + let custom_headers = [ + CustomSerializeHeader::new("name") + .rename("Student") + .set_header_format(&header_format), + CustomSerializeHeader::new("dob") + .rename("Birthday") + .set_cell_format(&date_format) + .set_header_format(&header_format), + CustomSerializeHeader::new("id") + .rename("ID") + .set_header_format(&header_format), + ]; + + worksheet.serialize_headers_with_options(0, 0, "Student", &custom_headers)?; + + // Serialize the data. + worksheet.serialize(&students)?; + + // Save the file. + workbook.save("serialize.xlsx")?; + + Ok(()) +} diff --git a/examples/doc_worksheet_serialize_datetime5.rs b/examples/doc_worksheet_serialize_datetime5.rs new file mode 100644 index 00000000..dcc6bbb7 --- /dev/null +++ b/examples/doc_worksheet_serialize_datetime5.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright 2022-2023, John McNamara, jmcnamara@cpan.org + +//! Example of a serializable struct with an Option Chrono Naive value with a +//! helper function. +//! +use chrono::NaiveDate; +use rust_xlsxwriter::utility::serialize_chrono_option_naive_to_excel; +use serde::Serialize; + +fn main() { + #[derive(Serialize)] + struct Student { + full_name: String, + + #[serde(serialize_with = "serialize_chrono_option_naive_to_excel")] + birth_date: Option, + + id_number: u32, + } +} diff --git a/examples/doc_worksheet_write_date_chrono.rs b/examples/doc_worksheet_write_date_chrono.rs index 1cc9146e..4e189bcf 100644 --- a/examples/doc_worksheet_write_date_chrono.rs +++ b/examples/doc_worksheet_write_date_chrono.rs @@ -28,11 +28,11 @@ fn main() -> Result<(), XlsxError> { let date = NaiveDate::from_ymd_opt(2023, 1, 25).unwrap(); // Write the date with different Excel formats. - worksheet.write_date_with_format(0, 0, &date, &format1)?; - worksheet.write_date_with_format(1, 0, &date, &format2)?; - worksheet.write_date_with_format(2, 0, &date, &format3)?; - worksheet.write_date_with_format(3, 0, &date, &format4)?; - worksheet.write_date_with_format(4, 0, &date, &format5)?; + worksheet.write_date_with_format(0, 0, date, &format1)?; + worksheet.write_date_with_format(1, 0, date, &format2)?; + worksheet.write_date_with_format(2, 0, date, &format3)?; + worksheet.write_date_with_format(3, 0, date, &format4)?; + worksheet.write_date_with_format(4, 0, date, &format5)?; workbook.save("worksheet.xlsx")?; diff --git a/examples/doc_worksheet_write_datetime_chrono.rs b/examples/doc_worksheet_write_datetime_chrono.rs index 0f12d218..ce381de5 100644 --- a/examples/doc_worksheet_write_datetime_chrono.rs +++ b/examples/doc_worksheet_write_datetime_chrono.rs @@ -31,11 +31,11 @@ fn main() -> Result<(), XlsxError> { .unwrap(); // Write the datetime with different Excel formats. - worksheet.write_datetime_with_format(0, 0, &datetime, &format1)?; - worksheet.write_datetime_with_format(1, 0, &datetime, &format2)?; - worksheet.write_datetime_with_format(2, 0, &datetime, &format3)?; - worksheet.write_datetime_with_format(3, 0, &datetime, &format4)?; - worksheet.write_datetime_with_format(4, 0, &datetime, &format5)?; + worksheet.write_datetime_with_format(0, 0, datetime, &format1)?; + worksheet.write_datetime_with_format(1, 0, datetime, &format2)?; + worksheet.write_datetime_with_format(2, 0, datetime, &format3)?; + worksheet.write_datetime_with_format(3, 0, datetime, &format4)?; + worksheet.write_datetime_with_format(4, 0, datetime, &format5)?; workbook.save("worksheet.xlsx")?; diff --git a/examples/doc_worksheet_write_time_chrono.rs b/examples/doc_worksheet_write_time_chrono.rs index fbea90d9..77f60e38 100644 --- a/examples/doc_worksheet_write_time_chrono.rs +++ b/examples/doc_worksheet_write_time_chrono.rs @@ -28,11 +28,11 @@ fn main() -> Result<(), XlsxError> { let time = NaiveTime::from_hms_milli_opt(2, 59, 3, 456).unwrap(); // Write the time with different Excel formats. - worksheet.write_time_with_format(0, 0, &time, &format1)?; - worksheet.write_time_with_format(1, 0, &time, &format2)?; - worksheet.write_time_with_format(2, 0, &time, &format3)?; - worksheet.write_time_with_format(3, 0, &time, &format4)?; - worksheet.write_time_with_format(4, 0, &time, &format5)?; + worksheet.write_time_with_format(0, 0, time, &format1)?; + worksheet.write_time_with_format(1, 0, time, &format2)?; + worksheet.write_time_with_format(2, 0, time, &format3)?; + worksheet.write_time_with_format(3, 0, time, &format4)?; + worksheet.write_time_with_format(4, 0, time, &format5)?; workbook.save("worksheet.xlsx")?; diff --git a/src/conditional_format.rs b/src/conditional_format.rs index c70b4819..e53524dc 100644 --- a/src/conditional_format.rs +++ b/src/conditional_format.rs @@ -6713,7 +6713,7 @@ impl From<&ExcelDateTime> for ConditionalFormatValue { #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl From<&NaiveDate> for ConditionalFormatValue { fn from(value: &NaiveDate) -> ConditionalFormatValue { - let value = ExcelDateTime::chrono_date_to_excel(*value).to_string(); + let value = ExcelDateTime::chrono_date_to_excel(value).to_string(); ConditionalFormatValue::new_from_string(value) } } @@ -6731,7 +6731,7 @@ impl From<&NaiveDateTime> for ConditionalFormatValue { #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl From<&NaiveTime> for ConditionalFormatValue { fn from(value: &NaiveTime) -> ConditionalFormatValue { - let value = ExcelDateTime::chrono_time_to_excel(*value).to_string(); + let value = ExcelDateTime::chrono_time_to_excel(value).to_string(); ConditionalFormatValue::new_from_string(value) } } diff --git a/src/datetime.rs b/src/datetime.rs index e34efe16..ea286e8d 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1237,8 +1237,8 @@ impl ExcelDateTime { // Convert a chrono::NaiveTime to an Excel serial datetime. #[cfg(feature = "chrono")] pub(crate) fn chrono_datetime_to_excel(datetime: &NaiveDateTime) -> f64 { - let excel_date = Self::chrono_date_to_excel(datetime.date()); - let excel_time = Self::chrono_time_to_excel(datetime.time()); + let excel_date = Self::chrono_date_to_excel(&datetime.date()); + let excel_time = Self::chrono_time_to_excel(&datetime.time()); excel_date + excel_time } @@ -1248,10 +1248,11 @@ impl ExcelDateTime { // 1904-01-01. #[cfg(feature = "chrono")] #[allow(clippy::cast_precision_loss)] - pub(crate) fn chrono_date_to_excel(date: NaiveDate) -> f64 { + #[allow(clippy::trivially_copy_pass_by_ref)] + pub(crate) fn chrono_date_to_excel(date: &NaiveDate) -> f64 { let epoch = NaiveDate::from_ymd_opt(1899, 12, 31).unwrap(); - let duration = date - epoch; + let duration = *date - epoch; let mut excel_date = duration.num_days() as f64; // For legacy reasons Excel treats 1900 as a leap year. We add an additional @@ -1268,9 +1269,10 @@ impl ExcelDateTime { // milliseconds in the day. #[cfg(feature = "chrono")] #[allow(clippy::cast_precision_loss)] - pub(crate) fn chrono_time_to_excel(time: NaiveTime) -> f64 { + #[allow(clippy::trivially_copy_pass_by_ref)] + pub(crate) fn chrono_time_to_excel(time: &NaiveTime) -> f64 { let midnight = NaiveTime::from_hms_milli_opt(0, 0, 0, 0).unwrap(); - let duration = time - midnight; + let duration = *time - midnight; duration.num_milliseconds() as f64 / (24.0 * 60.0 * 60.0 * 1000.0) } @@ -1318,17 +1320,17 @@ enum ExcelDateTimeType { pub trait IntoExcelDateTime { /// Trait method to convert a date or time into an Excel serial datetime. /// - fn to_excel_serial_date(self) -> f64; + fn to_excel_serial_date(&self) -> f64; } impl IntoExcelDateTime for &ExcelDateTime { - fn to_excel_serial_date(self) -> f64 { + fn to_excel_serial_date(&self) -> f64 { self.to_excel() } } impl IntoExcelDateTime for ExcelDateTime { - fn to_excel_serial_date(self) -> f64 { + fn to_excel_serial_date(&self) -> f64 { self.to_excel() } } @@ -1336,7 +1338,7 @@ impl IntoExcelDateTime for ExcelDateTime { #[cfg(feature = "chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl IntoExcelDateTime for &NaiveDateTime { - fn to_excel_serial_date(self) -> f64 { + fn to_excel_serial_date(&self) -> f64 { ExcelDateTime::chrono_datetime_to_excel(self) } } @@ -1344,16 +1346,40 @@ impl IntoExcelDateTime for &NaiveDateTime { #[cfg(feature = "chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl IntoExcelDateTime for &NaiveDate { - fn to_excel_serial_date(self) -> f64 { - ExcelDateTime::chrono_date_to_excel(*self) + fn to_excel_serial_date(&self) -> f64 { + ExcelDateTime::chrono_date_to_excel(self) } } #[cfg(feature = "chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl IntoExcelDateTime for &NaiveTime { - fn to_excel_serial_date(self) -> f64 { - ExcelDateTime::chrono_time_to_excel(*self) + fn to_excel_serial_date(&self) -> f64 { + ExcelDateTime::chrono_time_to_excel(self) + } +} + +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +impl IntoExcelDateTime for NaiveDateTime { + fn to_excel_serial_date(&self) -> f64 { + ExcelDateTime::chrono_datetime_to_excel(self) + } +} + +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +impl IntoExcelDateTime for NaiveDate { + fn to_excel_serial_date(&self) -> f64 { + ExcelDateTime::chrono_date_to_excel(self) + } +} + +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +impl IntoExcelDateTime for NaiveTime { + fn to_excel_serial_date(&self) -> f64 { + ExcelDateTime::chrono_time_to_excel(self) } } diff --git a/src/datetime/tests.rs b/src/datetime/tests.rs index fc5caa85..bb09ebf9 100644 --- a/src/datetime/tests.rs +++ b/src/datetime/tests.rs @@ -1832,7 +1832,7 @@ mod datetime_tests { for test_data in dates { let (year, month, day, expected) = test_data; let datetime = NaiveDate::from_ymd_opt(year, month, day).unwrap(); - assert_eq!(expected, ExcelDateTime::chrono_date_to_excel(datetime)); + assert_eq!(expected, ExcelDateTime::chrono_date_to_excel(&datetime)); } } @@ -1945,7 +1945,7 @@ mod datetime_tests { for test_data in times { let (hour, min, seconds, millis, expected) = test_data; let datetime = NaiveTime::from_hms_milli_opt(hour, min, seconds, millis).unwrap(); - let mut diff = ExcelDateTime::chrono_time_to_excel(datetime) - expected; + let mut diff = ExcelDateTime::chrono_time_to_excel(&datetime) - expected; diff = diff.abs(); assert!(diff < 0.00000000001); } diff --git a/src/utility.rs b/src/utility.rs index 7742ed63..0a17e11f 100644 --- a/src/utility.rs +++ b/src/utility.rs @@ -231,11 +231,11 @@ pub fn cell_range_absolute( } } -/// Serialize a `Chrono` naive date/time to and Excel value. +/// Serialize a Chrono naive date/time to and Excel value. /// -/// This is a helper function for serializing [`Chrono`] naive (i.e., timezone -/// unaware) date/time fields using [Serde](https://serde.rs). See [Working with -/// Serde](crate::serializer#working-with-serde) for more information. +/// This is a helper function for serializing [`Chrono`] naive date/time fields +/// using [Serde](https://serde.rs). "Naive" in the Chrono sense means that the +/// dates/times don't have timezone information, like Excel. /// /// The function works for the following types: /// - [`NaiveDateTime`] @@ -250,10 +250,40 @@ pub fn cell_range_absolute( /// [`NaiveDateTime`]: /// https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html /// +/// `Option` Chrono types can be handled with +/// [`serialize_chrono_option_naive_to_excel()`]. +/// +/// See [Working with Serde](crate::serializer#working-with-serde) for more +/// information about serialization with `rust_xlsxwriter`. +/// /// # Errors /// /// * [`XlsxError::SerdeError`] - A wrapped serialization error. /// +/// # Examples +/// +/// Example of a serializable struct with a Chrono Naive value with a helper +/// function. +/// +/// ``` +/// # // This code is available in examples/doc_worksheet_serialize_datetime3.rs +/// # +/// use rust_xlsxwriter::utility::serialize_chrono_naive_to_excel; +/// use serde::Serialize; +/// +/// fn main() { +/// #[derive(Serialize)] +/// struct Student { +/// full_name: String, +/// +/// #[serde(serialize_with = "serialize_chrono_naive_to_excel")] +/// birth_date: NaiveDate, +/// +/// id_number: u32, +/// } +/// } +/// ``` +/// #[cfg(feature = "serde")] pub fn serialize_chrono_naive_to_excel( datetime: impl IntoExcelDateTime, @@ -265,6 +295,79 @@ where serializer.serialize_f64(datetime.to_excel_serial_date()) } +/// Serialize an `Option<>` Chrono naive date/time to and Excel value. +/// +/// This is a helper function for serializing [`Chrono`] naive date/time fields +/// using [Serde](https://serde.rs). "Naive" in the Chrono sense means that the +/// dates/times don't have timezone information, like Excel. +/// +/// A helper function is provided for [`Option`] Chrono values since it is +/// common to have `Option` values as a result of deserialization. It +/// also takes care of the use case where you want a `None` value to be written +/// as a blank cell with the same cell format as other values of the field type. +/// +/// The function works for the following `Option` where T is: +/// - [`NaiveDateTime`] +/// - [`NaiveDate`] +/// - [`NaiveTime`] +/// +/// [`Chrono`]: https://docs.rs/chrono/latest/chrono +/// [`NaiveDate`]: +/// https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDate.html +/// [`NaiveTime`]: +/// https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html +/// [`NaiveDateTime`]: +/// https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html +/// +/// Non `Option` Chrono types can be handled with +/// [`serialize_chrono_naive_to_excel()`]. +/// +/// See [Working with Serde](crate::serializer#working-with-serde) for more +/// information about serialization with `rust_xlsxwriter`. +/// +/// # Errors +/// +/// * [`XlsxError::SerdeError`] - A wrapped serialization error. +/// +/// # Examples +/// +/// Example of a serializable struct with an Option Chrono Naive value with a +/// helper function. +/// +/// +/// ``` +/// # // This code is available in examples/doc_worksheet_serialize_datetime5.rs +/// # +/// use rust_xlsxwriter::utility::serialize_chrono_option_naive_to_excel; +/// use serde::Serialize; +/// +/// fn main() { +/// #[derive(Serialize)] +/// struct Student { +/// full_name: String, +/// +/// #[serde(serialize_with = "serialize_chrono_option_naive_to_excel")] +/// birth_date: Option, +/// +/// id_number: u32, +/// } +/// } +/// ``` +/// +#[cfg(feature = "serde")] +pub fn serialize_chrono_option_naive_to_excel( + datetime: &Option, + serializer: S, +) -> Result +where + S: Serializer, +{ + match datetime { + Some(datetime) => serializer.serialize_f64(datetime.to_excel_serial_date()), + None => serializer.serialize_none(), + } +} + // Convert zero indexed row and col cell references to a chart absolute // Sheet1!$A$1:$B$1 style range string. pub(crate) fn chart_range_abs( diff --git a/src/worksheet.rs b/src/worksheet.rs index 49b75949..898f27b1 100644 --- a/src/worksheet.rs +++ b/src/worksheet.rs @@ -12081,7 +12081,7 @@ impl IntoExcelData for &NaiveDate { row: RowNum, col: ColNum, ) -> Result<&mut Worksheet, XlsxError> { - let number = ExcelDateTime::chrono_date_to_excel(*self); + let number = ExcelDateTime::chrono_date_to_excel(self); worksheet.store_datetime(row, col, number, None) } @@ -12092,7 +12092,7 @@ impl IntoExcelData for &NaiveDate { col: ColNum, format: &'a Format, ) -> Result<&'a mut Worksheet, XlsxError> { - let number = ExcelDateTime::chrono_date_to_excel(*self); + let number = ExcelDateTime::chrono_date_to_excel(self); worksheet.store_datetime(row, col, number, Some(format)) } } @@ -12106,7 +12106,7 @@ impl IntoExcelData for &NaiveTime { row: RowNum, col: ColNum, ) -> Result<&mut Worksheet, XlsxError> { - let number = ExcelDateTime::chrono_time_to_excel(*self); + let number = ExcelDateTime::chrono_time_to_excel(self); worksheet.store_datetime(row, col, number, None) } @@ -12117,7 +12117,7 @@ impl IntoExcelData for &NaiveTime { col: ColNum, format: &'a Format, ) -> Result<&'a mut Worksheet, XlsxError> { - let number = ExcelDateTime::chrono_time_to_excel(*self); + let number = ExcelDateTime::chrono_time_to_excel(self); worksheet.store_datetime(row, col, number, Some(format)) } } diff --git a/tests/integration/autofit05.rs b/tests/integration/autofit05.rs index e90ebaf0..1e9987db 100644 --- a/tests/integration/autofit05.rs +++ b/tests/integration/autofit05.rs @@ -44,8 +44,8 @@ fn create_new_xlsx_file_2(filename: &str) -> Result<(), XlsxError> { let date1 = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(); let date2 = NaiveDate::from_ymd_opt(2023, 12, 12).unwrap(); - worksheet.write_date_with_format(0, 0, &date1, &date_format)?; - worksheet.write_date_with_format(0, 1, &date2, &date_format)?; + worksheet.write_date_with_format(0, 0, date1, &date_format)?; + worksheet.write_date_with_format(0, 1, date2, &date_format)?; worksheet.autofit(); diff --git a/tests/integration/bootstrap36.rs b/tests/integration/bootstrap36.rs index dcf13c3a..d85f58b0 100644 --- a/tests/integration/bootstrap36.rs +++ b/tests/integration/bootstrap36.rs @@ -102,12 +102,12 @@ fn create_new_xlsx_file_3(filename: &str) -> Result<(), XlsxError> { .unwrap(); let time = datetime2.time(); - worksheet.write_datetime_with_format(0, 0, &datetime, &format1)?; - worksheet.write_datetime_with_format(1, 0, &datetime, &format2)?; - worksheet.write_date_with_format(2, 0, &date, &format3)?; - worksheet.write_date_with_format(3, 0, &date, &format4)?; - worksheet.write_datetime_with_format(4, 0, &datetime2, &format5)?; - worksheet.write_time_with_format(5, 0, &time, &format6)?; + worksheet.write_datetime_with_format(0, 0, datetime, &format1)?; + worksheet.write_datetime_with_format(1, 0, datetime, &format2)?; + worksheet.write_date_with_format(2, 0, date, &format3)?; + worksheet.write_date_with_format(3, 0, date, &format4)?; + worksheet.write_datetime_with_format(4, 0, datetime2, &format5)?; + worksheet.write_time_with_format(5, 0, time, &format6)?; workbook.save(filename)?; diff --git a/tests/integration/serde10.rs b/tests/integration/serde10.rs index 690f019e..1612344f 100644 --- a/tests/integration/serde10.rs +++ b/tests/integration/serde10.rs @@ -13,6 +13,7 @@ use serde::Serialize; use chrono::{NaiveDate, NaiveDateTime}; use rust_xlsxwriter::utility::serialize_chrono_naive_to_excel; +use rust_xlsxwriter::utility::serialize_chrono_option_naive_to_excel; // Test case for Serde serialization. First test isn't serialized. fn create_new_xlsx_file_1(filename: &str) -> Result<(), XlsxError> { @@ -189,6 +190,54 @@ fn create_new_xlsx_file_4(filename: &str) -> Result<(), XlsxError> { Ok(()) } +// Test case for Serde serialization with chrono Option<>. +#[cfg(feature = "chrono")] +fn create_new_xlsx_file_5(filename: &str) -> Result<(), XlsxError> { + let mut workbook = Workbook::new(); + let worksheet = workbook.add_worksheet(); + worksheet.set_column_width(1, 11)?; + + let format = Format::new().set_num_format_index(14); + + // Create a serializable test struct. + #[derive(Serialize)] + struct MyStruct { + col1: &'static str, + #[serde(serialize_with = "serialize_chrono_option_naive_to_excel")] + col2: Option, + } + + let data1 = MyStruct { + col1: "aaa", + col2: NaiveDate::from_ymd_opt(2024, 1, 1), + }; + + let data2 = MyStruct { + col1: "bbb", + col2: NaiveDate::from_ymd_opt(2024, 1, 2), + }; + + let data3 = MyStruct { + col1: "ccc", + col2: NaiveDate::from_ymd_opt(2024, 1, 3), + }; + + let custom_headers = [ + CustomSerializeHeader::new("col1"), + CustomSerializeHeader::new("col2").set_cell_format(&format), + ]; + + worksheet.serialize_headers_with_options(0, 0, "MyStruct", &custom_headers)?; + + worksheet.serialize(&data1)?; + worksheet.serialize(&data2)?; + worksheet.serialize(&data3)?; + + workbook.save(filename)?; + + Ok(()) +} + #[test] fn test_serde10_1() { let test_runner = common::TestRunner::new() @@ -238,3 +287,16 @@ fn test_serde10_4() { test_runner.assert_eq(); test_runner.cleanup(); } + +#[test] +#[cfg(feature = "chrono")] +fn test_serde10_5() { + let test_runner = common::TestRunner::new() + .set_name("serde10") + .set_function(create_new_xlsx_file_5) + .unique("5") + .initialize(); + + test_runner.assert_eq(); + test_runner.cleanup(); +}