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