Skip to content

Commit b70ca04

Browse files
committed
allow executing any requests inside the versionchange transaction
1 parent bebe8e0 commit b70ca04

File tree

5 files changed

+79
-22
lines changed

5 files changed

+79
-22
lines changed

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ async fn example() -> anyhow::Result<()> {
3333
let db = factory
3434
.open("database", 1, |evt| async move {
3535
let db = evt.database();
36-
db.build_object_store("store").auto_increment().create()?;
36+
let store = db.build_object_store("store").auto_increment().create()?;
37+
38+
// You can also add objects from this callback
39+
store.add(&JsString::from("foo")).await?;
40+
3741
Ok(())
3842
})
3943
.await
@@ -44,8 +48,8 @@ async fn example() -> anyhow::Result<()> {
4448
.rw()
4549
.run(|t| async move {
4650
let store = t.object_store("store")?;
47-
store.add(&JsString::from("foo")).await?;
4851
store.add(&JsString::from("bar")).await?;
52+
store.add(&JsString::from("baz")).await?;
4953
Ok(())
5054
})
5155
.await?;
@@ -69,9 +73,9 @@ async fn example() -> anyhow::Result<()> {
6973
.rw()
7074
.run(|t| async move {
7175
let store = t.object_store("store")?;
72-
store.add(&JsString::from("baz")).await?;
73-
if store.count().await? > 2 {
74-
// Oops! In this example, we have 3 items by this point
76+
store.add(&JsString::from("quux")).await?;
77+
if store.count().await? > 3 {
78+
// Oops! In this example, we have 4 items by this point
7579
Err(std::io::Error::new(
7680
std::io::ErrorKind::Other,
7781
"Too many objects in store",
@@ -86,7 +90,7 @@ async fn example() -> anyhow::Result<()> {
8690
db.transaction(&["store"])
8791
.run(|t| async move {
8892
let num_items = t.object_store("store")?.count().await?;
89-
assert_eq!(num_items, 2);
93+
assert_eq!(num_items, 3);
9094
Ok(())
9195
})
9296
.await?;

examples/basic.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ async fn example() -> anyhow::Result<()> {
1212
let db = factory
1313
.open("database", 1, |evt| async move {
1414
let db = evt.database();
15-
db.build_object_store("store").auto_increment().create()?;
15+
let store = db.build_object_store("store").auto_increment().create()?;
16+
17+
// You can also add objects from this callback
18+
store.add(&JsString::from("foo")).await?;
19+
1620
Ok(())
1721
})
1822
.await
@@ -23,8 +27,8 @@ async fn example() -> anyhow::Result<()> {
2327
.rw()
2428
.run(|t| async move {
2529
let store = t.object_store("store")?;
26-
store.add(&JsString::from("foo")).await?;
2730
store.add(&JsString::from("bar")).await?;
31+
store.add(&JsString::from("baz")).await?;
2832
Ok(())
2933
})
3034
.await?;
@@ -48,9 +52,9 @@ async fn example() -> anyhow::Result<()> {
4852
.rw()
4953
.run(|t| async move {
5054
let store = t.object_store("store")?;
51-
store.add(&JsString::from("baz")).await?;
52-
if store.count().await? > 2 {
53-
// Oops! In this example, we have 3 items by this point
55+
store.add(&JsString::from("quux")).await?;
56+
if store.count().await? > 3 {
57+
// Oops! In this example, we have 4 items by this point
5458
Err(std::io::Error::new(
5559
std::io::ErrorKind::Other,
5660
"Too many objects in store",
@@ -65,7 +69,7 @@ async fn example() -> anyhow::Result<()> {
6569
db.transaction(&["store"])
6670
.run(|t| async move {
6771
let num_items = t.object_store("store")?.count().await?;
68-
assert_eq!(num_items, 2);
72+
assert_eq!(num_items, 3);
6973
Ok(())
7074
})
7175
.await?;

src/factory.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{utils::generic_request, Database};
1+
use crate::{transaction::TransactionPoller, utils::generic_request, Database, Transaction};
22
use futures_channel::oneshot;
33
use futures_util::{
44
future::{self, Either},
@@ -127,7 +127,14 @@ impl<Err: 'static> Factory<Err> {
127127
completion?;
128128
}
129129
Either::Left((evt, completion_fut)) => {
130-
on_upgrade_needed(evt.expect("Closure dropped before its end of scope")).await?;
130+
let evt = evt.expect("Closure dropped before its end of scope");
131+
let transaction = evt.transaction().as_sys().clone();
132+
TransactionPoller {
133+
fut: on_upgrade_needed(evt),
134+
transaction,
135+
pending_requests: 0,
136+
}
137+
.await?;
131138
completion_fut.await?;
132139
}
133140
}
@@ -147,21 +154,31 @@ impl<Err: 'static> Factory<Err> {
147154
pub struct VersionChangeEvent<Err> {
148155
sys: IdbVersionChangeEvent,
149156
db: Database<Err>,
157+
transaction: Transaction<Err>,
150158
}
151159

152160
impl<Err> VersionChangeEvent<Err> {
153161
fn from_sys(sys: IdbVersionChangeEvent) -> VersionChangeEvent<Err> {
154-
let db_sys = sys
162+
let db_req = sys
155163
.target()
156164
.expect("IDBVersionChangeEvent had no target")
157165
.dyn_into::<IdbOpenDbRequest>()
158-
.expect("IDBVersionChangeEvent target was not an IDBOpenDBRequest")
166+
.expect("IDBVersionChangeEvent target was not an IDBOpenDBRequest");
167+
let db_sys = db_req
159168
.result()
160169
.expect("IDBOpenDBRequest had no result in its on_upgrade_needed handler")
161170
.dyn_into::<IdbDatabase>()
162171
.expect("IDBOpenDBRequest result was not an IDBDatabase");
172+
let transaction_sys = db_req
173+
.transaction()
174+
.expect("IDBOpenDBRequest had no associated transaction");
163175
let db = Database::from_sys(db_sys);
164-
VersionChangeEvent { sys, db }
176+
let transaction = Transaction::from_sys(transaction_sys);
177+
VersionChangeEvent {
178+
sys,
179+
db,
180+
transaction,
181+
}
165182
}
166183

167184
/// The version before the database upgrade, clamped to `u32::MAX`
@@ -184,4 +201,11 @@ impl<Err> VersionChangeEvent<Err> {
184201
pub fn database(&self) -> &Database<Err> {
185202
&self.db
186203
}
204+
205+
/// The `versionchange` transaction that triggered this event
206+
///
207+
/// This transaction can be used to submit further requests.
208+
pub fn transaction(&self) -> &Transaction<Err> {
209+
&self.transaction
210+
}
187211
}

src/transaction.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,24 @@ pub(crate) async fn transaction_request<Err>(req: IdbRequest) -> crate::Result<J
126126
}
127127

128128
/// Wrapper for [`IDBTransaction`](https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction)
129+
#[derive(Debug)]
129130
pub struct Transaction<Err> {
130131
sys: IdbTransaction,
131132
_phantom: PhantomData<Err>,
132133
}
133134

134135
impl<Err> Transaction<Err> {
135-
fn from_sys(sys: IdbTransaction) -> Transaction<Err> {
136+
pub(crate) fn from_sys(sys: IdbTransaction) -> Transaction<Err> {
136137
Transaction {
137138
sys,
138139
_phantom: PhantomData,
139140
}
140141
}
141142

143+
pub(crate) fn as_sys(&self) -> &IdbTransaction {
144+
&self.sys
145+
}
146+
142147
/// Returns an [`ObjectStore`] that can be used to operate on data in this transaction
143148
///
144149
/// Internally, this uses [`IDBTransaction::object_store`](https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction/objectStore).
@@ -153,11 +158,11 @@ impl<Err> Transaction<Err> {
153158
}
154159

155160
pin_project_lite::pin_project! {
156-
struct TransactionPoller<F> {
161+
pub(crate) struct TransactionPoller<F> {
157162
#[pin]
158-
fut: F,
159-
transaction: IdbTransaction,
160-
pending_requests: usize,
163+
pub(crate) fut: F,
164+
pub(crate) transaction: IdbTransaction,
165+
pub(crate) pending_requests: usize,
161166
}
162167
}
163168

tests/js-panic.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,23 @@ async fn other_awaits_panic() {
3434

3535
tx.send(()).unwrap();
3636
}
37+
38+
#[wasm_bindgen_test]
39+
#[should_panic(expected = "Transaction blocked without any request under way")]
40+
async fn await_in_versionchange_panics() {
41+
let factory = Factory::<anyhow::Error>::get().unwrap();
42+
43+
let (tx, rx) = futures_channel::oneshot::channel();
44+
45+
factory
46+
.open("baz", 1, |evt| async move {
47+
let db = evt.database();
48+
db.build_object_store("data").auto_increment().create()?;
49+
rx.await.context("awaiting for something external")?;
50+
Ok(())
51+
})
52+
.await
53+
.unwrap();
54+
55+
tx.send(()).unwrap();
56+
}

0 commit comments

Comments
 (0)