use indexed_db_futures::{
database::Database,
error::{DomException, Error, OpenDbError},
transaction::Transaction,
};
use thiserror::Error;
pub mod current {
use super::{Version, v2};
pub const VERSION: Version = Version::V2;
pub use v2::keys;
}
#[allow(unused)]
pub async fn open_and_upgrade_db(name: &str) -> Result<Database, OpenDbError> {
Database::open(name)
.with_version(current::VERSION as u32)
.with_on_upgrade_needed(|event, transaction| {
let mut version = Version::try_from(event.old_version() as u32)?;
while version < current::VERSION {
version = match version.upgrade(transaction)? {
Some(next) => next,
None => current::VERSION,
};
}
Ok(())
})
.await
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Version {
V0 = 0,
V1 = 1,
V2 = 2,
}
impl Version {
pub fn upgrade(self, transaction: &Transaction<'_>) -> Result<Option<Self>, Error> {
match self {
Self::V0 => v0::upgrade(transaction).map(Some),
Self::V1 => v1::upgrade(transaction).map(Some),
Self::V2 => Ok(None),
}
}
}
#[derive(Debug, Error)]
#[error("unknown version: {0}")]
pub struct UnknownVersionError(u32);
impl TryFrom<u32> for Version {
type Error = UnknownVersionError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(Version::V0),
1 => Ok(Version::V1),
2 => Ok(Version::V2),
v => Err(UnknownVersionError(v)),
}
}
}
impl From<UnknownVersionError> for Error {
fn from(value: UnknownVersionError) -> Self {
let message = format!("unknown version: {}", value.0);
let name = "UnknownVersionError";
match web_sys::DomException::new_with_message_and_name(&message, name) {
Ok(inner) => Self::DomException(DomException::DataError(inner)),
Err(err) => err.into(),
}
}
}
pub mod v0 {
use super::*;
pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
v1::create_object_stores(transaction.db())?;
Ok(Version::V1)
}
}
pub mod v1 {
use indexed_db_futures::Build;
use super::*;
pub mod keys {
pub const CORE: &str = "core";
pub const CORE_KEY_PATH: &str = "id";
pub const LEASES: &str = "leases";
pub const LEASES_KEY_PATH: &str = "id";
pub const MEDIA_RETENTION_POLICY_KEY: &str = "media_retention_policy";
pub const MEDIA_CLEANUP_TIME_KEY: &str = "media_cleanup_time";
pub const MEDIA_METADATA: &str = "media_metadata";
pub const MEDIA_METADATA_KEY_PATH: &str = "id";
pub const MEDIA_METADATA_URI: &str = "media_metadata_uri";
pub const MEDIA_METADATA_URI_KEY_PATH: &str = "uri";
pub const MEDIA_METADATA_CONTENT_SIZE: &str = "media_metadata_content_size";
pub const MEDIA_METADATA_CONTENT_SIZE_KEY_PATH: &str = "content_size";
pub const MEDIA_METADATA_LAST_ACCESS: &str = "media_metadata_last_access";
pub const MEDIA_METADATA_LAST_ACCESS_KEY_PATH: &str = "last_access";
pub const MEDIA_METADATA_RETENTION: &str = "media_metadata_retention";
pub const MEDIA_METADATA_RETENTION_KEY_PATH: &str = "retention";
pub const MEDIA_CONTENT: &str = "media_content";
pub const MEDIA_CONTENT_KEY_PATH: &str = "id";
}
pub fn create_object_stores(db: &Database) -> Result<(), Error> {
create_core_object_store(db)?;
create_lease_object_store(db)?;
create_media_metadata_object_store(db)?;
create_media_content_object_store(db)?;
Ok(())
}
fn create_core_object_store(db: &Database) -> Result<(), Error> {
let _ =
db.create_object_store(keys::CORE).with_key_path(keys::CORE_KEY_PATH.into()).build()?;
Ok(())
}
fn create_lease_object_store(db: &Database) -> Result<(), Error> {
let _ = db
.create_object_store(keys::LEASES)
.with_key_path(keys::LEASES_KEY_PATH.into())
.build()?;
Ok(())
}
fn create_media_metadata_object_store(db: &Database) -> Result<(), Error> {
let media = db
.create_object_store(keys::MEDIA_METADATA)
.with_key_path(keys::MEDIA_METADATA_KEY_PATH.into())
.build()?;
let _ = media
.create_index(keys::MEDIA_METADATA_URI, keys::MEDIA_METADATA_URI_KEY_PATH.into())
.build()?;
let _ = media
.create_index(
keys::MEDIA_METADATA_CONTENT_SIZE,
keys::MEDIA_METADATA_CONTENT_SIZE_KEY_PATH.into(),
)
.build()?;
let _ = media
.create_index(
keys::MEDIA_METADATA_LAST_ACCESS,
keys::MEDIA_METADATA_LAST_ACCESS_KEY_PATH.into(),
)
.build()?;
let _ = media
.create_index(
keys::MEDIA_METADATA_RETENTION,
keys::MEDIA_METADATA_RETENTION_KEY_PATH.into(),
)
.build()?;
Ok(())
}
fn create_media_content_object_store(db: &Database) -> Result<(), Error> {
let _ = db
.create_object_store(keys::MEDIA_CONTENT)
.with_key_path(keys::MEDIA_CONTENT_KEY_PATH.into())
.build()?;
Ok(())
}
pub fn upgrade(transaction: &Transaction<'_>) -> Result<Version, Error> {
v2::empty_leases(transaction)?;
Ok(Version::V2)
}
}
mod v2 {
pub use super::v1::keys;
use super::*;
pub fn empty_leases(transaction: &Transaction<'_>) -> Result<(), Error> {
let object_store = transaction.object_store(keys::LEASES)?;
object_store.clear()?;
Ok(())
}
}