Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transaction.commit() and get rid of all unwrap_throw() in code #10

Merged
merged 1 commit into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ default = ["console_error_panic_hook"]

[dependencies]
console_error_panic_hook = { version = "0.1.7", optional = true }
js-sys = "0.3.56"
js-sys = "0.3.57"
num-traits = { version = "0.2.14", default-features = false }
thiserror = "1.0.30"
tokio = { version = "1.17.0", default-features = false, features = ["sync"] }
wasm-bindgen = "0.2.79"
web-sys = { version = "0.3.56", features = [
wasm-bindgen = "0.2.80"
web-sys = { version = "0.3.57", features = [
"console",
"DomException",
"DomStringList",
Expand Down Expand Up @@ -54,7 +54,7 @@ wee_alloc = { version = "0.4.5", optional = true }
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
serde-wasm-bindgen = "0.4.2"
wasm-bindgen-test = "0.3.29"
wasm-bindgen-test = "0.3.30"

[profile.release]
# Tell `rustc` to optimize for small code size.
Expand Down
11 changes: 11 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub type Result<T> = std::result::Result<T, Error>;

/// Error type for `rexie` crate
#[derive(Debug, Error, Clone, PartialEq)]
#[non_exhaustive]
pub enum Error {
/// Error when receiving message from async channel
#[error("error when receiving message from async channel")]
Expand Down Expand Up @@ -52,6 +53,10 @@ pub enum Error {
#[error("failed to execute indexed db request: {}", js_error_display(.0))]
IndexedDbRequestError(JsValue),

/// Failed to execute indexed db upgrade
#[error("failed to execute indexed db upgrade: {}", js_error_display(.0))]
IndexedDbUpgradeFailed(JsValue),

/// Key range error
#[error("key range error: {}", js_error_display(.0))]
KeyRangeError(JsValue),
Expand All @@ -64,6 +69,10 @@ pub enum Error {
#[error("failed to open object store: {}", js_error_display(.0))]
ObjectStoreOpenFailed(JsValue),

/// Failed to commit indexed db transaction
#[error("failed to commit indexed db transaction: {}", js_error_display(.0))]
TransactionCommitFailed(JsValue),

/// Failed to execute indexed db transaction
#[error("failed to execute db transaction: {}", js_error_display(.0))]
TransactionExecutionFailed(JsValue),
Expand Down Expand Up @@ -99,9 +108,11 @@ impl From<Error> for JsValue {
Error::IndexedDbNotFound => "IndexedDbNotFound".into(),
Error::IndexedDbNotSupported(js_value) => js_value,
Error::IndexedDbOpenFailed(js_value) => js_value,
Error::IndexedDbUpgradeFailed(js_value) => js_value,
Error::KeyRangeError(js_value) => js_value,
Error::ObjectStoreCreationFailed(js_value) => js_value,
Error::ObjectStoreOpenFailed(js_value) => js_value,
Error::TransactionCommitFailed(js_value) => js_value,
Error::TransactionExecutionFailed(js_value) => js_value,
Error::TransactionOpenFailed(js_value) => js_value,
Error::WindowNotFound => "WindowNotFound".into(),
Expand Down
6 changes: 3 additions & 3 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ impl Index {
}
}

/// Creates a new index with given name and compound key path
pub fn new_compound<S: ToString>(name: &str, key_path: impl IntoIterator<Item = S>) -> Self {
/// Creates a new index with given name and key path array
pub fn new_array<'a>(name: &str, key_path_array: impl IntoIterator<Item = &'a str>) -> Self {
Self {
name: name.to_owned(),
key_path: key_path.into_iter().map(|s| s.to_string()).collect(),
key_path: key_path_array.into_iter().map(ToOwned::to_owned).collect(),
unique: None,
multi_entry: None,
}
Expand Down
19 changes: 10 additions & 9 deletions src/object_store.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashSet, string::ToString};
use std::collections::HashSet;

use js_sys::Array;
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -31,12 +31,9 @@ impl ObjectStore {
self
}

/// Specify compound key path for the object store
pub fn key_path_array<S: ToString>(
mut self,
key_path_array: impl IntoIterator<Item = S>,
) -> Self {
self.key_path = key_path_array.into_iter().map(|s| s.to_string()).collect();
/// Specify key path array for the object store
pub fn key_path_array<'a>(mut self, key_path_array: impl IntoIterator<Item = &'a str>) -> Self {
self.key_path = key_path_array.into_iter().map(ToOwned::to_owned).collect();
self
}

Expand Down Expand Up @@ -107,7 +104,9 @@ impl ObjectStore {
let mut indexes_to_remove = Vec::new();

for index in 0..db_index_names.length() {
let db_index_name = db_index_names.get(index).unwrap_throw();
let db_index_name = db_index_names.get(index).ok_or_else(|| {
Error::ObjectStoreCreationFailed("unable to get index name".into())
})?;

if index_names.contains(&db_index_name) {
index_names.remove(&db_index_name);
Expand All @@ -117,7 +116,9 @@ impl ObjectStore {
}

for index_name in indexes_to_remove {
object_store.delete_index(&index_name).unwrap_throw();
object_store
.delete_index(&index_name)
.map_err(Error::ObjectStoreCreationFailed)?;
}

Ok(())
Expand Down
52 changes: 30 additions & 22 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,8 @@ pub async fn wait_request<R: DbRequest + Unpin>(
pub async fn wait_transaction_abort(transaction: IdbTransaction) -> crate::Result<()> {
let (sender, receiver) = oneshot::channel();

let abort_closure: Closure<dyn FnMut(Event)> = Closure::once(move |_| {
sender
.send(())
.map_err(|_| Error::AsyncChannelError)
.unwrap_throw();
});
let abort_closure: Closure<dyn FnMut(Event) -> crate::Result<()>> =
Closure::once(move |_| sender.send(()).map_err(|_| Error::AsyncChannelError));

transaction.set_onabort(Some(get_callback(&abort_closure)));

Expand Down Expand Up @@ -127,14 +123,27 @@ impl DbRequest for IdbTransaction {
}
}

impl<T> DbRequest for &T
where
T: DbRequest,
{
fn on_success(&self, callback: Option<&Function>) {
(**self).on_success(callback);
}

fn on_error(&self, callback: Option<&Function>) {
(**self).on_error(callback);
}
}

#[must_use = "futures do nothing unless polled or spawned"]
pub struct RequestFuture<R>
where
R: DbRequest + Unpin,
{
_inner: R,
_success_closure: Closure<dyn FnMut(Event)>,
_error_closure: Closure<dyn FnMut(Event)>,
_success_closure: Closure<dyn FnMut(Event) -> crate::Result<()>>,
_error_closure: Closure<dyn FnMut(Event) -> crate::Result<()>>,
receiver: UnboundedReceiver<Result<JsValue, JsValue>>,
map_err: fn(JsValue) -> Error,
}
Expand Down Expand Up @@ -179,23 +188,24 @@ where

fn get_success_closure(
sender: UnboundedSender<Result<JsValue, JsValue>>,
) -> Closure<dyn FnMut(Event)> {
) -> Closure<dyn FnMut(Event) -> crate::Result<()>> {
Closure::once(move |event: Event| {
let target = event.target().unwrap_throw();
let target = event.target().ok_or(Error::EventTargetNotFound)?;
let request: &IdbRequest = AsRef::<JsValue>::as_ref(&target).unchecked_ref();

sender
.send(request.result())
.map_err(|_| Error::AsyncChannelError)
.unwrap_throw();
.map_err(|_| Error::AsyncChannelError)?;

Ok(())
})
}

fn get_error_closure(
sender: UnboundedSender<Result<JsValue, JsValue>>,
) -> Closure<dyn FnMut(Event)> {
) -> Closure<dyn FnMut(Event) -> crate::Result<()>> {
Closure::once(move |event: Event| {
let target = event.target().unwrap_throw();
let target = event.target().ok_or(Error::EventTargetNotFound)?;
let request: &IdbRequest = AsRef::<JsValue>::as_ref(&target).unchecked_ref();

let error: Result<JsValue, JsValue> = match request.error() {
Expand All @@ -204,10 +214,9 @@ fn get_error_closure(
Err(error) => Err(error),
};

sender
.send(error)
.map_err(|_| Error::AsyncChannelError)
.unwrap_throw();
sender.send(error).map_err(|_| Error::AsyncChannelError)?;

Ok(())
})
}

Expand All @@ -217,15 +226,14 @@ fn get_cursor_closure(
advancing: Arc<AtomicBool>,
limit: Option<u32>,
offset: u32,
) -> Closure<dyn FnMut(Event)> {
) -> Closure<dyn FnMut(Event) -> crate::Result<()>> {
Closure::wrap(Box::new(move |event| {
sender
.send(cursor_closure_inner(
event, &seen, &advancing, limit, offset,
))
.map_err(|_| Error::AsyncChannelError)
.unwrap_throw();
}) as Box<dyn FnMut(Event)>)
}) as Box<dyn FnMut(Event) -> crate::Result<()>>)
}

fn cursor_closure_inner(
Expand Down Expand Up @@ -281,6 +289,6 @@ fn cursor_closure_inner(
}
}

fn get_callback(closure: &Closure<dyn FnMut(Event)>) -> &Function {
fn get_callback(closure: &Closure<dyn FnMut(Event) -> crate::Result<()>>) -> &Function {
closure.as_ref().unchecked_ref()
}
14 changes: 8 additions & 6 deletions src/rexie_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ fn get_idb_open_request(name: &str, version: Option<u32>) -> Result<IdbOpenDbReq
fn set_upgrade_handler(
idb_open_request: &IdbOpenDbRequest,
object_stores: Vec<ObjectStore>,
) -> Closure<dyn FnMut(Event)> {
let upgrade_handler = Closure::once(move |event: Event| {
upgrade_handler(event, object_stores).unwrap_throw();
});
) -> Closure<dyn FnMut(Event) -> Result<()>> {
let upgrade_handler =
Closure::once(move |event: Event| -> Result<()> { upgrade_handler(event, object_stores) });

idb_open_request.set_onupgradeneeded(Some(upgrade_handler.as_ref().unchecked_ref()));

Expand Down Expand Up @@ -117,7 +116,9 @@ fn upgrade_handler(event: Event, object_stores: Vec<ObjectStore>) -> Result<()>
let mut stores_to_remove = Vec::new();

for index in 0..db_store_names.length() {
let db_store_name = db_store_names.get(index).unwrap_throw();
let db_store_name = db_store_names
.get(index)
.ok_or_else(|| Error::IndexedDbUpgradeFailed("unable to get store name".into()))?;

if store_names.contains(&db_store_name) {
store_names.remove(&db_store_name);
Expand All @@ -127,7 +128,8 @@ fn upgrade_handler(event: Event, object_stores: Vec<ObjectStore>) -> Result<()>
}

for store_name in stores_to_remove {
idb.delete_object_store(&store_name).unwrap_throw();
idb.delete_object_store(&store_name)
.map_err(Error::IndexedDbUpgradeFailed)?;
}

Ok(())
Expand Down
23 changes: 22 additions & 1 deletion src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ pub struct Transaction {
impl Transaction {
/// Returns mode of the transaction
pub fn mode(&self) -> TransactionMode {
self.idb_transaction.mode().unwrap_throw().into()
self.idb_transaction
.mode()
.expect_throw("unable to get transaction mode")
.into()
}

/// Returns names of all stores in the transaction
Expand All @@ -77,6 +80,24 @@ impl Transaction {
wait_transaction_abort(self.idb_transaction).await
}

/// Commits a transaction
///
/// # Note
///
/// Note that `commit()` doesn't normally have to be called — a transaction will automatically commit when all
/// outstanding requests have been satisfied and no new requests have been made.
///
/// [Reference](https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction/commit)
pub async fn commit(self) -> Result<()> {
let done = wait_request(&self.idb_transaction, Error::TransactionExecutionFailed);

self.idb_transaction
.commit()
.map_err(Error::TransactionCommitFailed)?;

done.await.map(|_| ())
}

/// Waits for a transaction to complete.
pub async fn done(self) -> Result<()> {
wait_request(self.idb_transaction, Error::TransactionExecutionFailed)
Expand Down
8 changes: 4 additions & 4 deletions tests/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async fn create_db() -> Rexie {
.add_object_store(
ObjectStore::new("invoices")
.key_path_array(["id", "year"])
.add_index(Index::new_compound("agent_customer", ["agent", "customer"])),
.add_index(Index::new_array("agent_customer", ["agent", "customer"])),
)
.build()
.await;
Expand Down Expand Up @@ -100,7 +100,7 @@ async fn basic_test_db(rexie: &Rexie) {
assert!(email_index.unique());
assert!(!email_index.multi_entry());

assert!(transaction.done().await.is_ok());
assert!(transaction.commit().await.is_ok());
}

/// Closes and deletes the database
Expand All @@ -122,7 +122,7 @@ async fn add_employee(rexie: &Rexie, name: &str, email: &str) -> Result<u32> {
let employee = serde_wasm_bindgen::to_value(&employee).unwrap();
let employee_id = employees.add(&employee, None).await?;

transaction.done().await?;
transaction.commit().await?;
Ok(num_traits::cast(employee_id.as_f64().unwrap()).unwrap())
}

Expand Down Expand Up @@ -213,7 +213,7 @@ async fn add_invoice(
let invoice = serde_wasm_bindgen::to_value(&invoice).unwrap();
invoices.add(&invoice, None).await?;

transaction.done().await?;
transaction.commit().await?;
Ok(())
}

Expand Down