1#[cfg(feature = "db")]
2pub mod crud;
3#[cfg(feature = "db")]
4pub mod health;
5#[cfg(feature = "db")]
6pub(crate) mod queries;
7pub mod schemas;
8
9#[cfg(feature = "db")]
10use surrealdb::{engine::local::Db, Surreal};
11
12#[cfg(feature = "db")]
13#[cfg(not(tarpaulin_include))]
14static DB_DIR: once_cell::sync::OnceCell<std::path::PathBuf> = once_cell::sync::OnceCell::new();
15#[cfg(feature = "db")]
16#[cfg(not(tarpaulin_include))]
17static TEMP_DB_DIR: once_cell::sync::Lazy<tempfile::TempDir> = once_cell::sync::Lazy::new(|| {
18 tempfile::tempdir().expect("Failed to create temporary directory")
19});
20
21pub const FULL_TEXT_SEARCH_ANALYZER_NAME: &str = "custom_analyzer";
23
24#[cfg(feature = "db")]
30#[allow(clippy::missing_inline_in_public_items)]
31pub fn set_database_path(path: std::path::PathBuf) -> Result<(), crate::errors::Error> {
32 DB_DIR
33 .set(path)
34 .map_err(crate::errors::Error::DbPathSetError)?;
35 log::info!("Primed database path");
36 Ok(())
37}
38
39#[cfg(feature = "db")]
45#[allow(clippy::missing_inline_in_public_items)]
46pub async fn init_database() -> surrealdb::Result<Surreal<Db>> {
47 let db = Surreal::new(DB_DIR
48 .get().cloned()
49 .unwrap_or_else(|| {
50 log::warn!("DB_DIR not set, defaulting to a temporary directory `{}`, this is likely a bug because `init_database` should be called before `db`", TEMP_DB_DIR.path().display());
51 TEMP_DB_DIR.path()
52 .to_path_buf()
53 })).await?;
54
55 db.use_ns("mecomp").use_db("music").await?;
56
57 register_custom_analyzer(&db).await?;
58 surrealqlx::register_tables!(
59 &db,
60 schemas::album::Album,
61 schemas::artist::Artist,
62 schemas::song::Song,
63 schemas::collection::Collection,
64 schemas::playlist::Playlist,
65 schemas::dynamic::DynamicPlaylist
66 )?;
67 #[cfg(feature = "analysis")]
68 surrealqlx::register_tables!(&db, schemas::analysis::Analysis)?;
69
70 Ok(db)
71}
72
73#[cfg(feature = "db")]
74pub(crate) async fn register_custom_analyzer<C>(db: &Surreal<C>) -> surrealdb::Result<()>
75where
76 C: surrealdb::Connection,
77{
78 use queries::define_analyzer;
79 use surrealdb::sql::Tokenizer;
80
81 db.query(define_analyzer(
82 FULL_TEXT_SEARCH_ANALYZER_NAME,
83 Some(Tokenizer::Class),
84 &[
85 "ascii",
86 "lowercase",
87 "edgengram(1, 10)",
88 "snowball(English)",
89 ],
90 ))
91 .await?;
92
93 Ok(())
94}
95
96#[cfg(test)]
97mod test {
98 use super::schemas::{
99 album::Album, artist::Artist, collection::Collection, dynamic::DynamicPlaylist,
100 playlist::Playlist, song::Song,
101 };
102 use super::*;
103
104 use surrealdb::engine::local::Mem;
105 use surrealqlx::traits::Table;
106
107 #[tokio::test]
108 async fn test_register_tables() -> anyhow::Result<()> {
109 let db = Surreal::new::<Mem>(()).await?;
111 db.use_ns("test").use_db("test").await?;
112
113 register_custom_analyzer(&db).await?;
115
116 <Album as Table>::init_table(&db).await?;
118 <Artist as Table>::init_table(&db).await?;
119 <Song as Table>::init_table(&db).await?;
120 <Collection as Table>::init_table(&db).await?;
121 <Playlist as Table>::init_table(&db).await?;
122 <DynamicPlaylist as Table>::init_table(&db).await?;
123 <Album as Table>::init_table(&db).await?;
125
126 Ok(())
127 }
128}