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}