use js_sys::Array;
use num_traits::ToPrimitive;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use web_sys::{Event, EventTarget, IdbDatabase};
#[cfg(feature = "builder")]
use crate::builder::DatabaseBuilder;
use crate::{
utils::dom_string_list_to_vec, Error, ObjectStore, ObjectStoreParams, Transaction,
TransactionMode,
};
#[derive(Debug)]
pub struct Database {
inner: IdbDatabase,
abort_callback: Option<Closure<dyn FnMut(Event)>>,
close_callback: Option<Closure<dyn FnMut(Event)>>,
error_callback: Option<Closure<dyn FnMut(Event)>>,
version_change_callback: Option<Closure<dyn FnMut(Event)>>,
}
impl Database {
#[cfg(feature = "builder")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "builder")))]
pub fn builder(name: &str) -> DatabaseBuilder {
DatabaseBuilder::new(name)
}
pub fn name(&self) -> String {
self.inner.name()
}
pub fn version(&self) -> Result<u32, Error> {
self.inner
.version()
.to_u32()
.ok_or(Error::NumberConversionError)
}
pub fn store_names(&self) -> Vec<String> {
dom_string_list_to_vec(&self.inner.object_store_names())
}
pub fn transaction<T>(
&self,
store_names: &[T],
mode: TransactionMode,
) -> Result<Transaction, Error>
where
T: AsRef<str>,
{
let store_names: Array = store_names
.iter()
.map(|s| JsValue::from(s.as_ref()))
.collect();
self.inner
.transaction_with_str_sequence_and_mode(&store_names, mode.into())
.map(Into::into)
.map_err(Error::TransactionOpenFailed)
}
pub fn close(&self) {
self.inner.close()
}
pub fn create_object_store(
&self,
name: &str,
params: ObjectStoreParams,
) -> Result<ObjectStore, Error> {
self.inner
.create_object_store_with_optional_parameters(name, ¶ms.into())
.map(Into::into)
.map_err(Error::ObjectStoreCreateFailed)
}
pub fn delete_object_store(&self, name: &str) -> Result<(), Error> {
self.inner
.delete_object_store(name)
.map_err(Error::ObjectStoreDeleteFailed)
}
pub fn on_abort<F>(&mut self, callback: F)
where
F: FnOnce(Event) + 'static,
{
let closure = Closure::once(callback);
self.inner
.set_onabort(Some(closure.as_ref().unchecked_ref()));
self.abort_callback = Some(closure);
}
pub fn on_close<F>(&mut self, callback: F)
where
F: FnOnce(Event) + 'static,
{
let closure = Closure::once(callback);
self.inner
.set_onclose(Some(closure.as_ref().unchecked_ref()));
self.close_callback = Some(closure);
}
pub fn on_error<F>(&mut self, callback: F)
where
F: FnOnce(Event) + 'static,
{
let closure = Closure::once(callback);
self.inner
.set_onerror(Some(closure.as_ref().unchecked_ref()));
self.error_callback = Some(closure);
}
pub fn on_version_change<F>(&mut self, callback: F)
where
F: FnOnce(Event) + 'static,
{
let closure = Closure::once(callback);
self.inner
.set_onversionchange(Some(closure.as_ref().unchecked_ref()));
self.version_change_callback = Some(closure);
}
pub fn forget_callbacks(&mut self) {
let abort_callback = self.abort_callback.take();
let close_callback = self.close_callback.take();
let error_callback = self.error_callback.take();
let version_change_callback = self.version_change_callback.take();
if let Some(callback) = abort_callback {
callback.forget();
}
if let Some(callback) = close_callback {
callback.forget();
}
if let Some(callback) = error_callback {
callback.forget();
}
if let Some(callback) = version_change_callback {
callback.forget();
}
}
}
impl TryFrom<EventTarget> for Database {
type Error = Error;
fn try_from(target: EventTarget) -> Result<Self, Self::Error> {
let target: JsValue = target.into();
target
.dyn_into::<IdbDatabase>()
.map(Into::into)
.map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
}
}
impl From<IdbDatabase> for Database {
fn from(inner: IdbDatabase) -> Self {
Self {
inner,
abort_callback: None,
close_callback: None,
error_callback: None,
version_change_callback: None,
}
}
}
impl From<Database> for IdbDatabase {
fn from(database: Database) -> Self {
database.inner
}
}
impl TryFrom<JsValue> for Database {
type Error = Error;
fn try_from(value: JsValue) -> Result<Self, Self::Error> {
value
.dyn_into::<IdbDatabase>()
.map(Into::into)
.map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
}
}
impl From<Database> for JsValue {
fn from(value: Database) -> Self {
value.inner.into()
}
}