Coder Social home page Coder Social logo

rust-indexed-db's People

Contributors

alorel avatar dependabot[bot] avatar github-actions[bot] avatar hywan avatar kurtbuilds avatar max-niederman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rust-indexed-db's Issues

`IdbVersionChangeEvent` should expose the current `IdbTransaction`

This event is fired during a transaction with mode versionchange, which should be available in event.target.transaction.

Although accessing the IdbDatabase allows us to create/delete object stores and set their indexes, it doesn't allow to access current stores. We need to access them via the IdbTransaction.

It would allow to change the indexes of the existing object stores, and to fix data in the stores or even migrate data from one store to the other.

Default transactions to abort on drop

Hey!

Here's an idea for indexed-db-futures v0.5: what if transactions aborted on drop, instead of committing on drop?

This'd make them farther away from the indexed-db standard, for sure, but the indexed-db standard is based on callbacks, which are pretty different from futures anyway. And, in Rust, it's very easy to miss one early-return point, to make sure that returning Err from a function aborts the transaction.

TL;DR:

fn foo() {
    let transaction = [...];
    transaction.add_key_val(...).unwrap().await.unwrap();
    do_some_check(&transaction).await?;
    Ok(())
}

This will (AFAICT) commit the transaction if do_some_check were to return an Err. The behavior I'd expect from the code if just reading it intuitively, would be for the transaction to be aborted.

In order to get the behavior I'd instinctively expect, I need to use the following code, which is quite a bit less pleasant to both write and read:

fn foo() {
    let transaction = [...];
    transaction.add_key_val(...).unwrap().await.unwrap();
    if let Err(e) = do_some_check(&transaction).await {
        transaction.abort().unwrap();
        return Err(e);
    }
    Ok(())
}

WDYT about adding a commit(self) function to transaction, that'd commit it (ie. just drop it), and to have IdbTransaction's Drop implementation abort the transaction if it has not been explicitly committed?

Anyway, I'm just starting using indexed-db-futures, but it already seems much, much more usable than the web-sys version. So, thank you! :D

What is wrong in code

Need help, I am new to rust. I am not sure what is wrong in below code

 use indexed_db_futures::prelude::*;
 use wasm_bindgen::prelude::*;
 use wasm_bindgen::JsValue;
 use wee_alloc::WeeAlloc;
 use web_sys::DomException;

 // use wasm_bindgen_futures::{spawn_local, JsFuture};
 // Use `wee_alloc` as the global allocator.
 #[global_allocator]
 static ALLOC: WeeAlloc = WeeAlloc::INIT;

 #[wasm_bindgen]
 pub struct AppIDB {
    pub db_name: String,
    pub store_name: String,
    pub version: u32,
    pub db: IdbDatabase
 }

 #[wasm_bindgen]
 impl AppIDB {
    pub async fn open_db(db_name: String, store_name: String, version: u32) -> Result<Option<AppIDB>, JsValue> {
        let db_name_clone = db_name.clone();
        let store_name_clone = store_name.clone();
        let version_clone = version.clone();
        let sn1 = store_name_clone.clone();

        let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&db_name_clone, version_clone)?;

        db_req.set_on_upgrade_needed(Some(move |evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
            // Check if the object store exists; create it if it doesn't
            if let None = evt.db().object_store_names().find(|n| n == sn1.as_str()) {
                evt.db().create_object_store(sn1.as_str())?;
            }
            Ok(())
        }));
        let db = db_req.into_future().await?;
        let app_idb = AppIDB {
            db,
            db_name: db_name_clone,
            store_name: store_name_clone,
            version: version_clone
        };

        Ok(Some(app_idb))
    }
    pub async fn get(self: AppIDB, key: String) -> Result<Option<JsValue>, DomException> {
        let key_clone = key.clone();
        // Get a record
        let tx = self.db.transaction_on_one(&self.store_name)?;
        let store = tx.object_store(&self.store_name)?;

        let value: Option<JsValue> = store.get_owned(key_clone)?.await?;
        //use_value(value);

        // All of the requests in the transaction have already finished so we can just drop it to
        // avoid the unused future warning, or assign it to _.
        let _ = tx;

        Ok(value)
    }
    pub async fn insert(self: AppIDB, key: String, value: String) -> Result<(), DomException> {
        let key_clone = key.clone();
        let value_clone = value.clone();
        // Insert/overwrite a record
        let tx: IdbTransaction = self.db
            .transaction_on_one_with_mode(&self.store_name, IdbTransactionMode::Readwrite)?;
        let store: IdbObjectStore = tx.object_store(&self.store_name)?;

        //let value_to_put: String = value.to_string();
        store.put_key_val_owned(key_clone, &JsValue::from(value_clone))?;

        // IDBTransactions can have an Error or an Abort event; into_result() turns both into a
        // DOMException
        tx.await.into_result()?;
        Ok(())
    }
    pub async fn delete(self: AppIDB, key: String) -> Result<(), DomException> {
        let key_clone = key.clone();
        // Delete a record
        let tx = self.db.transaction_on_one_with_mode(&self.store_name, IdbTransactionMode::Readwrite)?;
        let store = tx.object_store(&self.store_name)?;
        store.delete_owned(key_clone)?;
        tx.await.into_result()?;
        Ok(())
    }
}

My testcase is failing. below is the error message:

12 | #[wasm_bindgen]
   | ^^^^^^^^^^^^^^^ the trait `IntoWasmAbi` is not implemented for `indexed_db_futures::IdbDatabase`
   |
   = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

Sample code produces error

Hi, thanks for the library! I am trying to get a basic string-storage working as a first step, but ran into a problem while following the sample code in the lib.rs file. When attempting to use the exact code from the sample, it produces an error. I am using rustc v1.24.3.

db_req.set_on_upgrade_needed(Some(|evt: IdbVersionChangeEvent| -> Result<(), JsValue> {
	// Check if the object store exists; create it if it doesn't
	if let None = evt.db().object_store_names().find(|n| n == "my_store") {
		evt.db().create_object_store("my_store")?;
	}
	Ok(())
}));
type mismatch in closure arguments
expected signature of `for<'r> fn(&'r indexed_db_futures::IdbVersionChangeEvent) -> _`rustcE0631
indexeddb.rs(9, 36): found signature of `fn(indexed_db_futures::IdbVersionChangeEvent) -> _`

Do you have any suggestions? Thanks a lot!

Transaction listeners panic on error

Took good care to make sure the IDB transaction's result RefCell is only borrowed once at a time, but clearly missed a case somewhere. Doing

store.add_keyval(key, value);
store.add_keyval(key, value); // same key as before to trigger an error
tx.await; // this will panic

crashes as the code tries to mutably borrow the RefCell.

indexed_db_futures correctness currently relies on invalid assumptions about the executor

Currently, indexed_db_futures relies on behavior of the futures executor that is not actually specified: it assumes that waking a task from an IndexedDB callback, and then returning from the IndexedDB callback, will result in the awoken task being executed before returning to the browser event loop.

This is not necessarily true, and in particular breaks with the multi-threaded wasm-bindgen executor. I opened an issue upstream, but unfortunately it seems hard to fix, and was closed as wontfix, because that specific behavior of the singlethread executor was never documented in the first place:

I'm opening this issue to let you know about this current limitation of indexed_db_futures. Please don't just comment on the upstream issue without carefully considering whether you're bringing something new to the table, as that would only be bad vibes for the wasm-bindgen maintainers, and they're doing an awesome job.

On my end I'm planning to fix this in my IndexedDB crate via this change that makes the transaction only ever execute from the callbacks. I verified and it works with the multi-threaded executor, but it also requires a slightly less convenient API. I'm hoping one of the places where I'm opening this issue will have an idea for how to handle this differently :)

Hope that helps!

Issues with example code: One of the specified object stores was not found.

Based on https://github.com/leptos-rs/leptos/tree/main/examples/tailwind_csr_trunk

use indexed_db_futures::{
    prelude::{IdbObjectStore, IdbTransaction},
    request::IdbOpenDbRequestLike,
    web_sys::IdbTransactionMode,
    IdbDatabase, IdbQuerySource, IdbVersionChangeEvent,
};
use leptos::*;
use wasm_bindgen::JsValue;

use crate::app::navbar::Navbar;

async fn prepare_db() -> String {
  println!("prepare db");

    let mut db_req = indexed_db_futures::IdbDatabase::open_u32("my_db", 1).unwrap();
    db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
        // Check if the object store exists; create it if it doesn't
        println!("upgrade needed");
        if let None = evt.db().object_store_names().find(|n| n == "my_store") {
            evt.db().create_object_store("my_store")?;
        }
        Ok(())
    }));

    Ok("test".to_string())
}

#[component]
pub fn Settings(cx: Scope) -> impl IntoView {
    let settings = create_local_resource(cx, || (), |_| async { prepare_db().await });

    view! { cx,
      <Navbar></Navbar>

      <div class="flex flex-col w-full border-opacity-50">
        <div class="grid h-20 card rounded-box place-items-center">
          <div class="card w-9/12 bg-secondary-content shadow-xl">
            <div class="card-body">

            <Await
            // `future` provides the `Future` to be resolved
            future=|cx| prepare_db()
            // the data is bound to whatever variable name you provide
            bind:data
            >
            <p>{data} " example..."</p>

            </Await>

          </div>
       </div>
    </div>
    }
}

the console prints

tailwind-csr-trunk-b03fafd26d188d41.js:353 panicked at src/app/settings.rs:29:10:
called `Result::unwrap()` on an `Err` value: DomException { obj: Object { obj: JsValue(NotFoundError: Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.
Error: Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.
    at http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41.js:640:33
    at handleError (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41.js:285:18)
    at imports.wbg.__wbg_transaction_d6f1ef0b34b58a31 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41.js:638:70)
    at web_sys::features::gen_IdbDatabase::IdbDatabase::transaction_with_str_and_mode::h9680dab48f88a29d (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[2074]:0x321e85)
    at indexed_db_futures::idb_database::IdbDatabase::transaction_on_one_with_mode::h6d88b6cf300c7fe8 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[1260]:0x2cd142)
    at tailwind_csr_trunk::app::settings::prepare_db::{{closure}}::h6768f907d6776f11 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[494]:0x2184ae)
    at tailwind_csr_trunk::app::settings::Settings::__Settings::{{closure}}::{{closure}}::h137b5fabeaf8260e (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[1226]:0x2c8ad7)
    at <core::pin::Pin<P> as core::future::future::Future>::poll::h5f631c18c6c04a0b (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[4431]:0x3ae561)
    at leptos_reactive::resource::ResourceState<S,T>::load::{{closure}}::{{closure}}::hf79dd7b6b235cccc (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[164]:0x41b99)
    at wasm_bindgen_futures::task::singlethread::Task::run::h9d4a0df6390fda94 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[1257]:0x2ccbca)) } }

Stack:

Error
    at imports.wbg.__wbg_new_abda76e883ba8a5f (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41.js:356:17)
    at console_error_panic_hook::Error::new::hf9c38ed484c370cc (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[7904]:0x40051c)
    at console_error_panic_hook::hook_impl::hf60de235cbe0d0f8 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[1679]:0x2fd107)
    at console_error_panic_hook::hook::h56198eb557ac7d98 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[8471]:0x4083af)
    at core::ops::function::Fn::call::ha69410e5d033974e (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[7598]:0x3fb91b)
    at std::panicking::rust_panic_with_hook::he756764f61c69795 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[3570]:0x387195)
    at std::panicking::begin_panic_handler::{{closure}}::hc32c236557ee2684 (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[4445]:0x3aee18)
    at std::sys_common::backtrace::__rust_end_short_backtrace::hb038a7c53aa47b5e (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[10033]:0x417335)
    at rust_begin_unwind (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[6207]:0x3e1634)
    at core::panicking::panic_fmt::h4f7b0621cfc33d8f (http://127.0.0.1:8080/tailwind-csr-trunk-b03fafd26d188d41_bg.wasm:wasm-function[8347]:0x406a4e)

Lifetime is wrong about &IdbVersionChangeEvent.

The cargo check's result is ok if i written the type of evt in the callback.
image
But if i am not writting the type of evt and let it infered by the compiler, the cargo check complains me...
image
Is it a problem of the rust compiler?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.