use std::collections::HashSet;
use indexmap::{IndexMap, IndexSet};
use crate::{Database, DatabaseEvent as _, Error, Event as _, Factory, Request as _};
use super::ObjectStoreBuilder;
#[derive(Debug)]
pub struct DatabaseBuilder {
name: String,
version: Option<u32>,
object_stores: IndexMap<String, ObjectStoreBuilder>,
object_stores_to_rename: IndexMap<String, (String, ObjectStoreBuilder)>,
object_stores_to_remove: IndexSet<String>,
}
impl DatabaseBuilder {
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
version: None,
object_stores: Default::default(),
object_stores_to_rename: Default::default(),
object_stores_to_remove: Default::default(),
}
}
pub fn version(mut self, version: u32) -> Self {
self.version = Some(version);
self
}
pub fn add_object_store(mut self, object_store: ObjectStoreBuilder) -> Self {
let name = object_store.name().to_owned();
self.object_stores.insert(name, object_store);
self
}
pub fn remove_object_store(mut self, object_store_name: &str) -> Self {
self.object_stores.shift_remove(object_store_name);
self.object_stores_to_remove
.insert(object_store_name.to_owned());
self
}
pub fn rename_object_store(mut self, old_name: &str, new_name: &str) -> Self {
let mut old_name = old_name.to_owned();
let mut previous_object_store = self
.object_stores
.shift_remove(&old_name)
.or_else(|| {
self.object_stores_to_rename.shift_remove(&old_name).map(
|(previous_old_name, store)| {
old_name = previous_old_name;
store
},
)
})
.expect("cannot rename an object store which does not exist");
previous_object_store.set_name(new_name);
self.object_stores_to_rename
.insert(new_name.to_owned(), (old_name, previous_object_store));
self
}
pub async fn build(mut 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 database = event.database().expect("database");
let mut existing_store_names = database.store_names();
for store_to_remove in self.object_stores_to_remove.iter() {
if existing_store_names.contains(store_to_remove) {
database
.delete_object_store(store_to_remove)
.expect("object store deletion");
}
}
let stores_to_retain = self
.object_stores
.keys()
.cloned()
.chain(self.object_stores_to_rename.keys().cloned())
.collect::<HashSet<_>>();
for (new_name, (old_name, store)) in self.object_stores_to_rename.into_iter() {
if !existing_store_names.contains(&old_name) {
self.object_stores.insert(new_name, store);
} else {
request
.transaction()
.expect("transaction")
.object_store(&old_name)
.expect("object store")
.to_owned()
.set_name(&new_name);
existing_store_names = database.store_names()
}
}
for object_store in self.object_stores.into_values() {
object_store
.apply(&database, &request)
.expect("object store creation");
}
for db_store_name in existing_store_names {
if !stores_to_retain.contains(&db_store_name)
&& !self.object_stores_to_remove.contains(&db_store_name)
{
database
.delete_object_store(&db_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)
}
}