use std::{
ops::Deref,
path::PathBuf,
sync::{LazyLock, Mutex},
};
use lunar_lib::database::{
ConflictableTransactionResult, Database, DatabaseEntry, DatabaseError, SledDb, Transactional,
caching::DbCache,
};
use crate::library::collection::STATIC_COLLECTIONS;
mod entry_extensions;
pub use entry_extensions::*;
mod tx_extensions;
pub use tx_extensions::*;
mod patchable;
pub(crate) use patchable::*;
mod mergeable;
pub use mergeable::*;
mod createable;
pub use createable::*;
mod deleteable;
pub use deleteable::*;
mod resolveable;
pub use resolveable::*;
use crate::{
data_dir,
library::{
album::Album,
artist::Artist,
collection::{Collection, STATIC_COLLECTIONS_VERSION},
track::Track,
},
};
pub mod validator;
#[derive(Clone)]
pub struct LibraryDb {
db: SledDb,
}
impl Deref for LibraryDb {
type Target = SledDb;
fn deref(&self) -> &Self::Target {
&self.db
}
}
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: SledDb) -> Self {
Self { db }
}
fn db(&self) -> &lunar_lib::database::SledDb {
&self.db
}
fn pre_open(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
ensure_hardcoded_collections(db)?;
Ok(())
}
fn path() -> PathBuf {
data_dir().join("library_data")
}
}
pub(crate) static TRACK_CACHE: LazyLock<Mutex<DbCache<Track>>> =
LazyLock::new(|| Mutex::new(DbCache::new()));
pub(crate) static ALBUM_CACHE: LazyLock<Mutex<DbCache<Album>>> =
LazyLock::new(|| Mutex::new(DbCache::new()));
pub(crate) static ARTIST_CACHE: LazyLock<Mutex<DbCache<Artist>>> =
LazyLock::new(|| Mutex::new(DbCache::new()));
pub(crate) static COLLECTION_CACHE: LazyLock<Mutex<DbCache<Collection>>> =
LazyLock::new(|| Mutex::new(DbCache::new()));
fn ensure_hardcoded_collections(
db: &LibraryDb,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
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.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 version: [u8; 4] = Collection::VERSION_NUMBER.to_be_bytes();
let mut buffer = Vec::from(version);
buffer.push(collection.read_only() as u8);
let buf = postcard::to_extend(collection, buffer)
.expect("Postcard failed to serialize. This cannot happen unless a serializer failed");
collection_tree.insert(&*collection.id(), &*buf)?;
}
db_tree.insert(KEY, &STATIC_COLLECTIONS_VERSION.to_be_bytes())?;
Ok(())
},
)?;
}
Ok(())
}