1use std::time::Duration;
2
3use reqwest::{header::HeaderMap, Client as HttpClient, Method, RequestBuilder, Response, Url};
4
5use crate::error::{ApiError, Result};
6
7#[derive(Clone, Debug)]
8pub struct Client {
10 base_url: Url,
11 http: HttpClient,
12 default_headers: HeaderMap,
13}
14
15impl Client {
16 pub fn new(base_url: &str) -> Result<Self> {
18 let http = HttpClient::builder()
19 .timeout(Duration::from_secs(30))
20 .user_agent("trace-moe-api-wrapper/0.1")
21 .build()?;
22
23 Ok(Self {
24 base_url: Url::parse(base_url)?,
25 http,
26 default_headers: HeaderMap::new(),
27 })
28 }
29
30 pub fn with_default_header(mut self, key: reqwest::header::HeaderName, value: reqwest::header::HeaderValue) -> Self {
32 self.default_headers.insert(key, value);
33 self
34 }
35
36 pub(crate) fn request(&self, method: Method, path: &str) -> Result<RequestBuilder> {
38 let url = self.base_url.join(path)?;
39 Ok(self.http.request(method, url).headers(self.default_headers.clone()))
40 }
41
42 pub async fn get_json<T: serde::de::DeserializeOwned>(&self, path: impl AsRef<str>) -> Result<T> {
44 let resp = self.request(Method::GET, path.as_ref())?.send().await?;
45 Self::parse_json(resp).await
46 }
47
48 pub(crate) async fn parse_json<T: serde::de::DeserializeOwned>(resp: Response) -> Result<T> {
50 let status = resp.status();
51 let text = resp.text().await?;
52 if !status.is_success() {
53 return Err(ApiError::Http {
54 status,
55 body: text,
56 });
57 }
58 let value = serde_json::from_str::<T>(&text)?;
59 Ok(value)
60 }
61}