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,
10};
11
12use crate::library::collection::STATIC_COLLECTIONS;
13
14mod entry_extensions;
15pub use entry_extensions::*;
16
17mod tx_extensions;
18pub use tx_extensions::*;
19
20mod patchable;
21pub(crate) use patchable::*;
22
23mod mergeable;
24pub use mergeable::*;
25
26mod createable;
27pub use createable::*;
28
29mod deleteable;
30pub use deleteable::*;
31
32mod resolveable;
33pub use resolveable::*;
34
35use crate::{
36    data_dir,
37    library::{
38        album::Album,
39        artist::Artist,
40        collection::{Collection, STATIC_COLLECTIONS_VERSION},
41        track::Track,
42    },
43};
44
45pub mod validator;
46
47#[derive(Clone)]
48pub struct LibraryDb {
49    db: SledDb,
50}
51
52impl Deref for LibraryDb {
53    type Target = SledDb;
54
55    fn deref(&self) -> &Self::Target {
56        &self.db
57    }
58}
59
60impl Database for LibraryDb {
61    const RETRY_MAX_ATTEMPTS: Option<usize> = None;
62
63    const RETRY_DURATION: std::time::Duration = std::time::Duration::from_millis(1);
64
65    fn new(db: SledDb) -> Self {
66        Self { db }
67    }
68
69    fn db(&self) -> &lunar_lib::database::SledDb {
70        &self.db
71    }
72
73    fn pre_open(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
74        ensure_hardcoded_collections(db)?;
75        Ok(())
76    }
77
78    fn path() -> PathBuf {
79        data_dir().join("library_data")
80    }
81}
82
83pub(crate) static TRACK_CACHE: LazyLock<Mutex<DbCache<Track>>> =
84    LazyLock::new(|| Mutex::new(DbCache::new()));
85
86pub(crate) static ALBUM_CACHE: LazyLock<Mutex<DbCache<Album>>> =
87    LazyLock::new(|| Mutex::new(DbCache::new()));
88
89pub(crate) static ARTIST_CACHE: LazyLock<Mutex<DbCache<Artist>>> =
90    LazyLock::new(|| Mutex::new(DbCache::new()));
91
92pub(crate) static COLLECTION_CACHE: LazyLock<Mutex<DbCache<Collection>>> =
93    LazyLock::new(|| Mutex::new(DbCache::new()));
94
95fn ensure_hardcoded_collections(
96    db: &LibraryDb,
97) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
98    const KEY: &[u8] = b"hardcoded_collections_version";
99
100    if db
101        .get(KEY)?
102        .is_none_or(|v| *v != STATIC_COLLECTIONS_VERSION.to_be_bytes())
103    {
104        let db_tree = &*db.db;
105        let collection_tree = Collection::__tree(db);
106
107        (db_tree, &collection_tree).transaction(
108            |(db_tree, collection_tree)| -> ConflictableTransactionResult<(), DatabaseError> {
109                for collection in STATIC_COLLECTIONS.iter() {
110                    let collection = *collection;
111
112                    let version: [u8; 4] = Collection::VERSION_NUMBER.to_be_bytes();
113
114                    let mut buffer = Vec::from(version);
115                    buffer.push(collection.read_only() as u8);
116
117                    let buf = postcard::to_extend(collection, buffer)
118                        .expect("Postcard failed to serialize. This cannot happen unless a serializer failed");
119
120                    collection_tree.insert(&*collection.id(), &*buf)?;
121                }
122
123                db_tree.insert(KEY, &STATIC_COLLECTIONS_VERSION.to_be_bytes())?;
124
125                Ok(())
126            },
127        )?;
128    }
129
130    Ok(())
131}