Skip to content

Commit

Permalink
Add builders (for convenience) for building database (#21)
Browse files Browse the repository at this point in the history
* Add builders (for convenience) for building database

* Don't include doc feature in CI
  • Loading branch information
devashishdxt authored Jan 25, 2024
1 parent ea37af7 commit c8c7605
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
toolchain: stable
targets: wasm32-unknown-unknown
- name: Run cargo build
run: cargo build --target wasm32-unknown-unknown
run: cargo build --features builder --target wasm32-unknown-unknown
lint:
name: Lint
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "idb"
version = "0.6.0"
version = "0.6.1"
authors = ["Devashish Dixit <[email protected]>"]
license = "MIT/Apache-2.0"
description = "A futures based crate for interacting with IndexedDB on browsers using webassembly"
Expand All @@ -16,6 +16,7 @@ edition = "2021"
[features]
default = ["futures"]
doc = []
builder = ["futures"]
futures = ["tokio"]

[dependencies]
Expand Down
86 changes: 86 additions & 0 deletions src/builder/database_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::collections::HashSet;

use crate::{Database, DatabaseEvent, Error, Event, Factory};

use super::ObjectStoreBuilder;

/// Builder for databases.
#[derive(Debug)]
pub struct DatabaseBuilder {
name: String,
version: Option<u32>,
object_stores: Vec<ObjectStoreBuilder>,
}

impl DatabaseBuilder {
/// Creates a new instance of [`DatabaseBuilder`].
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
version: None,
object_stores: Vec::new(),
}
}

/// Sets the version of the database.
pub fn version(mut self, version: u32) -> Self {
self.version = Some(version);
self
}

/// Adds an object store.
pub fn add_object_store(mut self, object_store: ObjectStoreBuilder) -> Self {
self.object_stores.push(object_store);
self
}

/// Builds the database.
pub async fn build(self) -> Result<Database, Error> {
let factory = Factory::new()?;
let mut request = factory.open(&self.name, self.version)?;

request.on_upgrade_needed(move |event| {
let request = event.target().expect("open database request");

let mut store_names: HashSet<_> = self
.object_stores
.iter()
.map(|store| store.name().to_owned())
.collect();

let database = event.database().expect("database");

for object_store in self.object_stores {
object_store
.apply(&database, &request)
.expect("object store creation");
}

let db_store_names = database.store_names();
let mut stores_to_remove = Vec::new();

for db_store_name in db_store_names {
if !store_names.contains(&db_store_name) {
store_names.remove(&db_store_name);
} else {
stores_to_remove.push(db_store_name);
}
}

for store_name in stores_to_remove {
database
.delete_object_store(&store_name)
.expect("object store deletion");
}
});

let mut database = request.await?;

database.on_version_change(|event| {
let database = event.database().expect("database");
database.close();
});

Ok(database)
}
}
56 changes: 56 additions & 0 deletions src/builder/index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::{Error, IndexParams, KeyPath, ObjectStore};

/// Builder for object store indexes.
#[derive(Debug)]
pub struct IndexBuilder {
name: String,
key_path: KeyPath,
unique: Option<bool>,
multi_entry: Option<bool>,
}

impl IndexBuilder {
/// Creates a new instance of [`IndexBuilder`].
pub fn new(name: String, key_path: KeyPath) -> Self {
Self {
name,
key_path,
unique: None,
multi_entry: None,
}
}

/// Returns the name of the index.
pub fn name(&self) -> &str {
&self.name
}

/// Sets the `unique` flag.
pub fn unique(mut self, unique: bool) -> Self {
self.unique = Some(unique);
self
}

/// Sets the `multi_entry` flag.
pub fn multi_entry(mut self, multi_entry: bool) -> Self {
self.multi_entry = Some(multi_entry);
self
}

/// Applies the index to the given object store.
pub(crate) fn apply(self, object_store: &ObjectStore) -> Result<(), Error> {
let mut params = IndexParams::new();

if let Some(unique) = self.unique {
params.unique(unique);
}

if let Some(multi_entry) = self.multi_entry {
params.multi_entry(multi_entry);
}

object_store.create_index(&self.name, self.key_path, Some(params))?;

Ok(())
}
}
8 changes: 8 additions & 0 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Contains the builder for the database.
mod database_builder;
mod index;
mod object_store;

pub use self::{
database_builder::DatabaseBuilder, index::IndexBuilder, object_store::ObjectStoreBuilder,
};
102 changes: 102 additions & 0 deletions src/builder/object_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::collections::HashSet;

use crate::{request::OpenDatabaseRequest, Database, Error, KeyPath, ObjectStoreParams, Request};

use super::IndexBuilder;

/// Builder for object stores.
#[derive(Debug)]
pub struct ObjectStoreBuilder {
name: String,
auto_increment: Option<bool>,
key_path: Option<KeyPath>,
indexes: Vec<IndexBuilder>,
}

impl ObjectStoreBuilder {
/// Creates a new instance of [`ObjectStoreBuilder`].
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
auto_increment: None,
key_path: None,
indexes: Vec::new(),
}
}

/// Returns the name of the object store.
pub fn name(&self) -> &str {
&self.name
}

/// Sets the `auto_increment` flag.
pub fn auto_increment(mut self, auto_increment: bool) -> Self {
self.auto_increment = Some(auto_increment);
self
}

/// Sets the key path.
pub fn key_path(mut self, key_path: Option<KeyPath>) -> Self {
self.key_path = key_path;
self
}

/// Adds an index.
pub fn add_index(mut self, index: IndexBuilder) -> Self {
self.indexes.push(index);
self
}

pub(crate) fn apply(
self,
database: &Database,
request: &OpenDatabaseRequest,
) -> Result<(), Error> {
let mut index_names: HashSet<_> = self
.indexes
.iter()
.map(|index| index.name().to_owned())
.collect();

let object_store = if database.store_names().contains(&self.name) {
let transaction = request
.transaction()
.ok_or_else(|| Error::TransactionNotFound)?;

transaction.object_store(&self.name)
} else {
let mut params = ObjectStoreParams::new();

if let Some(auto_increment) = self.auto_increment {
params.auto_increment(auto_increment);
}

if let Some(key_path) = self.key_path {
params.key_path(Some(key_path));
}

database.create_object_store(&self.name, params)
}?;

for index in self.indexes {
index.apply(&object_store)?;
}

let db_index_names = object_store.index_names();
let mut indexes_to_remove = Vec::new();

for db_index_name in db_index_names {
if index_names.contains(db_index_name.as_str()) {
index_names.remove(db_index_name.as_str());
} else {
indexes_to_remove.push(db_index_name);
}
}

for index_name in indexes_to_remove {
object_store.delete_index(&index_name)?;
}

Ok(())
}
}
5 changes: 4 additions & 1 deletion src/cursor/key_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::{
CursorDirection, Error,
};

use super::ManagedKeyCursor;
#[cfg(feature = "futures")]
use crate::ManagedKeyCursor;

/// Represents a key cursor for traversing or iterating over multiple records (only keys) in a database.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -97,6 +98,8 @@ impl KeyCursor {
}

/// Returns a managed version of this cursor.
#[cfg(feature = "futures")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "futures")))]
pub fn into_managed(self) -> ManagedKeyCursor {
self.into()
}
Expand Down
2 changes: 2 additions & 0 deletions src/cursor/managed_key_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use wasm_bindgen::JsValue;
use crate::{CursorDirection, Error, KeyCursor};

/// A key cursor that is managed by the library (for ease of use).
#[cfg(feature = "futures")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "futures")))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ManagedKeyCursor {
inner: Option<KeyCursor>,
Expand Down
2 changes: 2 additions & 0 deletions src/cursor/managed_value_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use wasm_bindgen::JsValue;
use crate::{Cursor, CursorDirection, Error};

/// A cursor that is managed by the library (for ease of use).
#[cfg(feature = "futures")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "futures")))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ManagedCursor {
inner: Option<Cursor>,
Expand Down
9 changes: 5 additions & 4 deletions src/cursor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod cursor_direction;
mod key_cursor;
#[cfg(feature = "futures")]
mod managed_key_cursor;
#[cfg(feature = "futures")]
mod managed_value_cursor;
mod value_cursor;

pub use self::{
cursor_direction::CursorDirection, key_cursor::KeyCursor, managed_key_cursor::ManagedKeyCursor,
managed_value_cursor::ManagedCursor, value_cursor::Cursor,
};
pub use self::{cursor_direction::CursorDirection, key_cursor::KeyCursor, value_cursor::Cursor};
#[cfg(feature = "futures")]
pub use self::{managed_key_cursor::ManagedKeyCursor, managed_value_cursor::ManagedCursor};
6 changes: 5 additions & 1 deletion src/cursor/value_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ use js_sys::Object;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::IdbCursorWithValue;

#[cfg(feature = "futures")]
use crate::ManagedCursor;
use crate::{
request::{DeleteStoreRequest, OpenCursorStoreRequest, UpdateStoreRequest},
CursorDirection, Error, ManagedCursor,
CursorDirection, Error,
};

/// Represents a cursor for traversing or iterating over multiple records in a database.
Expand Down Expand Up @@ -100,6 +102,8 @@ impl Cursor {
}

/// Returns a managed cursor.
#[cfg(feature = "futures")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "futures")))]
pub fn into_managed(self) -> ManagedCursor {
self.into()
}
Expand Down
9 changes: 9 additions & 0 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use num_traits::ToPrimitive;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use web_sys::{Event, EventTarget, IdbDatabase};

#[cfg(feature = "builder")]
use crate::builder::DatabaseBuilder;
use crate::{
utils::dom_string_list_to_vec, Error, ObjectStore, ObjectStoreParams, Transaction,
TransactionMode,
Expand All @@ -21,6 +23,13 @@ pub struct Database {
}

impl Database {
/// Creates a new instance of [`DatabaseBuilder`].
#[cfg(feature = "builder")]
#[cfg_attr(any(docsrs, feature = "doc"), doc(cfg(feature = "builder")))]
pub fn builder(name: &str) -> DatabaseBuilder {
DatabaseBuilder::new(name)
}

/// Returns the name of the database.
pub fn name(&self) -> String {
self.inner.name()
Expand Down
Loading

0 comments on commit c8c7605

Please sign in to comment.