taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

timestamp.rs (6221B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024, 2025, 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::{fmt::Display, ops::Add, time::Duration};
     18 
     19 use jiff::{Timestamp, civil::Time, tz::TimeZone};
     20 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeStruct}; // codespell:ignore
     21 use serde_json::Value;
     22 
     23 /// <https://docs.taler.net/core/api-common.html#tsref-type-Timestamp>
     24 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
     25 pub enum TalerTimestamp {
     26     Never,
     27     Timestamp(Timestamp),
     28 }
     29 
     30 #[derive(Serialize, Deserialize)]
     31 struct TimestampImpl {
     32     t_s: Value,
     33 }
     34 
     35 impl<'de> Deserialize<'de> for TalerTimestamp {
     36     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     37     where
     38         D: Deserializer<'de>,
     39     {
     40         let tmp = TimestampImpl::deserialize(deserializer)?;
     41         match tmp.t_s {
     42             Value::Number(s) => {
     43                 if let Some(since_epoch_s) = s.as_i64() {
     44                     jiff::Timestamp::from_second(since_epoch_s)
     45                         .map(Self::Timestamp)
     46                         .map_err(Error::custom)
     47                 } else {
     48                     Err(Error::custom("Expected epoch time"))
     49                 }
     50             }
     51             Value::String(str) if str == "never" => Ok(Self::Never),
     52             _ => Err(Error::custom("Expected epoch time or 'never'")),
     53         }
     54     }
     55 }
     56 
     57 impl Serialize for TalerTimestamp {
     58     fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
     59     where
     60         S: Serializer,
     61     {
     62         let mut se_struct = se.serialize_struct("Timestamp", 1)?;
     63         match self {
     64             TalerTimestamp::Never => se_struct.serialize_field("t_s", "never")?,
     65             TalerTimestamp::Timestamp(timestamp) => {
     66                 se_struct.serialize_field("t_s", &timestamp.as_second())?
     67             }
     68         }
     69         se_struct.end()
     70     }
     71 }
     72 
     73 impl Display for TalerTimestamp {
     74     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     75         match self {
     76             TalerTimestamp::Never => f.write_str("never"),
     77             TalerTimestamp::Timestamp(timestamp) => timestamp.fmt(f),
     78         }
     79     }
     80 }
     81 
     82 impl From<jiff::Timestamp> for TalerTimestamp {
     83     fn from(time: jiff::Timestamp) -> Self {
     84         Self::Timestamp(time)
     85     }
     86 }
     87 
     88 impl From<jiff::civil::Date> for TalerTimestamp {
     89     fn from(date: jiff::civil::Date) -> Self {
     90         date.to_datetime(Time::midnight())
     91             .to_zoned(TimeZone::UTC)
     92             .unwrap()
     93             .timestamp()
     94             .into()
     95     }
     96 }
     97 
     98 impl Add<jiff::Span> for TalerTimestamp {
     99     type Output = Self;
    100 
    101     fn add(self, rhs: jiff::Span) -> Self::Output {
    102         match self {
    103             TalerTimestamp::Never => TalerTimestamp::Never,
    104             TalerTimestamp::Timestamp(timestamp) => TalerTimestamp::Timestamp(timestamp + rhs),
    105         }
    106     }
    107 }
    108 
    109 impl sqlx::Type<sqlx::Postgres> for TalerTimestamp {
    110     fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
    111         Option::<i64>::type_info()
    112     }
    113 }
    114 
    115 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for TalerTimestamp {
    116     fn encode_by_ref(
    117         &self,
    118         buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
    119     ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
    120         match self {
    121             TalerTimestamp::Never => None,
    122             TalerTimestamp::Timestamp(timestamp) => Some(timestamp.as_microsecond()),
    123         }
    124         .encode_by_ref(buf)
    125     }
    126 }
    127 
    128 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TalerTimestamp {
    129     fn decode(
    130         value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
    131     ) -> Result<Self, sqlx::error::BoxDynError> {
    132         let micros = Option::<i64>::decode(value)?;
    133         Ok(match micros {
    134             Some(micros) => Self::Timestamp(Timestamp::from_microsecond(micros)?),
    135             None => Self::Never,
    136         })
    137     }
    138 }
    139 
    140 /// <https://docs.taler.net/core/api-common.html#tsref-type-RelativeTime>
    141 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
    142 pub enum RelativeTime {
    143     Forever,
    144     Duration(Duration),
    145 }
    146 
    147 #[derive(Serialize, Deserialize)]
    148 struct RelativeTimeImpl {
    149     d_us: Value,
    150 }
    151 
    152 impl<'de> Deserialize<'de> for RelativeTime {
    153     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    154     where
    155         D: Deserializer<'de>,
    156     {
    157         let tmp = RelativeTimeImpl::deserialize(deserializer)?;
    158         match tmp.d_us {
    159             Value::Number(s) => {
    160                 if let Some(micros) = s.as_u64() {
    161                     Ok(Self::Duration(Duration::from_micros(micros)))
    162                 } else {
    163                     Err(Error::custom("Expected microseconds"))
    164                 }
    165             }
    166             Value::String(str) if str == "forever" => Ok(Self::Forever),
    167             _ => Err(Error::custom("Expected epoch time or 'forever'")),
    168         }
    169     }
    170 }
    171 
    172 impl Serialize for RelativeTime {
    173     fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
    174     where
    175         S: Serializer,
    176     {
    177         let mut se_struct = se.serialize_struct("RelativeTime", 1)?;
    178         match self {
    179             RelativeTime::Forever => se_struct.serialize_field("d_us", "forever")?,
    180             RelativeTime::Duration(duration) => {
    181                 se_struct.serialize_field("d_us", &duration.as_micros())?
    182             }
    183         }
    184         se_struct.end()
    185     }
    186 }
    187 
    188 impl Display for RelativeTime {
    189     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    190         match self {
    191             RelativeTime::Forever => f.write_str("forever"),
    192             RelativeTime::Duration(duration) => write!(f, "{duration:?}"),
    193         }
    194     }
    195 }