near_primitives_core/
serialize.rs1use base64::Engine;
2use base64::display::Base64Display;
3use base64::engine::general_purpose::GeneralPurpose;
4use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
5
6pub fn to_base64(input: &[u8]) -> String {
7 BASE64_STANDARD.encode(input)
8}
9
10pub fn base64_display(input: &[u8]) -> Base64Display<'_, 'static, GeneralPurpose> {
11 Base64Display::new(input, &BASE64_STANDARD)
12}
13
14pub fn from_base64(encoded: &str) -> Result<Vec<u8>, base64::DecodeError> {
15 BASE64_STANDARD.decode(encoded)
16}
17
18#[cfg(feature = "schemars")]
19pub fn base64_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
20 let mut schema = <String as schemars::JsonSchema>::json_schema(generator);
21 schema.ensure_object().insert("format".into(), "bytes".into());
22 schema
23}
24
25pub mod dec_format {
32 use serde::de;
33 use serde::{Deserializer, Serializer};
34
35 #[derive(thiserror::Error, Debug)]
36 #[error("cannot parse from unit")]
37 pub struct ParseUnitError;
38
39 pub trait DecType: Sized {
41 fn serialize(&self) -> Option<String>;
43
44 fn try_from_unit() -> Result<Self, ParseUnitError> {
47 Err(ParseUnitError)
48 }
49
50 fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError>;
52
53 fn from_u64(value: u64) -> Self;
55 }
56
57 impl DecType for u64 {
58 fn serialize(&self) -> Option<String> {
59 Some(self.to_string())
60 }
61 fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
62 Self::from_str_radix(value, 10)
63 }
64 fn from_u64(value: u64) -> Self {
65 value
66 }
67 }
68
69 impl DecType for crate::types::Gas {
70 fn serialize(&self) -> Option<String> {
71 Some(self.as_gas().to_string())
72 }
73 fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
74 u64::try_from_str(value).map(crate::types::Gas::from_gas)
75 }
76 fn from_u64(value: u64) -> Self {
77 crate::types::Gas::from_gas(value)
78 }
79 }
80
81 impl DecType for u128 {
82 fn serialize(&self) -> Option<String> {
83 Some(self.to_string())
84 }
85 fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
86 Self::from_str_radix(value, 10)
87 }
88 fn from_u64(value: u64) -> Self {
89 value.into()
90 }
91 }
92
93 impl<T: DecType> DecType for Option<T> {
94 fn serialize(&self) -> Option<String> {
95 self.as_ref().and_then(DecType::serialize)
96 }
97 fn try_from_unit() -> Result<Self, ParseUnitError> {
98 Ok(None)
99 }
100 fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
101 Some(T::try_from_str(value)).transpose()
102 }
103 fn from_u64(value: u64) -> Self {
104 Some(T::from_u64(value))
105 }
106 }
107
108 struct Visitor<T>(core::marker::PhantomData<T>);
109
110 impl<'de, T: DecType> de::Visitor<'de> for Visitor<T> {
111 type Value = T;
112
113 fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
114 fmt.write_str("a non-negative integer as a string")
115 }
116
117 fn visit_unit<E: de::Error>(self) -> Result<T, E> {
118 T::try_from_unit().map_err(|_| de::Error::invalid_type(de::Unexpected::Option, &self))
119 }
120
121 fn visit_u64<E: de::Error>(self, value: u64) -> Result<T, E> {
122 Ok(T::from_u64(value))
123 }
124
125 fn visit_str<E: de::Error>(self, value: &str) -> Result<T, E> {
126 T::try_from_str(value).map_err(de::Error::custom)
127 }
128 }
129
130 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
131 where
132 D: Deserializer<'de>,
133 T: DecType,
134 {
135 deserializer.deserialize_any(Visitor(Default::default()))
136 }
137
138 pub fn serialize<S, T>(num: &T, serializer: S) -> Result<S::Ok, S::Error>
139 where
140 S: Serializer,
141 T: DecType,
142 {
143 match num.serialize() {
144 Some(value) => serializer.serialize_str(&value),
145 None => serializer.serialize_none(),
146 }
147 }
148}
149
150#[test]
151fn test_u64_dec_format() {
152 #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
153 struct Test {
154 #[serde(with = "dec_format")]
155 field: u64,
156 }
157
158 assert_round_trip("{\"field\":\"42\"}", Test { field: 42 });
159 assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX });
160 assert_deserialize("{\"field\":42}", Test { field: 42 });
161 assert_de_error::<Test>("{\"field\":18446744073709551616}");
162 assert_de_error::<Test>("{\"field\":\"18446744073709551616\"}");
163 assert_de_error::<Test>("{\"field\":42.0}");
164}
165
166#[test]
167fn test_u128_dec_format() {
168 #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
169 struct Test {
170 #[serde(with = "dec_format")]
171 field: u128,
172 }
173
174 assert_round_trip("{\"field\":\"42\"}", Test { field: 42 });
175 assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX as u128 });
176 assert_round_trip("{\"field\":\"18446744073709551616\"}", Test { field: 18446744073709551616 });
177 assert_deserialize("{\"field\":42}", Test { field: 42 });
178 assert_de_error::<Test>("{\"field\":null}");
179 assert_de_error::<Test>("{\"field\":42.0}");
180}
181
182#[test]
183fn test_option_u128_dec_format() {
184 #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
185 struct Test {
186 #[serde(with = "dec_format")]
187 field: Option<u128>,
188 }
189
190 assert_round_trip("{\"field\":null}", Test { field: None });
191 assert_round_trip("{\"field\":\"42\"}", Test { field: Some(42) });
192 assert_round_trip(
193 "{\"field\":\"18446744073709551615\"}",
194 Test { field: Some(u64::MAX as u128) },
195 );
196 assert_round_trip(
197 "{\"field\":\"18446744073709551616\"}",
198 Test { field: Some(18446744073709551616) },
199 );
200 assert_deserialize("{\"field\":42}", Test { field: Some(42) });
201 assert_de_error::<Test>("{\"field\":42.0}");
202}
203
204#[cfg(test)]
205#[track_caller]
206fn assert_round_trip<'a, T>(serialized: &'a str, obj: T)
207where
208 T: serde::Deserialize<'a> + serde::Serialize + std::fmt::Debug + std::cmp::PartialEq,
209{
210 assert_eq!(serialized, serde_json::to_string(&obj).unwrap());
211 assert_eq!(obj, serde_json::from_str(serialized).unwrap());
212}
213
214#[cfg(test)]
215#[track_caller]
216fn assert_deserialize<'a, T>(serialized: &'a str, obj: T)
217where
218 T: serde::Deserialize<'a> + std::fmt::Debug + std::cmp::PartialEq,
219{
220 assert_eq!(obj, serde_json::from_str(serialized).unwrap());
221}
222
223#[cfg(test)]
224#[track_caller]
225fn assert_de_error<'a, T: serde::Deserialize<'a> + std::fmt::Debug>(serialized: &'a str) {
226 serde_json::from_str::<T>(serialized).unwrap_err();
227}