1use crate::error::{Error, Result};
4use crate::query::DEFAULT_USER_AGENT;
5use serde_json::Value;
6
7#[derive(Debug, Clone)]
9pub struct RestClient {
10 base_url: String,
11 http: reqwest::Client,
12}
13
14impl RestClient {
15 pub fn new(base_url: impl Into<String>) -> Self {
17 Self::with_client(base_url, reqwest::Client::new())
18 }
19
20 pub fn with_client(base_url: impl Into<String>, http: reqwest::Client) -> Self {
23 let base = base_url.into();
24 Self {
25 base_url: base.trim_end_matches('/').to_string(),
26 http,
27 }
28 }
29
30 pub fn base_url(&self) -> &str {
32 &self.base_url
33 }
34
35 fn join(&self, path: &str) -> String {
36 format!("{}/{}", self.base_url, path.trim_start_matches('/'))
37 }
38
39 pub async fn get(&self, path: &str, query: &[(&str, &str)]) -> Result<Value> {
42 let url = self.join(path);
43 let mut req = self
44 .http
45 .get(&url)
46 .header("Accept", "application/json")
47 .header("User-Agent", DEFAULT_USER_AGENT);
48 let filtered: Vec<(&str, &str)> = query
49 .iter()
50 .copied()
51 .filter(|(_, v)| !v.is_empty())
52 .collect();
53 if !filtered.is_empty() {
54 req = req.query(&filtered);
55 }
56 let resp = req.send().await?;
57 let status = resp.status();
58 let body = resp.text().await?;
59 if !status.is_success() {
60 return Err(Error::Http {
61 status: status.as_u16(),
62 url,
63 body,
64 });
65 }
66 serde_json::from_str(&body).map_err(|e| Error::InvalidResponse(e.to_string()))
67 }
68
69 pub async fn get_all_balances(&self, address: &str) -> Result<Value> {
71 self.get(&format!("/cosmos/bank/v1beta1/balances/{address}"), &[])
72 .await
73 }
74
75 pub async fn get_ai_stats(&self) -> Result<Value> {
77 self.get("/qorechain/ai/v1/stats", &[]).await
78 }
79
80 pub async fn get_fee_estimate(&self, urgency: &str) -> Result<Value> {
83 self.get("/qorechain/ai/v1/fee-estimate", &[("urgency", urgency)])
84 .await
85 }
86
87 pub async fn get_bridge_chains(&self) -> Result<Value> {
89 self.get("/qorechain/bridge/v1/chains", &[]).await
90 }
91
92 pub async fn get_pqc_account(&self, address: &str) -> Result<Value> {
94 self.get(&format!("/qorechain/pqc/v1/accounts/{address}"), &[])
95 .await
96 }
97
98 pub async fn get_reputation(&self, validator_address: &str) -> Result<Value> {
100 self.get(
101 &format!("/qorechain/reputation/v1/validators/{validator_address}"),
102 &[],
103 )
104 .await
105 }
106
107 pub async fn get_burn_stats(&self) -> Result<Value> {
109 self.get("/qorechain/burn/v1/stats", &[]).await
110 }
111
112 pub async fn get_xqore_position(&self, address: &str) -> Result<Value> {
114 self.get(&format!("/qorechain/xqore/v1/position/{address}"), &[])
115 .await
116 }
117
118 pub async fn get_inflation_rate(&self) -> Result<Value> {
120 self.get("/qorechain/inflation/v1/rate", &[]).await
121 }
122}