idb_sys/
database.rs

1use js_sys::Array;
2use num_traits::ToPrimitive;
3use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
4use web_sys::{Event, EventTarget, IdbDatabase};
5
6use crate::{
7    utils::dom_string_list_to_vec, Error, ObjectStore, ObjectStoreParams, Transaction,
8    TransactionMode,
9};
10
11/// [`Database`] provides a connection to a database; you can use an [`Database`] object to open a transaction on your
12/// database then create, manipulate, and delete objects (data) in that database. The object provides the only way to
13/// get and manage versions of the database.
14#[derive(Debug)]
15pub struct Database {
16    inner: IdbDatabase,
17    abort_callback: Option<Closure<dyn FnMut(Event)>>,
18    close_callback: Option<Closure<dyn FnMut(Event)>>,
19    error_callback: Option<Closure<dyn FnMut(Event)>>,
20    version_change_callback: Option<Closure<dyn FnMut(Event)>>,
21}
22
23impl Database {
24    /// Returns the name of the database.
25    pub fn name(&self) -> String {
26        self.inner.name()
27    }
28
29    /// Returns the version of the database.
30    pub fn version(&self) -> Result<u32, Error> {
31        self.inner
32            .version()
33            .to_u32()
34            .ok_or(Error::NumberConversionError)
35    }
36
37    /// Returns a list of the names of [`ObjectStore`]s in the database.
38    pub fn store_names(&self) -> Vec<String> {
39        dom_string_list_to_vec(&self.inner.object_store_names())
40    }
41
42    /// Returns a new transaction with the given scope (which can be a single object store name or an array of names),
43    /// mode ([`TransactionMode::ReadOnly`] or [`TransactionMode::ReadWrite`]).
44    pub fn transaction<T>(
45        &self,
46        store_names: &[T],
47        mode: TransactionMode,
48    ) -> Result<Transaction, Error>
49    where
50        T: AsRef<str>,
51    {
52        let store_names: Array = store_names
53            .iter()
54            .map(|s| JsValue::from(s.as_ref()))
55            .collect();
56
57        self.inner
58            .transaction_with_str_sequence_and_mode(&store_names, mode.into())
59            .map(Into::into)
60            .map_err(Error::TransactionOpenFailed)
61    }
62
63    /// Closes the connection once all running transactions have finished.
64    pub fn close(&self) {
65        self.inner.close()
66    }
67
68    /// Creates a new object store with the given name and options and returns a new [`ObjectStore`]. Returns an
69    /// [`Error`] if not called within an upgrade transaction.
70    pub fn create_object_store(
71        &self,
72        name: &str,
73        params: ObjectStoreParams,
74    ) -> Result<ObjectStore, Error> {
75        self.inner
76            .create_object_store_with_optional_parameters(name, &params.into())
77            .map(Into::into)
78            .map_err(Error::ObjectStoreCreateFailed)
79    }
80
81    /// Deletes the object store with the given name. Returns an [`Error`] if not called within an upgrade transaction.
82    pub fn delete_object_store(&self, name: &str) -> Result<(), Error> {
83        self.inner
84            .delete_object_store(name)
85            .map_err(Error::ObjectStoreDeleteFailed)
86    }
87
88    /// Adds an event handler for `abort` event.
89    pub fn on_abort<F>(&mut self, callback: F)
90    where
91        F: FnOnce(Event) + 'static,
92    {
93        let closure = Closure::once(callback);
94        self.inner
95            .set_onabort(Some(closure.as_ref().unchecked_ref()));
96        self.abort_callback = Some(closure);
97    }
98
99    /// Adds an event handler for `close` event.
100    pub fn on_close<F>(&mut self, callback: F)
101    where
102        F: FnOnce(Event) + 'static,
103    {
104        let closure = Closure::once(callback);
105        self.inner
106            .set_onclose(Some(closure.as_ref().unchecked_ref()));
107        self.close_callback = Some(closure);
108    }
109
110    /// Adds an event handler for `error` event.
111    pub fn on_error<F>(&mut self, callback: F)
112    where
113        F: FnOnce(Event) + 'static,
114    {
115        let closure = Closure::once(callback);
116        self.inner
117            .set_onerror(Some(closure.as_ref().unchecked_ref()));
118        self.error_callback = Some(closure);
119    }
120
121    /// Adds an event handler for `versionchange` event.
122    pub fn on_version_change<F>(&mut self, callback: F)
123    where
124        F: FnOnce(Event) + 'static,
125    {
126        let closure = Closure::once(callback);
127        self.inner
128            .set_onversionchange(Some(closure.as_ref().unchecked_ref()));
129        self.version_change_callback = Some(closure);
130    }
131}
132
133impl TryFrom<EventTarget> for Database {
134    type Error = Error;
135
136    fn try_from(target: EventTarget) -> Result<Self, Self::Error> {
137        let target: JsValue = target.into();
138        target
139            .dyn_into::<IdbDatabase>()
140            .map(Into::into)
141            .map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
142    }
143}
144
145impl From<IdbDatabase> for Database {
146    fn from(inner: IdbDatabase) -> Self {
147        Self {
148            inner,
149            abort_callback: None,
150            close_callback: None,
151            error_callback: None,
152            version_change_callback: None,
153        }
154    }
155}
156
157impl From<Database> for IdbDatabase {
158    fn from(database: Database) -> Self {
159        database.inner
160    }
161}
162
163impl TryFrom<JsValue> for Database {
164    type Error = Error;
165
166    fn try_from(value: JsValue) -> Result<Self, Self::Error> {
167        value
168            .dyn_into::<IdbDatabase>()
169            .map(Into::into)
170            .map_err(|value| Error::UnexpectedJsType("IdbDatabase", value))
171    }
172}
173
174impl From<Database> for JsValue {
175    fn from(value: Database) -> Self {
176        value.inner.into()
177    }
178}