use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::error::ClientError;
use crate::rest::RestClient;
#[derive(Debug)]
pub struct Explorer<'a> {
pub(crate) client: &'a RestClient,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Block {
pub height: u64,
pub hash: String,
pub parent_hash: String,
pub ts_ms: u64,
pub proposer: String,
pub tx_hashes: Vec<String>,
pub app_hash: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Transaction {
pub hash: String,
pub block_height: u64,
pub action_type: String,
pub signer: String,
pub nonce: u64,
pub status: String,
#[serde(default)]
pub error: Option<String>,
}
impl<'a> Explorer<'a> {
pub async fn block_by_height(&self, height: u64) -> Result<Block, ClientError> {
self.client
.post_json(
"/explorer",
&json!({ "type": "block_by_height", "height": height }),
)
.await
}
pub async fn tx_by_hash(&self, hash: &str) -> Result<Transaction, ClientError> {
self.client
.post_json("/explorer", &json!({ "type": "tx_by_hash", "hash": hash }))
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_round_trips() {
let b = Block {
height: 100,
hash: "0xdeadbeef".repeat(8),
parent_hash: "0xcafebabe".repeat(8),
ts_ms: 1_700_000_000_000,
proposer: "0x".to_string() + &"ab".repeat(20),
tx_hashes: vec!["0x01".into(), "0x02".into()],
app_hash: "0x".to_string() + &"00".repeat(32),
};
let j = serde_json::to_string(&b).unwrap();
let dec: Block = serde_json::from_str(&j).unwrap();
assert_eq!(b, dec);
}
#[test]
fn transaction_uses_snake_case_fields() {
let t = Transaction {
hash: "0x01".into(),
block_height: 100,
action_type: "submit_order".into(),
signer: "0xab".into(),
nonce: 1_700_000_000_000,
status: "accepted".into(),
error: None,
};
let j = serde_json::to_value(&t).unwrap();
for key in ["block_height", "action_type"] {
assert!(j.get(key).is_some(), "missing {key}");
}
for key in ["blockHeight", "actionType"] {
assert!(j.get(key).is_none(), "wire leak: {key}");
}
}
}