near_primitives_core/
serialize.rs

1use 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
25/// Serializes number as a string; deserializes either as a string or number.
26///
27/// This format works for `u64`, `u128`, `Option<u64>` and `Option<u128>` types.
28/// When serializing, numbers are serialized as decimal strings.  When
29/// deserializing, strings are parsed as decimal numbers while numbers are
30/// interpreted as is.
31pub 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    /// Abstraction between integers that we serialize.
40    pub trait DecType: Sized {
41        /// Formats number as a decimal string; passes `None` as is.
42        fn serialize(&self) -> Option<String>;
43
44        /// Constructs Self from a `null` value.  Returns error if this type
45        /// does not accept `null` values.
46        fn try_from_unit() -> Result<Self, ParseUnitError> {
47            Err(ParseUnitError)
48        }
49
50        /// Tries to parse decimal string as an integer.
51        fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError>;
52
53        /// Constructs Self from a 64-bit unsigned integer.
54        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 u128 {
70        fn serialize(&self) -> Option<String> {
71            Some(self.to_string())
72        }
73        fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
74            Self::from_str_radix(value, 10)
75        }
76        fn from_u64(value: u64) -> Self {
77            value.into()
78        }
79    }
80
81    impl<T: DecType> DecType for Option<T> {
82        fn serialize(&self) -> Option<String> {
83            self.as_ref().and_then(DecType::serialize)
84        }
85        fn try_from_unit() -> Result<Self, ParseUnitError> {
86            Ok(None)
87        }
88        fn try_from_str(value: &str) -> Result<Self, std::num::ParseIntError> {
89            Some(T::try_from_str(value)).transpose()
90        }
91        fn from_u64(value: u64) -> Self {
92            Some(T::from_u64(value))
93        }
94    }
95
96    struct Visitor<T>(core::marker::PhantomData<T>);
97
98    impl<'de, T: DecType> de::Visitor<'de> for Visitor<T> {
99        type Value = T;
100
101        fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
102            fmt.write_str("a non-negative integer as a string")
103        }
104
105        fn visit_unit<E: de::Error>(self) -> Result<T, E> {
106            T::try_from_unit().map_err(|_| de::Error::invalid_type(de::Unexpected::Option, &self))
107        }
108
109        fn visit_u64<E: de::Error>(self, value: u64) -> Result<T, E> {
110            Ok(T::from_u64(value))
111        }
112
113        fn visit_str<E: de::Error>(self, value: &str) -> Result<T, E> {
114            T::try_from_str(value).map_err(de::Error::custom)
115        }
116    }
117
118    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
119    where
120        D: Deserializer<'de>,
121        T: DecType,
122    {
123        deserializer.deserialize_any(Visitor(Default::default()))
124    }
125
126    pub fn serialize<S, T>(num: &T, serializer: S) -> Result<S::Ok, S::Error>
127    where
128        S: Serializer,
129        T: DecType,
130    {
131        match num.serialize() {
132            Some(value) => serializer.serialize_str(&value),
133            None => serializer.serialize_none(),
134        }
135    }
136}
137
138#[test]
139fn test_u64_dec_format() {
140    #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
141    struct Test {
142        #[serde(with = "dec_format")]
143        field: u64,
144    }
145
146    assert_round_trip("{\"field\":\"42\"}", Test { field: 42 });
147    assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX });
148    assert_deserialize("{\"field\":42}", Test { field: 42 });
149    assert_de_error::<Test>("{\"field\":18446744073709551616}");
150    assert_de_error::<Test>("{\"field\":\"18446744073709551616\"}");
151    assert_de_error::<Test>("{\"field\":42.0}");
152}
153
154#[test]
155fn test_u128_dec_format() {
156    #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
157    struct Test {
158        #[serde(with = "dec_format")]
159        field: u128,
160    }
161
162    assert_round_trip("{\"field\":\"42\"}", Test { field: 42 });
163    assert_round_trip("{\"field\":\"18446744073709551615\"}", Test { field: u64::MAX as u128 });
164    assert_round_trip("{\"field\":\"18446744073709551616\"}", Test { field: 18446744073709551616 });
165    assert_deserialize("{\"field\":42}", Test { field: 42 });
166    assert_de_error::<Test>("{\"field\":null}");
167    assert_de_error::<Test>("{\"field\":42.0}");
168}
169
170#[test]
171fn test_option_u128_dec_format() {
172    #[derive(PartialEq, Debug, serde::Deserialize, serde::Serialize)]
173    struct Test {
174        #[serde(with = "dec_format")]
175        field: Option<u128>,
176    }
177
178    assert_round_trip("{\"field\":null}", Test { field: None });
179    assert_round_trip("{\"field\":\"42\"}", Test { field: Some(42) });
180    assert_round_trip(
181        "{\"field\":\"18446744073709551615\"}",
182        Test { field: Some(u64::MAX as u128) },
183    );
184    assert_round_trip(
185        "{\"field\":\"18446744073709551616\"}",
186        Test { field: Some(18446744073709551616) },
187    );
188    assert_deserialize("{\"field\":42}", Test { field: Some(42) });
189    assert_de_error::<Test>("{\"field\":42.0}");
190}
191
192#[cfg(test)]
193#[track_caller]
194fn assert_round_trip<'a, T>(serialized: &'a str, obj: T)
195where
196    T: serde::Deserialize<'a> + serde::Serialize + std::fmt::Debug + std::cmp::PartialEq,
197{
198    assert_eq!(serialized, serde_json::to_string(&obj).unwrap());
199    assert_eq!(obj, serde_json::from_str(serialized).unwrap());
200}
201
202#[cfg(test)]
203#[track_caller]
204fn assert_deserialize<'a, T>(serialized: &'a str, obj: T)
205where
206    T: serde::Deserialize<'a> + std::fmt::Debug + std::cmp::PartialEq,
207{
208    assert_eq!(obj, serde_json::from_str(serialized).unwrap());
209}
210
211#[cfg(test)]
212#[track_caller]
213fn assert_de_error<'a, T: serde::Deserialize<'a> + std::fmt::Debug>(serialized: &'a str) {
214    serde_json::from_str::<T>(serialized).unwrap_err();
215}