cloudflare_kv_proxy/
lib.rs

1mod types;
2use reqwest::{header, Response};
3use serde::Serialize;
4use std::time::Duration;
5use types::ApiResult;
6pub use types::{Error, NotFoundMapping, Result};
7
8macro_rules! execute {
9    ($send: expr) => {
10        $send
11            .send()
12            .await
13            .and_then(crate::Response::error_for_status)?
14            .json::<crate::ApiResult<_>>()
15            .await?
16            .into()
17    };
18}
19
20#[cfg(feature = "cache")]
21mod cache;
22
23/// KV Client.
24#[derive(Debug)]
25pub struct Client {
26    endpoint: String,
27    client: reqwest::Client,
28
29    #[cfg(feature = "cache")]
30    cache: cache::Cache,
31}
32
33/// Error when creating KV Client.
34#[derive(thiserror::Error, Debug)]
35pub enum ClientError {
36    #[error("token format error {0}")]
37    Token(#[from] reqwest::header::InvalidHeaderValue),
38    #[error("client build error {0}")]
39    Client(#[from] reqwest::Error),
40}
41
42impl Client {
43    /// Create client with endpoint and token.
44    /// If cache is enabled, you may set cache size and ttl.
45    pub fn new<T: Into<String>, E: Into<String>>(
46        endpoint: E,
47        token: T,
48        timeout: Duration,
49        #[cfg(feature = "cache")] cache_size: usize,
50        #[cfg(feature = "cache")] expire_ttl: std::time::Duration,
51    ) -> std::result::Result<Self, ClientError> {
52        // normalize endpoint
53        let mut endpoint: String = endpoint.into();
54        if !endpoint.ends_with('/') {
55            endpoint.push('/');
56        }
57        let token = token.into();
58        let mut headers = header::HeaderMap::new();
59        headers.insert("Authorization", header::HeaderValue::from_str(&token)?);
60        Ok(Self {
61            endpoint,
62            client: reqwest::Client::builder()
63                .default_headers(headers)
64                .timeout(timeout)
65                .tcp_nodelay(true)
66                .build()?,
67            #[cfg(feature = "cache")]
68            cache: cache::new_cache(cache_size, expire_ttl.into()),
69        })
70    }
71
72    /// Get value of a key.
73    #[cfg(not(feature = "cache"))]
74    pub async fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T> {
75        execute!(self.client.get(format!("{}{}", self.endpoint, key)))
76    }
77
78    /// Set a key value pair.
79    pub async fn put<T: Serialize + ?Sized>(&self, key: &str, value: &T) -> Result<()> {
80        let r: Result<()> = execute!(self
81            .client
82            .put(format!("{}{}", self.endpoint, key))
83            .json(value));
84        #[cfg(feature = "cache")]
85        if r.is_ok() {
86            self.set_cache(key, value);
87        }
88        r
89    }
90
91    /// Set a key value pair with ttl.
92    pub async fn put_with_ttl<T: Serialize + ?Sized>(
93        &self,
94        key: &str,
95        value: &T,
96        ttl: Duration,
97    ) -> Result<()> {
98        let r: Result<()> = execute!(self
99            .client
100            .put(format!("{}{}", self.endpoint, key))
101            .header("ttl", ttl.as_secs())
102            .json(value));
103        #[cfg(feature = "cache")]
104        if r.is_ok() {
105            self.set_cache(key, value);
106        }
107        r
108    }
109
110    /// Delete a key value pair.
111    pub async fn delete(&self, key: &str) -> Result<()> {
112        let r: Result<()> = execute!(self.client.delete(format!("{}{}", self.endpoint, key)));
113        #[cfg(feature = "cache")]
114        if r.is_ok() {
115            self.prune_cached(key);
116        }
117        r
118    }
119}