haystack_client/transport/
http.rs1use reqwest::Client;
2
3use crate::error::ClientError;
4use crate::transport::Transport;
5use haystack_core::codecs::codec_for;
6use haystack_core::data::HGrid;
7use haystack_core::kinds::Kind;
8
9const GET_OPS: &[&str] = &["about", "ops", "formats"];
11
12pub struct HttpTransport {
17 client: Client,
18 base_url: String,
19 auth_token: String,
20 format: String,
21}
22
23impl HttpTransport {
24 pub fn new(base_url: &str, auth_token: String) -> Self {
29 Self {
30 client: Client::new(),
31 base_url: base_url.trim_end_matches('/').to_string(),
32 auth_token,
33 format: "text/zinc".to_string(),
34 }
35 }
36
37 pub fn with_format(base_url: &str, auth_token: String, format: &str) -> Self {
39 Self {
40 client: Client::new(),
41 base_url: base_url.trim_end_matches('/').to_string(),
42 auth_token,
43 format: format.to_string(),
44 }
45 }
46}
47
48impl Transport for HttpTransport {
49 async fn call(&self, op: &str, req: &HGrid) -> Result<HGrid, ClientError> {
50 let url = format!("{}/{}", self.base_url, op);
51 let codec = codec_for(&self.format)
52 .ok_or_else(|| ClientError::Codec(format!("unsupported format: {}", self.format)))?;
53
54 let response = if GET_OPS.contains(&op) {
55 self.client
57 .get(&url)
58 .header(
59 "Authorization",
60 format!("BEARER authToken={}", self.auth_token),
61 )
62 .header("Accept", codec.mime_type())
63 .send()
64 .await
65 .map_err(|e| ClientError::Transport(e.to_string()))?
66 } else {
67 let body = codec
69 .encode_grid(req)
70 .map_err(|e| ClientError::Codec(e.to_string()))?;
71
72 self.client
73 .post(&url)
74 .header(
75 "Authorization",
76 format!("BEARER authToken={}", self.auth_token),
77 )
78 .header("Content-Type", codec.mime_type())
79 .header("Accept", codec.mime_type())
80 .body(body)
81 .send()
82 .await
83 .map_err(|e| ClientError::Transport(e.to_string()))?
84 };
85
86 let status = response.status();
87 let resp_body = response
88 .text()
89 .await
90 .map_err(|e| ClientError::Transport(e.to_string()))?;
91
92 if !status.is_success() {
93 return Err(ClientError::ServerError(format!(
94 "HTTP {} — {}",
95 status, resp_body
96 )));
97 }
98
99 let grid = codec
101 .decode_grid(&resp_body)
102 .map_err(|e| ClientError::Codec(e.to_string()))?;
103
104 if grid.is_err() {
106 let dis = grid
107 .meta
108 .get("dis")
109 .and_then(|k| {
110 if let Kind::Str(s) = k {
111 Some(s.as_str())
112 } else {
113 None
114 }
115 })
116 .unwrap_or("unknown server error");
117 return Err(ClientError::ServerError(dis.to_string()));
118 }
119
120 Ok(grid)
121 }
122
123 async fn close(&self) -> Result<(), ClientError> {
124 Ok(())
126 }
127}