Skip to content

Commit

Permalink
Merge pull request #44 from osmosis-labs/boss/fix-json-deser
Browse files Browse the repository at this point in the history
  • Loading branch information
iboss-ptk authored Sep 30, 2022
2 parents 90fabac + 3142a24 commit 599a023
Show file tree
Hide file tree
Showing 60 changed files with 3,656 additions and 389 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["packages/*"]
members = ["packages/*", "tests/*"]
2 changes: 1 addition & 1 deletion dependencies/osmosis
Submodule osmosis updated 758 files
4 changes: 2 additions & 2 deletions packages/osmosis-std-derive/tests/query.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use cosmwasm_std::{Empty, QueryRequest};
use osmosis_std_derive::CosmwasmExt;

#[derive(Clone, PartialEq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest")]
#[proto_query(
path = "/osmosis.tokenfactory.v1beta1.Query/DenomsFromCreator",
Expand All @@ -11,7 +11,7 @@ pub struct QueryDenomsFromCreatorRequest {
#[prost(string, tag = "1")]
pub creator: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse")]
pub struct QueryDenomsFromCreatorResponse {
#[prost(string, repeated, tag = "1")]
Expand Down
2 changes: 1 addition & 1 deletion packages/osmosis-std-derive/tests/struct.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use cosmwasm_std::CosmosMsg;
use osmosis_std_derive::CosmwasmExt;

#[derive(Clone, PartialEq, ::prost::Message, CosmwasmExt)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenom")]
pub struct MsgCreateDenom {
#[prost(string, tag = "1")]
Expand Down
2 changes: 2 additions & 0 deletions packages/osmosis-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ version = "0.1.7"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
chrono = {version = "0.4.22", default-features = false}
cosmwasm-std = {version = "1.1.2", features = ["stargate"]}
osmosis-std-derive = {version = "0.1.7", path = "../osmosis-std-derive"}
prost = {version = "0.11.0", default-features = false, features = ["prost-derive"]}
Expand All @@ -16,3 +17,4 @@ schemars = "0.8.8"

# for query
serde = {version = "1.0", default-features = false, features = ["derive"]}
serde-cw-value = "0.7.0"
1 change: 1 addition & 0 deletions packages/osmosis-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
/// The version (commit hash) of the Cosmos SDK used when generating this library.
pub const OSMOSISD_VERSION: &str = include_str!("types/OSMOSIS_COMMIT");

mod serde;
pub mod shim;
pub mod types;
22 changes: 22 additions & 0 deletions packages/osmosis-std/src/serde/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pub mod as_str {
use serde::{de, Deserialize, Deserializer, Serializer};
use std::{fmt::Display, str::FromStr};

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: FromStr,
T::Err: Display,
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
T::from_str(&s).map_err(de::Error::custom)
}

pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Display,
{
serializer.serialize_str(&value.to_string())
}
}
239 changes: 229 additions & 10 deletions packages/osmosis-std/src/shim.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
#[derive(
Clone, PartialEq, ::prost::Message, schemars::JsonSchema, serde::Serialize, serde::Deserialize,
)]
use ::serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
use chrono::{DateTime, NaiveDateTime, Utc};
use serde::de;
use serde::de::Visitor;

use std::fmt;
use std::str::FromStr;

use prost::Message;

#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
pub struct Timestamp {
/// Represents seconds of UTC time since Unix epoch
/// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
Expand All @@ -15,9 +23,63 @@ pub struct Timestamp {
pub nanos: i32,
}

#[derive(
Clone, PartialEq, ::prost::Message, schemars::JsonSchema, serde::Serialize, serde::Deserialize,
)]
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 = NaiveDateTime::from_timestamp(ts.seconds, ts.nanos as u32);
let dt: DateTime<Utc> = DateTime::from_utc(dt, Utc);
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 {
/// Signed seconds of the span of time. Must be from -315,576,000,000
/// to +315,576,000,000 inclusive. Note: these bounds are computed from:
Expand All @@ -34,9 +96,47 @@ pub struct Duration {
pub nanos: i32,
}

#[derive(
Clone, PartialEq, ::prost::Message, schemars::JsonSchema, serde::Serialize, serde::Deserialize,
)]
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 {
/// A URL/resource name that uniquely identifies the type of the serialized
/// protocol buffer message. This string must contain at least
Expand Down Expand Up @@ -73,6 +173,125 @@ pub struct Any {
pub value: ::prost::alloc::vec::Vec<u8>,
}

macro_rules! expand_as_any {
($($ty:path,)*) => {

// TODO: make serialized data contains `@type` (https://github.com/osmosis-labs/osmosis-rust/issues/43)
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);
}
};

// must be map er else error
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 {
// @type found
Some(t) => {
$(
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);
}
)*
}
// @type not found, try match the type structure
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())
}
}
)*
};
}

// [HACK] Register all types that can serde as Any manually for now.
// must order by type that has more information for Any deserialization to
// work correctly. Since after serialization, it currently loses @type tag.
// And deserialization works by trying to iteratively match the structure.
expand_as_any!(
// pools have distincted structure
crate::types::osmosis::gamm::v1beta1::Pool,
crate::types::osmosis::gamm::poolmodels::stableswap::v1beta1::Pool,
// balancer pool param has more fields
crate::types::osmosis::gamm::v1beta1::PoolParams,
crate::types::osmosis::gamm::poolmodels::stableswap::v1beta1::PoolParams,
);

macro_rules! impl_prost_types_exact_conversion {
($t:ident | $($arg:ident),*) => {
impl From<$t> for prost_types::$t {
Expand Down Expand Up @@ -117,7 +336,7 @@ impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
) -> cosmwasm_std::StdResult<Self> {
Ok(cosmwasm_std::Coin {
denom: denom.into(),
denom,
amount: amount.parse()?,
})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/osmosis-std/src/types/OSMOSIS_COMMIT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v11.0.0
v12.0.0
Loading

0 comments on commit 599a023

Please sign in to comment.