taler-rust

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

timestamp.rs (6830B)


      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, str::FromStr, 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 impl FromStr for TalerTimestamp {
     31     type Err = anyhow::Error;
     32 
     33     fn from_str(s: &str) -> Result<Self, Self::Err> {
     34         if s == "never" {
     35             return Ok(Self::Never);
     36         }
     37         let s: i64 = s.parse()?;
     38 
     39         Ok(Self::Timestamp(jiff::Timestamp::from_second(s)?))
     40     }
     41 }
     42 
     43 #[derive(Serialize, Deserialize)]
     44 struct TimestampImpl {
     45     t_s: Value,
     46 }
     47 
     48 impl<'de> Deserialize<'de> for TalerTimestamp {
     49     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     50     where
     51         D: Deserializer<'de>,
     52     {
     53         let tmp = TimestampImpl::deserialize(deserializer)?;
     54         match tmp.t_s {
     55             Value::Number(s) => {
     56                 if let Some(since_epoch_s) = s.as_i64() {
     57                     jiff::Timestamp::from_second(since_epoch_s)
     58                         .map(Self::Timestamp)
     59                         .map_err(Error::custom)
     60                 } else {
     61                     Err(Error::custom("Expected epoch time"))
     62                 }
     63             }
     64             Value::String(str) if str == "never" => Ok(Self::Never),
     65             _ => Err(Error::custom("Expected epoch time or 'never'")),
     66         }
     67     }
     68 }
     69 
     70 impl Serialize for TalerTimestamp {
     71     fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
     72     where
     73         S: Serializer,
     74     {
     75         let mut se_struct = se.serialize_struct("Timestamp", 1)?;
     76         match self {
     77             TalerTimestamp::Never => se_struct.serialize_field("t_s", "never")?,
     78             TalerTimestamp::Timestamp(timestamp) => {
     79                 se_struct.serialize_field("t_s", &timestamp.as_second())?
     80             }
     81         }
     82         se_struct.end()
     83     }
     84 }
     85 
     86 impl Display for TalerTimestamp {
     87     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     88         match self {
     89             TalerTimestamp::Never => f.write_str("never"),
     90             TalerTimestamp::Timestamp(timestamp) => timestamp.fmt(f),
     91         }
     92     }
     93 }
     94 
     95 impl From<jiff::Timestamp> for TalerTimestamp {
     96     fn from(time: jiff::Timestamp) -> Self {
     97         Self::Timestamp(time)
     98     }
     99 }
    100 
    101 impl From<jiff::civil::Date> for TalerTimestamp {
    102     fn from(date: jiff::civil::Date) -> Self {
    103         date.to_datetime(Time::midnight())
    104             .to_zoned(TimeZone::UTC)
    105             .unwrap()
    106             .timestamp()
    107             .into()
    108     }
    109 }
    110 
    111 impl Add<jiff::Span> for TalerTimestamp {
    112     type Output = Self;
    113 
    114     fn add(self, rhs: jiff::Span) -> Self::Output {
    115         match self {
    116             TalerTimestamp::Never => TalerTimestamp::Never,
    117             TalerTimestamp::Timestamp(timestamp) => TalerTimestamp::Timestamp(timestamp + rhs),
    118         }
    119     }
    120 }
    121 
    122 impl sqlx::Type<sqlx::Postgres> for TalerTimestamp {
    123     fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
    124         Option::<i64>::type_info()
    125     }
    126 }
    127 
    128 impl<'q> sqlx::Encode<'q, sqlx::Postgres> for TalerTimestamp {
    129     fn encode_by_ref(
    130         &self,
    131         buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
    132     ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
    133         match self {
    134             TalerTimestamp::Never => None,
    135             TalerTimestamp::Timestamp(timestamp) => Some(timestamp.as_microsecond()),
    136         }
    137         .encode_by_ref(buf)
    138     }
    139 }
    140 
    141 impl<'r> sqlx::Decode<'r, sqlx::Postgres> for TalerTimestamp {
    142     fn decode(
    143         value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
    144     ) -> Result<Self, sqlx::error::BoxDynError> {
    145         let micros = Option::<i64>::decode(value)?;
    146         Ok(match micros {
    147             Some(micros) => Self::Timestamp(Timestamp::from_microsecond(micros)?),
    148             None => Self::Never,
    149         })
    150     }
    151 }
    152 
    153 /// <https://docs.taler.net/core/api-common.html#tsref-type-RelativeTime>
    154 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
    155 pub enum RelativeTime {
    156     Forever,
    157     Duration(Duration),
    158 }
    159 
    160 impl FromStr for RelativeTime {
    161     type Err = anyhow::Error;
    162 
    163     fn from_str(s: &str) -> Result<Self, Self::Err> {
    164         if s == "forever" {
    165             return Ok(Self::Forever);
    166         }
    167         let micros: u64 = s.parse()?;
    168 
    169         Ok(Self::Duration(Duration::from_micros(micros)))
    170     }
    171 }
    172 
    173 #[derive(Serialize, Deserialize)]
    174 struct RelativeTimeImpl {
    175     d_us: Value,
    176 }
    177 
    178 impl<'de> Deserialize<'de> for RelativeTime {
    179     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    180     where
    181         D: Deserializer<'de>,
    182     {
    183         let tmp = RelativeTimeImpl::deserialize(deserializer)?;
    184         match tmp.d_us {
    185             Value::Number(s) => {
    186                 if let Some(micros) = s.as_u64() {
    187                     Ok(Self::Duration(Duration::from_micros(micros)))
    188                 } else {
    189                     Err(Error::custom("Expected microseconds"))
    190                 }
    191             }
    192             Value::String(str) if str == "forever" => Ok(Self::Forever),
    193             _ => Err(Error::custom("Expected epoch time or 'forever'")),
    194         }
    195     }
    196 }
    197 
    198 impl Serialize for RelativeTime {
    199     fn serialize<S>(&self, se: S) -> Result<S::Ok, S::Error>
    200     where
    201         S: Serializer,
    202     {
    203         let mut se_struct = se.serialize_struct("RelativeTime", 1)?;
    204         match self {
    205             RelativeTime::Forever => se_struct.serialize_field("d_us", "forever")?,
    206             RelativeTime::Duration(duration) => {
    207                 se_struct.serialize_field("d_us", &duration.as_micros())?
    208             }
    209         }
    210         se_struct.end()
    211     }
    212 }
    213 
    214 impl Display for RelativeTime {
    215     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    216         match self {
    217             RelativeTime::Forever => f.write_str("forever"),
    218             RelativeTime::Duration(duration) => write!(f, "{duration:?}"),
    219         }
    220     }
    221 }