1use crate::{
2 transaction::transaction_request,
3 utils::{
4 make_key_range, map_cursor_advance_err, map_cursor_advance_until_err,
5 map_cursor_advance_until_primary_key_err, map_cursor_delete_err, map_cursor_update_err,
6 map_open_cursor_err,
7 },
8};
9use futures_util::future::Either;
10use std::{future::Future, marker::PhantomData, ops::RangeBounds};
11use web_sys::{
12 wasm_bindgen::{JsCast, JsValue},
13 IdbCursor, IdbCursorDirection, IdbCursorWithValue, IdbIndex, IdbObjectStore, IdbRequest,
14};
15
16#[cfg(doc)]
17use crate::{Index, ObjectStore};
18#[cfg(doc)]
19use web_sys::js_sys::Array;
20
21pub enum CursorDirection {
23 Next,
25
26 NextUnique,
28
29 Prev,
31
32 PrevUnique,
34}
35
36impl CursorDirection {
37 pub(crate) fn to_sys(&self) -> IdbCursorDirection {
38 match self {
39 CursorDirection::Next => IdbCursorDirection::Next,
40 CursorDirection::NextUnique => IdbCursorDirection::Nextunique,
41 CursorDirection::Prev => IdbCursorDirection::Prev,
42 CursorDirection::PrevUnique => IdbCursorDirection::Prevunique,
43 }
44 }
45}
46
47pub struct CursorBuilder<Err> {
49 source: Either<IdbObjectStore, IdbIndex>,
50 query: JsValue,
51 direction: IdbCursorDirection,
52 _phantom: PhantomData<Err>,
53}
54
55impl<Err> CursorBuilder<Err> {
56 pub(crate) fn from_store(store: IdbObjectStore) -> CursorBuilder<Err> {
57 CursorBuilder {
58 source: Either::Left(store),
59 query: JsValue::UNDEFINED,
60 direction: IdbCursorDirection::Next,
61 _phantom: PhantomData,
62 }
63 }
64
65 pub(crate) fn from_index(index: IdbIndex) -> CursorBuilder<Err> {
66 CursorBuilder {
67 source: Either::Right(index),
68 query: JsValue::UNDEFINED,
69 direction: IdbCursorDirection::Next,
70 _phantom: PhantomData,
71 }
72 }
73
74 pub fn open(self) -> impl Future<Output = crate::Result<Cursor<Err>, Err>> {
78 let req = match self.source {
79 Either::Left(store) => {
80 store.open_cursor_with_range_and_direction(&self.query, self.direction)
81 }
82 Either::Right(index) => {
83 index.open_cursor_with_range_and_direction(&self.query, self.direction)
84 }
85 };
86 match req {
87 Ok(open_req) => Either::Right(Cursor::from(open_req)),
88 Err(err) => Either::Left(std::future::ready(Err(map_open_cursor_err(err)))),
89 }
90 }
91
92 pub fn open_key(self) -> impl Future<Output = crate::Result<Cursor<Err>, Err>> {
96 let req = match self.source {
97 Either::Left(store) => {
98 store.open_key_cursor_with_range_and_direction(&self.query, self.direction)
99 }
100 Either::Right(index) => {
101 index.open_key_cursor_with_range_and_direction(&self.query, self.direction)
102 }
103 };
104 match req {
105 Ok(open_req) => Either::Right(Cursor::from(open_req)),
106 Err(err) => Either::Left(std::future::ready(Err(map_open_cursor_err(err)))),
107 }
108 }
109
110 pub fn range(mut self, range: impl RangeBounds<JsValue>) -> crate::Result<Self, Err> {
114 self.query = make_key_range(range)?;
115 Ok(self)
116 }
117
118 pub fn direction(mut self, direction: CursorDirection) -> Self {
122 self.direction = direction.to_sys();
123 self
124 }
125}
126
127pub struct Cursor<Err> {
129 sys: Option<IdbCursor>,
130 req: IdbRequest,
131 _phantom: PhantomData<Err>,
132}
133
134impl<Err> Cursor<Err> {
135 pub(crate) async fn from(req: IdbRequest) -> crate::Result<Cursor<Err>, Err> {
136 let res = transaction_request(req.clone())
137 .await
138 .map_err(map_open_cursor_err)?;
139 let is_already_over = res.is_null();
140 let sys = (!is_already_over).then(|| {
141 res.dyn_into::<IdbCursor>()
142 .expect("Cursor-returning request did not return an IDBCursor")
143 });
144 Ok(Cursor {
145 sys,
146 req,
147 _phantom: PhantomData,
148 })
149 }
150
151 pub fn value(&self) -> Option<JsValue> {
157 self.sys.as_ref().map(|sys| {
158 sys.dyn_ref::<IdbCursorWithValue>()
159 .expect("Called Cursor::value on a key-only cursor")
160 .value()
161 .expect("Unable to retrieve value from known-good cursor")
162 })
163 }
164
165 pub fn key(&self) -> Option<JsValue> {
169 self.sys.as_ref().map(|sys| {
170 sys.key()
171 .expect("Failed retrieving key from known-good cursor")
172 })
173 }
174
175 pub fn primary_key(&self) -> Option<JsValue> {
179 self.sys.as_ref().map(|sys| {
180 sys.primary_key()
181 .expect("Failed retrieving primary key from known-good cursor")
182 })
183 }
184
185 pub async fn advance(&mut self, count: u32) -> crate::Result<(), Err> {
189 let Some(sys) = &self.sys else {
190 return Err(crate::Error::CursorCompleted);
191 };
192 sys.advance(count).map_err(map_cursor_advance_err)?;
193 if transaction_request(self.req.clone())
194 .await
195 .map_err(map_cursor_advance_err)?
196 .is_null()
197 {
198 self.sys = None;
199 }
200 Ok(())
201 }
202
203 pub async fn advance_until(&mut self, key: &JsValue) -> crate::Result<(), Err> {
207 let Some(sys) = &self.sys else {
208 return Err(crate::Error::CursorCompleted);
209 };
210 sys.continue_with_key(key)
211 .map_err(map_cursor_advance_until_err)?;
212 if transaction_request(self.req.clone())
213 .await
214 .map_err(map_cursor_advance_until_err)?
215 .is_null()
216 {
217 self.sys = None;
218 }
219 Ok(())
220 }
221
222 pub async fn advance_until_primary_key(
233 &mut self,
234 index_key: &JsValue,
235 primary_key: &JsValue,
236 ) -> crate::Result<(), Err> {
237 let Some(sys) = &self.sys else {
238 return Err(crate::Error::CursorCompleted);
239 };
240 sys.continue_primary_key(&index_key, primary_key)
241 .map_err(map_cursor_advance_until_primary_key_err)?;
242 if transaction_request(self.req.clone())
243 .await
244 .map_err(map_cursor_advance_until_primary_key_err)?
245 .is_null()
246 {
247 self.sys = None;
248 }
249 Ok(())
250 }
251
252 pub async fn delete(&self) -> crate::Result<(), Err> {
258 let Some(sys) = &self.sys else {
259 return Err(crate::Error::CursorCompleted);
260 };
261 let req = sys.delete().map_err(map_cursor_delete_err)?;
262 transaction_request(req)
263 .await
264 .map_err(map_cursor_delete_err)?;
265 Ok(())
266 }
267
268 pub async fn update(&self, value: &JsValue) -> crate::Result<(), Err> {
274 let Some(sys) = &self.sys else {
275 return Err(crate::Error::CursorCompleted);
276 };
277 let req = sys.update(value).map_err(map_cursor_update_err)?;
278 transaction_request(req)
279 .await
280 .map_err(map_cursor_update_err)?;
281 Ok(())
282 }
283}