1#[cfg(feature = "db")]
2pub mod crud;
3#[cfg(feature = "db")]
4pub mod health;
5#[cfg(feature = "db")]
6pub 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")]
30pub fn set_database_path(path: std::path::PathBuf) -> Result<(), crate::errors::Error> {
31 DB_DIR
32 .set(path)
33 .map_err(crate::errors::Error::DbPathSetError)?;
34 log::info!("Primed database path");
35 Ok(())
36}
37
38#[cfg(feature = "db")]
44pub async fn init_database() -> surrealdb::Result<Surreal<Db>> {
45 let db = Surreal::new(DB_DIR
46 .get().cloned()
47 .unwrap_or_else(|| {
48 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());
49 TEMP_DB_DIR.path()
50 .to_path_buf()
51 })).await?;
52
53 db.use_ns("mecomp").use_db("music").await?;
54
55 register_custom_analyzer(&db).await?;
56 surrealqlx::register_tables!(
57 &db,
58 schemas::album::Album,
59 schemas::artist::Artist,
60 schemas::song::Song,
61 schemas::collection::Collection,
62 schemas::playlist::Playlist,
63 schemas::dynamic::DynamicPlaylist
64 )?;
65 #[cfg(feature = "analysis")]
66 surrealqlx::register_tables!(&db, schemas::analysis::Analysis)?;
67
68 Ok(db)
69}
70
71#[cfg(feature = "db")]
72pub(crate) async fn register_custom_analyzer<C>(db: &Surreal<C>) -> surrealdb::Result<()>
73where
74 C: surrealdb::Connection,
75{
76 use queries::define_analyzer;
77 use surrealdb::sql::Tokenizer;
78
79 db.query(define_analyzer(
80 FULL_TEXT_SEARCH_ANALYZER_NAME,
81 Some(Tokenizer::Class),
82 &[
83 "ascii",
84 "lowercase",
85 "edgengram(1, 10)",
86 "snowball(english)",
87 ],
88 ))
89 .await?;
90
91 Ok(())
92}
93
94#[cfg(test)]
95mod test {
96 use super::schemas::{
97 album::Album, artist::Artist, collection::Collection, dynamic::DynamicPlaylist,
98 playlist::Playlist, song::Song,
99 };
100 use super::*;
101
102 use surrealdb::engine::local::Mem;
103 use surrealqlx::traits::Table;
104
105 #[tokio::test]
106 async fn test_register_tables() -> anyhow::Result<()> {
107 let db = Surreal::new::<Mem>(()).await?;
109 db.use_ns("test").use_db("test").await?;
110
111 register_custom_analyzer(&db).await?;
113
114 <Album as Table>::init_table(&db).await?;
116 <Artist as Table>::init_table(&db).await?;
117 <Song as Table>::init_table(&db).await?;
118 <Collection as Table>::init_table(&db).await?;
119 <Playlist as Table>::init_table(&db).await?;
120 <DynamicPlaylist as Table>::init_table(&db).await?;
121 <Album as Table>::init_table(&db).await?;
123
124 Ok(())
125 }
126}