1use std::{
2 ops::Deref,
3 path::PathBuf,
4 sync::{LazyLock, Mutex},
5};
6
7use lunar_lib::database::{
8 ConflictableTransactionResult, Database, DatabaseEntry, DatabaseError, SledDb, Transactional,
9 caching::DbCache, serialize_to_ivec,
10};
11
12use crate::library::collection::STATIC_COLLECTIONS;
13
14mod tx_extensions;
15pub use tx_extensions::*;
16
17mod resolveable;
18pub use resolveable::*;
19
20use crate::{
21 data_dir,
22 library::{
23 album::Album,
24 artist::Artist,
25 collection::{Collection, STATIC_COLLECTIONS_VERSION},
26 track::Track,
27 },
28};
29
30pub mod validator;
31
32#[derive(Clone)]
33pub struct LibraryDb {
34 db: SledDb,
35}
36
37impl Deref for LibraryDb {
38 type Target = SledDb;
39
40 fn deref(&self) -> &Self::Target {
41 &self.db
42 }
43}
44
45impl Database for LibraryDb {
46 const RETRY_MAX_ATTEMPTS: Option<usize> = None;
47
48 const RETRY_DURATION: std::time::Duration = std::time::Duration::from_millis(1);
49
50 fn new(db: SledDb) -> Self {
51 Self { db }
52 }
53
54 fn db(&self) -> &lunar_lib::database::SledDb {
55 &self.db
56 }
57
58 fn pre_open(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
59 ensure_hardcoded_collections(db)?;
60 Ok(())
61 }
62
63 fn path() -> PathBuf {
64 data_dir().join("library_data")
65 }
66}
67
68pub(crate) static TRACK_CACHE: LazyLock<Mutex<DbCache<Track>>> =
69 LazyLock::new(|| Mutex::new(DbCache::new()));
70
71pub(crate) static ALBUM_CACHE: LazyLock<Mutex<DbCache<Album>>> =
72 LazyLock::new(|| Mutex::new(DbCache::new()));
73
74pub(crate) static ARTIST_CACHE: LazyLock<Mutex<DbCache<Artist>>> =
75 LazyLock::new(|| Mutex::new(DbCache::new()));
76
77pub(crate) static COLLECTION_CACHE: LazyLock<Mutex<DbCache<Collection>>> =
78 LazyLock::new(|| Mutex::new(DbCache::new()));
79
80fn ensure_hardcoded_collections(
81 db: &LibraryDb,
82) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
83 const KEY: &[u8] = b"hardcoded_collections_version";
84
85 if db
86 .get(KEY)?
87 .is_none_or(|v| *v != STATIC_COLLECTIONS_VERSION.to_be_bytes())
88 {
89 let db_tree = &*db.db;
90 let collection_tree = Collection::__tree(db);
91
92 (db_tree, &collection_tree).transaction(
93 |(db_tree, collection_tree)| -> ConflictableTransactionResult<(), DatabaseError> {
94 for collection in STATIC_COLLECTIONS.iter() {
95 let collection = *collection;
96
97 let ivec = serialize_to_ivec(collection);
98 collection_tree.insert(&*collection.id(), &*ivec)?;
99 }
100
101 db_tree.insert(KEY, &STATIC_COLLECTIONS_VERSION.to_be_bytes())?;
102
103 Ok(())
104 },
105 )?;
106 }
107
108 Ok(())
109}