ddapi_rs/api/
mod.rs

1use anyhow::{Context, Result};
2use moka::future::Cache;
3use reqwest::Client;
4use serde::de::DeserializeOwned;
5
6#[derive(Clone, Default)]
7pub struct DDApi {
8    client: Client,
9    cache: Option<Cache<String, String>>,
10}
11
12impl DDApi {
13    pub fn new() -> Self {
14        DDApi {
15            client: Client::new(),
16            cache: None,
17        }
18    }
19
20    pub fn new_with_client(client: Client) -> Self {
21        DDApi {
22            client,
23            cache: None,
24        }
25    }
26
27    #[cfg(feature = "cache")]
28    pub fn set_cache(&mut self, capacity: u64, time_to_live: u64) {
29        use std::time::Duration;
30
31        self.cache = Some(
32            Cache::builder()
33                .max_capacity(capacity)
34                .time_to_live(Duration::from_secs(time_to_live))
35                .build(),
36        );
37    }
38
39    async fn send_request(&self, uri: &str) -> Result<String> {
40        let response = self
41            .client
42            .get(uri)
43            .send()
44            .await
45            .context("Failed to send request")?
46            .error_for_status()
47            .context("Server returned error status")?;
48
49        let text = response
50            .text()
51            .await
52            .context("Failed to read response body")?;
53
54        if text.is_empty() {
55            anyhow::bail!("API returned empty response");
56        }
57
58        Ok(text)
59    }
60
61    pub async fn _generator<T>(&self, uri: &str, cache: bool) -> Result<T>
62    where
63        T: DeserializeOwned + Send + Sync + 'static,
64    {
65        if cache {
66            self._generator_cached(uri).await
67        } else {
68            self._generator_no_cache(uri).await
69        }
70    }
71
72    async fn _generator_cached<T>(&self, uri: &str) -> Result<T>
73    where
74        T: DeserializeOwned + Send + Sync + 'static,
75    {
76        let type_name = std::any::type_name::<T>();
77        let cache_key = format!("{}:{}", type_name, uri);
78
79        match &self.cache {
80            Some(cache) => {
81                if let Some(value) = cache.get(&cache_key).await {
82                    serde_json::from_str(&value).context("Failed to parse CACHE response")
83                } else {
84                    let response_text = self.send_request(uri).await?;
85                    cache.insert(cache_key, response_text.clone()).await;
86                    serde_json::from_str(&response_text).context("Failed to parse API response")
87                }
88            }
89            None => self._generator_no_cache(uri).await,
90        }
91    }
92
93    async fn _generator_no_cache<T>(&self, uri: &str) -> Result<T>
94    where
95        T: DeserializeOwned,
96    {
97        let response_text = self.send_request(uri).await?;
98        serde_json::from_str(&response_text).context("Failed to parse API response")
99    }
100}
101
102#[cfg(feature = "ddnet")]
103pub mod ddnet;
104
105#[cfg(feature = "ddstats")]
106pub mod ddstats;