agentlink_core/http/
mod.rs1use async_trait::async_trait;
7use serde::{de::DeserializeOwned, Serialize};
8
9use crate::error::SdkResult;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum HttpMethod {
14 Get,
15 Post,
16 Put,
17 Delete,
18 Patch,
19}
20
21#[derive(Debug, Clone)]
23pub struct HttpRequest {
24 pub method: HttpMethod,
25 pub url: String,
26 pub headers: Vec<(String, String)>,
27 pub body: Option<String>,
28}
29
30impl HttpRequest {
31 pub fn new(method: HttpMethod, url: impl Into<String>) -> Self {
32 Self {
33 method,
34 url: url.into(),
35 headers: Vec::new(),
36 body: None,
37 }
38 }
39
40 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
41 self.headers.push((key.into(), value.into()));
42 self
43 }
44
45 pub fn json_body(mut self, body: impl Serialize) -> SdkResult<Self> {
46 self.body = Some(serde_json::to_string(&body)?);
47 self.headers.push(("Content-Type".to_string(), "application/json".to_string()));
48 Ok(self)
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct HttpResponse {
55 pub status: u16,
56 pub headers: Vec<(String, String)>,
57 pub body: String,
58}
59
60impl HttpResponse {
61 pub fn is_success(&self) -> bool {
62 self.status >= 200 && self.status < 300
63 }
64
65 pub fn json<T: DeserializeOwned>(&self) -> SdkResult<T> {
66 Ok(serde_json::from_str(&self.body)?)
67 }
68}
69
70#[async_trait]
74#[cfg(not(target_arch = "wasm32"))]
75pub trait HttpClient: Send + Sync {
76 async fn request(&self, request: HttpRequest) -> SdkResult<HttpResponse>;
78
79 fn set_auth_token(&mut self, token: String);
81
82 fn auth_token(&self) -> Option<String>;
84}
85
86#[async_trait(?Send)]
90#[cfg(target_arch = "wasm32")]
91pub trait HttpClient {
92 async fn request(&self, request: HttpRequest) -> SdkResult<HttpResponse>;
94
95 fn set_auth_token(&mut self, token: String);
97
98 fn auth_token(&self) -> Option<String>;
100}
101
102#[cfg(not(target_arch = "wasm32"))]
106pub trait HttpClientFactory: Send + Sync {
107 fn create_client(&self, base_url: &str) -> Box<dyn HttpClient>;
108}
109
110#[cfg(target_arch = "wasm32")]
112pub trait HttpClientFactory {
113 fn create_client(&self, base_url: &str) -> Box<dyn HttpClient>;
114}
115
116#[async_trait]
118#[cfg(not(target_arch = "wasm32"))]
119pub trait HttpClientExt: HttpClient {
120 async fn get<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
121 let request = HttpRequest::new(HttpMethod::Get, path.to_string());
122 let response = self.request(request).await?;
123 if response.is_success() {
124 response.json()
125 } else {
126 Err(crate::error::SdkError::Http(format!(
127 "HTTP {}: {}",
128 response.status, response.body
129 )))
130 }
131 }
132
133 async fn post<T: DeserializeOwned, B: Serialize + Send + Sync>(
134 &self,
135 path: &str,
136 body: &B,
137 ) -> SdkResult<T> {
138 let request = HttpRequest::new(HttpMethod::Post, path.to_string())
139 .json_body(body)?;
140 let response = self.request(request).await?;
141 if response.is_success() {
142 response.json()
143 } else {
144 Err(crate::error::SdkError::Http(format!(
145 "HTTP {}: {}",
146 response.status, response.body
147 )))
148 }
149 }
150
151 async fn put<T: DeserializeOwned, B: Serialize + Send + Sync>(
152 &self,
153 path: &str,
154 body: &B,
155 ) -> SdkResult<T> {
156 let request = HttpRequest::new(HttpMethod::Put, path.to_string())
157 .json_body(body)?;
158 let response = self.request(request).await?;
159 if response.is_success() {
160 response.json()
161 } else {
162 Err(crate::error::SdkError::Http(format!(
163 "HTTP {}: {}",
164 response.status, response.body
165 )))
166 }
167 }
168
169 async fn delete<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
170 let request = HttpRequest::new(HttpMethod::Delete, path.to_string());
171 let response = self.request(request).await?;
172 if response.is_success() {
173 response.json()
174 } else {
175 Err(crate::error::SdkError::Http(format!(
176 "HTTP {}: {}",
177 response.status, response.body
178 )))
179 }
180 }
181}
182
183#[async_trait]
184#[cfg(not(target_arch = "wasm32"))]
185impl<T: HttpClient> HttpClientExt for T {}
186
187#[async_trait(?Send)]
189#[cfg(target_arch = "wasm32")]
190pub trait HttpClientExt: HttpClient {
191 async fn get<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
192 let request = HttpRequest::new(HttpMethod::Get, path.to_string());
193 let response = self.request(request).await?;
194 if response.is_success() {
195 response.json()
196 } else {
197 Err(crate::error::SdkError::Http(format!(
198 "HTTP {}: {}",
199 response.status, response.body
200 )))
201 }
202 }
203
204 async fn post<T: DeserializeOwned, B: Serialize>(
205 &self,
206 path: &str,
207 body: &B,
208 ) -> SdkResult<T> {
209 let request = HttpRequest::new(HttpMethod::Post, path.to_string())
210 .json_body(body)?;
211 let response = self.request(request).await?;
212 if response.is_success() {
213 response.json()
214 } else {
215 Err(crate::error::SdkError::Http(format!(
216 "HTTP {}: {}",
217 response.status, response.body
218 )))
219 }
220 }
221
222 async fn put<T: DeserializeOwned, B: Serialize>(
223 &self,
224 path: &str,
225 body: &B,
226 ) -> SdkResult<T> {
227 let request = HttpRequest::new(HttpMethod::Put, path.to_string())
228 .json_body(body)?;
229 let response = self.request(request).await?;
230 if response.is_success() {
231 response.json()
232 } else {
233 Err(crate::error::SdkError::Http(format!(
234 "HTTP {}: {}",
235 response.status, response.body
236 )))
237 }
238 }
239
240 async fn delete<T: DeserializeOwned>(&self, path: &str) -> SdkResult<T> {
241 let request = HttpRequest::new(HttpMethod::Delete, path.to_string());
242 let response = self.request(request).await?;
243 if response.is_success() {
244 response.json()
245 } else {
246 Err(crate::error::SdkError::Http(format!(
247 "HTTP {}: {}",
248 response.status, response.body
249 )))
250 }
251 }
252}
253
254#[async_trait(?Send)]
255#[cfg(target_arch = "wasm32")]
256impl<T: HttpClient> HttpClientExt for T {}