refactor: have two object stores

This commit is contained in:
Jeremy Wall 2024-07-12 01:15:29 -04:00
parent f75652befa
commit 84cc2a2713
2 changed files with 43 additions and 69 deletions

View File

@ -100,13 +100,13 @@ const APP_STATE_KEY: &'static str = "app-state";
impl LocalStore { impl LocalStore {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
store: DBFactory::new("app-state", Some(1)), store: DBFactory::default(),
//old_store: js_lib::get_storage(), //old_store: js_lib::get_storage(),
} }
} }
pub async fn store_app_state(&self, state: &AppState) { pub async fn store_app_state(&self, state: &AppState) {
self.migrate_local_store().await; //self.migrate_local_store().await;
let state = match to_value(state) { let state = match to_value(state) {
Ok(state) => state, Ok(state) => state,
Err(err) => { Err(err) => {
@ -116,9 +116,9 @@ impl LocalStore {
}; };
let key = to_value(APP_STATE_KEY).expect("Failed to serialize key"); let key = to_value(APP_STATE_KEY).expect("Failed to serialize key");
self.store self.store
.rw_transaction(|trx| async move { .rw_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.put_kv( .put_kv(
@ -141,10 +141,10 @@ impl LocalStore {
debug!("Loading state from local store"); debug!("Loading state from local store");
let recipes = parse_recipes(&self.get_recipes().await).expect("Failed to parse recipes"); let recipes = parse_recipes(&self.get_recipes().await).expect("Failed to parse recipes");
self.store self.store
.ro_transaction(|trx| async move { .ro_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let key = to_value(APP_STATE_KEY).expect("Failed to serialize key"); let key = to_value(APP_STATE_KEY).expect("Failed to serialize key");
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
let mut app_state: AppState = let mut app_state: AppState =
match object_store.get(&key).await.expect("Failed to read from") { match object_store.get(&key).await.expect("Failed to read from") {
@ -185,10 +185,10 @@ impl LocalStore {
/// Gets user data from local storage. /// Gets user data from local storage.
pub async fn get_user_data(&self) -> Option<UserData> { pub async fn get_user_data(&self) -> Option<UserData> {
self.store self.store
.ro_transaction(|trx| async move { .ro_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let key = to_value("user_data").expect("Failed to serialize key"); let key = to_value("user_data").expect("Failed to serialize key");
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
let user_data: UserData = match object_store let user_data: UserData = match object_store
.get(&key) .get(&key)
@ -215,9 +215,9 @@ impl LocalStore {
if let Some(data) = data { if let Some(data) = data {
let data = data.clone(); let data = data.clone();
self.store self.store
.rw_transaction(|trx| async move { .rw_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.put_kv( .put_kv(
@ -239,9 +239,9 @@ impl LocalStore {
// .expect("Failed to set user_data"); // .expect("Failed to set user_data");
} else { } else {
self.store self.store
.rw_transaction(|trx| async move { .rw_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.delete(&key) .delete(&key)
@ -258,12 +258,12 @@ impl LocalStore {
} }
} }
async fn get_storage_keys(&self) -> Vec<String> { async fn get_recipe_keys(&self) -> impl Iterator<Item = String> {
self.store self.store
.ro_transaction(|trx| async move { .ro_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let mut keys = Vec::new(); let mut keys = Vec::new();
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
let key_vec = object_store let key_vec = object_store
.get_all_keys(None) .get_all_keys(None)
@ -277,39 +277,7 @@ impl LocalStore {
Ok(keys) Ok(keys)
}) })
.await .await
.expect("Failed to get storage keys") .expect("Failed to get storage keys").into_iter()
}
async fn migrate_local_store(&self) {
// FIXME(zaphar): Migration from local storage to indexed db
for k in self.get_storage_keys().await.into_iter().filter(|k| {
k.starts_with("categor") || k == "inventory" || k.starts_with("plan") || k == "staples"
}) {
// Deleting old local store key
let key = to_value(&k).expect("Failed to serialize key");
self.store
.rw_transaction(|trx| async move {
let object_store = trx
.object_store(js_lib::STORE_NAME)
.expect("Failed to get object store");
object_store
.delete(&key)
.await
.expect("Failed to delete user_data");
Ok(())
})
.await
.expect("Failed to delete user_data");
//debug!("Deleting old local store key {}", k);
//self.store.delete(&k).expect("Failed to delete storage key");
}
}
async fn get_recipe_keys(&self) -> impl Iterator<Item = String> {
self.get_storage_keys()
.await
.into_iter()
.filter(|k| k.starts_with("recipe:"))
} }
/// Gets all the recipes from local storage. /// Gets all the recipes from local storage.
@ -319,9 +287,9 @@ impl LocalStore {
let key = to_value(&recipe_key).expect("Failed to serialize key"); let key = to_value(&recipe_key).expect("Failed to serialize key");
let entry = self let entry = self
.store .store
.ro_transaction(|trx| async move { .ro_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
let entry: Option<RecipeEntry> = match object_store let entry: Option<RecipeEntry> = match object_store
.get(&key) .get(&key)
@ -348,9 +316,9 @@ impl LocalStore {
pub async fn get_recipe_entry(&self, id: &str) -> Option<RecipeEntry> { pub async fn get_recipe_entry(&self, id: &str) -> Option<RecipeEntry> {
let key = to_value(&recipe_key(id)).expect("Failed to serialize key"); let key = to_value(&recipe_key(id)).expect("Failed to serialize key");
self.store self.store
.ro_transaction(|trx| async move { .ro_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
let entry: Option<RecipeEntry> = match object_store let entry: Option<RecipeEntry> = match object_store
.get(&key) .get(&key)
@ -378,9 +346,9 @@ impl LocalStore {
for recipe_key in self.get_recipe_keys().await { for recipe_key in self.get_recipe_keys().await {
let key = to_value(&recipe_key).expect("Failed to serialize key"); let key = to_value(&recipe_key).expect("Failed to serialize key");
self.store self.store
.rw_transaction(|trx| async move { .rw_transaction(&[js_lib::STATE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::STATE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.delete(&key) .delete(&key)
@ -398,9 +366,9 @@ impl LocalStore {
let entry = entry.clone(); let entry = entry.clone();
let key = let key =
to_value(&recipe_key(entry.recipe_id())).expect("Failed to serialize recipe key"); to_value(&recipe_key(entry.recipe_id())).expect("Failed to serialize recipe key");
self.store.rw_transaction(|trx| async move { self.store.rw_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.put_kv( .put_kv(
@ -419,9 +387,9 @@ impl LocalStore {
pub async fn set_recipe_entry(&self, entry: &RecipeEntry) { pub async fn set_recipe_entry(&self, entry: &RecipeEntry) {
let entry = entry.clone(); let entry = entry.clone();
let key = to_value(&recipe_key(entry.recipe_id())).expect("Failed to serialize recipe key"); let key = to_value(&recipe_key(entry.recipe_id())).expect("Failed to serialize recipe key");
self.store.rw_transaction(|trx| async move { self.store.rw_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.put_kv( .put_kv(
@ -445,9 +413,9 @@ impl LocalStore {
pub async fn delete_recipe_entry(&self, recipe_id: &str) { pub async fn delete_recipe_entry(&self, recipe_id: &str) {
let key = to_value(recipe_id).expect("Failed to serialize key"); let key = to_value(recipe_id).expect("Failed to serialize key");
self.store self.store
.rw_transaction(|trx| async move { .rw_transaction(&[js_lib::RECIPE_STORE_NAME], |trx| async move {
let object_store = trx let object_store = trx
.object_store(js_lib::STORE_NAME) .object_store(js_lib::RECIPE_STORE_NAME)
.expect("Failed to get object store"); .expect("Failed to get object store");
object_store object_store
.delete(&key) .delete(&key)

View File

@ -25,7 +25,9 @@ pub fn get_storage() -> web_sys::Storage {
.expect("No storage available") .expect("No storage available")
} }
pub const STORE_NAME: &'static str = "state-store"; pub const STATE_STORE_NAME: &'static str = "state-store";
pub const RECIPE_STORE_NAME: &'static str = "recipe-store";
pub const DB_VERSION: u32 = 1;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DBFactory<'name> { pub struct DBFactory<'name> {
@ -33,11 +35,13 @@ pub struct DBFactory<'name> {
version: Option<u32>, version: Option<u32>,
} }
impl<'name> DBFactory<'name> { impl Default for DBFactory<'static> {
pub fn new(name: &'name str, version: Option<u32>) -> Self { fn default() -> Self {
Self { name, version } DBFactory { name: STATE_STORE_NAME, version: Some(DB_VERSION) }
} }
}
impl<'name> DBFactory<'name> {
pub async fn get_indexed_db(&self) -> Result<Database<std::io::Error>> { pub async fn get_indexed_db(&self) -> Result<Database<std::io::Error>> {
let factory = Factory::<std::io::Error>::get().context("opening IndexedDB")?; let factory = Factory::<std::io::Error>::get().context("opening IndexedDB")?;
let db = factory.open(self.name, self.version.unwrap_or(0), |evt| async move { let db = factory.open(self.name, self.version.unwrap_or(0), |evt| async move {
@ -47,32 +51,34 @@ impl<'name> DBFactory<'name> {
// NOTE(jwall): This needs to be somewhat clever in handling version upgrades. // NOTE(jwall): This needs to be somewhat clever in handling version upgrades.
if db.version() == 1 { if db.version() == 1 {
// We use out of line keys for this object store // We use out of line keys for this object store
db.build_object_store(STORE_NAME).create()?; db.build_object_store(STATE_STORE_NAME).create()?;
db.build_object_store(RECIPE_STORE_NAME).create()?;
// TODO(jwall): Do we need indexes?
} }
Ok(()) Ok(())
}).await.context(format!("Opening or creating the database {}", self.name))?; }).await.context(format!("Opening or creating the database {}", self.name))?;
Ok(db) Ok(db)
} }
pub async fn rw_transaction<Fun, RetFut, Ret>(&self, transaction: Fun) -> indexed_db::Result<Ret, std::io::Error> pub async fn rw_transaction<Fun, RetFut, Ret>(&self, stores: &[&str], transaction: Fun) -> indexed_db::Result<Ret, std::io::Error>
where where
Fun: 'static + FnOnce(Transaction<std::io::Error>) -> RetFut, Fun: 'static + FnOnce(Transaction<std::io::Error>) -> RetFut,
RetFut: 'static + Future<Output = indexed_db::Result<Ret, std::io::Error>>, RetFut: 'static + Future<Output = indexed_db::Result<Ret, std::io::Error>>,
Ret: 'static, Ret: 'static,
{ {
self.get_indexed_db().await.expect("Failed to open database") self.get_indexed_db().await.expect("Failed to open database")
.transaction(&[STORE_NAME]).rw() .transaction(stores).rw()
.run(transaction).await .run(transaction).await
} }
pub async fn ro_transaction<Fun, RetFut, Ret>(&self, transaction: Fun) -> indexed_db::Result<Ret, std::io::Error> pub async fn ro_transaction<Fun, RetFut, Ret>(&self, stores: &[&str], transaction: Fun) -> indexed_db::Result<Ret, std::io::Error>
where where
Fun: 'static + FnOnce(Transaction<std::io::Error>) -> RetFut, Fun: 'static + FnOnce(Transaction<std::io::Error>) -> RetFut,
RetFut: 'static + Future<Output = indexed_db::Result<Ret, std::io::Error>>, RetFut: 'static + Future<Output = indexed_db::Result<Ret, std::io::Error>>,
Ret: 'static, Ret: 'static,
{ {
self.get_indexed_db().await.expect("Failed to open database") self.get_indexed_db().await.expect("Failed to open database")
.transaction(&[STORE_NAME]) .transaction(stores)
.run(transaction).await .run(transaction).await
} }
} }