ocpi 0.3.5

Unofficial, in progress, OCPI implementation
Documentation
use std::{fmt, str::FromStr};

static PARSE_FORMAT: &str = "%FT%T%.f%Z";
static FORMAT: &str = "%FT%T%.f";

pub struct Date(chrono::NaiveDate);

impl Date {
    pub fn from_ymd(year: i32, month: u32, day: u32) -> Date {
        Date(chrono::NaiveDate::from_ymd(year, month, day))
    }

    pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime {
        DateTime(self.0.and_hms(hour, min, sec))
    }

    pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, millis: u32) -> DateTime {
        DateTime(self.0.and_hms_milli(hour, min, sec, millis))
    }
}

/// DateTime is valid OCPI datetime. It's always in UTC.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
pub struct DateTime(pub chrono::NaiveDateTime);

impl DateTime {
    pub fn now() -> Self {
        Self(chrono::Utc::now().naive_utc())
    }

    pub fn utc(&self) -> chrono::DateTime<chrono::Utc> {
        chrono::DateTime::from_utc(self.0, chrono::Utc)
    }
}

impl From<chrono::DateTime<chrono::Utc>> for DateTime {
    fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
        Self(dt.naive_utc())
    }
}

impl From<chrono::NaiveDateTime> for DateTime {
    fn from(dt: chrono::NaiveDateTime) -> Self {
        Self(dt)
    }
}
impl From<&chrono::NaiveDateTime> for DateTime {
    fn from(dt: &chrono::NaiveDateTime) -> Self {
        Self(*dt)
    }
}

impl FromStr for DateTime {
    type Err = chrono::ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self(chrono::NaiveDateTime::parse_from_str(
            s,
            PARSE_FORMAT,
        )?))
    }
}

impl fmt::Display for DateTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.format(FORMAT))
    }
}

impl<'de> serde::Deserialize<'de> for DateTime {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        deserializer.deserialize_str(crate::common::FromStrVisitor::expecting(
            "a valid OCPI datetime",
        ))
    }
}

#[cfg(test)]
mod tests {

    use chrono::{Datelike, Timelike};

    use super::*;

    #[test]
    fn test_display() {
        assert_eq!(
            "2018-01-01T01:08:01.123",
            Date::from_ymd(2018, 1, 1)
                .and_hms_milli(1, 8, 1, 123)
                .to_string()
        );
    }

    #[test]
    fn test_from_str() {
        "2015-06-29T20:39:09Z".parse::<DateTime>().expect("Parsing");
        "2015-06-29T20:39:09".parse::<DateTime>().expect("Parsing");
        "2016-12-29T17:45:09.2Z"
            .parse::<DateTime>()
            .expect("Parsing");
        "2016-12-29T17:45:09.2"
            .parse::<DateTime>()
            .expect("Parsing");
        "2018-01-01T01:08:01.123Z"
            .parse::<DateTime>()
            .expect("Parsing");
        "2018-01-01T01:08:01.123"
            .parse::<DateTime>()
            .expect("Parsing");
    }
    #[test]
    fn test_deserialize() {
        let res =
            serde_json::from_str::<DateTime>(r#""2015-06-29T20:39:09Z""#).expect("Deserializing");
        assert_eq!(res.0.year(), 2015);
        assert_eq!(res.0.month(), 6);
        assert_eq!(res.0.day(), 29);
        assert_eq!(res.0.hour(), 20);
        assert_eq!(res.0.minute(), 39);
        assert_eq!(res.0.second(), 9);
        let res = serde_json::from_str::<DateTime>(r#""2015-06-29T20:39:09""#).expect("Deser");
        assert_eq!(res.0.year(), 2015);
        assert_eq!(res.0.month(), 6);
        assert_eq!(res.0.day(), 29);
        assert_eq!(res.0.hour(), 20);
        assert_eq!(res.0.minute(), 39);
        assert_eq!(res.0.second(), 9);

        let res = serde_json::from_str::<DateTime>(r#""2016-12-29T17:45:09.2Z""#).expect("Deser");
        assert_eq!(res.0.year(), 2016);
        assert_eq!(res.0.month(), 12);
        assert_eq!(res.0.day(), 29);
        assert_eq!(res.0.hour(), 17);
        assert_eq!(res.0.minute(), 45);
        assert_eq!(res.0.second(), 9);
        assert_eq!(res.0.nanosecond(), 200000000);

        let res = serde_json::from_str::<DateTime>(r#""2016-12-29T17:45:09.2""#).expect("Deser");
        assert_eq!(res.0.year(), 2016);
        assert_eq!(res.0.month(), 12);
        assert_eq!(res.0.day(), 29);
        assert_eq!(res.0.hour(), 17);
        assert_eq!(res.0.minute(), 45);
        assert_eq!(res.0.second(), 9);
        assert_eq!(res.0.nanosecond(), 200000000);

        let res = serde_json::from_str::<DateTime>(r#""2018-01-01T01:08:01.123Z""#).expect("Deser");
        assert_eq!(res.0.year(), 2018);
        assert_eq!(res.0.month(), 1);
        assert_eq!(res.0.day(), 1);
        assert_eq!(res.0.hour(), 1);
        assert_eq!(res.0.minute(), 8);
        assert_eq!(res.0.second(), 1);
        assert_eq!(res.0.nanosecond(), 123000000);

        let res = serde_json::from_str::<DateTime>(r#""2018-01-01T01:08:01.123""#).expect("Deser");
        assert_eq!(res.0.year(), 2018);
        assert_eq!(res.0.month(), 1);
        assert_eq!(res.0.day(), 1);
        assert_eq!(res.0.hour(), 1);
        assert_eq!(res.0.minute(), 8);
        assert_eq!(res.0.second(), 1);
        assert_eq!(res.0.nanosecond(), 123000000);
    }
}