1#![deny(clippy::all)]
7#![deny(missing_docs)]
8
9mod error;
10mod indexed_db;
11
12use keyvaluedb::{
13 DBKeyRef, DBKeyValueRef, DBTransaction, DBTransactionError, DBValue, KeyValueDBPinBoxFuture,
14};
15use keyvaluedb_memorydb::{self as in_memory, InMemory};
16use send_wrapper::SendWrapper;
17use std::io;
18use std::sync::Arc;
19
20pub use crate::error::*;
21pub use keyvaluedb::KeyValueDB;
22
23use futures::prelude::*;
24
25use web_sys::IdbDatabase;
26
27struct DatabaseUnlockedInner {
28 table_name: String,
29 version: u32,
30 columns: u32,
31 on_disk_columns: u32,
32 in_memory: Option<InMemory>,
33 indexed_db: SendWrapper<IdbDatabase>,
34}
35
36impl Drop for DatabaseUnlockedInner {
37 fn drop(&mut self) {
38 self.indexed_db.close();
39 }
40}
41
42#[derive(Clone)]
44pub struct Database {
45 unlocked_inner: Arc<DatabaseUnlockedInner>,
46}
47
48impl Database {
49 pub async fn open(
52 table_name: &str,
53 columns: u32,
54 memory_cached: bool,
55 ) -> Result<Database, error::Error> {
56 let db = indexed_db::open(table_name, None, columns)
58 .await
59 .map_err(io_err_string)?;
60
61 let db = if columns > db.columns {
67 let next_version = db.version + 1;
68 drop(db);
69 indexed_db::open(table_name, Some(next_version), columns)
70 .await
71 .map_err(io_err_string)?
72 } else {
73 db
74 };
75 let indexed_db::IndexedDB {
77 version,
78 inner,
79 columns: on_disk_columns,
80 } = db;
81 let in_memory = if memory_cached {
82 let in_memory = in_memory::create(columns);
83 for column in 0..columns {
85 let mut tx = DBTransaction::new();
86 let mut stream = indexed_db::idb_cursor(&inner, column, None, None)
87 .map_err(error::Error::from)?;
88 while let Some(kv) = stream.next().await {
89 match kv {
90 Ok((key, value)) => {
91 tx.put(column, &key, &value);
92 }
93 Err(e) => {
94 return Err(e.into());
95 }
96 }
97 }
98 in_memory
100 .write(tx)
101 .await
102 .expect("writing in memory always succeeds; qed");
103 }
104 Some(in_memory)
105 } else {
106 None
107 };
108
109 Ok(Database {
110 unlocked_inner: Arc::new(DatabaseUnlockedInner {
111 table_name: table_name.to_owned(),
112 version,
113 columns,
114 on_disk_columns: on_disk_columns.max(columns),
115 in_memory,
116 indexed_db: inner,
117 }),
118 })
119 }
120
121 pub async fn delete(table_name: &str) -> io::Result<bool> {
125 indexed_db::delete(table_name).await.map_err(io_err_string)
126 }
127
128 pub fn list(
132 opt_prefix: Option<&str>,
133 ) -> KeyValueDBPinBoxFuture<'_, io::Result<Vec<(String, u32)>>> {
134 let opt_prefix = opt_prefix.map(|p| p.to_owned());
135 Box::pin(SendWrapper::new(async move {
136 let names = indexed_db::names_with_versions()
137 .await
138 .map_err(io_err_string)?;
139 let Some(prefix) = opt_prefix else {
140 return Ok(names);
141 };
142 Ok(names
143 .into_iter()
144 .filter(|(name, _ver)| name.starts_with(&prefix))
145 .collect())
146 }))
147 }
148
149 pub fn name(&self) -> String {
151 self.unlocked_inner.table_name.clone()
152 }
153
154 pub fn version(&self) -> u32 {
156 self.unlocked_inner.version
157 }
158}
159
160impl KeyValueDB for Database {
161 fn get<'a>(
162 &'a self,
163 col: u32,
164 key: &'a [u8],
165 ) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBValue>>> {
166 let this = self.clone();
167 Box::pin(SendWrapper::new(async move {
168 if col >= this.unlocked_inner.columns {
169 return Err(io::Error::from(io::ErrorKind::NotFound));
170 }
171
172 if let Some(in_memory) = &this.unlocked_inner.in_memory {
173 in_memory.get(col, key).await
174 } else {
175 indexed_db::idb_get(&this.unlocked_inner.indexed_db, col, key).await
176 }
177 }))
178 }
179
180 fn delete<'a>(
181 &'a self,
182 col: u32,
183 key: &'a [u8],
184 ) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBValue>>> {
185 let this = self.clone();
186 Box::pin(SendWrapper::new(async move {
187 if col >= this.unlocked_inner.columns {
188 return Err(io::Error::from(io::ErrorKind::NotFound));
189 }
190
191 let someval = indexed_db::idb_get(&this.unlocked_inner.indexed_db, col, key).await?;
192
193 let mut transaction = DBTransaction::new();
194 transaction.delete(col, key);
195
196 match indexed_db::idb_commit_transaction(
197 &this.unlocked_inner.indexed_db,
198 &transaction,
199 this.unlocked_inner.columns,
200 )
201 .await
202 {
203 Ok(()) => {}
204 Err(error) => {
205 return Err(io_err_string(format!("delete failed: {:?}", error)));
206 }
207 };
208
209 if let Some(in_memory) = &this.unlocked_inner.in_memory {
210 in_memory.delete(col, key).await?;
211 }
212
213 Ok(someval)
214 }))
215 }
216
217 fn write(
218 &self,
219 transaction: DBTransaction,
220 ) -> KeyValueDBPinBoxFuture<'_, Result<(), DBTransactionError>> {
221 let this = self.clone();
222 Box::pin(SendWrapper::new(async move {
223 {
224 match indexed_db::idb_commit_transaction(
225 &this.unlocked_inner.indexed_db,
226 &transaction,
227 this.unlocked_inner.columns,
228 )
229 .await
230 {
231 Ok(()) => {}
232 Err(error) => {
233 return Err(DBTransactionError { error, transaction });
234 }
235 };
236 }
237 if let Some(in_memory) = &this.unlocked_inner.in_memory {
238 in_memory.write(transaction).await
239 } else {
240 Ok(())
241 }
242 }))
243 }
244
245 fn iter<
246 'a,
247 T: Send + 'static,
248 C: Send + 'static,
249 F: FnMut(&mut C, DBKeyValueRef) -> io::Result<Option<T>> + Send + Sync + 'static,
250 >(
251 &'a self,
252 col: u32,
253 prefix: Option<&'a [u8]>,
254 mut context: C,
255 mut f: F,
256 ) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>> {
257 let this = self.clone();
258 Box::pin(async move {
259 if col >= this.unlocked_inner.columns {
260 return Err(io::Error::from(io::ErrorKind::NotFound));
261 }
262 if let Some(in_memory) = &this.unlocked_inner.in_memory {
263 in_memory.iter(col, prefix, context, f).await
264 } else {
265 let mut stream = indexed_db::idb_cursor(
266 &this.unlocked_inner.indexed_db,
267 col,
268 None,
269 prefix.map(|p| p.to_vec()),
270 )?;
271 while let Some(kv) = stream.next().await {
272 match kv {
273 Ok((key, value)) => {
274 if let Some(out) = f(&mut context, (&key, &value))? {
275 return Ok((context, Some(out)));
276 }
277 }
278 Err(e) => {
279 return Err(e);
280 }
281 }
282 }
283 Ok((context, None))
284 }
285 })
286 }
287
288 fn iter_keys<
289 'a,
290 T: Send + 'static,
291 C: Send + 'static,
292 F: FnMut(&mut C, DBKeyRef) -> io::Result<Option<T>> + Send + Sync + 'static,
293 >(
294 &'a self,
295 col: u32,
296 prefix: Option<&'a [u8]>,
297 mut context: C,
298 mut f: F,
299 ) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>> {
300 let this = self.clone();
301 Box::pin(async move {
302 if col >= this.unlocked_inner.columns {
303 return Err(io::Error::from(io::ErrorKind::NotFound));
304 }
305
306 if let Some(in_memory) = &this.unlocked_inner.in_memory {
307 in_memory.iter_keys(col, prefix, context, f).await
308 } else {
309 let mut stream = indexed_db::idb_cursor_keys(
310 &this.unlocked_inner.indexed_db,
311 col,
312 prefix.map(|p| p.to_vec()),
313 )?;
314 while let Some(k) = stream.next().await {
315 match k {
316 Ok(key) => {
317 if let Some(out) = f(&mut context, &key)? {
318 return Ok((context, Some(out)));
319 }
320 }
321 Err(e) => {
322 return Err(e);
323 }
324 }
325 }
326 Ok((context, None))
327 }
328 })
329 }
330
331 fn num_columns(&self) -> Result<u32, io::Error> {
333 Ok(self.unlocked_inner.on_disk_columns)
334 }
335
336 fn num_keys(&self, col: u32) -> KeyValueDBPinBoxFuture<'_, io::Result<u64>> {
337 let this = self.clone();
338 Box::pin(async move {
339 if col >= this.unlocked_inner.columns {
340 return Err(io::Error::from(io::ErrorKind::NotFound));
341 }
342
343 if let Some(in_memory) = &this.unlocked_inner.in_memory {
344 in_memory.num_keys(col).await
345 } else {
346 let mut stream =
347 indexed_db::idb_get_key_count(&this.unlocked_inner.indexed_db, col, None)?;
348 if let Some(v) = stream.next().await {
349 match v {
350 Ok(value) => {
351 return Ok(value as u64);
352 }
353 Err(e) => {
354 return Err(e);
355 }
356 }
357 }
358 Err(io::Error::from(io::ErrorKind::InvalidData))
359 }
360 })
361 }
362}