1use std::fmt;
2use std::str::FromStr;
3
4use ::serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
5use chrono::{DateTime, Utc};
6use cosmwasm_std::{Binary, StdResult};
7use serde::de;
8use serde::de::Visitor;
9use serde::ser::SerializeMap;
10
11#[derive(Clone, Copy, 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 = DateTime::from_timestamp(ts.seconds, ts.nanos as u32).ok_or_else(|| {
37 ser::Error::custom(format!(
38 "failed to parse as NativeDateTime: seconds: {}, nanos: {}",
39 self.seconds, self.nanos
40 ))
41 })?;
42 serializer.serialize_str(format!("{:?}", dt).as_str())
43 }
44}
45
46impl<'de> Deserialize<'de> for Timestamp {
47 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
48 where
49 D: Deserializer<'de>,
50 {
51 struct TimestampVisitor;
52
53 impl<'de> Visitor<'de> for TimestampVisitor {
54 type Value = Timestamp;
55
56 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
57 formatter.write_str("Timestamp in RFC3339 format")
58 }
59
60 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
61 where
62 E: de::Error,
63 {
64 let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
65 serde::de::Error::custom(format!(
66 "Failed to parse {} as datetime: {:?}",
67 value, err
68 ))
69 })?;
70 let ts = Timestamp::from(utc);
71 Ok(ts)
72 }
73 }
74 deserializer.deserialize_str(TimestampVisitor)
75 }
76}
77
78impl From<DateTime<Utc>> for Timestamp {
79 fn from(dt: DateTime<Utc>) -> Self {
80 Timestamp {
81 seconds: dt.timestamp(),
82 nanos: dt.timestamp_subsec_nanos() as i32,
83 }
84 }
85}
86#[derive(Clone, Copy, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
87pub struct Duration {
88 #[prost(int64, tag = "1")]
92 pub seconds: i64,
93 #[prost(int32, tag = "2")]
100 pub nanos: i32,
101}
102
103impl Serialize for Duration {
104 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
105 where
106 S: Serializer,
107 {
108 let mut d = prost_types::Duration::from(self.to_owned());
109 d.normalize();
110
111 serializer.serialize_str(d.to_string().as_str())
112 }
113}
114
115impl<'de> Deserialize<'de> for Duration {
116 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
117 where
118 D: Deserializer<'de>,
119 {
120 struct DurationVisitor;
121
122 impl<'de> Visitor<'de> for DurationVisitor {
123 type Value = Duration;
124
125 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
126 formatter.write_str("Timestamp in RFC3339 format")
127 }
128
129 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
130 where
131 E: de::Error,
132 {
133 value
134 .parse::<prost_types::Duration>()
135 .map(Into::into)
136 .map_err(de::Error::custom)
137 }
138 }
139 deserializer.deserialize_str(DurationVisitor)
140 }
141}
142
143#[derive(Clone, PartialEq, Eq, ::prost::Message, schemars::JsonSchema)]
144pub struct Any {
145 #[prost(string, tag = "1")]
174 pub type_url: ::prost::alloc::string::String,
175 #[prost(bytes = "vec", tag = "2")]
177 pub value: ::prost::alloc::vec::Vec<u8>,
178}
179
180impl Serialize for Any {
181 fn serialize<S>(
182 &self,
183 serializer: S,
184 ) -> Result<<S as ::serde::Serializer>::Ok, <S as ::serde::Serializer>::Error>
185 where
186 S: ::serde::Serializer,
187 {
188 let mut map = serializer.serialize_map(Some(2))?;
189 map.serialize_entry("@type", &self.type_url)?;
190 map.serialize_entry("value", &Binary::from(self.value.clone()))?;
191 map.end()
192 }
193}
194
195impl<'de> Deserialize<'de> for Any {
196 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
197 where
198 D: serde::Deserializer<'de>,
199 {
200 let raw = match serde_cw_value::Value::deserialize(deserializer) {
202 Ok(raw) => raw,
203 Err(err) => {
204 return Err(err);
205 }
206 };
207
208 let map = match raw.clone() {
210 serde_cw_value::Value::Map(m) => Ok(m),
211 _ => Err(serde::de::Error::custom("data must have map structure")),
212 }?;
213
214 let type_url = map
216 .get(&serde_cw_value::Value::String("@type".to_string()))
217 .map(|t| match t.to_owned() {
218 serde_cw_value::Value::String(s) => Ok(s),
219 _ => Err(serde::de::Error::custom("type_url must be String")),
220 })
221 .transpose()?
222 .unwrap_or_default();
223
224 let value_in_base64 = map
226 .get(&serde_cw_value::Value::String("value".to_string()))
227 .map(|t| match t.to_owned() {
228 serde_cw_value::Value::String(s) => Ok(s),
229 _ => Err(serde::de::Error::custom("value must be String")),
230 })
231 .transpose()?
232 .unwrap_or_default();
233
234 let value = match Binary::from_base64(&value_in_base64) {
236 Ok(v) => Ok(v),
237 _ => Err(serde::de::Error::custom("value must be base64 encoded")),
238 }?
239 .to_vec();
240
241 Ok(Any { type_url, value })
242 }
243}
244
245macro_rules! impl_prost_types_exact_conversion {
246 ($t:ident | $($arg:ident),*) => {
247 impl From<$t> for prost_types::$t {
248 fn from(src: $t) -> Self {
249 prost_types::$t {
250 $(
251 $arg: src.$arg,
252 )*
253 }
254 }
255 }
256
257 impl From<prost_types::$t> for $t {
258 fn from(src: prost_types::$t) -> Self {
259 $t {
260 $(
261 $arg: src.$arg,
262 )*
263 }
264 }
265 }
266 };
267}
268
269impl_prost_types_exact_conversion! { Timestamp | seconds, nanos }
270impl_prost_types_exact_conversion! { Duration | seconds, nanos }
271impl_prost_types_exact_conversion! { Any | type_url, value }
272
273impl From<cosmwasm_std::Coin> for crate::types::cosmos::base::v1beta1::Coin {
274 fn from(cosmwasm_std::Coin { denom, amount }: cosmwasm_std::Coin) -> Self {
275 crate::types::cosmos::base::v1beta1::Coin {
276 denom,
277 amount: amount.into(),
278 }
279 }
280}
281
282impl TryFrom<crate::types::cosmos::base::v1beta1::Coin> for cosmwasm_std::Coin {
283 type Error = cosmwasm_std::StdError;
284
285 fn try_from(
286 crate::types::cosmos::base::v1beta1::Coin { denom, amount }: crate::types::cosmos::base::v1beta1::Coin,
287 ) -> StdResult<Self> {
288 Ok(cosmwasm_std::Coin {
289 denom,
290 amount: amount.parse()?,
291 })
292 }
293}
294
295pub fn try_proto_to_cosmwasm_coins(
297 coins: impl IntoIterator<Item = crate::types::cosmos::base::v1beta1::Coin>,
298) -> StdResult<Vec<cosmwasm_std::Coin>> {
299 coins.into_iter().map(|c| c.try_into()).collect()
300}
301
302pub fn cosmwasm_to_proto_coins(
304 coins: impl IntoIterator<Item = cosmwasm_std::Coin>,
305) -> Vec<crate::types::cosmos::base::v1beta1::Coin> {
306 coins.into_iter().map(|c| c.into()).collect()
307}
308
309#[cfg(test)]
310mod tests {
311 use cosmwasm_std::Uint128;
312
313 use super::*;
314
315 #[test]
316 fn test_coins_conversion() {
317 let coins = vec![
318 cosmwasm_std::Coin {
319 denom: "uatom".to_string(),
320 amount: Uint128::new(100),
321 },
322 cosmwasm_std::Coin {
323 denom: "nhash".to_string(),
324 amount: Uint128::new(200),
325 },
326 ];
327
328 let proto_coins = cosmwasm_to_proto_coins(coins.clone());
329 let cosmwasm_coins = try_proto_to_cosmwasm_coins(proto_coins).unwrap();
330
331 assert_eq!(coins, cosmwasm_coins);
332 }
333}