indexed_db/
index.rs

1use crate::{
2    transaction::transaction_request,
3    utils::{
4        array_to_vec, make_key_range, map_count_err, map_count_res, map_get_err, none_if_undefined,
5    },
6    CursorBuilder,
7};
8use futures_util::future::{Either, FutureExt};
9use std::{future::Future, marker::PhantomData, ops::RangeBounds};
10use web_sys::{wasm_bindgen::JsValue, IdbIndex};
11
12#[cfg(doc)]
13use crate::Cursor;
14
15/// Wrapper for [`IDBIndex`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex),
16/// for use in transactions
17///
18/// Most of the functions here take a [`JsValue`] as the key(s) to use in the index. If the index was
19/// built with a compound key, then you should use eg. `js_sys::Array::from_iter([key_1, key_2])` as
20/// the key.
21pub struct Index<Err> {
22    sys: IdbIndex,
23    _phantom: PhantomData<Err>,
24}
25
26impl<Err> Index<Err> {
27    pub(crate) fn from_sys(sys: IdbIndex) -> Index<Err> {
28        Index {
29            sys,
30            _phantom: PhantomData,
31        }
32    }
33
34    /// Checks whether the provided key (for this index) already exists
35    ///
36    /// Internally, this uses [`IDBIndex::count`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/count).
37    pub fn contains(&self, key: &JsValue) -> impl Future<Output = crate::Result<bool, Err>> {
38        match self.sys.count_with_key(key) {
39            Ok(count_req) => Either::Right(
40                transaction_request(count_req)
41                    .map(|res| res.map_err(map_count_err).map(|n| map_count_res(n) != 0)),
42            ),
43            Err(e) => Either::Left(std::future::ready(Err(map_count_err(e)))),
44        }
45    }
46
47    /// Count all the keys (for this index) in the provided range
48    ///
49    /// Internally, this uses [`IDBIndex::count`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/count).
50    pub fn count_in(
51        &self,
52        range: impl RangeBounds<JsValue>,
53    ) -> impl Future<Output = crate::Result<usize, Err>> {
54        let range = match make_key_range(range) {
55            Ok(range) => range,
56            Err(e) => return Either::Left(std::future::ready(Err(e))),
57        };
58        match self.sys.count_with_key(&range) {
59            Ok(count_req) => Either::Right(
60                transaction_request(count_req)
61                    .map(|res| res.map_err(map_count_err).map(map_count_res)),
62            ),
63            Err(e) => Either::Left(std::future::ready(Err(map_count_err(e)))),
64        }
65    }
66
67    /// Get the object with key `key` for this index
68    ///
69    /// Internally, this uses [`IDBIndex::get`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/get).
70    pub fn get(&self, key: &JsValue) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
71        match self.sys.get(key) {
72            Ok(get_req) => Either::Right(
73                transaction_request(get_req)
74                    .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
75            ),
76            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
77        }
78    }
79
80    /// Get the first value with a key (for this index) in `range`, ordered by key (for this index)
81    ///
82    /// Note that the unbounded range is not a valid range for IndexedDB.
83    ///
84    /// Internally, this uses [`IDBIndex::get`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/get).
85    pub fn get_first_in(
86        &self,
87        range: impl RangeBounds<JsValue>,
88    ) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
89        let range = match make_key_range(range) {
90            Ok(range) => range,
91            Err(e) => return Either::Left(std::future::ready(Err(e))),
92        };
93        match self.sys.get(&range) {
94            Ok(get_req) => Either::Right(
95                transaction_request(get_req)
96                    .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
97            ),
98            Err(e) => Either::Left(std::future::ready(Err(map_get_err(e)))),
99        }
100    }
101
102    /// Get all the objects in the store, ordered by this index, with a maximum number of results of `limit`
103    ///
104    /// Internally, this uses [`IDBIndex::getAll`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAll).
105    pub fn get_all(
106        &self,
107        limit: Option<u32>,
108    ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
109        let get_req = match limit {
110            None => self.sys.get_all(),
111            Some(limit) => self
112                .sys
113                .get_all_with_key_and_limit(&JsValue::UNDEFINED, limit),
114        };
115        match get_req {
116            Ok(get_req) => Either::Right(
117                transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
118            ),
119            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
120        }
121    }
122
123    /// Get all the objects with a key (for this index) in the provided range, with a maximum number of
124    /// results of `limit`, ordered by this index
125    ///
126    /// Internally, this uses [`IDBIndex::getAll`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAll).
127    pub fn get_all_in(
128        &self,
129        range: impl RangeBounds<JsValue>,
130        limit: Option<u32>,
131    ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
132        let range = match make_key_range(range) {
133            Ok(range) => range,
134            Err(e) => return Either::Left(std::future::ready(Err(e))),
135        };
136        let get_req = match limit {
137            None => self.sys.get_all_with_key(&range),
138            Some(limit) => self.sys.get_all_with_key_and_limit(&range, limit),
139        };
140        match get_req {
141            Ok(get_req) => Either::Right(
142                transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
143            ),
144            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
145        }
146    }
147
148    /// Get the first existing primary key for an object that has a key (for this index) in the provided range
149    ///
150    /// Internally, this uses [`IDBIndex::getKey`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getKey).
151    pub fn get_first_key_in(
152        &self,
153        range: impl RangeBounds<JsValue>,
154    ) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
155        let range = match make_key_range(range) {
156            Ok(range) => range,
157            Err(e) => return Either::Left(std::future::ready(Err(e))),
158        };
159        match self.sys.get_key(&range) {
160            Ok(get_req) => Either::Right(
161                transaction_request(get_req)
162                    .map(|res| res.map_err(map_get_err).map(none_if_undefined)),
163            ),
164            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
165        }
166    }
167
168    /// List all the primary keys in the object store, with a maximum number of results of `limit`, ordered by this index
169    ///
170    /// Internally, this uses [`IDBIndex::getAllKeys`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAllKeys).
171    pub fn get_all_keys(
172        &self,
173        limit: Option<u32>,
174    ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
175        let get_req = match limit {
176            None => self.sys.get_all_keys(),
177            Some(limit) => self
178                .sys
179                .get_all_keys_with_key_and_limit(&JsValue::UNDEFINED, limit),
180        };
181        match get_req {
182            Ok(get_req) => Either::Right(
183                transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
184            ),
185            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
186        }
187    }
188
189    /// List all the primary keys of objects with a key (for this index)in the provided range, with a maximum number
190    /// of results of `limit`, ordered by this index
191    ///
192    /// Internally, this uses [`IDBIndex::getAllKeys`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAllKeys).
193    pub fn get_all_keys_in(
194        &self,
195        range: impl RangeBounds<JsValue>,
196        limit: Option<u32>,
197    ) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
198        let range = match make_key_range(range) {
199            Ok(range) => range,
200            Err(e) => return Either::Left(std::future::ready(Err(e))),
201        };
202        let get_req = match limit {
203            None => self.sys.get_all_keys_with_key(&range),
204            Some(limit) => self.sys.get_all_keys_with_key_and_limit(&range, limit),
205        };
206        match get_req {
207            Ok(get_req) => Either::Right(
208                transaction_request(get_req).map(|res| res.map_err(map_get_err).map(array_to_vec)),
209            ),
210            Err(err) => Either::Left(std::future::ready(Err(map_get_err(err)))),
211        }
212    }
213
214    /// Open a [`Cursor`] on this index
215    pub fn cursor(&self) -> CursorBuilder<Err> {
216        CursorBuilder::from_index(self.sys.clone())
217    }
218}