Skip to main content

hal_sdk/
client.rs

1use crate::error::HalError;
2use crate::query::SearchQuery;
3use crate::response::SearchResponse;
4
5/// The default HAL search endpoint.
6pub const DEFAULT_BASE_URL: &str = "https://api.archives-ouvertes.fr/search/";
7
8/// An asynchronous client for the HAL search API.
9///
10/// The client is cheap to clone and holds a reusable `reqwest` client. It works
11/// on native targets and on `wasm32` (where it uses the browser `fetch` API).
12///
13/// ```no_run
14/// use hal_sdk::HalClient;
15///
16/// # async fn run() -> Result<(), hal_sdk::HalError> {
17/// let client = HalClient::new();
18/// let results = client.basic_search("rust").await?;
19/// println!("{} results", results.num_found());
20/// # Ok(())
21/// # }
22/// ```
23#[derive(Clone, Debug)]
24pub struct HalClient {
25    base_url: String,
26    http: reqwest::Client,
27}
28
29impl Default for HalClient {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl HalClient {
36    /// Create a client pointing at the public HAL search endpoint.
37    pub fn new() -> Self {
38        HalClient {
39            base_url: DEFAULT_BASE_URL.to_owned(),
40            http: reqwest::Client::new(),
41        }
42    }
43
44    /// Create a client pointing at a custom base URL (useful for tests or mirrors).
45    pub fn with_base_url(base_url: impl Into<String>) -> Self {
46        HalClient {
47            base_url: base_url.into(),
48            http: reqwest::Client::new(),
49        }
50    }
51
52    /// The base URL this client targets.
53    pub fn base_url(&self) -> &str {
54        &self.base_url
55    }
56
57    /// Run a basic search over all fields, matching the original chapter-16 behaviour.
58    pub async fn basic_search(&self, query: &str) -> Result<SearchResponse, HalError> {
59        self.search(&SearchQuery::basic(query)).await
60    }
61
62    /// Run an arbitrary [`SearchQuery`].
63    pub async fn search(&self, query: &SearchQuery) -> Result<SearchResponse, HalError> {
64        let response = self
65            .http
66            .get(&self.base_url)
67            .query(&query.to_params())
68            .send()
69            .await?;
70
71        let status = response.status();
72        let body = response.text().await?;
73
74        if !status.is_success() {
75            let mut body = body;
76            body.truncate(500);
77            return Err(HalError::Api {
78                status: status.as_u16(),
79                body,
80            });
81        }
82
83        Ok(serde_json::from_str(&body)?)
84    }
85}