worker_kv/
builder.rs

1use js_sys::{ArrayBuffer, Function, Object, Promise, Uint8Array};
2use serde::{de::DeserializeOwned, Serialize};
3use serde_json::Value;
4use serde_wasm_bindgen::Serializer;
5use wasm_bindgen::{JsCast, JsValue};
6use wasm_bindgen_futures::JsFuture;
7
8use crate::{KvError, ListResponse};
9
10/// A builder to configure put requests.
11#[derive(Debug, Clone)]
12#[must_use = "PutOptionsBuilder does nothing until you 'execute' it"]
13pub struct PutOptionsBuilder {
14    pub(crate) this: Object,
15    pub(crate) put_function: Function,
16    pub(crate) name: JsValue,
17    pub(crate) value: JsValue,
18    pub(crate) expiration: Option<u64>,
19    pub(crate) expiration_ttl: Option<u64>,
20    pub(crate) metadata: Option<Value>,
21}
22
23#[derive(Serialize)]
24struct PutOptions {
25    #[serde(skip_serializing_if = "Option::is_none")]
26    expiration: Option<u64>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    #[serde(rename = "expirationTtl")]
29    expiration_ttl: Option<u64>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    metadata: Option<Value>,
32}
33
34impl PutOptionsBuilder {
35    /// When (expressed as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time)) the key
36    /// value pair will expire in the store.
37    pub fn expiration(mut self, expiration: u64) -> Self {
38        self.expiration = Some(expiration);
39        self
40    }
41    /// How many seconds until the key value pair will expire.
42    pub fn expiration_ttl(mut self, expiration_ttl: u64) -> Self {
43        self.expiration_ttl = Some(expiration_ttl);
44        self
45    }
46    /// Metadata to be stored with the key value pair.
47    pub fn metadata<T: Serialize>(mut self, metadata: T) -> Result<Self, KvError> {
48        self.metadata = Some(serde_json::to_value(metadata)?);
49        Ok(self)
50    }
51    /// Puts the value in the kv store.
52    pub async fn execute(self) -> Result<(), KvError> {
53        let ser = Serializer::json_compatible();
54        let options_object = PutOptions {
55            expiration: self.expiration,
56            expiration_ttl: self.expiration_ttl,
57            metadata: self.metadata,
58        }
59        .serialize(&ser)
60        .map_err(JsValue::from)?;
61
62        let promise: Promise = self
63            .put_function
64            .call3(&self.this, &self.name, &self.value, &options_object)?
65            .into();
66        JsFuture::from(promise)
67            .await
68            .map(|_| ())
69            .map_err(KvError::from)
70    }
71}
72
73/// A builder to configure list requests.
74#[derive(Debug, Clone)]
75#[must_use = "ListOptionsBuilder does nothing until you 'execute' it"]
76pub struct ListOptionsBuilder {
77    pub(crate) this: Object,
78    pub(crate) list_function: Function,
79    pub(crate) limit: Option<u64>,
80    pub(crate) cursor: Option<String>,
81    pub(crate) prefix: Option<String>,
82}
83
84#[derive(Serialize)]
85struct ListOptions {
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub(crate) limit: Option<u64>,
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub(crate) cursor: Option<String>,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub(crate) prefix: Option<String>,
92}
93
94impl ListOptionsBuilder {
95    /// The maximum number of keys returned. The default is 1000, which is the maximum. It is
96    /// unlikely that you will want to change this default, but it is included for completeness.
97    pub fn limit(mut self, limit: u64) -> Self {
98        self.limit = Some(limit);
99        self
100    }
101    /// A string returned by a previous response used to paginate the keys in the store.
102    pub fn cursor(mut self, cursor: String) -> Self {
103        self.cursor = Some(cursor);
104        self
105    }
106    /// A prefix that all keys must start with for them to be included in the response.
107    pub fn prefix(mut self, prefix: String) -> Self {
108        self.prefix = Some(prefix);
109        self
110    }
111    /// Lists the key value pairs in the kv store.
112    pub async fn execute(self) -> Result<ListResponse, KvError> {
113        let ser = Serializer::json_compatible();
114        let options_object = ListOptions {
115            limit: self.limit,
116            cursor: self.cursor,
117            prefix: self.prefix,
118        }
119        .serialize(&ser)
120        .map_err(JsValue::from)?;
121
122        let promise: Promise = self
123            .list_function
124            .call1(&self.this, &options_object)?
125            .into();
126
127        let value = JsFuture::from(promise).await?;
128        let resp = serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?;
129        Ok(resp)
130    }
131}
132
133/// A builder to configure get requests.
134#[derive(Debug, Clone)]
135#[must_use = "GetOptionsBuilder does nothing until you 'get' it"]
136pub struct GetOptionsBuilder {
137    pub(crate) this: Object,
138    pub(crate) get_function: Function,
139    pub(crate) get_with_meta_function: Function,
140    pub(crate) name: JsValue,
141    pub(crate) cache_ttl: Option<u64>,
142    pub(crate) value_type: Option<GetValueType>,
143}
144
145#[derive(Serialize)]
146struct GetOptions {
147    #[serde(rename = "cacheTtl", skip_serializing_if = "Option::is_none")]
148    pub(crate) cache_ttl: Option<u64>,
149    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
150    pub(crate) value_type: Option<GetValueType>,
151}
152
153impl GetOptionsBuilder {
154    /// The cache_ttl parameter must be an integer that is greater than or equal to 60. It defines
155    /// the length of time in seconds that a KV result is cached in the edge location that it is
156    /// accessed from. This can be useful for reducing cold read latency on keys that are read
157    /// relatively infrequently. It is especially useful if your data is write-once or
158    /// write-rarely, but is not recommended if your data is updated often and you need to see
159    /// updates shortly after they're written, because writes that happen from other edge locations
160    /// won't be visible until the cached value expires.
161    pub fn cache_ttl(mut self, cache_ttl: u64) -> Self {
162        self.cache_ttl = Some(cache_ttl);
163        self
164    }
165
166    fn value_type(mut self, value_type: GetValueType) -> Self {
167        self.value_type = Some(value_type);
168        self
169    }
170
171    fn options(&self) -> Result<JsValue, KvError> {
172        let ser = Serializer::json_compatible();
173        Ok(GetOptions {
174            cache_ttl: self.cache_ttl,
175            value_type: self.value_type,
176        }
177        .serialize(&ser)
178        .map_err(JsValue::from)?)
179    }
180
181    async fn get(self) -> Result<JsValue, KvError> {
182        let options_object = self.options()?;
183
184        let promise: Promise = self
185            .get_function
186            .call2(&self.this, &self.name, &options_object)?
187            .into();
188        JsFuture::from(promise).await.map_err(KvError::from)
189    }
190
191    /// Gets the value as a string.
192    pub async fn text(self) -> Result<Option<String>, KvError> {
193        let value = self.value_type(GetValueType::Text).get().await?;
194        Ok(value.as_string())
195    }
196
197    /// Tries to deserialize the inner text to the generic type.
198    pub async fn json<T>(self) -> Result<Option<T>, KvError>
199    where
200        T: DeserializeOwned,
201    {
202        let value = self.value_type(GetValueType::Json).get().await?;
203        Ok(if value.is_null() {
204            None
205        } else {
206            Some(serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?)
207        })
208    }
209
210    /// Gets the value as a byte slice.
211    pub async fn bytes(self) -> Result<Option<Vec<u8>>, KvError> {
212        let v = self.value_type(GetValueType::ArrayBuffer).get().await?;
213        if ArrayBuffer::instanceof(&v) {
214            let buffer = ArrayBuffer::from(v);
215            let buffer = Uint8Array::new(&buffer);
216            Ok(Some(buffer.to_vec()))
217        } else {
218            Ok(None)
219        }
220    }
221
222    async fn get_with_metadata<M>(&self) -> Result<(JsValue, Option<M>), KvError>
223    where
224        M: DeserializeOwned,
225    {
226        let options_object = self.options()?;
227
228        let promise: Promise = self
229            .get_with_meta_function
230            .call2(&self.this, &self.name, &options_object)?
231            .into();
232
233        let pair = JsFuture::from(promise).await?;
234        let metadata = crate::get(&pair, "metadata")?;
235        let value = crate::get(&pair, "value")?;
236
237        Ok((
238            value,
239            if metadata.is_null() {
240                None
241            } else {
242                Some(serde_wasm_bindgen::from_value(metadata).map_err(JsValue::from)?)
243            },
244        ))
245    }
246
247    /// Gets the value as a string and it's associated metadata.
248    pub async fn text_with_metadata<M>(self) -> Result<(Option<String>, Option<M>), KvError>
249    where
250        M: DeserializeOwned,
251    {
252        let (value, metadata) = self
253            .value_type(GetValueType::Text)
254            .get_with_metadata()
255            .await?;
256        Ok((value.as_string(), metadata))
257    }
258
259    /// Tries to deserialize the inner text to the generic type along with it's metadata.
260    pub async fn json_with_metadata<T, M>(self) -> Result<(Option<T>, Option<M>), KvError>
261    where
262        T: DeserializeOwned,
263        M: DeserializeOwned,
264    {
265        let (value, metadata) = self
266            .value_type(GetValueType::Json)
267            .get_with_metadata()
268            .await?;
269        Ok((
270            if value.is_null() {
271                None
272            } else {
273                Some(serde_wasm_bindgen::from_value(value).map_err(JsValue::from)?)
274            },
275            metadata,
276        ))
277    }
278
279    /// Gets the value as a byte slice and it's associated metadata.
280    pub async fn bytes_with_metadata<M>(self) -> Result<(Option<Vec<u8>>, Option<M>), KvError>
281    where
282        M: DeserializeOwned,
283    {
284        let (value, metadata) = self
285            .value_type(GetValueType::ArrayBuffer)
286            .get_with_metadata()
287            .await?;
288
289        if ArrayBuffer::instanceof(&value) {
290            let buffer = ArrayBuffer::from(value);
291            let buffer = Uint8Array::new(&buffer);
292            Ok((Some(buffer.to_vec()), metadata))
293        } else {
294            Ok((None, metadata))
295        }
296    }
297}
298
299#[derive(Debug, Clone, Serialize, Copy)]
300#[serde(rename_all = "camelCase")]
301pub(crate) enum GetValueType {
302    Text,
303    ArrayBuffer,
304    Json,
305}