use std::{hint::unreachable_unchecked, marker::PhantomData}; use js_sys::{Array, JsString, Object}; use wasm_bindgen::JsValue; pub struct IdbObject(Array, PhantomData); impl IdbObject { fn add_tuple(self, key: &str, value: &JsValue) -> IdbObject { let array = Array::new(); array.push(&JsString::from(key)); array.push(value); self.0.push(&array); IdbObject(self.0, PhantomData) } } impl From> for Object { fn from(db_object: IdbObject) -> Self { match Self::from_entries(db_object.as_ref()) { Ok(o) => o, // SAFETY: IdbObject maintains the invariant that it can eventually // be constructed into a JS object. _ => unsafe { unreachable_unchecked() }, } } } impl IdbObject { pub fn new() -> Self { Self(Array::new(), PhantomData) } pub fn archive(self) -> IdbObject { self.add_tuple("type", &JsString::from("archive")) } pub fn video(self) -> IdbObject { self.add_tuple("type", &JsString::from("video")) } pub fn audio(self) -> IdbObject { self.add_tuple("type", &JsString::from("audio")) } pub fn image(self) -> IdbObject { self.add_tuple("type", &JsString::from("image")) } pub fn blob(self) -> IdbObject { self.add_tuple("type", &JsString::from("blob")) } pub fn string(self) -> IdbObject { self.add_tuple("type", &JsString::from("string")) } } impl Default for IdbObject { fn default() -> Self { Self::new() } } impl IdbObject { pub fn expiration_text(self, expires: &str) -> IdbObject { self.add_tuple("expiration", &JsString::from(expires)) } } impl IdbObject { pub fn data(self, value: &JsValue) -> IdbObject { self.add_tuple("data", value) } } impl IdbObject { pub fn extra(self, key: &str, value: impl Into) -> Self { self.add_tuple(key, &value.into()) } } impl AsRef for IdbObject { fn as_ref(&self) -> &JsValue { self.0.as_ref() } } macro_rules! impl_idb_object_state { ($($ident:ident),*) => { pub trait IdbObjectState {} $( pub enum $ident {} impl IdbObjectState for $ident {} )* }; } impl_idb_object_state!(NeedsType, NeedsExpiration, NeedsData, Ready);