use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
use chrono::{DateTime, Utc};
use cosmwasm_std::StdResult;
use serde::de;
use serde::de::Visitor;
use std::fmt;
use std::str::FromStr;
#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
pub struct Timestamp {
#[prost(int64, tag = "1")]
pub seconds: i64,
#[prost(int32, tag = "2")]
pub nanos: i32,
}
impl Serialize for Timestamp {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut ts = prost_types::Timestamp {
seconds: self.seconds,
nanos: self.nanos,
};
ts.normalize();
let dt = DateTime::from_timestamp(ts.seconds, ts.nanos as u32)
.expect("invalid or out-of-range datetime");
serializer.serialize_str(format!("{:?}", dt).as_str())
}
}
impl<'de> Deserialize<'de> for Timestamp {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct TimestampVisitor;
impl<'de> Visitor<'de> for TimestampVisitor {
type Value = Timestamp;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Timestamp in RFC3339 format")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
serde::de::Error::custom(format!(
"Failed to parse {} as datetime: {:?}",
value, err
))
})?;
let ts = Timestamp::from(utc);
Ok(ts)
}
}
deserializer.deserialize_str(TimestampVisitor)
}
}
impl From<DateTime<Utc>> for Timestamp {
fn from(dt: DateTime<Utc>) -> Self {
Timestamp {
seconds: dt.timestamp(),
nanos: dt.timestamp_subsec_nanos() as i32,
}
}
}
#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
pub struct Duration {
#[prost(int64, tag = "1")]
pub seconds: i64,
#[prost(int32, tag = "2")]
pub nanos: i32,
}
impl Serialize for Duration {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut d = prost_types::Duration::from(self.to_owned());
d.normalize();
serializer.serialize_str(d.to_string().as_str())
}
}
impl<'de> Deserialize<'de> for Duration {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct DurationVisitor;
impl<'de> Visitor<'de> for DurationVisitor {
type Value = Duration;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Timestamp in RFC3339 format")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
value
.parse::<prost_types::Duration>()
.map(Into::into)
.map_err(de::Error::custom)
}
}
deserializer.deserialize_str(DurationVisitor)
}
}
#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
pub struct Any {
#[prost(string, tag = "1")]
pub type_url: ::prost::alloc::string::String,
#[prost(bytes = "vec", tag = "2")]
pub value: ::prost::alloc::vec::Vec<u8>,
}
macro_rules! expand_as_any {
($($ty:path,)*) => {
impl Serialize for Any {
fn serialize<S>(
&self,
_serializer: S,
) -> Result<<S as ::serde::Serializer>::Ok, <S as ::serde::Serializer>::Error>
where
S: ::serde::Serializer,
{
$(
if self.type_url == <$ty>::TYPE_URL {
let value: Result<$ty, <S as ::serde::Serializer>::Error> =
prost::Message::decode(self.value.as_slice()).map_err(ser::Error::custom);
if let Ok(value) = value {
return value.serialize(serializer);
}
}
)*
Err(serde::ser::Error::custom(
"data did not match any type that supports serialization as `Any`",
))
}
}
impl<'de> Deserialize<'de> for Any {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = match serde_cw_value::Value::deserialize(deserializer) {
Ok(value) => value,
Err(err) => {
return Err(err);
}
};
let type_url = if let serde_cw_value::Value::Map(m) = value.clone() {
m.get(&serde_cw_value::Value::String("@type".to_string()))
.map(|t| match t.to_owned() {
serde_cw_value::Value::String(s) => Ok(s),
_ => Err(serde::de::Error::custom("type_url must be String")),
})
.transpose()
} else {
Err(serde::de::Error::custom("data must have map structure"))
}?;
match type_url {
Some(_) => {
$(
if t == <$ty>::TYPE_URL {
return <$ty>::deserialize(
serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
value.clone(),
),
)
.map(|v| Any {
type_url: <$ty>::TYPE_URL.to_string(),
value: v.encode_to_vec(),
})
.map_err(serde::de::Error::custom);
}
)*
}
None => {
$(
if let Ok(v) = <$ty>::deserialize(
serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
value.clone(),
),
) {
return Ok(Any {
type_url: <$ty>::TYPE_URL.to_string(),
value: v.encode_to_vec(),
});
}
)*
}
};
Err(serde::de::Error::custom(
"data did not match any type that supports deserialization as `Any`",
))
}
}
$(
impl TryFrom<Any> for $ty {
type Error = prost::DecodeError;
fn try_from(value: Any) -> Result<Self, Self::Error> {
prost::Message::decode(value.value.as_slice())
}
}
)*
};
}
expand_as_any!();
macro_rules! impl_prost_types_exact_conversion {
($t:ident | $($arg:ident),*) => {
impl From<$t> for prost_types::$t {
fn from(src: $t) -> Self {
prost_types::$t {
$(
$arg: src.$arg,
)*
}
}
}
impl From<prost_types::$t> for $t {
fn from(src: prost_types::$t) -> Self {
$t {
$(
$arg: src.$arg,
)*
}
}
}
};
}
impl_prost_types_exact_conversion! { Timestamp | seconds, nanos }
impl_prost_types_exact_conversion! { Duration | seconds, nanos }
impl_prost_types_exact_conversion! { Any | type_url, value }
impl From<cosmwasm_std::Coin> for crate::types::cosmos::base::v1beta1::Coin {
fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self {
crate::types::cosmos::base::v1beta1::Coin {
denom,
amount: amount.into(),
}
}
}
impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
type Error = cosmwasm_std::StdError;
fn try_from(
crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
) -> cosmwasm_std::StdResult<Self> {
Ok(cosmwasm_std::Coin {
denom,
amount: amount.parse()?,
})
}
}
pub fn try_proto_to_cosmwasm_coins(
coins: impl IntoIterator<Item = crate::types::cosmos::base::v1beta1::Coin>,
) -> StdResult<Vec<cosmwasm_std::Coin>> {
coins.into_iter().map(|c| c.try_into()).collect()
}
pub fn cosmwasm_to_proto_coins(
coins: impl IntoIterator<Item = cosmwasm_std::Coin>,
) -> Vec<crate::types::cosmos::base::v1beta1::Coin> {
coins.into_iter().map(|c| c.into()).collect()
}
#[cfg(test)]
mod tests {
use cosmwasm_std::Uint128;
use super::*;
#[test]
fn test_coins_conversion() {
let coins = vec![
cosmwasm_std::Coin {
denom: "uatom".to_string(),
amount: Uint128::new(100),
},
cosmwasm_std::Coin {
denom: "uosmo".to_string(),
amount: Uint128::new(200),
},
];
let proto_coins = cosmwasm_to_proto_coins(coins.clone());
let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap();
assert_eq!(coins, cosmwasm_coins);
}
}