1use bitcoin::{OutPoint, Txid};
2use reqwest::{header::HeaderMap, Client as AsyncReqwestClient};
3use std::{collections::HashMap, str::FromStr};
4use titan_types_api::*;
5use titan_types_core::*;
6
7use crate::Error;
8
9use super::TitanApiAsync;
10
11#[derive(Clone)]
12pub struct AsyncClient {
13 http_client: AsyncReqwestClient,
15 base_url: String,
17}
18
19impl AsyncClient {
20 pub fn new(base_url: &str) -> Self {
22 Self {
23 http_client: AsyncReqwestClient::new(),
24 base_url: base_url.trim_end_matches('/').to_string(),
25 }
26 }
27
28 async fn call_text(&self, path: &str) -> Result<String, Error> {
29 let url = format!("{}{}", self.base_url, path);
30 let response = self.http_client.get(&url).send().await?;
31 if response.status().is_success() {
32 Ok(response.text().await?)
33 } else {
34 Err(Error::TitanError(response.status(), response.text().await?))
35 }
36 }
37
38 async fn call_bytes(&self, path: &str) -> Result<Vec<u8>, Error> {
39 let url = format!("{}{}", self.base_url, path);
40 let response = self.http_client.get(&url).send().await?;
41 if response.status().is_success() {
42 Ok(response.bytes().await?.to_vec())
43 } else {
44 Err(Error::TitanError(response.status(), response.text().await?))
45 }
46 }
47
48 async fn post_text(&self, path: &str, body: String) -> Result<String, Error> {
49 let url = format!("{}{}", self.base_url, path);
50 let response = self.http_client.post(&url).body(body).send().await?;
51 if response.status().is_success() {
52 Ok(response.text().await?)
53 } else {
54 Err(Error::TitanError(response.status(), response.text().await?))
55 }
56 }
57
58 async fn delete(&self, path: &str) -> Result<(), Error> {
59 let url = format!("{}{}", self.base_url, path);
60 let response = self.http_client.delete(&url).send().await?;
61 if response.status().is_success() {
62 Ok(())
63 } else {
64 Err(Error::TitanError(response.status(), response.text().await?))
65 }
66 }
67}
68
69#[async_trait::async_trait]
70impl TitanApiAsync for AsyncClient {
71 async fn get_status(&self) -> Result<Status, Error> {
72 let text = self.call_text("/status").await?;
73 serde_json::from_str(&text).map_err(Error::from)
74 }
75
76 async fn get_tip(&self) -> Result<BlockTip, Error> {
77 let text = self.call_text("/tip").await?;
78 serde_json::from_str(&text).map_err(Error::from)
79 }
80
81 async fn get_block(&self, query: &query::Block) -> Result<Block, Error> {
82 let text = self.call_text(&format!("/block/{}", query)).await?;
83 serde_json::from_str(&text).map_err(Error::from)
84 }
85
86 async fn get_block_hash_by_height(&self, height: u64) -> Result<String, Error> {
87 self.call_text(&format!("/block/{}/hash", height)).await
88 }
89
90 async fn get_block_txids(&self, query: &query::Block) -> Result<Vec<String>, Error> {
91 let text = self.call_text(&format!("/block/{}/txids", query)).await?;
92 serde_json::from_str(&text).map_err(Error::from)
93 }
94
95 async fn get_address(&self, address: &str) -> Result<AddressData, Error> {
96 let text = self.call_text(&format!("/address/{}", address)).await?;
97 serde_json::from_str(&text).map_err(Error::from)
98 }
99
100 async fn get_transaction(&self, txid: &Txid) -> Result<Transaction, Error> {
101 let text = self.call_text(&format!("/tx/{}", txid)).await?;
102 serde_json::from_str(&text).map_err(Error::from)
103 }
104
105 async fn get_transaction_raw(&self, txid: &Txid) -> Result<Vec<u8>, Error> {
106 self.call_bytes(&format!("/tx/{}/raw", txid)).await
107 }
108
109 async fn get_transaction_hex(&self, txid: &Txid) -> Result<String, Error> {
110 self.call_text(&format!("/tx/{}/hex", txid)).await
111 }
112
113 async fn get_transaction_status(&self, txid: &Txid) -> Result<TransactionStatus, Error> {
114 let text = self.call_text(&format!("/tx/{}/status", txid)).await?;
115 serde_json::from_str(&text).map_err(Error::from)
116 }
117
118 async fn send_transaction(&self, tx_hex: String) -> Result<Txid, Error> {
119 let text = self.post_text("/tx/broadcast", tx_hex).await?;
120 Txid::from_str(&text).map_err(Error::from)
121 }
122
123 async fn get_output(&self, outpoint: &OutPoint) -> Result<TxOut, Error> {
124 let text = self.call_text(&format!("/output/{}", outpoint)).await?;
125 serde_json::from_str(&text).map_err(Error::from)
126 }
127
128 async fn get_inscription(
129 &self,
130 inscription_id: &InscriptionId,
131 ) -> Result<(HeaderMap, Vec<u8>), Error> {
132 let url = format!("{}/inscription/{}", self.base_url, inscription_id);
133 let resp = self.http_client.get(&url).send().await?;
134 let status = resp.status();
135 if !status.is_success() {
136 let body = resp.text().await.unwrap_or_default();
137 return Err(Error::TitanError(status, body));
138 }
139 let headers = resp.headers().clone();
140 let bytes = resp.bytes().await?.to_vec();
141 Ok((headers, bytes))
142 }
143
144 async fn get_runes(
145 &self,
146 pagination: Option<Pagination>,
147 ) -> Result<PaginationResponse<RuneResponse>, Error> {
148 let mut path = "/runes".to_string();
149 if let Some(p) = pagination {
150 path = format!("{}?skip={}&limit={}", path, p.skip, p.limit);
151 }
152 let text = self.call_text(&path).await?;
153 serde_json::from_str(&text).map_err(Error::from)
154 }
155
156 async fn get_rune(&self, rune: &query::Rune) -> Result<RuneResponse, Error> {
157 let text = self.call_text(&format!("/rune/{}", rune)).await?;
158 serde_json::from_str(&text).map_err(Error::from)
159 }
160
161 async fn get_rune_transactions(
162 &self,
163 rune: &query::Rune,
164 pagination: Option<Pagination>,
165 ) -> Result<PaginationResponse<Txid>, Error> {
166 let mut path = format!("/rune/{}/transactions", rune);
167 if let Some(p) = pagination {
168 path = format!("{}?skip={}&limit={}", path, p.skip, p.limit);
169 }
170 let text = self.call_text(&path).await?;
171 serde_json::from_str(&text).map_err(Error::from)
172 }
173
174 async fn get_mempool_txids(&self) -> Result<Vec<Txid>, Error> {
175 let text = self.call_text("/mempool/txids").await?;
176 serde_json::from_str(&text).map_err(Error::from)
177 }
178
179 async fn get_mempool_entry(&self, txid: &Txid) -> Result<MempoolEntry, Error> {
180 let text = self.call_text(&format!("/mempool/entry/{}", txid)).await?;
181 serde_json::from_str(&text).map_err(Error::from)
182 }
183
184 async fn get_mempool_entries(
185 &self,
186 txids: &[Txid],
187 ) -> Result<HashMap<Txid, Option<MempoolEntry>>, Error> {
188 let text = self
189 .post_text("/mempool/entries", serde_json::to_string(txids)?)
190 .await?;
191 serde_json::from_str(&text).map_err(Error::from)
192 }
193
194 async fn get_all_mempool_entries(&self) -> Result<HashMap<Txid, MempoolEntry>, Error> {
195 let text = self.call_text("/mempool/entries/all").await?;
196 serde_json::from_str(&text).map_err(Error::from)
197 }
198
199 async fn get_mempool_entries_with_ancestors(
200 &self,
201 txids: &[Txid],
202 ) -> Result<HashMap<Txid, MempoolEntry>, Error> {
203 let url = format!("{}/mempool/entries/ancestors", self.base_url);
204 let response = self.http_client.post(&url).json(txids).send().await?;
205
206 if response.status().is_success() {
207 let text = response.text().await?;
208 serde_json::from_str(&text).map_err(Error::from)
209 } else {
210 Err(Error::TitanError(response.status(), response.text().await?))
211 }
212 }
213
214 async fn get_subscription(&self, id: &str) -> Result<Subscription, Error> {
215 let text = self.call_text(&format!("/subscription/{}", id)).await?;
216 serde_json::from_str(&text).map_err(Error::from)
217 }
218
219 async fn list_subscriptions(&self) -> Result<Vec<Subscription>, Error> {
220 let text = self.call_text("/subscriptions").await?;
221 serde_json::from_str(&text).map_err(Error::from)
222 }
223
224 async fn add_subscription(&self, subscription: &Subscription) -> Result<Subscription, Error> {
225 let text = self
226 .post_text("/subscription", serde_json::to_string(subscription)?)
227 .await?;
228 serde_json::from_str(&text).map_err(Error::from)
229 }
230
231 async fn delete_subscription(&self, id: &str) -> Result<(), Error> {
232 self.delete(&format!("/subscription/{}", id)).await
233 }
234}