hdbconnect_impl/serde_db_impl/time/
hana_time.rs

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