1use std::{
2 ops::Deref,
3 path::{Path, PathBuf},
4 sync::LazyLock,
5};
6
7use lunar_lib::database::{
8 ConflictableTransactionResult, Database, DatabaseEntry, DatabaseError, Db, Transactional, Tree,
9};
10
11pub mod entry_extensions;
12pub mod tx_extensions;
13
14mod patchable;
15pub(crate) use patchable::*;
16
17mod mergeable;
18pub use mergeable::*;
19
20mod createable;
21pub use createable::*;
22
23mod deleteable;
24pub use deleteable::*;
25
26use crate::{
27 data_dir,
28 library::collection::{Collection, STATIC_COLLECTIONS, STATIC_COLLECTIONS_VERSION},
29};
30
31pub mod validator;
32
33#[derive(Clone)]
34pub struct LibraryDb {
35 db: Db,
36}
37
38impl Deref for LibraryDb {
39 type Target = Db;
40
41 fn deref(&self) -> &Self::Target {
42 &self.db
43 }
44}
45
46static LIBRARY_DB_PATH: LazyLock<PathBuf> = LazyLock::new(|| data_dir().join("library_data"));
47
48impl Database for LibraryDb {
49 const RETRY_MAX_ATTEMPTS: Option<usize> = None;
50
51 const RETRY_DURATION: std::time::Duration = std::time::Duration::from_millis(1);
52
53 fn new(db: Db) -> Self {
54 Self { db }
55 }
56
57 fn pre_open(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error>> {
58 ensure_hardcoded_collections(db)?;
59 Ok(())
60 }
61
62 fn path() -> &'static Path {
63 &LIBRARY_DB_PATH
64 }
65}
66
67pub(crate) fn track_tree(db: &LibraryDb) -> Tree {
68 db.open_tree("track").expect("Failed to open 'track' tree")
69}
70pub(crate) fn album_tree(db: &LibraryDb) -> Tree {
71 db.open_tree("album").expect("Failed to open 'album' tree")
72}
73pub(crate) fn artist_tree(db: &LibraryDb) -> Tree {
74 db.open_tree("artist")
75 .expect("Failed to open 'artist' tree")
76}
77pub(crate) fn collection_tree(db: &LibraryDb) -> Tree {
78 db.open_tree("collection")
79 .expect("Failed to open 'collection' tree")
80}
81
82fn ensure_hardcoded_collections(db: &LibraryDb) -> Result<(), Box<dyn std::error::Error>> {
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;
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 mut buf = Vec::new();
98 ciborium::into_writer(collection, &mut buf).unwrap();
99
100 collection_tree.insert(&*collection.id(), &*buf)?;
101 }
102
103 db_tree.insert(KEY, &STATIC_COLLECTIONS_VERSION.to_be_bytes())?;
104
105 Ok(())
106 },
107 )?;
108 }
109
110 Ok(())
111}