use molecule::prelude::Entity;
use schemars::{JsonSchema, Schema, SchemaGenerator};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{serde_as, serde_conv, DeserializeAs, SerializeAs};
pub fn from_hex<'de, D, E>(deserializer: D) -> Result<E, D::Error>
where
D: Deserializer<'de>,
E: TryFrom<Vec<u8>>,
E::Error: core::fmt::Debug,
{
String::deserialize(deserializer)
.and_then(|string| {
let hex_str = string
.strip_prefix("0x")
.or_else(|| string.strip_prefix("0X"))
.unwrap_or(&string);
hex::decode(hex_str).map_err(|err| {
Error::custom(format!(
"failed to decode hex string {}: {:?}",
&string, err
))
})
})
.and_then(|vec| {
vec.try_into().map_err(|err| {
Error::custom(format!("failed to convert vector into type: {:?}", err))
})
})
}
fn to_hex_with_prefix<E, S>(e: E, serializer: S, with_prefix: bool) -> Result<S::Ok, S::Error>
where
E: AsRef<[u8]>,
S: Serializer,
{
let hex_str = hex::encode(e.as_ref());
let prefix = if with_prefix { "0x" } else { "" };
serializer.serialize_str(&format!("{}{}", prefix, hex_str))
}
pub struct SliceHex;
impl<T> SerializeAs<T> for SliceHex
where
T: AsRef<[u8]>,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
to_hex_with_prefix(source, serializer, true)
}
}
impl<'de, T> DeserializeAs<'de, T> for SliceHex
where
T: TryFrom<Vec<u8>>,
T::Error: core::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
from_hex(deserializer)
}
}
pub struct EntityHex;
impl<T> SerializeAs<T> for EntityHex
where
T: Entity,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
to_hex_with_prefix(source.as_slice(), serializer, true)
}
}
impl<'de, T> DeserializeAs<'de, T> for EntityHex
where
T: Entity,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
let v: Vec<u8> = from_hex(deserializer)?;
T::from_slice(&v).map_err(Error::custom)
}
}
macro_rules! uint_as_hex {
($name:ident, $ty:ty) => {
serde_conv!(
pub $name,
$ty,
|u: &$ty| format!("0x{:x}", u),
|hex: String| -> Result<$ty, String> {
let bytes = hex.as_bytes();
if bytes.len() < 3 || &bytes[..2] != b"0x" {
return Err(format!("uint hex string does not start with 0x: {}", hex));
}
if bytes.len() > 3 && &bytes[2..3] == b"0" {
return Err(format!(
"uint hex string starts with redundant leading zeros: {}",
hex
));
};
<$ty>::from_str_radix(&hex[2..], 16)
.map_err(|err| format!("failed to parse uint hex {}: {:?}", hex, err))
}
);
};
}
uint_as_hex!(U128Hex, u128);
uint_as_hex!(U64Hex, u64);
uint_as_hex!(U32Hex, u32);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HexU32(pub u32);
impl serde::Serialize for HexU32 {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&format!("0x{:x}", self.0))
}
}
impl<'de> serde::Deserialize<'de> for HexU32 {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let hex: String = String::deserialize(deserializer)?;
let bytes = hex.as_bytes();
if bytes.len() < 3 || &bytes[..2] != b"0x" {
return Err(D::Error::custom(format!(
"uint hex string does not start with 0x: {}",
hex
)));
}
if bytes.len() > 3 && &bytes[2..3] == b"0" {
return Err(D::Error::custom(format!(
"uint hex string starts with redundant leading zeros: {}",
hex
)));
}
u32::from_str_radix(&hex[2..], 16)
.map(HexU32)
.map_err(|err| D::Error::custom(format!("failed to parse uint hex {}: {:?}", hex, err)))
}
}
impl From<u32> for HexU32 {
fn from(v: u32) -> Self {
HexU32(v)
}
}
impl From<HexU32> for u32 {
fn from(v: HexU32) -> Self {
v.0
}
}
impl JsonSchema for HexU32 {
fn schema_name() -> std::borrow::Cow<'static, str> {
"HexU32".into()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = String::json_schema(generator);
schema.insert("pattern".into(), "^0x(0|[1-9a-fA-F][0-9a-fA-F]*)$".into());
schema
}
}
pub struct SliceHexNoPrefix;
impl<T> SerializeAs<T> for SliceHexNoPrefix
where
T: AsRef<[u8]>,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
to_hex_with_prefix(source, serializer, false)
}
}
impl<'de, T> DeserializeAs<'de, T> for SliceHexNoPrefix
where
T: TryFrom<Vec<u8>>,
T::Error: core::fmt::Debug,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
from_hex(deserializer)
}
}
#[serde_as]
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Pubkey(#[serde_as(as = "SliceHexNoPrefix")] pub [u8; 33]);
impl Pubkey {
pub fn from_slice(bytes: &[u8]) -> Result<Self, String> {
if bytes.len() != 33 {
return Err(format!(
"invalid pubkey length: expected 33 bytes, got {}",
bytes.len()
));
}
let mut arr = [0u8; 33];
arr.copy_from_slice(bytes);
Ok(Pubkey(arr))
}
pub fn as_bytes(&self) -> &[u8; 33] {
&self.0
}
}
impl core::fmt::Debug for Pubkey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Pubkey({})", hex::encode(self.0))
}
}
impl core::fmt::Display for Pubkey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
impl core::str::FromStr for Pubkey {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex_str = s
.strip_prefix("0x")
.or_else(|| s.strip_prefix("0X"))
.unwrap_or(s);
let bytes =
hex::decode(hex_str).map_err(|e| format!("invalid pubkey hex '{}': {}", s, e))?;
Pubkey::from_slice(&bytes)
}
}
impl JsonSchema for Pubkey {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Pubkey".into()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = String::json_schema(generator);
schema.insert("pattern".into(), "^[0-9a-fA-F]{66}$".into());
schema
}
}
#[serde_as]
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Privkey(#[serde_as(as = "SliceHexNoPrefix")] pub [u8; 32]);
impl Privkey {
pub fn from_slice(bytes: &[u8]) -> Result<Self, String> {
if bytes.len() != 32 {
return Err(format!(
"invalid privkey length: expected 32 bytes, got {}",
bytes.len()
));
}
let mut arr = [0u8; 32];
arr.copy_from_slice(bytes);
Ok(Privkey(arr))
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl From<[u8; 32]> for Privkey {
fn from(value: [u8; 32]) -> Self {
Self(value)
}
}
impl From<Privkey> for [u8; 32] {
fn from(value: Privkey) -> Self {
value.0
}
}
impl core::fmt::Debug for Privkey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Privkey(<redacted>)")
}
}
impl core::fmt::Display for Privkey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex::encode(self.0))
}
}
impl core::str::FromStr for Privkey {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex_str = s
.strip_prefix("0x")
.or_else(|| s.strip_prefix("0X"))
.unwrap_or(s);
let bytes =
hex::decode(hex_str).map_err(|e| format!("invalid privkey hex '{}': {}", s, e))?;
Privkey::from_slice(&bytes)
}
}
impl JsonSchema for Privkey {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Privkey".into()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = String::json_schema(generator);
schema.insert("pattern".into(), "^[0-9a-fA-F]{64}$".into());
schema
}
}
#[serde_as]
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Hash256(#[serde_as(as = "SliceHex")] pub [u8; 32]);
impl Hash256 {
pub fn from_slice(bytes: &[u8]) -> Result<Self, String> {
if bytes.len() != 32 {
return Err(format!(
"invalid hash256 length: expected 32 bytes, got {}",
bytes.len()
));
}
let mut arr = [0u8; 32];
arr.copy_from_slice(bytes);
Ok(Hash256(arr))
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl core::fmt::Debug for Hash256 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Hash256(0x{})", hex::encode(self.0))
}
}
impl core::fmt::Display for Hash256 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x{}", hex::encode(self.0))
}
}
impl core::str::FromStr for Hash256 {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let hex_str = s
.strip_prefix("0x")
.or_else(|| s.strip_prefix("0X"))
.unwrap_or(s);
let bytes =
hex::decode(hex_str).map_err(|e| format!("invalid hash256 hex '{}': {}", s, e))?;
Hash256::from_slice(&bytes)
}
}
impl JsonSchema for Hash256 {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Hash256".into()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let mut schema = String::json_schema(generator);
schema.insert("pattern".into(), "^0x[0-9a-fA-F]{64}$".into());
schema
}
}
pub mod duration_hex {
use core::time::Duration;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let nanos = duration.as_secs();
serializer.serialize_str(&format!("0x{:x}", nanos))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
let hex_str = String::deserialize(deserializer)?;
let seconds = u64::from_str_radix(&hex_str[2..], 16).map_err(|err| {
serde::de::Error::custom(format!(
"failed to parse duration hex {}: {:?}",
hex_str, err
))
})?;
Ok(Duration::from_secs(seconds))
}
}
#[macro_export]
macro_rules! define_rpc_flags {
(
$(#[$struct_meta:meta])*
pub struct $name:ident($ty:ty) {
$($(#[$flag_meta:meta])* const $flag_name:ident = $flag_value:expr;)*
}
) => {
$(#[$struct_meta])*
pub struct $name(pub $ty);
impl $name {
$(pub const $flag_name: $ty = $flag_value;)*
#[allow(clippy::wrong_self_convention)]
fn to_strings(self) -> Vec<String> {
let mut names = Vec::new();
$(
if self.0 & Self::$flag_name != 0 {
names.push(stringify!($flag_name).to_string());
}
)*
names
}
fn from_string(s: &str) -> Option<Self> {
let mut flags: $ty = 0;
for name in s.split('|') {
let name = name.trim();
match name {
$(stringify!($flag_name) => flags |= Self::$flag_name,)*
_ => return None,
}
}
Some($name(flags))
}
}
impl serde::Serialize for $name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let names = self.clone().to_strings();
if names.is_empty() {
serializer.serialize_str("")
} else {
serializer.serialize_str(&names.join("|"))
}
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
if s.is_empty() {
Ok($name(0))
} else {
$name::from_string(&s)
.ok_or_else(|| serde::de::Error::custom(format!("Invalid {}: {}", stringify!($name), s)))
}
}
}
impl From<$ty> for $name {
fn from(v: $ty) -> Self {
$name(v)
}
}
impl From<$name> for $ty {
fn from(v: $name) -> Self {
v.0
}
}
impl schemars::JsonSchema for $name {
fn schema_name() -> std::borrow::Cow<'static, str> {
stringify!($name).into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
let flag_names: Vec<String> = vec![
$(stringify!($flag_name).to_string(),)*
];
let single = flag_names.join("|");
let pattern = format!("^(({single})(\\s*\\|\\s*({single}))*)?$");
schemars::json_schema!({
"type": "string",
"pattern": pattern,
})
}
}
};
}