taler-rust

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

de.rs (6337B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 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 serde::{
     18     Deserialize,
     19     de::{self, Error, Visitor, value::SeqDeserializer},
     20     forward_to_deserialize_any,
     21 };
     22 
     23 /// Deserialize a value `T` from a Postgres notification payload string
     24 pub fn from_str<'de, T: Deserialize<'de>>(notification: &'de str) -> Result<T, de::value::Error> {
     25     T::deserialize(NotifDe(notification))
     26 }
     27 
     28 macro_rules! forward_to_parse {
     29     ($($deserialize_fn:ident => $visit_fn:ident),* $(,)?) => {
     30         $(
     31             fn $deserialize_fn<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     32                 visitor.$visit_fn(self.0.parse().map_err(Error::custom)?)
     33             }
     34         )*
     35     };
     36 }
     37 
     38 /// Space-separated values deserializer for Postgres notification
     39 struct NotifDe<'de>(&'de str);
     40 
     41 impl<'de> de::Deserializer<'de> for NotifDe<'de> {
     42     type Error = de::value::Error;
     43 
     44     fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     45         let de = SeqDeserializer::new(self.0.split(' ').map(PlainDe));
     46         visitor.visit_seq(de)
     47     }
     48 
     49     fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     50         PlainDe(self.0).deserialize_unit(visitor)
     51     }
     52 
     53     forward_to_deserialize_any! {
     54         bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
     55         bytes byte_buf option unit_struct newtype_struct seq tuple
     56         tuple_struct map struct enum identifier ignored_any
     57     }
     58 }
     59 
     60 /// Values deserializer for Postgres notifications
     61 struct PlainDe<'de>(&'de str);
     62 
     63 impl<'de> de::IntoDeserializer<'de> for PlainDe<'de> {
     64     type Deserializer = Self;
     65 
     66     fn into_deserializer(self) -> Self::Deserializer {
     67         self
     68     }
     69 }
     70 
     71 impl<'de> de::Deserializer<'de> for PlainDe<'de> {
     72     type Error = de::value::Error;
     73 
     74     fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     75         visitor.visit_borrowed_str(self.0)
     76     }
     77 
     78     fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     79         if self.0.is_empty() {
     80             visitor.visit_none()
     81         } else {
     82             visitor.visit_some(self)
     83         }
     84     }
     85 
     86     fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
     87         if self.0.is_empty() {
     88             visitor.visit_unit()
     89         } else {
     90             Err(Error::custom("expected empty token for unit type"))
     91         }
     92     }
     93 
     94     fn deserialize_unit_struct<V: Visitor<'de>>(
     95         self,
     96         _name: &'static str,
     97         visitor: V,
     98     ) -> Result<V::Value, Self::Error> {
     99         self.deserialize_unit(visitor)
    100     }
    101 
    102     fn deserialize_newtype_struct<V: Visitor<'de>>(
    103         self,
    104         _name: &'static str,
    105         visitor: V,
    106     ) -> Result<V::Value, Self::Error> {
    107         visitor.visit_newtype_struct(self)
    108     }
    109 
    110     fn deserialize_enum<V: Visitor<'de>>(
    111         self,
    112         name: &'static str,
    113         variants: &'static [&'static str],
    114         visitor: V,
    115     ) -> Result<V::Value, Self::Error> {
    116         use de::IntoDeserializer;
    117         self.0
    118             .into_deserializer()
    119             .deserialize_enum(name, variants, visitor)
    120     }
    121 
    122     forward_to_parse! {
    123         deserialize_i8 => visit_i8,
    124         deserialize_i16 => visit_i16,
    125         deserialize_i32 => visit_i32,
    126         deserialize_i64 => visit_i64,
    127         deserialize_i128 => visit_i128,
    128         deserialize_u8 => visit_u8,
    129         deserialize_u16 => visit_u16,
    130         deserialize_u32 => visit_u32,
    131         deserialize_u64 => visit_u64,
    132         deserialize_u128 => visit_u128,
    133         deserialize_f32 => visit_f32,
    134         deserialize_f64 => visit_f64,
    135         deserialize_char => visit_char,
    136         deserialize_bool => visit_bool,
    137     }
    138 
    139     forward_to_deserialize_any! {
    140         str string bytes byte_buf seq tuple tuple_struct map struct identifier ignored_any
    141     }
    142 }
    143 
    144 #[cfg(test)]
    145 mod tests {
    146     use super::from_str;
    147 
    148     #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
    149     #[allow(non_camel_case_types)]
    150     enum Enum {
    151         pending,
    152         active,
    153         closed,
    154     }
    155 
    156     #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
    157     struct NewType(u32);
    158 
    159     #[test]
    160     fn parse() {
    161         // Single primitives
    162         assert_eq!((1u8,), from_str::<(u8,)>("1").unwrap());
    163         assert_eq!((false,), from_str::<(bool,)>("false").unwrap());
    164         assert_eq!(("hello",), from_str::<(&str,)>("hello").unwrap());
    165 
    166         // Composite
    167         assert_eq!((1u8, false), from_str::<(u8, bool)>("1 false").unwrap());
    168         assert_eq!([1u8, 2, 3, 4], from_str::<[u8; 4]>("1 2 3 4").unwrap());
    169 
    170         // Empty payload → unit
    171         assert_eq!((), from_str::<()>("").unwrap());
    172 
    173         // Trailing space → None / ()
    174         assert_eq!((1u8, None), from_str::<(u8, Option<bool>)>("1 ").unwrap());
    175         assert_eq!((1u8, ()), from_str::<(u8, ())>("1 ").unwrap());
    176 
    177         // Double space → empty token in the middle
    178         assert_eq!(
    179             (1u8, (), true),
    180             from_str::<(u8, (), bool)>("1  true").unwrap()
    181         );
    182 
    183         // Enums matched by variant name
    184         assert_eq!(
    185             (42u32, Enum::active),
    186             from_str::<(u32, Enum)>("42 active").unwrap()
    187         );
    188         assert_eq!(
    189             (1u8, (), true, Enum::closed),
    190             from_str::<(u8, (), bool, Enum)>("1  true closed").unwrap()
    191         );
    192 
    193         // Newtype struct forwards to inner type
    194         assert_eq!(
    195             (NewType(7), true),
    196             from_str::<(NewType, bool)>("7 true").unwrap()
    197         );
    198 
    199         // Unknown enum variant is an error
    200         assert!(from_str::<(Enum,)>("unknown").is_err());
    201     }
    202 }