use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
macro_rules! js_value_conversions {
($t:ty) => {
impl std::convert::From<wasm_bindgen::JsValue> for $t {
fn from(t: JsValue) -> $t {
t.into_serde::<$t>().unwrap()
}
}
impl std::convert::From<$t> for wasm_bindgen::JsValue {
fn from(t: $t) -> wasm_bindgen::JsValue {
wasm_bindgen::JsValue::from_serde(&t).unwrap()
}
}
};
}
#[wasm_bindgen]
extern "C" {
type KV;
#[wasm_bindgen(catch, js_name = "get", static_method_of = KV)]
async fn get(key: &JsValue, r#type: &JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "getWithMetadata", static_method_of = KV)]
async fn get_with_metadata(key: &JsValue, r#type: &JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "put", static_method_of = KV)]
async fn put(key: &JsValue, value: &JsValue, options: &JsValue) -> Result<(), JsValue>;
#[wasm_bindgen(catch, js_name = "delete", static_method_of = KV)]
async fn delete(key: &JsValue) -> Result<(), JsValue>;
#[wasm_bindgen(catch, js_name = "list", static_method_of = KV)]
async fn list(options: &JsValue) -> Result<JsValue, JsValue>;
}
pub struct Implementation;
impl Implementation {
pub async fn get(key: String, r#type: Option<Type>) -> Result<Option<Value>, JsValue> {
let key: JsValue = key.into();
let r#type: JsValue = match r#type {
Some(r#type) => r#type.into(),
None => JsValue::UNDEFINED,
};
let result = match KV::get(&key, &r#type).await {
Ok(result) => result,
Err(_) => return Err("oops".into()),
};
if result.is_null() {
return Ok(None);
}
Ok(Some(result.into()))
}
pub async fn get_with_metadata(
key: String,
r#type: Option<Type>,
) -> Result<ValueWithMetadata, JsValue> {
let key: JsValue = key.into();
let r#type: JsValue = match r#type {
Some(r#type) => r#type.into(),
None => JsValue::UNDEFINED,
};
match KV::get_with_metadata(&key, &r#type).await {
Ok(result) => Ok(result.into()),
Err(_) => Err("oops".into()),
}
}
pub async fn put(
key: String,
value: Value,
options: Option<PutOptions>,
) -> Result<(), JsValue> {
let key: JsValue = key.into();
let value: JsValue = value.into();
let options: JsValue = match options {
Some(options) => options.into(),
None => JsValue::UNDEFINED,
};
match KV::put(&key, &value, &options).await {
Ok(result) => Ok(result),
Err(_) => Err("oops".into()),
}
}
pub async fn delete(key: String) -> Result<(), JsValue> {
let key: JsValue = key.into();
match KV::delete(&key).await {
Ok(result) => Ok(result),
Err(_) => Err("oops".into()),
}
}
pub async fn list(options: Option<ListOptions>) -> Result<ListValue, JsValue> {
let options: JsValue = match options {
Some(options) => options.into(),
None => JsValue::UNDEFINED,
};
match KV::list(&options).await {
Ok(result) => Ok(result.into()),
Err(_) => Err("oops".into()),
}
}
}
pub type JSON = HashMap<String, serde_json::Value>;
pub enum Value {
Text(String),
JSON(JSON),
ArrayBuffer(js_sys::ArrayBuffer),
Stream(web_sys::ReadableStream),
}
impl std::convert::From<wasm_bindgen::JsValue> for Value {
fn from(t: JsValue) -> Value {
if let Ok(value) = t.clone().dyn_into::<js_sys::ArrayBuffer>() {
return Value::ArrayBuffer(value);
}
if let Ok(value) = t.clone().dyn_into::<web_sys::ReadableStream>() {
return Value::Stream(value);
}
if let Ok(value) = t.clone().into_serde() {
return Value::JSON(value);
}
Value::Text(String::from(js_sys::JsString::from(t)))
}
}
impl std::convert::From<Value> for wasm_bindgen::JsValue {
fn from(t: Value) -> wasm_bindgen::JsValue {
match t {
Value::Text(value) => JsValue::from(value),
Value::JSON(value) => JsValue::from_serde(&value).unwrap(),
Value::ArrayBuffer(value) => JsValue::from(value),
Value::Stream(value) => JsValue::from(value),
}
}
}
pub struct ValueWithMetadata {
pub value: Option<Value>,
pub metadata: Option<JSON>,
}
impl std::convert::From<wasm_bindgen::JsValue> for ValueWithMetadata {
fn from(t: JsValue) -> ValueWithMetadata {
t.into()
}
}
impl std::convert::From<ValueWithMetadata> for wasm_bindgen::JsValue {
fn from(t: ValueWithMetadata) -> wasm_bindgen::JsValue {
t.into()
}
}
#[derive(Deserialize, Serialize)]
pub enum Type {
#[serde(rename = "text")]
Text,
#[serde(rename = "json")]
JSON,
#[serde(rename = "arrayBuffer")]
ArrayBuffer,
#[serde(rename = "stream")]
Stream,
}
js_value_conversions!(Type);
#[derive(Deserialize, Serialize)]
pub struct PutOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration: Option<u64>,
#[serde(rename = "expirationTtl", skip_serializing_if = "Option::is_none")]
pub expiration_ttl: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<JSON>,
}
js_value_conversions!(PutOptions);
#[derive(Deserialize, Serialize)]
pub struct ListOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
js_value_conversions!(ListOptions);
#[derive(Deserialize, Serialize)]
pub struct ListValue {
pub keys: Vec<ListValueKey>,
pub list_complete: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
js_value_conversions!(ListValue);
#[derive(Deserialize, Serialize)]
pub struct ListValueKey {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub expiration: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<JSON>,
}
js_value_conversions!(ListValueKey);