Skip to content
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
62 changes: 46 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,32 @@ use indexed_db_futures::database::Database;
use indexed_db_futures::prelude::*;
use indexed_db_futures::transaction::TransactionMode;

#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Serialize, Deserialize)]
struct MySerdeType(u8, String);

#[derive(Deserialize)]
#[serde(untagged)]
enum ObjectOrString {
Object(MySerdeType),
String(String),
}

async fn main() -> indexed_db_futures::OpenDbResult<()> {
let db = Database::open("my_db")
.with_version(2u8)
.with_on_upgrade_needed(|event, db| {
// Convert versions from floats to integers to allow using them in match expressions
let old_version = event.old_version() as u64;
let new_version = event.new_version().map(|v| v as u64);

match (event.old_version(), event.new_version()) {
(0.0, Some(1.0)) => {
(0, Some(1)) => {
db.create_object_store("my_store")
.with_auto_increment(true)
.build()?;
}
(prev, Some(2.0)) => {
if prev == 1.0 {
(prev, Some(2)) => {
if prev == 1 {
let _ = db.delete_object_store("my_store");
}

Expand All @@ -59,28 +70,47 @@ async fn main() -> indexed_db_futures::OpenDbResult<()> {

store
.put("a primitive value that doesn't need serde")
.with_key("my_key")
.await?;

// awaiting individual requests is optional - they still go out
store.put(MySerdeType(10, "foos".into())).serde()?;
// Awaiting individual requests is optional - they still go out
store
.put(MySerdeType(10, "foos".into()))
.with_key("my_serde_key")
.with_key_type::<String>() // `serde` keys must be deserialisable; String is, but the &str above isn't
.serde()?;

// Unlike JS, transactions ROLL BACK INSTEAD OF COMMITTING BY DEFAULT
transaction.commit().await?;

// Read some data
let transaction = db.transaction("my_other_store").build()?;
let store = transaction.object_store("my_other_store")?;
let Some(mut cursor) = store.open_cursor().await? else {
// `None` is returned if the cursor is empty
return Ok(());
};

loop {
match cursor.next_record_ser::<MySerdeType>().await {
Ok(Some(record)) => handle_record(record),
Ok(None) => break,
Err(e) => handle_error(e),

// `None` is returned if the cursor is empty
if let Some(mut cursor) = store.open_cursor().await? {
// Use a limited loop in case we made a mistake and result in an infinite loop
for _ in 0..5 {
// We inserted a serde record and a primitive one so we need to deserialise as an enum that supports both
match cursor.next_record_ser::<ObjectOrString>().await {
Ok(Some(record)) => match record {
ObjectOrString::Object(serde_record) => {
assert_eq!(serde_record.0, 10);
assert_eq!(serde_record.1, "foos");
}
ObjectOrString::String(string_record) => {
assert_eq!(
string_record.as_str(),
"a primitive value that doesn't need serde"
);
}
},
Err(e) => return Err(e.into()),
Ok(None) => return Ok(()), // reached cursor end
}
}

panic!("Got an infinite loop!");
}

Ok(())
Expand Down
28 changes: 12 additions & 16 deletions src/error/serde.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cfg_if::cfg_if;
use std::fmt;

use super::Error;
use super::SerialisationError;
Expand All @@ -15,28 +16,23 @@ cfg_if! {
/// [`serde_wasm_bindgen::Error`](https://docs.rs/serde-wasm-bindgen/0.6.5/serde_wasm_bindgen/struct.Error.html).
///
/// Is an empty struct if the `serde` feature is not enabled.
#[cfg_attr(feature = "serde", derive(derive_more::From))]
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(derive_more::From), repr(transparent))]
#[cfg_attr(not(feature = "serde"), derive(StructName))]
pub struct SerdeError(#[cfg(feature = "serde")] BaseError);

macro_rules! display_like {
($for: ty > $($which: ident),+) => {
$(impl std::fmt::$which for $for {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::cfg_if::cfg_if! {
if #[cfg(feature = "serde")] {
std::fmt::$which::fmt(&self.0, f)
} else {
f.write_str(&<Self as StructName>::TYPE_NAME)
}
}
impl fmt::Display for SerdeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
::cfg_if::cfg_if! {
if #[cfg(feature = "serde")] {
fmt::Display::fmt(&self.0, f)
} else {
f.write_str(<Self as StructName>::TYPE_NAME)
}
})+
};
}
}
}

display_like!(SerdeError > Debug, Display);

impl ::std::error::Error for SerdeError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Expand Down
13 changes: 9 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,26 @@
//! Ok(())
//! })
//! .with_on_upgrade_needed_fut(|event, db| async move {
//! match (event.old_version(), event.new_version()) {
//! (0.0, Some(1.0)) => {
//! // Convert versions from floats to integers to allow using them in match expressions
//! let old_version = event.old_version() as u64;
//! let new_version = event.new_version().map(|v| v as u64);
//!
//! match (old_version, new_version) {
//! (0, Some(1)) => {
//! db.create_object_store("my_store")
//! .with_auto_increment(true)
//! .build()?;
//! }
//! (prev, Some(2.0)) => {
//! if prev == 1.0 {
//! (prev, Some(2)) => {
//! if prev == 1 {
//! if let Err(e) = db.delete_object_store("my_store") {
//! log::error!("Error deleting v1 object store: {}", e);
//! }
//! }
//!
//! // Create an object store and await its transaction before inserting data.
//! db.create_object_store("my_other_store")
//! .with_auto_increment(true)
//! .build()?
//! .transaction()
//! .on_done()?
Expand Down
Loading