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}