indexed_db/
database.rs

1use crate::{transaction::TransactionBuilder, utils::str_slice_to_array, ObjectStore};
2use std::marker::PhantomData;
3use web_sys::{js_sys::JsString, IdbDatabase, IdbObjectStoreParameters};
4
5/// Wrapper for [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase)
6#[derive(Debug)]
7pub struct Database<Err> {
8    sys: IdbDatabase,
9    _phantom: PhantomData<Err>,
10}
11
12impl<Err> Database<Err> {
13    pub(crate) fn from_sys(sys: IdbDatabase) -> Database<Err> {
14        Database {
15            sys,
16            _phantom: PhantomData,
17        }
18    }
19
20    /// The name of this database
21    ///
22    /// Internally, this uses [`IDBDatabase::name`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/name).
23    pub fn name(&self) -> String {
24        self.sys.name()
25    }
26
27    /// The version of this database, clamped at `u32::MAX`
28    ///
29    /// Internally, this uses [`IDBDatabase::version`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/version).
30    pub fn version(&self) -> u32 {
31        self.sys.version() as u32
32    }
33
34    /// The names of all [`ObjectStore`]s in this [`Database`]
35    ///
36    /// Internally, this uses [`IDBDatabase::objectStoreNames`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/objectStoreNames).
37    pub fn object_store_names(&self) -> Vec<String> {
38        let names = self.sys.object_store_names();
39        let len = names.length();
40        let mut res = Vec::with_capacity(usize::try_from(len).unwrap());
41        for i in 0..len {
42            res.push(
43                names
44                    .get(i)
45                    .expect("DOMStringList did not contain as many elements as its length"),
46            );
47        }
48        res
49    }
50
51    /// Build an [`ObjectStore`]
52    ///
53    /// Note that this method can only be called from within an `on_upgrade_needed` callback. It returns
54    /// a builder, and calling the `create` method on this builder will perform the actual creation.
55    ///
56    /// Internally, this uses [`IDBDatabase::createObjectStore`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore).
57    pub fn build_object_store<'a>(&self, name: &'a str) -> ObjectStoreBuilder<'a, Err> {
58        ObjectStoreBuilder {
59            db: self.sys.clone(),
60            name,
61            options: IdbObjectStoreParameters::new(),
62            _phantom: PhantomData,
63        }
64    }
65
66    /// Deletes an [`ObjectStore`]
67    ///
68    /// Note that this method can only be called from within an `on_upgrade_needed` callback.
69    ///
70    /// Internally, this uses [`IDBDatabase::deleteObjectStore`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/deleteObjectStore).
71    pub fn delete_object_store(&self, name: &str) -> crate::Result<(), Err> {
72        self.sys.delete_object_store(name).map_err(|err| match error_name!(&err) {
73            Some("InvalidStateError") => crate::Error::InvalidCall,
74            Some("TransactionInactiveError") => panic!("Tried to delete an object store with the `versionchange` transaction having already aborted"),
75            Some("NotFoundError") => crate::Error::DoesNotExist,
76            _ => crate::Error::from_js_value(err),
77        })
78    }
79
80    /// Run a transaction
81    ///
82    /// This will open the object stores identified by `stores`. See the methods of [`TransactionBuilder`]
83    /// for more details about how transactions actually happen.
84    pub fn transaction(&self, stores: &[&str]) -> TransactionBuilder<Err> {
85        TransactionBuilder::from_names(self.sys.clone(), stores)
86    }
87
88    /// Closes this database connection
89    ///
90    /// Note that the closing will actually happen asynchronously with no way for the client to
91    /// identify when the database was closed.
92    ///
93    /// Internally, this uses [`IDBDatabase::close`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/close).
94    pub fn close(&self) {
95        self.sys.close();
96    }
97}
98
99/// Helper to build an object store
100pub struct ObjectStoreBuilder<'a, Err> {
101    db: IdbDatabase,
102    name: &'a str,
103    options: IdbObjectStoreParameters,
104    _phantom: PhantomData<Err>,
105}
106
107impl<'a, Err> ObjectStoreBuilder<'a, Err> {
108    /// Create the object store
109    ///
110    /// Internally, this uses [`IDBDatabase::createObjectStore`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore).
111    pub fn create(self) -> crate::Result<ObjectStore<Err>, Err> {
112        self.db
113            .create_object_store_with_optional_parameters(self.name, &self.options)
114            .map_err(
115                |err| match error_name!(&err) {
116                    Some("InvalidStateError") => crate::Error::InvalidCall,
117                    Some("TransactionInactiveError") => panic!("Tried to create an object store with the `versionchange` transaction having already aborted"),
118                    Some("ConstraintError") => crate::Error::AlreadyExists,
119                    Some("InvalidAccessError") => crate::Error::InvalidArgument,
120                    _ => crate::Error::from_js_value(err),
121                },
122            )
123            .map(ObjectStore::from_sys)
124    }
125
126    /// Set the key path for out-of-line keys
127    ///
128    /// If you want to use a compound primary key made of multiple attributes, please see [`ObjectStoreBuilder::compound_key_path`].
129    ///
130    /// Internally, this [sets this setting](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore#keypath).
131    pub fn key_path(self, path: &str) -> Self {
132        self.options.set_key_path(&JsString::from(path));
133        self
134    }
135
136    /// Set the key path for out-of-line keys
137    ///
138    /// Internally, this [sets this setting](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore#keypath).
139    pub fn compound_key_path(self, paths: &[&str]) -> Self {
140        self.options.set_key_path(&str_slice_to_array(paths));
141        self
142    }
143
144    /// Enable auto-increment for the key
145    ///
146    /// Internally, this [sets this setting](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore#autoincrement).
147    pub fn auto_increment(self) -> Self {
148        self.options.set_auto_increment(true);
149        self
150    }
151}