use fvm_shared::address::Address;
use fvm_shared::bigint::BigInt;
use fvm_shared::econ::TokenAmount;
use num_traits::Num;
use recall_ipc_api::subnet_id::SubnetID;
use serde::de::{DeserializeOwned, Error};
use serde::{de, Deserialize, Serialize, Serializer};
use serde_with::{DeserializeAs, SerializeAs};
use std::any::type_name;
use std::fmt::Display;
use std::str::FromStr;
use cid::Cid;
pub struct IsHumanReadable;
pub fn serialize_str<T, S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: ToString + Serialize,
S: Serializer,
{
{
if serializer.is_human_readable() {
source.to_string().serialize(serializer)
} else {
source.serialize(serializer)
}
}
}
pub fn deserialize_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: FromStr + DeserializeOwned,
<T as FromStr>::Err: Display,
D: de::Deserializer<'de>,
{
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
match T::from_str(&s) {
Ok(a) => Ok(a),
Err(e) => Err(D::Error::custom(format!(
"error deserializing {}: {}",
type_name::<T>(),
e
))),
}
} else {
T::deserialize(deserializer)
}
}
}
#[macro_export]
macro_rules! human_readable_str {
($typ:ty : $mark:ty) => {
impl serde_with::SerializeAs<$typ> for $mark {
fn serialize_as<S>(value: &$typ, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
$crate::serialize_str(value, serializer)
}
}
impl<'de> serde_with::DeserializeAs<'de, $typ> for $mark {
fn deserialize_as<D>(deserializer: D) -> Result<$typ, D::Error>
where
D: serde::de::Deserializer<'de>,
{
$crate::deserialize_str(deserializer)
}
}
};
($typ: ty) => {
human_readable_str!($typ: IsHumanReadable);
};
}
#[macro_export]
macro_rules! human_readable_delegate {
($typ:ty : $mark:ty => $deleg:ty) => {
impl serde_with::SerializeAs<$typ> for $mark {
fn serialize_as<S>(value: &$typ, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
$deleg: serde_with::SerializeAs<$typ>,
{
<$deleg>::serialize_as::<S>(value, serializer)
}
}
impl<'de> serde_with::DeserializeAs<'de, $typ> for $mark {
fn deserialize_as<D>(deserializer: D) -> Result<$typ, D::Error>
where
D: serde::de::Deserializer<'de>,
$deleg: serde_with::DeserializeAs<'de, $typ>,
{
<$deleg>::deserialize_as::<D>(deserializer)
}
}
};
($typ: ty : $mark:ty) => {
human_readable_delegate!($typ : $mark => $crate::IsHumanReadable);
};
($typ: ty) => {
human_readable_delegate!($typ : IsHumanReadable => $crate::IsHumanReadable);
};
}
human_readable_str!(Address);
human_readable_str!(Cid);
human_readable_str!(SubnetID);
impl SerializeAs<TokenAmount> for IsHumanReadable {
fn serialize_as<S>(tokens: &TokenAmount, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
tokens.atto().to_str_radix(10).serialize(serializer)
} else {
tokens.serialize(serializer)
}
}
}
impl<'de> DeserializeAs<'de, TokenAmount> for IsHumanReadable {
fn deserialize_as<D>(deserializer: D) -> Result<TokenAmount, D::Error>
where
D: de::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
match BigInt::from_str_radix(&s, 10) {
Ok(a) => Ok(TokenAmount::from_atto(a)),
Err(e) => Err(D::Error::custom(format!(
"error deserializing tokens: {}",
e
))),
}
} else {
TokenAmount::deserialize(deserializer)
}
}
}