use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
pub trait Prefix {
fn prefix() -> &'static str;
}
pub struct ZeroxPrefix;
impl Prefix for ZeroxPrefix {
fn prefix() -> &'static str {
"0x"
}
}
pub struct BytesToHexSerde<P> {
_marker: std::marker::PhantomData<P>,
}
impl<P: Prefix> BytesToHexSerde<P> {
pub fn serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
let hex_value = format!("{}{}", P::prefix(), hex::encode(value));
String::serialize(&hex_value, serializer)
} else {
<[u8]>::serialize(value, serializer)
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let deserialized_string = String::deserialize(deserializer)?;
if let Some(deserialized_string) = deserialized_string.strip_prefix(P::prefix()) {
hex::decode(deserialized_string).map_err(de::Error::custom)
} else {
Err(de::Error::custom(format!(
"string value missing prefix: {:?}",
P::prefix()
)))
}
} else {
<Vec<u8>>::deserialize(deserializer)
}
}
}
pub type ZeroPrefixHexSerde = BytesToHexSerde<ZeroxPrefix>;
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use crate::ZeroPrefixHexSerde;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Execute {
#[serde(with = "ZeroPrefixHexSerde")]
pub calldata: Vec<u8>,
}
#[test]
fn test_hex_serde_bincode() {
let original = Execute {
calldata: vec![0, 1, 2, 3, 4],
};
let encoded: Vec<u8> = vec![5, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4];
let decoded: Execute = bincode::deserialize(&encoded).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn test_hex_serde_json() {
let original = Execute {
calldata: vec![0, 1, 2, 3, 4],
};
let encoded = serde_json::to_string(&original).unwrap();
assert_eq!(r#"{"calldata":"0x0001020304"}"#, encoded);
let decoded: Execute = serde_json::from_str(&encoded).unwrap();
assert_eq!(original, decoded);
}
}