hdbconnect_impl/serde_db_impl/time/
hana_primitive_date_time.rs

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