Skip to main content

selene_core/
database.rs

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}