Skip to content

Commit

Permalink
Initial implementation for rexie
Browse files Browse the repository at this point in the history
  • Loading branch information
devashishdxt committed Jan 9, 2022
1 parent 3e7ac97 commit 89ecc89
Show file tree
Hide file tree
Showing 15 changed files with 857 additions and 937 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]
js-test = []
js = []

[dependencies]
console_error_panic_hook = { version = "0.1.7", optional = true }
js-sys = "0.3.55"
num-traits = { version = "0.2.14", default-features = false }
thiserror = "1.0.30"
tokio = { version = "1.15.0", default-features = false, features = [
"macros",
Expand All @@ -27,6 +26,7 @@ web-sys = { version = "0.3.55", features = [
"DomException",
"DomStringList",
"Event",
"IdbCursorWithValue",
"IdbDatabase",
"IdbFactory",
"IdbIndex",
Expand All @@ -45,6 +45,8 @@ web-sys = { version = "0.3.55", features = [
wee_alloc = { version = "0.4.5", optional = true }

[dev-dependencies]
serde = { version = "1.0.133", features = ["derive"] }
serde-wasm-bindgen = "0.4.1"
wasm-bindgen-test = "0.3.28"

[profile.release]
Expand Down
169 changes: 83 additions & 86 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,119 +1,116 @@
use std::fmt;

use js_sys::Error as JsError;
use thiserror::Error;
use wasm_bindgen::prelude::*;

#[cfg(not(feature = "js"))]
/// Result with `rexie::Error` as error type.
pub type Result<T> = std::result::Result<T, Error>;

#[cfg(feature = "js")]
pub type Result<T> = std::result::Result<T, JsValue>;

#[derive(Debug, Error, Clone)]
pub struct Error {
error_type: ErrorType,
inner: Option<JsValue>,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.inner {
None => write!(f, "{}", self.error_type),
Some(ref inner) => write!(f, "{}: {}", self.error_type, option_display(inner)),
}
}
}

fn option_display(option: &JsValue) -> String {
match option.as_string() {
Some(s) => s,
None => "".to_string(),
}
}

impl Error {
pub fn error_type(&self) -> ErrorType {
self.error_type
}

pub(crate) fn set_inner(mut self, inner: JsValue) -> Self {
self.inner = Some(inner);
self
}
}

impl From<ErrorType> for Error {
fn from(error_type: ErrorType) -> Self {
Self {
error_type,
inner: None,
}
}
}

#[derive(Debug, Error, Clone, Copy)]
pub enum ErrorType {
/// Error type for `rexie` crate
#[derive(Debug, Error, Clone, PartialEq)]
pub enum Error {
/// Error when receiving message from async channel
#[error("error when receiving message from async channel")]
AsyncChannelError,

/// Error when fetching DOM exception
#[error("error when fetching DOM exception: {}", js_error_display(.0))]
DomExceptionError(JsValue),

/// DOM Exception is none
#[error("dom exception is none")]
DomExceptionNotFound,

/// Event target is none
#[error("event target is none")]
EventTargetNotFound,

#[error("index creation failed")]
IndexCreationFailed,
/// Index creation failed
#[error("index creation failed: {}", js_error_display(.0))]
IndexCreationFailed(JsValue),

#[error("index open failed")]
IndexOpenFailed,
/// Index open failed
#[error("index open failed: {}", js_error_display(.0))]
IndexOpenFailed(JsValue),

#[error("indexed db error")]
IndexedDBError,
/// Failed to delete indexed db
#[error("failed to delete indexed db: {}", js_error_display(.0))]
IndexedDbDeleteFailed(JsValue),

/// Indexed db not found
#[error("indexed db is none")]
IndexedDBNotFound,
IndexedDbNotFound,

#[error("indexed db not supported")]
IndexedDBNotSupported,
/// Indexed db not supported
#[error("indexed db not supported: {}", js_error_display(.0))]
IndexedDbNotSupported(JsValue),

#[error("failed to open indexed db")]
IndexedDBOpenFailed,
/// Failed to open indexed db
#[error("failed to open indexed db: {}", js_error_display(.0))]
IndexedDbOpenFailed(JsValue),

#[error("key range error")]
KeyRangeError,
/// Failed to execute indexed db request
#[error("failed to execute indexed db request: {}", js_error_display(.0))]
IndexedDbRequestError(JsValue),

#[error("object store creation failed")]
ObjectStoreCreationFailed,
/// Key range error
#[error("key range error: {}", js_error_display(.0))]
KeyRangeError(JsValue),

#[error("failed to open object store")]
ObjectStoreOpenFailed,
/// Object store creation failed
#[error("object store creation failed: {}", js_error_display(.0))]
ObjectStoreCreationFailed(JsValue),

#[error("failed to execute db transaction")]
TransactionExecutionFailed,
/// Failed to open object store
#[error("failed to open object store: {}", js_error_display(.0))]
ObjectStoreOpenFailed(JsValue),

#[error("failed to open db transaction")]
TransactionOpenFailed,
/// Failed to execute indexed db transaction
#[error("failed to execute db transaction: {}", js_error_display(.0))]
TransactionExecutionFailed(JsValue),

#[error("window object not found")]
WindowNotFound,
}
/// Transaction is none
#[error("transaction is none")]
TransactionNotFound,

impl ErrorType {
pub(crate) fn into_error(self) -> Error {
Error::from(self)
}
/// failed to open db transaction
#[error("failed to open db transaction: {}", js_error_display(.0))]
TransactionOpenFailed(JsValue),

/// Unexpected JS type
#[error("unexpected js type")]
UnexpectedJsType,

/// window object is none
#[error("window object is none")]
WindowNotFound,
}

impl From<ErrorType> for JsValue {
fn from(error_type: ErrorType) -> Self {
JsError::new(&error_type.to_string()).into()
}
fn js_error_display(option: &JsValue) -> String {
ToString::to_string(&JsError::from(option.clone()).to_string())
}

impl From<Error> for JsValue {
fn from(error: Error) -> JsValue {
match error.inner {
Some(inner) => inner,
None => error.error_type.into(),
fn from(error: Error) -> Self {
match error {
Error::AsyncChannelError => "AsyncChannelError".into(),
Error::EventTargetNotFound => "EventTargetNotFound".into(),
Error::IndexCreationFailed(js_value) => js_value,
Error::IndexOpenFailed(js_value) => js_value,
Error::IndexedDbNotFound => "IndexedDbNotFound".into(),
Error::IndexedDbNotSupported(js_value) => js_value,
Error::IndexedDbOpenFailed(js_value) => js_value,
Error::KeyRangeError(js_value) => js_value,
Error::ObjectStoreCreationFailed(js_value) => js_value,
Error::ObjectStoreOpenFailed(js_value) => js_value,
Error::TransactionExecutionFailed(js_value) => js_value,
Error::TransactionOpenFailed(js_value) => js_value,
Error::WindowNotFound => "WindowNotFound".into(),
Error::DomExceptionError(js_value) => js_value,
Error::DomExceptionNotFound => "DomExceptionNotFound".into(),
Error::IndexedDbDeleteFailed(js_value) => js_value,
Error::TransactionNotFound => "TransactionNotFound".into(),
Error::IndexedDbRequestError(js_value) => js_value,
Error::UnexpectedJsType => "UnxpectedJsType".into(),
}
}
}
18 changes: 6 additions & 12 deletions src/index.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
#[cfg(feature = "js")]
use wasm_bindgen::prelude::*;
use web_sys::{IdbIndexParameters, IdbObjectStore};

use crate::{ErrorType, Result};
use crate::{Error, Result};

#[cfg_attr(feature = "js", wasm_bindgen)]
/// An index builder.
pub struct Index {
pub(crate) name: String,
pub(crate) key_path: String,
pub(crate) unique: Option<bool>,
pub(crate) multi_entry: Option<bool>,
}

#[cfg_attr(feature = "js", wasm_bindgen)]
impl Index {
#[cfg_attr(feature = "js", wasm_bindgen(constructor))]
/// Creates a new index with given name and key path
pub fn new(name: &str, key_path: &str) -> Self {
Self {
name: name.to_owned(),
Expand All @@ -24,12 +21,13 @@ impl Index {
}
}

/// Specify whether the index should be unique
pub fn unique(mut self, unique: bool) -> Self {
self.unique = Some(unique);
self
}

#[cfg_attr(feature = "js", wasm_bindgen(js_name = "multiEntry"))]
/// Specify whether the index should be multi-entry, i.e., type of the value contained in key path is an array
pub fn multi_entry(mut self, multi_entry: bool) -> Self {
self.multi_entry = Some(multi_entry);
self
Expand All @@ -51,11 +49,7 @@ impl Index {

object_store
.create_index_with_str_and_optional_parameters(&self.name, &self.key_path, &params)
.map_err(|js_value| {
ErrorType::IndexCreationFailed
.into_error()
.set_inner(js_value)
})?;
.map_err(Error::IndexCreationFailed)?;
}

Ok(())
Expand Down
26 changes: 14 additions & 12 deletions src/key_range.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
#[cfg(feature = "js")]
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use web_sys::IdbKeyRange;

use crate::{ErrorType, Result};
use crate::{Error, Result};

#[cfg_attr(feature = "js", wasm_bindgen)]
/// A key range.
pub struct KeyRange {
idb_key_range: IdbKeyRange,
}

#[cfg_attr(feature = "js", wasm_bindgen)]
impl KeyRange {
#[cfg_attr(feature = "js", wasm_bindgen(js_name = "lowerBound"))]
/// Creates a new key range with given lower bound. The lower bound is inclusive if `lower_open` is false and
/// exclusive if `lower_open` is true.
pub fn lower_bound(lower_bound: &JsValue, lower_open: bool) -> Result<KeyRange> {
let idb_key_range = IdbKeyRange::lower_bound_with_open(lower_bound, lower_open)
.map_err(|js_value| ErrorType::KeyRangeError.into_error().set_inner(js_value))?;
.map_err(Error::KeyRangeError)?;

Ok(KeyRange { idb_key_range })
}

#[cfg_attr(feature = "js", wasm_bindgen(js_name = "upperBound"))]
/// Creates a new key range with given upper bound. The upper bound is inclusive if `upper_open` is false and
/// exclusive if `upper_open` is true.
pub fn upper_bound(upper_bound: &JsValue, upper_open: bool) -> Result<KeyRange> {
let idb_key_range = IdbKeyRange::upper_bound_with_open(upper_bound, upper_open)
.map_err(|js_value| ErrorType::KeyRangeError.into_error().set_inner(js_value))?;
.map_err(Error::KeyRangeError)?;

Ok(KeyRange { idb_key_range })
}

/// Creates a new key range with given lower and upper bound. The lower bound is inclusive if `lower_open` is false
/// and exclusive if `lower_open` is true. The upper bound is inclusive if `upper_open` is false and exclusive if
/// `upper_open` is true.
pub fn bound(
lower: &JsValue,
upper: &JsValue,
Expand All @@ -36,14 +38,14 @@ impl KeyRange {
) -> Result<KeyRange> {
let idb_key_range =
IdbKeyRange::bound_with_lower_open_and_upper_open(lower, upper, lower_open, upper_open)
.map_err(|js_value| ErrorType::KeyRangeError.into_error().set_inner(js_value))?;
.map_err(Error::KeyRangeError)?;

Ok(Self { idb_key_range })
}

/// Creates a new key range that matches with only one value.
pub fn only(value: &JsValue) -> Result<KeyRange> {
let idb_key_range = IdbKeyRange::only(value)
.map_err(|js_value| ErrorType::KeyRangeError.into_error().set_inner(js_value))?;
let idb_key_range = IdbKeyRange::only(value).map_err(Error::KeyRangeError)?;

Ok(Self { idb_key_range })
}
Expand Down
Loading

0 comments on commit 89ecc89

Please sign in to comment.