cosm_script/data_structures/
tx_resp.rs

1use 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 tx: serde_json::Value,
23    pub timestamp: DateTime<Utc>,
24    pub events: Vec<Event>,
25}
26impl CosmTxResponse {
27    /// find a attribute's value from TX logs.
28    /// returns: msg_index and value
29
30    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            //      log::info!("logs{:?}", events);
43            let events_filtered = events
44                .iter()
45                .filter(|event| event.s_type == event_type)
46                .collect::<Vec<_>>();
47            //      log::info!("Filtered Events {:?}", events_filtered);
48            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    /// get the list of event types from a TX record
66    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            //      log::info!("logs{:?}", events);
72            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    /// get all key/values from the event that have the key 'key'
140    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    /// return the first value of the first attribute that has the key 'key'
148    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 log: Option<String>,
159    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                // block 6877827 has this
189                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}