1use crate::{
2 transaction::transaction_request,
3 utils::{
4 array_to_vec, make_key_range, map_add_err, map_clear_err, map_count_err, map_count_res,
5 map_delete_err, map_get_err, none_if_undefined, str_slice_to_array,
6 },
7 CursorBuilder, Index,
8};
9use futures_util::future::{Either, FutureExt};
10use std::{future::Future, marker::PhantomData, ops::RangeBounds};
11use web_sys::{js_sys::JsString, wasm_bindgen::JsValue, IdbIndexParameters, IdbObjectStore};
12
13#[cfg(doc)]
14use crate::Cursor;
15
16#[derive(Debug)]
19pub struct ObjectStore<Err> {
20 sys: IdbObjectStore,
21 _phantom: PhantomData<Err>,
22}
23
24impl<Err> ObjectStore<Err> {
25 pub(crate) fn from_sys(sys: IdbObjectStore) -> ObjectStore<Err> {
26 ObjectStore {
27 sys,
28 _phantom: PhantomData,
29 }
30 }
31
32 pub fn build_index<'a>(&self, name: &'a str, key_path: &str) -> IndexBuilder<'a, Err> {
41 IndexBuilder {
42 store: self.sys.clone(),
43 name,
44 key_path: JsString::from(key_path).into(),
45 options: IdbIndexParameters::new(),
46 _phantom: PhantomData,
47 }
48 }
49
50 pub fn build_compound_index<'a>(
63 &self,
64 name: &'a str,
65 key_paths: &[&str],
66 ) -> IndexBuilder<'a, Err> {
67 IndexBuilder {
68 store: self.sys.clone(),
69 name,
70 key_path: str_slice_to_array(key_paths).into(),
71 options: IdbIndexParameters::new(),
72 _phantom: PhantomData,
73 }
74 }
75
76 pub fn delete_index(&self, name: &str) -> crate::Result<(), Err> {
82 self.sys
83 .delete_index(name)
84 .map_err(|err| match error_name!(&err) {
85 Some("InvalidStateError") => crate::Error::ObjectStoreWasRemoved,
86 Some("NotFoundError") => crate::Error::DoesNotExist,
87 _ => crate::Error::from_js_value(err),
88 })
89 }
90
91 pub fn add(&self, value: &JsValue) -> impl Future<Output = crate::Result<JsValue, Err>> {
97 match self.sys.add(value) {
98 Ok(add_req) => {
99 Either::Left(transaction_request(add_req).map(|res| res.map_err(map_add_err)))
100 }
101 Err(e) => Either::Right(std::future::ready(Err(map_add_err(e)))),
102 }
103 }
104
105 pub fn add_kv(
111 &self,
112 key: &JsValue,
113 value: &JsValue,
114 ) -> impl Future<Output = crate::Result<(), Err>> {
115 match self.sys.add_with_key(value, key) {
116 Ok(add_req) => Either::Left(
117 transaction_request(add_req).map(|res| res.map_err(map_add_err).map(|_| ())),
118 ),
119 Err(e) => Either::Right(std::future::ready(Err(map_add_err(e)))),
120 }
121 }
122
123 pub fn put(&self, value: &JsValue) -> impl Future<Output = crate::Result<JsValue, Err>> {
129 match self.sys.put(value) {
130 Ok(add_req) => {
131 Either::Left(transaction_request(add_req).map(|res| res.map_err(map_add_err)))
132 }
133 Err(e) => Either::Right(std::future::ready(Err(map_add_err(e)))),
134 }
135 }
136
137 pub fn put_kv(
143 &self,
144 key: &JsValue,
145 value: &JsValue,
146 ) -> impl Future<Output = crate::Result<(), Err>> {
147 match self.sys.put_with_key(value, key) {
148 Ok(add_req) => Either::Left(
149 transaction_request(add_req).map(|res| res.map_err(map_add_err).map(|_| ())),
150 ),
151 Err(e) => Either::Right(std::future::ready(Err(map_add_err(e)))),
152 }
153 }
154
155 pub fn clear(&self) -> impl Future<Output = crate::Result<(), Err>> {
159 match self.sys.clear() {
160 Ok(clear_req) => Either::Left(
161 transaction_request(clear_req).map(|res| res.map_err(map_clear_err).map(|_| ())),
162 ),
163 Err(err) => Either::Right(std::future::ready(Err(map_clear_err(err)))),
164 }
165 }
166
167 pub fn count(&self) -> impl Future<Output = crate::Result<usize, Err>> {
171 match self.sys.count() {
172 Ok(count_req) => Either::Left(
173 transaction_request(count_req)
174 .map(|res| res.map_err(map_count_err).map(map_count_res)),
175 ),
176 Err(e) => Either::Right(std::future::ready(Err(map_count_err(e)))),
177 }
178 }
179
180 pub fn contains(&self, key: &JsValue) -> impl Future<Output = crate::Result<bool, Err>> {
184 match self.sys.count_with_key(key) {
185 Ok(count_req) => Either::Left(
186 transaction_request(count_req)
187 .map(|res| res.map_err(map_count_err).map(|n| map_count_res(n) != 0)),
188 ),
189 Err(e) => Either::Right(std::future::ready(Err(map_count_err(e)))),
190 }
191 }
192
193 pub fn count_in(
199 &self,
200 range: impl RangeBounds<JsValue>,
201 ) -> impl Future<Output = crate::Result<usize, Err>> {
202 let range = match make_key_range(range) {
203 Ok(range) => range,
204 Err(e) => return Either::Left(std::future::ready(Err(e))),
205 };
206 match self.sys.count_with_key(&range) {
207 Ok(count_req) => Either::Right(
208 transaction_request(count_req)
209 .map(|res| res.map_err(map_count_err).map(map_count_res)),
210 ),
211 Err(e) => Either::Left(std::future::ready(Err(map_count_err(e)))),
212 }
213 }
214
215 pub fn delete(&self, key: &JsValue) -> impl Future<Output = crate::Result<(), Err>> {
221 match self.sys.delete(key) {
222 Ok(delete_req) => Either::Left(
223 transaction_request(delete_req).map(|res| res.map_err(map_delete_err).map(|_| ())),
224 ),
225 Err(e) => Either::Right(std::future::ready(Err(map_delete_err(e)))),
226 }
227 }
228
229 pub fn delete_range(
236 &self,
237 range: impl RangeBounds<JsValue>,
238 ) -> impl Future<Output = crate::Result<(), Err>> {
239 let range = match make_key_range(range) {
240 Ok(range) => range,
241 Err(e) => return Either::Left(std::future::ready(Err(e))),
242 };
243 match self.sys.delete(&range) {
244 Ok(delete_req) => Either::Right(
245 transaction_request(delete_req).map(|res| res.map_err(map_delete_err).map(|_| ())),
246 ),
247 Err(e) => Either::Left(std::future::ready(Err(map_delete_err(e)))),
248 }
249 }
250
251 pub fn get(&self, key: &JsValue) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
255 match self.sys.get(key) {
256 Ok(get_req) => Either::Right(
257 transaction_request(get_req)
258 .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
259 ),
260 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
261 }
262 }
263
264 pub fn get_first_in(
270 &self,
271 range: impl RangeBounds<JsValue>,
272 ) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
273 let range = match make_key_range(range) {
274 Ok(range) => range,
275 Err(e) => return Either::Left(std::future::ready(Err(e))),
276 };
277 match self.sys.get(&range) {
278 Ok(get_req) => Either::Right(
279 transaction_request(get_req)
280 .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
281 ),
282 Err(e) => Either::Left(std::future::ready(Err(map_get_err(e)))),
283 }
284 }
285
286 pub fn get_all(
290 &self,
291 limit: Option<u32>,
292 ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
293 let get_req = match limit {
294 None => self.sys.get_all(),
295 Some(limit) => self
296 .sys
297 .get_all_with_key_and_limit(&JsValue::UNDEFINED, limit),
298 };
299 match get_req {
300 Ok(get_req) => Either::Right(
301 transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
302 ),
303 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
304 }
305 }
306
307 pub fn get_all_in(
311 &self,
312 range: impl RangeBounds<JsValue>,
313 limit: Option<u32>,
314 ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
315 let range = match make_key_range(range) {
316 Ok(range) => range,
317 Err(e) => return Either::Left(std::future::ready(Err(e))),
318 };
319 let get_req = match limit {
320 None => self.sys.get_all_with_key(&range),
321 Some(limit) => self.sys.get_all_with_key_and_limit(&range, limit),
322 };
323 match get_req {
324 Ok(get_req) => Either::Right(
325 transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
326 ),
327 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
328 }
329 }
330
331 pub fn get_first_key_in(
335 &self,
336 range: impl RangeBounds<JsValue>,
337 ) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
338 let range = match make_key_range(range) {
339 Ok(range) => range,
340 Err(e) => return Either::Left(std::future::ready(Err(e))),
341 };
342 match self.sys.get_key(&range) {
343 Ok(get_req) => Either::Right(
344 transaction_request(get_req)
345 .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
346 ),
347 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
348 }
349 }
350
351 pub fn get_all_keys(
355 &self,
356 limit: Option<u32>,
357 ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
358 let get_req = match limit {
359 None => self.sys.get_all_keys(),
360 Some(limit) => self
361 .sys
362 .get_all_keys_with_key_and_limit(&JsValue::UNDEFINED, limit),
363 };
364 match get_req {
365 Ok(get_req) => Either::Right(
366 transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
367 ),
368 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
369 }
370 }
371
372 pub fn get_all_keys_in(
376 &self,
377 range: impl RangeBounds<JsValue>,
378 limit: Option<u32>,
379 ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
380 let range = match make_key_range(range) {
381 Ok(range) => range,
382 Err(e) => return Either::Left(std::future::ready(Err(e))),
383 };
384 let get_req = match limit {
385 None => self.sys.get_all_keys_with_key(&range),
386 Some(limit) => self.sys.get_all_keys_with_key_and_limit(&range, limit),
387 };
388 match get_req {
389 Ok(get_req) => Either::Right(
390 transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
391 ),
392 Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
393 }
394 }
395
396 pub fn index(&self, name: &str) -> crate::Result<Index<Err>, Err> {
400 Ok(Index::from_sys(self.sys.index(name).map_err(
401 |err| match error_name!(&err) {
402 Some("InvalidStateError") => crate::Error::ObjectStoreWasRemoved,
403 Some("NotFoundError") => crate::Error::DoesNotExist,
404 _ => crate::Error::from_js_value(err),
405 },
406 )?))
407 }
408
409 pub fn cursor(&self) -> CursorBuilder<Err> {
411 CursorBuilder::from_store(self.sys.clone())
412 }
413}
414
415pub struct IndexBuilder<'a, Err> {
417 store: IdbObjectStore,
418 name: &'a str,
419 key_path: JsValue,
420 options: IdbIndexParameters,
421 _phantom: PhantomData<Err>,
422}
423
424impl<'a, Err> IndexBuilder<'a, Err> {
425 pub fn create(self) -> crate::Result<(), Err> {
429 self.store
430 .create_index_with_str_sequence_and_optional_parameters(
431 self.name,
432 &self.key_path,
433 &self.options,
434 )
435 .map_err(|err| match error_name!(&err) {
436 Some("ConstraintError") => crate::Error::AlreadyExists,
437 Some("InvalidAccessError") => crate::Error::InvalidArgument,
438 Some("InvalidStateError") => crate::Error::ObjectStoreWasRemoved,
439 Some("SyntaxError") => crate::Error::InvalidKey,
440 _ => crate::Error::from_js_value(err),
441 })
442 .map(|_| ())
443 }
444
445 pub fn unique(self) -> Self {
449 self.options.set_unique(true);
450 self
451 }
452
453 pub fn multi_entry(self) -> Self {
457 self.options.set_multi_entry(true);
458 self
459 }
460}