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 }