1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
use super::hana_primitive_date_time::HanaPrimitiveDateTimeVisitor;
use crate::ToHana;
use serde::ser::Error as _;
use time::{format_description::FormatItem, macros::format_description, OffsetDateTime};
/// Wraps a `time::OffsetDateTime`, helps with serializing from and deserializing
/// into `time::OffsetDateTime`.
///
/// Note that this is completely based on
/// [`time::HanaPrimitiveDateTime`](crate::time::HanaPrimitiveDateTime),
/// since HANA's own date formats have no understanding of timezones.
/// All deserialized instances of `OffsetDateTime` have offset `UTC`.
/// All serialized instances of `OffsetDateTime` _must_ have offset `UTC`.
///
/// # Example for serialization
/// ```rust, no_run
/// # let stmt = "...";
/// use hdbconnect::ToHana;
/// use time::{macros::datetime, OffsetDateTime};
/// # let connection = hdbconnect::Connection::new("...").unwrap();
/// let ts: OffsetDateTime = datetime!(2012-02-02 02:02:02.200000000 +2);
/// let response = connection.prepare_and_execute(stmt, &(ts.to_hana())).unwrap();
/// ```
///
/// # Example for deserialization
///
/// Deserialize into `HanaOffsetDateTime`,
/// then use `deref` or `to_inner()` to access the contained `OffsetDateTime`.
///
/// ```rust, no_run
/// use hdbconnect::time::HanaOffsetDateTime;
/// # let the_query = "...";
/// # let mut connection = hdbconnect::Connection::new("...").unwrap();
/// let dates: Vec<HanaOffsetDateTime> = connection.query(the_query).unwrap().try_into().unwrap();
/// let year = (*dates[0]).year();
/// ```
#[derive(Debug)]
pub struct HanaOffsetDateTime(OffsetDateTime);
impl HanaOffsetDateTime {
    /// Consumes the `HanaOffsetDateTime`, returning the wrapped `OffsetDateTime`.
    pub fn into_inner(self) -> OffsetDateTime {
        self.0
    }
}
impl std::ops::Deref for HanaOffsetDateTime {
    type Target = OffsetDateTime;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
// ***********
// deserialize
// ***********
impl<'de> serde::de::Deserialize<'de> for HanaOffsetDateTime {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        deserializer.deserialize_str(HanaOffsetDateTimeVisitor)
    }
}
struct HanaOffsetDateTimeVisitor;
impl<'de> serde::de::Visitor<'de> for HanaOffsetDateTimeVisitor {
    type Value = HanaOffsetDateTime;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        HanaPrimitiveDateTimeVisitor.expecting(formatter)
    }
    fn visit_str<E>(self, value: &str) -> Result<HanaOffsetDateTime, E>
    where
        E: serde::de::Error,
    {
        Ok(HanaOffsetDateTime(
            OffsetDateTime::now_utc()
                .replace_date_time(HanaPrimitiveDateTimeVisitor.visit_str(value)?.into_inner()),
        ))
    }
}
/// Helper method for deserializing database values
/// into values of type `time::OffsetDateTime`.
///
/// Since HANA's types [`LongDate`](crate::types::LongDate) and
/// [`SecondDate`](crate::types::SecondDate) have no understanding of time zones,
/// they deserialize only into `OffsetDateTime` values with zero offset
/// (offset =
/// [`time::UtcOffset::UTC`](https://docs.rs/time/latest/time/struct.UtcOffset.html#associatedconstant.UTC)).
///
/// # Example
///
/// Use serde's annotation `serde(deserialize_with = "..")` to refer to this method:
///
/// ```rust
///     #[derive(serde::Deserialize)]
///     struct WithTs {
///         #[serde(deserialize_with = "hdbconnect::time::to_offset_date_time")]
///         ts_o: time::OffsetDateTime,
///     }
/// ```
///
/// Unfortunately, the serde-annotation `deserialize_with` does not cover all cases,
/// since it can only be applied to struct fields;
/// it cannot be applied if you want to deserialize into a `Vec<OffsetDateTime>`
/// or a plain `OffsetDateTime`.
/// The best you can do then is deserialize instead into [`HanaOffsetDateTime`] and use
/// `deref()` or `into_inner()` to access the contained `time::OffsetDateTime`.
#[allow(clippy::missing_errors_doc)]
pub fn to_offset_date_time<'de, D>(input: D) -> Result<OffsetDateTime, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    input
        .deserialize_str(HanaOffsetDateTimeVisitor)
        .map(HanaOffsetDateTime::into_inner)
}
//
// serialize
//
impl ToHana<HanaOffsetDateTime> for OffsetDateTime {
    fn to_hana(self) -> HanaOffsetDateTime {
        HanaOffsetDateTime(self)
    }
}
impl serde::ser::Serialize for HanaOffsetDateTime {
    fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        const DATE_T_TIME: &[FormatItem<'static>] = format_description!(
            "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9]"
        );
        serializer.serialize_str(
            &self
                .0
                .format(DATE_T_TIME)
                .map_err(|_| S::Error::custom("failed formatting `OffsetDateTime`"))?,
        )
    }
}