-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathserde_utils.rs
More file actions
148 lines (131 loc) · 5.1 KB
/
serde_utils.rs
File metadata and controls
148 lines (131 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Utilities for serialising/deserialising hexadecimal values.
#[cfg(test)]
#[path = "serde_utils_test.rs"]
mod serde_utils_test;
use serde::de::{Deserialize, Visitor};
use serde::ser::{Serialize, SerializeTuple};
use serde::Deserializer;
use thiserror_no_std::Error;
use crate::deprecated_contract_class::ContractClassAbiEntry;
use crate::stdlib::borrow::ToOwned;
use crate::stdlib::string::{String, ToString};
use crate::stdlib::vec::Vec;
use crate::stdlib::{fmt, format, vec};
/// A [BytesAsHex](`crate::serde_utils::BytesAsHex`) prefixed with '0x'.
pub type PrefixedBytesAsHex<const N: usize> = BytesAsHex<N, true>;
/// A byte array that serializes as a hex string.
///
/// The `PREFIXED` generic type symbolize whether a string representation of the hex value should be
/// prefixed by `0x` or not.
#[derive(Debug, Eq, PartialEq)]
pub struct BytesAsHex<const N: usize, const PREFIXED: bool>(pub(crate) [u8; N]);
impl<'de, const N: usize, const PREFIXED: bool> Deserialize<'de> for BytesAsHex<N, PREFIXED> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ByteArrayVisitor<const N: usize, const PREFIXED: bool>;
impl<'de, const N: usize, const PREFIXED: bool> Visitor<'de> for ByteArrayVisitor<N, PREFIXED> {
type Value = BytesAsHex<N, PREFIXED>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a byte array")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut res = [0u8; N];
let mut i = 0;
while let Some(value) = seq.next_element()? {
res[i] = value;
i += 1;
}
Ok(BytesAsHex(res))
}
}
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
bytes_from_hex_str::<N, PREFIXED>(s.as_str())
.map_err(serde::de::Error::custom)
.map(BytesAsHex)
} else {
deserializer.deserialize_tuple(N, ByteArrayVisitor)
}
}
}
impl<const N: usize, const PREFIXED: bool> Serialize for BytesAsHex<N, PREFIXED> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let hex_str = hex_str_from_bytes::<N, PREFIXED>(self.0);
serializer.serialize_str(&hex_str)
} else {
let mut seq = serializer.serialize_tuple(N)?;
for element in &self.0[..] {
seq.serialize_element(element)?;
}
seq.end()
}
}
}
/// The error type returned by the inner deserialization.
#[derive(Error, Clone, Debug)]
pub enum InnerDeserializationError {
/// Error parsing the hex string.
#[error(transparent)]
FromHex(#[from] hex::FromHexError),
/// Missing 0x prefix in the hex string.
#[error("Missing prefix 0x in {hex_str}")]
MissingPrefix { hex_str: String },
/// Unexpected input byte count.
#[error("Bad input - expected #bytes: {expected_byte_count}, string found: {string_found}.")]
BadInput { expected_byte_count: usize, string_found: String },
}
/// Deserializes a Hex decoded as string to a byte array.
pub fn bytes_from_hex_str<const N: usize, const PREFIXED: bool>(
hex_str: &str,
) -> Result<[u8; N], InnerDeserializationError> {
let hex_str = if PREFIXED {
hex_str
.strip_prefix("0x")
.ok_or(InnerDeserializationError::MissingPrefix { hex_str: hex_str.into() })?
} else {
hex_str
};
// Make sure string is not too long.
if hex_str.len() > 2 * N {
let mut err_str = "0x".to_owned();
err_str.push_str(hex_str);
return Err(InnerDeserializationError::BadInput {
expected_byte_count: N,
string_found: err_str,
});
}
// Pad if needed.
let to_add = 2 * N - hex_str.len();
let padded_str = vec!["0"; to_add].join("") + hex_str;
Ok(hex::decode(padded_str)?.try_into().expect("Unexpected length of deserialized hex bytes."))
}
/// Encodes a byte array to a string.
pub fn hex_str_from_bytes<const N: usize, const PREFIXED: bool>(bytes: [u8; N]) -> String {
let hex_str = hex::encode(bytes);
let mut hex_str = hex_str.trim_start_matches('0');
hex_str = if hex_str.is_empty() { "0" } else { hex_str };
if PREFIXED { format!("0x{hex_str}") } else { hex_str.to_string() }
}
pub fn deserialize_optional_contract_class_abi_entry_vector<'de, D>(
deserializer: D,
) -> Result<Option<Vec<ContractClassAbiEntry>>, D::Error>
where
D: Deserializer<'de>,
{
// Deserialize the field as an `Option<Vec<ContractClassAbiEntry>>`
let result: Result<Option<Vec<ContractClassAbiEntry>>, _> = Option::deserialize(deserializer);
// If the field contains junk or an invalid value, return `None`.
match result {
Ok(value) => Ok(value),
Err(_) => Ok(None),
}
}