1use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
2use chrono::{DateTime, NaiveDateTime, Utc};
3use cosmwasm_std::StdResult;
4use serde::de::Visitor;
5use serde::{de, ser};
6
7use prost::Message;
8use std::fmt;
9use std::str::FromStr;
10
11#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
12pub struct Timestamp {
13 #[prost(int64, tag = "1")]
17 pub seconds: i64,
18 #[prost(int32, tag = "2")]
23 pub nanos: i32,
24}
25
26impl Serialize for Timestamp {
27 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
28 where
29 S: Serializer,
30 {
31 let mut ts = prost_types::Timestamp {
32 seconds: self.seconds,
33 nanos: self.nanos,
34 };
35 ts.normalize();
36 let dt = NaiveDateTime::from_timestamp_opt(ts.seconds, ts.nanos as u32)
37 .expect("invalid or out-of-range datetime");
38 let dt: DateTime<Utc> = DateTime::from_utc(dt, Utc);
39 serializer.serialize_str(format!("{:?}", dt).as_str())
40 }
41}
42
43impl<'de> Deserialize<'de> for Timestamp {
44 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
45 where
46 D: Deserializer<'de>,
47 {
48 struct TimestampVisitor;
49
50 impl<'de> Visitor<'de> for TimestampVisitor {
51 type Value = Timestamp;
52
53 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54 formatter.write_str("Timestamp in RFC3339 format")
55 }
56
57 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
58 where
59 E: de::Error,
60 {
61 let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
62 serde::de::Error::custom(format!(
63 "Failed to parse {} as datetime: {:?}",
64 value, err
65 ))
66 })?;
67 let ts = Timestamp::from(utc);
68 Ok(ts)
69 }
70 }
71 deserializer.deserialize_str(TimestampVisitor)
72 }
73}
74
75impl From<DateTime<Utc>> for Timestamp {
76 fn from(dt: DateTime<Utc>) -> Self {
77 Timestamp {
78 seconds: dt.timestamp(),
79 nanos: dt.timestamp_subsec_nanos() as i32,
80 }
81 }
82}
83#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
84pub struct Duration {
85 #[prost(int64, tag = "1")]
89 pub seconds: i64,
90 #[prost(int32, tag = "2")]
97 pub nanos: i32,
98}
99
100impl Serialize for Duration {
101 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
102 where
103 S: Serializer,
104 {
105 let mut d = prost_types::Duration::from(self.to_owned());
106 d.normalize();
107
108 serializer.serialize_str(d.to_string().as_str())
109 }
110}
111
112impl<'de> Deserialize<'de> for Duration {
113 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
114 where
115 D: Deserializer<'de>,
116 {
117 struct DurationVisitor;
118
119 impl<'de> Visitor<'de> for DurationVisitor {
120 type Value = Duration;
121
122 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
123 formatter.write_str("Timestamp in RFC3339 format")
124 }
125
126 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
127 where
128 E: de::Error,
129 {
130 value
131 .parse::<prost_types::Duration>()
132 .map(Into::into)
133 .map_err(de::Error::custom)
134 }
135 }
136 deserializer.deserialize_str(DurationVisitor)
137 }
138}
139
140#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
141pub struct Any {
142 #[prost(string, tag = "1")]
171 pub type_url: ::prost::alloc::string::String,
172 #[prost(bytes = "vec", tag = "2")]
174 pub value: ::prost::alloc::vec::Vec<u8>,
175}
176
177macro_rules! expand_as_any {
178 ($($ty:path,)*) => {
179
180 impl Serialize for Any {
182 fn serialize<S>(
183 &self,
184 serializer: S,
185 ) -> Result<<S as ::serde::Serializer>::Ok, <S as ::serde::Serializer>::Error>
186 where
187 S: ::serde::Serializer,
188 {
189 $(
190 if self.type_url == <$ty>::TYPE_URL {
191 let value: Result<$ty, <S as ::serde::Serializer>::Error> =
192 prost::Message::decode(self.value.as_slice()).map_err(ser::Error::custom);
193
194 if let Ok(value) = value {
195 return value.serialize(serializer);
196 }
197 }
198 )*
199
200 Err(serde::ser::Error::custom(
201 "data did not match any type that supports serialization as `Any`",
202 ))
203 }
204 }
205
206 impl<'de> Deserialize<'de> for Any {
207 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
208 where
209 D: serde::Deserializer<'de>,
210 {
211 let value = match serde_cw_value::Value::deserialize(deserializer) {
212 Ok(value) => value,
213 Err(err) => {
214 return Err(err);
215 }
216 };
217
218 let type_url = if let serde_cw_value::Value::Map(m) = value.clone() {
220 m.get(&serde_cw_value::Value::String("@type".to_string()))
221 .map(|t| match t.to_owned() {
222 serde_cw_value::Value::String(s) => Ok(s),
223 _ => Err(serde::de::Error::custom("type_url must be String")),
224 })
225 .transpose()
226 } else {
227 Err(serde::de::Error::custom("data must have map structure"))
228 }?;
229
230 match type_url {
231 Some(t) => {
233 $(
234 if t == <$ty>::TYPE_URL {
235 return <$ty>::deserialize(
236 serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
237 value.clone(),
238 ),
239 )
240 .map(|v| Any {
241 type_url: <$ty>::TYPE_URL.to_string(),
242 value: v.encode_to_vec(),
243 })
244 .map_err(serde::de::Error::custom);
245 }
246 )*
247 }
248 None => {
250 $(
251 if let Ok(v) = <$ty>::deserialize(
252 serde_cw_value::ValueDeserializer::<serde_cw_value::DeserializerError>::new(
253 value.clone(),
254 ),
255 ) {
256 return Ok(Any {
257 type_url: <$ty>::TYPE_URL.to_string(),
258 value: v.encode_to_vec(),
259 });
260 }
261 )*
262 }
263 };
264
265 Err(serde::de::Error::custom(
266 "data did not match any type that supports deserialization as `Any`",
267 ))
268 }
269 }
270
271 $(
272 impl TryFrom<Any> for $ty {
273 type Error = prost::DecodeError;
274
275 fn try_from(value: Any) -> Result<Self, Self::Error> {
276 prost::Message::decode(value.value.as_slice())
277 }
278 }
279 )*
280 };
281}
282
283expand_as_any!(
288 crate::types::cosmwasm::wasm::v1::MsgExecuteContract,
290 crate::types::cosmos::gov::v1::Proposal,
291);
292
293macro_rules! impl_prost_types_exact_conversion {
294 ($t:ident | $($arg:ident),*) => {
295 impl From<$t> for prost_types::$t {
296 fn from(src: $t) -> Self {
297 prost_types::$t {
298 $(
299 $arg: src.$arg,
300 )*
301 }
302 }
303 }
304
305 impl From<prost_types::$t> for $t {
306 fn from(src: prost_types::$t) -> Self {
307 $t {
308 $(
309 $arg: src.$arg,
310 )*
311 }
312 }
313 }
314 };
315}
316
317impl_prost_types_exact_conversion! { Timestamp | seconds, nanos }
318impl_prost_types_exact_conversion! { Duration | seconds, nanos }
319impl_prost_types_exact_conversion! { Any | type_url, value }
320
321impl From<cosmwasm_std::Coin> for crate::types::cosmos::base::v1beta1::Coin {
322 fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self {
323 crate::types::cosmos::base::v1beta1::Coin {
324 denom,
325 amount: amount.into(),
326 }
327 }
328}
329
330impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
331 type Error = cosmwasm_std::StdError;
332
333 fn try_from(
334 crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
335 ) -> cosmwasm_std::StdResult<Self> {
336 Ok(cosmwasm_std::Coin {
337 denom,
338 amount: amount.parse()?,
339 })
340 }
341}
342
343pub fn try_proto_to_cosmwasm_coins(
345 coins: impl IntoIterator<Item = crate::types::cosmos::base::v1beta1::Coin>,
346) -> StdResult<Vec<cosmwasm_std::Coin>> {
347 coins.into_iter().map(|c| c.try_into()).collect()
348}
349
350pub fn cosmwasm_to_proto_coins(
352 coins: impl IntoIterator<Item = cosmwasm_std::Coin>,
353) -> Vec<crate::types::cosmos::base::v1beta1::Coin> {
354 coins.into_iter().map(|c| c.into()).collect()
355}
356
357#[cfg(test)]
358mod tests {
359 use cosmwasm_std::Uint128;
360
361 use super::*;
362
363 #[test]
364 fn test_coins_conversion() {
365 let coins = vec![
366 cosmwasm_std::Coin {
367 denom: "uatom".to_string(),
368 amount: Uint128::new(100),
369 },
370 cosmwasm_std::Coin {
371 denom: "uosmo".to_string(),
372 amount: Uint128::new(200),
373 },
374 ];
375
376 let proto_coins = cosmwasm_to_proto_coins(coins.clone());
377 let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap();
378
379 assert_eq!(coins, cosmwasm_coins);
380 }
381}