parkright 1.7.0

Wrapper around Parkright Settlement API
Documentation
use core::fmt;
use std::str;

#[derive(Debug, thiserror::Error)]
#[error("invalid date `{given}`: {err}")]
pub struct InvalidDate {
    given: String,
    err: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Date(pub time::Date);

impl From<time::Date> for Date {
    fn from(value: time::Date) -> Self {
        Self(value)
    }
}

impl From<Date> for time::Date {
    fn from(d: Date) -> Self {
        d.0
    }
}

static DATE_FORMAT: &[time::format_description::BorrowedFormatItem<'_>] =
    time::macros::format_description!("[year]-[month]-[day]");

impl str::FromStr for Date {
    type Err = InvalidDate;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        time::Date::parse(s, DATE_FORMAT)
            .map_err(|err| InvalidDate {
                given: s.into(),
                err: err.to_string(),
            })
            .map(Date)
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = self.0.format(DATE_FORMAT).expect("valid format");
        f.write_str(&s)
    }
}

mod serde_impls {

    use super::*;

    use serde::de;

    struct Visitor;

    impl<'de> de::Deserialize<'de> for Date {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: de::Deserializer<'de>,
        {
            deserializer.deserialize_str(Visitor)
        }
    }

    impl<'de> de::Visitor<'de> for Visitor {
        type Value = Date;

        fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            f.write_str("a date on the form [year]-[month]-[day]")
        }

        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            v.parse::<Date>().map_err(E::custom)
        }
    }

    impl serde::Serialize for Date {
        fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            let s = self.to_string();
            ser.serialize_str(&s)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn parse_date() {
        assert_eq!(
            "2025-01-01".parse::<Date>().expect("parsing"),
            Date(time::macros::date!(2025 - 01 - 01)),
        );

        assert_eq!(
            "1900-12-31".parse::<Date>().expect("parsing"),
            Date(time::macros::date!(1900 - 12 - 31)),
        );
    }

    #[test]
    fn fmt_date() {
        assert_eq!(
            Date(time::macros::date!(2025 - 01 - 01)).to_string(),
            "2025-01-01",
        );

        assert_eq!(
            Date(time::macros::date!(1900 - 12 - 31)).to_string(),
            "1900-12-31"
        );
    }
}