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;