1use std::time::Duration;
8
9use reqwest::{Body, Response};
10use rkyv::Archive;
11
12use crate::Error;
13
14const REQUIRED_RUSK_VERSION: &str = "1.0.0-rc.0";
16
17pub const CONTRACTS_TARGET: &str = "contracts";
19
20#[derive(Clone)]
21pub struct RuesHttpClient {
23 client: reqwest::Client,
24 uri: String,
25}
26
27impl RuesHttpClient {
28 pub fn new<S: Into<String>>(uri: S) -> Result<Self, Error> {
30 let client = reqwest::ClientBuilder::new()
31 .connect_timeout(Duration::from_secs(30))
32 .build();
33
34 match client {
35 Ok(client) => Ok(Self {
36 uri: uri.into(),
37 client,
38 }),
39 Err(_) => Err(Error::HttpClient),
40 }
41 }
42
43 pub async fn contract_query<I, C, const N: usize>(
45 &self,
46 contract: C,
47 method: &str,
48 value: &I,
49 ) -> Result<Vec<u8>, Error>
50 where
51 I: Archive,
52 I: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<N>>,
53 C: Into<Option<&'static str>>,
54 {
55 let data = rkyv::to_bytes(value).map_err(|_| Error::Rkyv)?.to_vec();
56
57 let response = self
58 .call_raw(CONTRACTS_TARGET, contract.into(), method, &data, false)
59 .await?;
60
61 Ok(response.bytes().await?.to_vec())
62 }
63
64 pub async fn check_connection(&self) -> Result<(), reqwest::Error> {
66 self.client.post(&self.uri).send().await?;
67
68 Ok(())
69 }
70
71 pub async fn call<E>(
75 &self,
76 target: &str,
77 entity: E,
78 topic: &str,
79 request: &[u8],
80 ) -> Result<Vec<u8>, Error>
81 where
82 E: Into<Option<&'static str>>,
83 {
84 let response =
85 self.call_raw(target, entity, topic, request, false).await?;
86 let data = response.bytes().await?;
87 Ok(data.to_vec())
88 }
89
90 pub async fn call_raw<E>(
92 &self,
93 target: &str,
94 entity: E,
95 topic: &str,
96 data: &[u8],
97 feed: bool,
98 ) -> Result<Response, Error>
99 where
100 E: Into<Option<&'static str>>,
101 {
102 let uri = &self.uri;
103 let entity = entity.into().map(|e| format!(":{e}")).unwrap_or_default();
104
105 let rues_prefix = if uri.ends_with('/') { "on" } else { "/on" };
106 let mut request = self
107 .client
108 .post(format!("{uri}{rues_prefix}/{target}{entity}/{topic}"))
109 .body(Body::from(data.to_vec()))
110 .header("Content-Type", "application/octet-stream")
111 .header("rusk-version", REQUIRED_RUSK_VERSION);
112
113 if feed {
114 request = request.header("Rusk-Feeder", "1");
115 }
116 let response = request.send().await?;
117
118 let status = response.status();
119 if status.is_client_error() || status.is_server_error() {
120 let error = &response.bytes().await?;
121
122 let error = String::from_utf8(error.to_vec())
123 .unwrap_or("unparsable error".into());
124
125 let msg = format!("{status}: {error}");
126
127 Err(Error::Rusk(msg))
128 } else {
129 Ok(response)
130 }
131 }
132}