Skip to main content

trace_moe/
client.rs

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)]
8/// Low-level HTTP client wrapper for the trace.moe API.
9pub struct Client {
10    base_url: Url,
11    http: HttpClient,
12    default_headers: HeaderMap,
13}
14
15impl Client {
16    /// Create a new client with the given base URL.
17    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    /// Add a header that will be included on all requests.
31    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    /// Build a request against a relative `path` under `base_url`.
37    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    /// Execute a GET request and deserialize JSON.
43    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    /// Read the response body, map non-2xx to `ApiError::Http`, then parse JSON.
49    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}