smart_config/de/
primitive_types_impl.rs1use serde::{Serialize, de::DeserializeOwned};
2
3use crate::{
4 de::{DeserializeContext, DeserializeParam, Qualified, Serde, WellKnown, WellKnownOption},
5 error::ErrorWithOrigin,
6 metadata::{BasicTypes, ParamMetadata, TypeDescription},
7 value::Value,
8};
9
10const HASH_DE: Qualified<Serde![str]> =
11 Qualified::new(Serde![str], "hex string with optional 0x prefix");
12
13macro_rules! impl_well_known_hash {
14 ($($ty:ident),+) => {
15 $(
16 #[cfg_attr(docsrs, doc(cfg(feature = "primitive-types")))]
18 impl WellKnown for primitive_types::$ty {
19 type Deserializer = Qualified<Serde![str]>;
20 const DE: Self::Deserializer = HASH_DE;
21 }
22
23 #[cfg_attr(docsrs, doc(cfg(feature = "primitive-types")))]
24 impl WellKnownOption for primitive_types::$ty {}
25 )+
26 };
27}
28
29impl_well_known_hash!(H128, H160, H256, H384, H512, H768);
30
31#[derive(Debug)]
34pub struct HexUintDeserializer;
35
36impl<T: Serialize + DeserializeOwned> DeserializeParam<T> for HexUintDeserializer {
38 const EXPECTING: BasicTypes = BasicTypes::STRING;
39
40 fn describe(&self, description: &mut TypeDescription) {
41 description.set_details("0x-prefixed hex number");
42 }
43
44 fn deserialize_param(
45 &self,
46 ctx: DeserializeContext<'_>,
47 param: &'static ParamMetadata,
48 ) -> Result<T, ErrorWithOrigin> {
49 let deserializer = ctx.current_value_deserializer(param.name)?;
50 if let Value::String(s) = deserializer.value() {
51 if !s.expose().starts_with("0x") {
52 return Err(deserializer.invalid_type("0x-prefixed hex number"));
53 }
54 }
55 T::deserialize(deserializer)
56 }
57
58 fn serialize_param(&self, param: &T) -> serde_json::Value {
59 serde_json::to_value(param).expect("failed serializing value")
60 }
61}
62
63macro_rules! impl_well_known_uint {
64 ($($ty:ident),+) => {
65 $(
66 #[cfg_attr(docsrs, doc(cfg(feature = "primitive-types")))]
69 impl WellKnown for primitive_types::$ty {
70 type Deserializer = HexUintDeserializer;
71 const DE: Self::Deserializer = HexUintDeserializer;
72 }
73
74 #[cfg_attr(docsrs, doc(cfg(feature = "primitive-types")))]
75 impl WellKnownOption for primitive_types::$ty {}
76 )+
77 };
78}
79
80impl_well_known_uint!(U128, U256, U512);
81
82#[cfg(test)]
83mod tests {
84 use std::collections::HashMap;
85
86 use primitive_types::{H160 as Address, H256, U128, U256};
87 use smart_config_derive::{DescribeConfig, DeserializeConfig};
88
89 use crate::{
90 config,
91 testing::{test, test_complete},
92 };
93
94 #[derive(Debug, PartialEq, DescribeConfig, DeserializeConfig)]
95 #[config(crate = crate)]
96 struct TestConfig {
97 int: U128,
98 #[config(default)]
99 hash: Option<H256>,
100 #[config(default)]
101 addresses: Vec<Address>,
102 #[config(default)]
103 balances: HashMap<Address, U256>,
104 }
105
106 #[test]
107 fn deserializing_values() {
108 let json = config!(
109 "int": "0x123",
110 "hash": "fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe",
111 "addresses": [
112 "0x0000000000000000000000000000000000001234",
113 "1212121212121212121212121212121212121212",
114 ],
115 "balances": HashMap::from([
116 ("0x0000000000000000000000000000000000004321", "0x3"),
117 ])
118 );
119 let config = test_complete::<TestConfig>(json).unwrap();
120 assert_eq!(config.int, U128::from(0x123));
121 assert_eq!(config.hash, Some(H256::repeat_byte(0xfe)));
122 assert_eq!(
123 config.addresses,
124 [Address::from_low_u64_be(0x1234), Address::repeat_byte(0x12)]
125 );
126 assert_eq!(
127 config.balances,
128 HashMap::from([(Address::from_low_u64_be(0x4321), U256::from(3))])
129 );
130 }
131
132 #[test]
133 fn uint_prefix_error() {
134 let json = config!("int": "123");
135 let err = test::<TestConfig>(json).unwrap_err();
136 assert_eq!(err.len(), 1);
137 let err = err.first();
138 assert_eq!(err.path(), "int");
139 let inner = err.inner().to_string();
140 assert!(
141 inner.contains("invalid type") && inner.contains("0x-prefixed hex number"),
142 "{inner}"
143 );
144 }
145}