yandex_pay_api 0.5.0

Yandex Pay API
Documentation
use serde::{self, Deserialize, Deserializer, Serializer};

pub mod string_as_float {
    use super::*;

    pub fn serialize<S>(value: &f64, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&value.to_string())
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<f64, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        s.parse::<f64>().map_err(serde::de::Error::custom)
    }
}

pub mod option_string_as_float {
    use super::*;

    pub fn serialize<S>(value: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match value {
            Some(v) => serializer.serialize_str(&v.to_string()),
            None => serializer.serialize_none(),
        }
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let opt = Option::<String>::deserialize(deserializer)?;
        opt.map(|s| s.parse::<f64>().map_err(serde::de::Error::custom))
            .transpose()
    }
}

fn parse_datetime<E: serde::de::Error>(s: &str) -> Result<chrono::DateTime<chrono::Utc>, E> {
    match chrono::DateTime::parse_from_rfc3339(&s).map(|d| d.to_utc()) {
        Ok(d) => Ok(d),
        Err(err) => {
            if err.kind() == chrono::format::ParseErrorKind::TooShort {
                let d = chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(|err| {
                    serde::de::Error::custom(format!("Failed to parse date: {}", err))
                })?;
                let result = chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
                    d.and_hms_opt(0, 0, 0).unwrap_or_default(),
                    chrono::Utc,
                );
                return Ok(result);
            }
            return Err(serde::de::Error::custom(format!(
                "Failed to parse date: {}",
                err
            )));
        }
    }
}
pub mod iso8601 {
    use super::*;

    pub fn serialize<S>(value: &crate::Time, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&value.to_rfc3339())
    }

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

pub mod option_iso8601 {
    use super::*;

    pub fn serialize<S>(value: &Option<crate::Time>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match value {
            Some(v) => serializer.serialize_str(&v.to_rfc3339()),
            None => serializer.serialize_none(),
        }
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<crate::Time>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let opt = Option::<String>::deserialize(deserializer)?;
        opt.map(|s| parse_datetime(&s)).transpose()
    }
}

#[cfg(test)]
mod tests {
    use crate::Time;

    use super::*;

    #[allow(unused)]
    #[test]
    fn test_option_iso8601() {
        let json = r#"
        {
            "from_date": "2022-12-29",
            "from_date2": "2025-05-11T19:21:09Z"
        }
        "#;

        #[derive(Debug, Deserialize)]
        struct Test {
            #[serde(with = "option_iso8601")]
            from_date: Option<Time>,
            #[serde(with = "iso8601")]
            from_date2: Time,
        }
        let parsed: Test = serde_json::from_str(json).unwrap();
        println!("{:?}", parsed);
    }
}