Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add interchain security protobuf #NTRN-408 #8

Merged
merged 9 commits into from
Nov 27, 2024
19 changes: 14 additions & 5 deletions packages/neutron-std-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ pub fn derive_cosmwasm_ext(input: TokenStream) -> TokenStream {
// provided buffer had insufficient capacity. Message encoding is otherwise
// infallible.

let (query_request_conversion, cosmwasm_query) = if get_attr("proto_query", &input.attrs)
.is_some()
let (query_request_conversion, cosmwasm_query, path_token) = if get_attr(
"proto_query",
&input.attrs,
)
.is_some()
{
let path = get_query_attrs(&input.attrs, match_kv_attr!("path", Literal));
let res = get_query_attrs(&input.attrs, match_kv_attr!("response_type", Ident));

let query_request_conversion = quote! {
impl <Q: cosmwasm_std::CustomQuery> From<#ident> for cosmwasm_std::QueryRequest<Q> {
fn from(msg: #ident) -> Self {
Expand Down Expand Up @@ -70,14 +72,21 @@ pub fn derive_cosmwasm_ext(input: TokenStream) -> TokenStream {
}
};

(query_request_conversion, cosmwasm_query)
let path_token = quote! {
pub const PATH: &'static str = #path;
};

(query_request_conversion, cosmwasm_query, path_token)
} else {
(quote!(), quote!())
(quote!(), quote!(), quote!())
};

(quote! {
impl #ident {
pub const TYPE_URL: &'static str = #type_url;

#path_token

#cosmwasm_query

pub fn to_proto_bytes(&self) -> Vec<u8> {
Expand Down
113 changes: 103 additions & 10 deletions packages/neutron-std/src/serde/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,77 @@
use serde::{
de::{
self,
value::{I64Deserializer, U64Deserializer},
},
Deserialize,
};
use std::{
fmt::{Display, Formatter},
marker::PhantomData,
str::FromStr,
};

struct StringOrNumberVisitor<T> {
pr0n00gler marked this conversation as resolved.
Show resolved Hide resolved
p: PhantomData<T>,
}
// The Visitor helps deserialize a number, both from its numerical JSON representation and from a string.
// For example, for the struct:
// ```rust
// struct Foo {
// pub bar: i32;
// }
// ```
// Both JSON representations `'{"bar":"12"}'` and `'{"bar":12}'` will give the same result upon deserialization:
impl<'de, T> de::Visitor<'de> for StringOrNumberVisitor<T>
where
T: Deserialize<'de>,
T::Err: Display,
T: FromStr,
{
type Value = T;

fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a string or a number")
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
T::deserialize(U64Deserializer::new(value))
}

fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
T::deserialize(I64Deserializer::new(value))
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
value.parse::<T>().map_err(de::Error::custom)
}
}

// Helps (de)serialize any type that implements FromStr trait as str
// For example some structs in Cosmos SDK returns ints as strs and this mod helps deal with that
pub mod as_str {
use serde::{de, Deserialize, Deserializer, Serializer};
use std::{fmt::Display, str::FromStr};
use serde::{Deserialize, Deserializer, Serializer};
use std::{fmt::Display, marker::PhantomData, str::FromStr};

use super::StringOrNumberVisitor;

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

pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
Expand All @@ -29,15 +89,18 @@ pub mod option_as_str {
use serde::{de, Deserialize, Deserializer, Serializer};
use std::{fmt::Display, str::FromStr};

use super::NumberOrString;

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: FromStr,
T: FromStr + Deserialize<'de>,
T::Err: Display,
D: Deserializer<'de>,
{
let encoded_string: Option<String> = Option::deserialize(deserializer)?;
let encoded_string: Option<NumberOrString<T>> = Option::deserialize(deserializer)?;
match encoded_string {
Some(s) => T::from_str(&s).map(Some).map_err(de::Error::custom),
Some(NumberOrString::String(s)) => T::from_str(&s).map(Some).map_err(de::Error::custom),
Some(NumberOrString::Number(n)) => Ok(Some(n)),
None => Ok(None),
}
}
Expand All @@ -58,16 +121,21 @@ pub mod as_str_vec {
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt::Display, str::FromStr};

use super::NumberOrString;

pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where
T: FromStr,
T: FromStr + Deserialize<'de>,
T::Err: Display,
D: Deserializer<'de>,
{
let vec_of_strings: Vec<String> = Vec::deserialize(deserializer)?;
let vec_of_strings: Vec<NumberOrString<T>> = Vec::deserialize(deserializer)?;
vec_of_strings
.into_iter()
.map(|s| T::from_str(&s).map_err(de::Error::custom))
.map(|v| match v {
NumberOrString::Number(n) => Ok(n),
NumberOrString::String(s) => T::from_str(&s).map_err(de::Error::custom),
})
.collect()
}

Expand Down Expand Up @@ -135,3 +203,28 @@ pub mod as_option_base64_encoded_string {
}
}
}


// NumberOrString is a helper enum that helps us determine which
// JSON numeric representation we are working with. If it's a string,
// we will get the value `NumberOrString::String("-11")`.
// If we are dealing with a numeric representation,
// we will get `NumberOrString::Number(-11i64)`.
// Then, using pattern matching, we can select the appropriate algorithm to work with the data.
#[derive(Deserialize, Debug, PartialEq)]
#[serde(untagged)]
enum NumberOrString<T> {
Number(T),
String(String),
}

#[test]
fn parse_test() {
let int = "-11";
let res = serde_json_wasm::from_str::<NumberOrString<i64>>(int).unwrap();
assert_eq!(res, NumberOrString::Number(-11i64));

let str = "\"-11\"";
let res = serde_json_wasm::from_str::<NumberOrString<i64>>(str).unwrap();
assert_eq!(res, NumberOrString::String("-11".to_string()));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod v1;
Loading