mtotp_lib/database/
mod.rs1use std::path::{Path, PathBuf};
2use once_cell::sync::OnceCell;
3use sea_orm::prelude::DatabaseConnection;
4use sea_orm::{ConnectionTrait, EntityTrait, Schema, Statement};
5use std::time::Duration;
6use tokio::sync::Mutex;
7
8pub mod registered;
9
10static FOLDER: OnceCell<String> = OnceCell::new();
11
12static CONNECT: OnceCell<Mutex<DatabaseConnection>> = OnceCell::new();
13
14pub async fn init(folder: String) {
15 FOLDER.set(folder).unwrap();
16 init_database().await;
17}
18
19pub(crate) async fn init_database() {
20 let folder = FOLDER.get().unwrap();
21 create_dir_if_not_exists(folder);
22 CONNECT.set(Mutex::new(
23 connect_db(join_paths(vec![folder.as_str(), "mtotp.db"]).as_str()).await
24 )).unwrap();
25 registered::init().await;
26}
27
28pub(crate) async fn connect_db(path: &str) -> DatabaseConnection {
29 let url = format!("sqlite:{}?mode=rwc", path);
30 let mut opt = sea_orm::ConnectOptions::new(url);
31 opt.max_connections(20)
32 .min_connections(5)
33 .connect_timeout(Duration::from_secs(8))
34 .idle_timeout(Duration::from_secs(8))
35 .sqlx_logging(true);
36 sea_orm::Database::connect(opt).await.unwrap()
37}
38
39pub(crate) async fn create_table_if_not_exists<E>(db: &DatabaseConnection, entity: E)
40 where
41 E: EntityTrait,
42{
43 if !has_table(db, entity.table_name()).await {
44 create_table(db, entity).await;
45 };
46}
47
48pub(crate) async fn has_table(db: &DatabaseConnection, table_name: &str) -> bool {
49 let stmt = Statement::from_string(
50 db.get_database_backend(),
51 format!(
52 "SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='{}';",
53 table_name,
54 ),
55 );
56 let rsp = db.query_one(stmt).await.unwrap().unwrap();
57 let count: i32 = rsp.try_get("", "c").unwrap();
58 count > 0
59}
60
61pub(crate) async fn create_table<E>(db: &DatabaseConnection, entity: E)
62 where
63 E: EntityTrait,
64{
65 let builder = db.get_database_backend();
66 let schema = Schema::new(builder);
67 let stmt = &schema.create_table_from_entity(entity);
68 let stmt = builder.build(stmt);
69 db.execute(stmt).await.unwrap();
70}
71
72pub(crate) async fn index_exists(
73 db: &DatabaseConnection,
74 table_name: &str,
75 index_name: &str,
76) -> bool {
77 let stmt = Statement::from_string(
78 db.get_database_backend(),
79 format!(
80 "select COUNT(*) AS c from sqlite_master where type='index' AND tbl_name='{}' AND name='{}';",
81 table_name, index_name,
82 ),
83 );
84 db.query_one(stmt)
85 .await
86 .unwrap()
87 .unwrap()
88 .try_get::<i32>("", "c")
89 .unwrap()
90 > 0
91}
92
93pub(crate) async fn create_index_a(
94 db: &DatabaseConnection,
95 table_name: &str,
96 columns: Vec<&str>,
97 index_name: &str,
98 uk: bool,
99) {
100 let stmt = Statement::from_string(
101 db.get_database_backend(),
102 format!(
103 "CREATE {} INDEX {} ON {}({});",
104 if uk { "UNIQUE" } else { "" },
105 index_name,
106 table_name,
107 columns.join(","),
108 ),
109 );
110 db.execute(stmt).await.unwrap();
111}
112
113#[allow(dead_code)]
114pub(crate) async fn create_index(
115 db: &DatabaseConnection,
116 table_name: &str,
117 columns: Vec<&str>,
118 index_name: &str,
119) {
120 create_index_a(db, table_name, columns, index_name, false).await
121}
122
123pub fn join_paths<P: AsRef<Path>>(paths: Vec<P>) -> String {
124 match paths.len() {
125 0 => String::default(),
126 _ => {
127 let mut path: PathBuf = PathBuf::new();
128 for x in paths {
129 path = path.join(x);
130 }
131 return path.to_str().unwrap().to_string();
132 }
133 }
134}
135
136pub(crate) fn create_dir_if_not_exists(path: &String) {
137 if !Path::new(path).exists() {
138 std::fs::create_dir_all(path).unwrap();
139 }
140}