139 lines
3.8 KiB
Rust
139 lines
3.8 KiB
Rust
use js_sys::{Promise, Uint8Array};
|
|
use serde::{Deserialize, Serialize};
|
|
use wasm_bindgen::prelude::*;
|
|
use wasm_bindgen_futures::JsFuture;
|
|
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
// Response type of KV.getWithMetadata()
|
|
type JsKvGetMetadata;
|
|
|
|
#[wasm_bindgen(method, getter)]
|
|
pub fn value(this: &JsKvGetMetadata) -> JsValue;
|
|
#[wasm_bindgen(method, getter)]
|
|
pub fn metadata(this: &JsKvGetMetadata) -> JsValue;
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
type JsKvNamespace;
|
|
|
|
#[wasm_bindgen(method, js_name = "put")]
|
|
pub fn put_with_opts(
|
|
this: &JsKvNamespace,
|
|
key: &str,
|
|
value: JsValue,
|
|
options: JsValue,
|
|
) -> Promise;
|
|
#[wasm_bindgen(method, js_name = "getWithMetadata")]
|
|
pub fn get_with_metadata_opts(this: &JsKvNamespace, key: &str, opts: JsValue) -> Promise;
|
|
}
|
|
|
|
// wasm-bindgen types are not Send + Sync, thus not usable in async_static
|
|
// but we're sure that this program only runs in one thread, so to work
|
|
// around the limitation, we unsafely implement Sync + Send for JsKvNamespace
|
|
// TODO: is there a better way to work around this?
|
|
unsafe impl Sync for JsKvNamespace {}
|
|
unsafe impl Send for JsKvNamespace {}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct KvPutOptions {
|
|
expiration: Option<u64>, // seconds since epoch
|
|
#[serde(rename = "expirationTtl")]
|
|
expiration_ttl: Option<u64>, // seconds
|
|
metadata: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct KvGetOptions {
|
|
#[serde(rename = "type")]
|
|
data_type: String,
|
|
}
|
|
|
|
pub struct KvNamespace {
|
|
inner: JsKvNamespace,
|
|
}
|
|
|
|
impl KvNamespace {
|
|
fn wrap(inner: JsKvNamespace) -> KvNamespace {
|
|
KvNamespace { inner }
|
|
}
|
|
|
|
pub async fn put_buf_ttl_metadata<T: Serialize>(
|
|
&self,
|
|
key: &str,
|
|
value: &[u8],
|
|
ttl: u64,
|
|
metadata: T,
|
|
) -> Result<(), String> {
|
|
let u8arr = Uint8Array::from(value);
|
|
let promise = self.inner.put_with_opts(
|
|
key,
|
|
u8arr.into(),
|
|
JsValue::from_serde(&KvPutOptions {
|
|
expiration: None,
|
|
expiration_ttl: Some(ttl),
|
|
metadata: Some(
|
|
serde_json::to_value(metadata)
|
|
.map_err(|_| "Cannot serialize metadata".to_string())?,
|
|
),
|
|
})
|
|
.unwrap(),
|
|
);
|
|
match JsFuture::from(promise).await {
|
|
Ok(_) => Ok(()),
|
|
Err(_) => Err("Failed to put buffer to KV with TTL".to_string()),
|
|
}
|
|
}
|
|
|
|
// Get a buffer value from KV with its metadata
|
|
// we assume that all metadata are pure JSON objects (Deserialize)
|
|
pub async fn get_buf_metadata<T: for<'de> Deserialize<'de>>(
|
|
&self,
|
|
key: &str,
|
|
) -> (Option<Vec<u8>>, Option<T>) {
|
|
let promise = self.inner.get_with_metadata_opts(
|
|
key,
|
|
JsValue::from_serde(&KvGetOptions {
|
|
data_type: "arrayBuffer".to_string(), // Must provide type of the expected return value (buffer)
|
|
})
|
|
.unwrap(),
|
|
);
|
|
let obj = match JsFuture::from(promise).await {
|
|
Ok(v) => v,
|
|
Err(_) => return (None, None),
|
|
};
|
|
|
|
if obj.is_null() {
|
|
return (None, None);
|
|
}
|
|
|
|
let obj: JsKvGetMetadata = obj.into();
|
|
|
|
(
|
|
if obj.value().is_null() {
|
|
None
|
|
} else {
|
|
Some(Uint8Array::new(&obj.value()).to_vec())
|
|
},
|
|
if obj.metadata().is_null() {
|
|
None
|
|
} else {
|
|
obj.metadata().into_serde().ok()
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
extern "C" {
|
|
type Global;
|
|
|
|
#[wasm_bindgen(getter, static_method_of = Global, js_class = globalThis, js_name = DNS_CACHE)]
|
|
fn dns_cache() -> JsKvNamespace;
|
|
}
|
|
|
|
pub fn get_dns_cache() -> KvNamespace {
|
|
KvNamespace::wrap(Global::dns_cache())
|
|
}
|