use std::{
ops::Deref,
path::{Path, PathBuf},
sync::LazyLock,
};
use lunar_lib::database::{
ConflictableTransactionResult, Database, DatabaseEntry, DatabaseError, Db, Transactional, Tree,
};
pub mod entry_extensions;
pub mod tx_extensions;
mod patchable;
pub(crate) use patchable::*;
mod mergeable;
pub use mergeable::*;
mod createable;
pub use createable::*;
mod deleteable;
pub use deleteable::*;
use crate::{
data_dir,
library::collection::{Collection, STATIC_COLLECTIONS, STATIC_COLLECTIONS_VERSION},
};
pub mod validator;
#[derive(Clone)]
pub struct LibraryDb {
db: Db,
}
impl Deref for LibraryDb {
type Target = Db;
fn deref(&self) -> &Self::Target {
&self.db
}
}
static LIBRARY_DB_PATH: LazyLock<PathBuf> = LazyLock::new(|| data_dir().join("library_data"));
impl Database for LibraryDb {
const RETRY_MAX_ATTEMPTS: Option<usize> = None;
const RETRY_DURATION: std::time::Duration = std::time::Duration::from_millis(1);
fn new(db: Db) -> Self {
Self { db }
}
fn pre_open(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error>> {
ensure_hardcoded_collections(db)?;
Ok(())
}
fn path() -> &'static Path {
&LIBRARY_DB_PATH
}
}
pub(crate) fn track_tree(db: &LibraryDb) -> Tree {
db.open_tree("track").expect("Failed to open 'track' tree")
}
pub(crate) fn album_tree(db: &LibraryDb) -> Tree {
db.open_tree("album").expect("Failed to open 'album' tree")
}
pub(crate) fn artist_tree(db: &LibraryDb) -> Tree {
db.open_tree("artist")
.expect("Failed to open 'artist' tree")
}
pub(crate) fn collection_tree(db: &LibraryDb) -> Tree {
db.open_tree("collection")
.expect("Failed to open 'collection' tree")
}
fn ensure_hardcoded_collections(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error>> {
const KEY: &[u8] = b"hardcoded_collections_version";
if db
.get(KEY)?
.is_none_or(|v| *v != STATIC_COLLECTIONS_VERSION.to_be_bytes())
{
let db_tree = &***db;
let collection_tree = Collection::tree(db);
(db_tree, &collection_tree).transaction(
|(db_tree, collection_tree)| -> ConflictableTransactionResult<(), DatabaseError> {
for collection in STATIC_COLLECTIONS.iter() {
let collection = *collection;
let mut buf = Vec::new();
ciborium::into_writer(collection, &mut buf).unwrap();
collection_tree.insert(&*collection.id(), &*buf)?;
}
db_tree.insert(KEY, &STATIC_COLLECTIONS_VERSION.to_be_bytes())?;
Ok(())
},
)?;
}
Ok(())
}