cosm_script/data_structures/
tx_resp.rs1use crate::cosmos_modules::abci::{AbciMessageLog, Attribute, StringEvent, TxResponse};
2use crate::cosmos_modules::tendermint_abci::Event;
3use crate::error::CosmScriptError;
4use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
5use serde::{Deserialize, Serialize};
6const FORMAT: &str = "%Y-%m-%dT%H:%M:%S%.f";
7const FORMAT_TZ_SUPPLIED: &str = "%Y-%m-%dT%H:%M:%S.%f%:z";
8const FORMAT_SHORT_Z: &str = "%Y-%m-%dT%H:%M:%SZ";
9const FORMAT_SHORT_Z2: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
10#[derive(Debug)]
11pub struct CosmTxResponse {
12 pub height: u64,
13 pub txhash: String,
14 pub codespace: String,
15 pub code: usize,
16 pub data: String,
17 pub raw_log: String,
18 pub logs: Vec<TxResultBlockMsg>,
19 pub info: String,
20 pub gas_wanted: u64,
21 pub gas_used: u64,
22 pub timestamp: DateTime<Utc>,
24 pub events: Vec<Event>,
25}
26impl CosmTxResponse {
27 pub fn get_attribute_from_logs(
31 &self,
32
33 event_type: &str,
34 attribute_key: &str,
35 ) -> Vec<(usize, String)> {
36 let mut response: Vec<(usize, String)> = Default::default();
37 let logs = &self.logs;
38
39 for log_part in logs {
40 let msg_index = log_part.msg_index.unwrap_or_default();
41 let events = &log_part.events;
42 let events_filtered = events
44 .iter()
45 .filter(|event| event.s_type == event_type)
46 .collect::<Vec<_>>();
47 if let Some(event) = events_filtered.first() {
49 let attributes_filtered = event
50 .attributes
51 .iter()
52 .filter(|attr| attr.key == attribute_key)
53 .map(|f| Some(f.value.clone()))
54 .flatten()
55 .collect::<Vec<_>>();
56
57 if let Some(attr_key) = attributes_filtered.first() {
58 response.push((msg_index, attr_key.clone()));
59 }
60 }
61 }
62
63 response
64 }
65 pub fn get_events(&self, event_type: &str) -> Vec<TxResultBlockEvent> {
67 let mut response: Vec<TxResultBlockEvent> = Default::default();
68
69 for log_part in &self.logs {
70 let events = &log_part.events;
71 let events_filtered = events
73 .iter()
74 .filter(|event| event.s_type == event_type)
75 .collect::<Vec<_>>();
76 for event in events_filtered {
77 response.push(event.clone());
78 }
79 }
80
81 response
82 }
83}
84
85impl From<TxResponse> for CosmTxResponse {
86 fn from(tx: TxResponse) -> Self {
87 Self {
88 height: tx.height as u64,
89 txhash: tx.txhash,
90 codespace: tx.codespace,
91 code: tx.code as usize,
92 data: tx.data,
93 raw_log: tx.raw_log,
94 logs: tx.logs.into_iter().map(TxResultBlockMsg::from).collect(),
95 info: tx.info,
96 gas_wanted: tx.gas_wanted as u64,
97 gas_used: tx.gas_used as u64,
98 timestamp: parse_timestamp(tx.timestamp).unwrap(),
99 events: tx.events,
100 }
101 }
102}
103
104#[derive(Deserialize, Serialize, Clone, Debug)]
105pub struct TxResultBlockAttribute {
106 pub key: String,
107 pub value: String,
108}
109
110impl From<Attribute> for TxResultBlockAttribute {
111 fn from(a: Attribute) -> Self {
112 Self {
113 key: a.key,
114 value: a.value,
115 }
116 }
117}
118#[derive(Deserialize, Clone, Serialize, Debug)]
119pub struct TxResultBlockEvent {
120 #[serde(rename = "type")]
121 pub s_type: String,
122 pub attributes: Vec<TxResultBlockAttribute>,
123}
124
125impl From<StringEvent> for TxResultBlockEvent {
126 fn from(event: StringEvent) -> Self {
127 Self {
128 s_type: event.r#type,
129 attributes: event
130 .attributes
131 .into_iter()
132 .map(TxResultBlockAttribute::from)
133 .collect(),
134 }
135 }
136}
137
138impl TxResultBlockEvent {
139 pub fn get_attribute(&self, key: &str) -> Vec<TxResultBlockAttribute> {
141 self.attributes
142 .iter()
143 .filter(|attr| attr.key == key)
144 .cloned()
145 .collect()
146 }
147 pub fn get_first_value(&self, key: &str) -> Option<String> {
149 self.get_attribute(key)
150 .first()
151 .map(|attr| attr.value.clone())
152 }
153}
154
155#[derive(Deserialize, Clone, Serialize, Debug)]
156pub struct TxResultBlockMsg {
157 pub msg_index: Option<usize>,
158 pub events: Vec<TxResultBlockEvent>,
160}
161
162impl From<AbciMessageLog> for TxResultBlockMsg {
163 fn from(msg: AbciMessageLog) -> Self {
164 Self {
165 msg_index: Some(msg.msg_index as usize),
166 events: msg
167 .events
168 .into_iter()
169 .map(TxResultBlockEvent::from)
170 .collect(),
171 }
172 }
173}
174
175pub fn parse_timestamp(s: String) -> Result<DateTime<Utc>, CosmScriptError> {
176 let len = s.len();
177 let slice_len = if s.contains('.') {
178 len.saturating_sub(4)
179 } else {
180 len
181 };
182
183 let sliced = &s[0..slice_len];
184
185 match NaiveDateTime::parse_from_str(sliced, FORMAT) {
186 Err(_e) => match NaiveDateTime::parse_from_str(&s, FORMAT_TZ_SUPPLIED) {
187 Err(_e2) => match NaiveDateTime::parse_from_str(sliced, FORMAT_SHORT_Z) {
188 Err(_e3) => match NaiveDateTime::parse_from_str(&s, FORMAT_SHORT_Z2) {
190 Err(_e4) => {
191 eprintln!("DateTime Fail {} {:#?}", s, _e4);
192 Err(CosmScriptError::StdErr(_e4.to_string()))
193 }
194 Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
195 },
196 Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
197 },
198 Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
199 },
200 Ok(dt) => Ok(Utc.from_utc_datetime(&dt)),
201 }
202}