tktax-date 0.2.2

Serialization/Deserialization of Chrono NaiveDate with flexible US-centric formats.
Documentation
// ---------------- [ File: tktax-date/src/serde_naive_date_format.rs ]
crate::ix!();

pub mod naive_date_format {
    use super::*;

    const FORMAT: &'static str = "%m/%d/%Y";

    pub fn serialize<S>(date: &NaiveDate, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&format!("{}", date.format(FORMAT)))
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveDate, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;

        // Define possible date formats to handle variations
        let formats = [
            "%-m/%-d/%y",
            "%m/%d/%y", 
            "%m/%d/%Y", 
        ];

        // Iterate over each format and attempt to parse the date
        for format in &formats {
            if let Ok(date) = NaiveDate::parse_from_str(&s, format) {

                return Ok(date);
            }
        }

        // If none of the formats match, return an error
        Err(serde::de::Error::custom("Failed to parse date"))
    }
}

#[cfg(test)]
mod serde_naive_date_format_tests {
    use super::*;
    use chrono::NaiveDate;
    use serde_test::{assert_de_tokens_error, Token};
    use serde::de::value::Error;
    use serde::de::IntoDeserializer;
    use serde::de::value::StringDeserializer;

    #[test]
    fn test_deserialize_date() {
        // Define sample date strings with various formats
        let date_strings = vec![
            "01/02/2023", 
            "01/02/23", 
            "1/2/23", 
            "invalid_date"
        ];

        // Parse each date string and collect the results
        let parsed_dates: Vec<Result<NaiveDate, _>> = date_strings.iter()
            .map(|date_str| {
                naive_date_format::deserialize::<StringDeserializer<Error>>(
                    date_str.to_string().into_deserializer()
                )
            })
        .collect();

        // Verify that all parsed dates are equal
        let first_date = match parsed_dates.first() {
            Some(Ok(date)) => date.clone(),
            _ => return assert!(false, "No valid dates found for comparison"),
        };

        for parsed_date in parsed_dates.iter() {
            match parsed_date {
                Ok(date) => assert_eq!(*date, first_date, "All dates should deserialize to the same NaiveDate"),
                _ => continue, // Skip invalid dates
            }
        }

        // Iterate over each date string and test deserialization
        for date_str in date_strings {
            let result: Result<NaiveDate, _> = naive_date_format::deserialize::<StringDeserializer<Error>>(
                date_str.to_string().into_deserializer()
            );

            // Verify deserialization result
            match date_str {
                "01/02/2023" | "01/02/23" | "1/2/23" => {
                    assert!(result.is_ok(), "Date {:?} should deserialize successfully", date_str);
                }
                _ => {
                    assert_de_tokens_error::<NaiveDate>(
                        &[Token::String(date_str)],
                        "input contains invalid characters",
                    );
                }
            }
        }
    }
}