1use std::{collections::HashMap, path::Path};
10
11use diesel::{sql_query, Connection, ConnectionError, RunQueryDsl, SqliteConnection};
12use tracing::{debug, trace};
13
14use crate::migration::{apply_migrations, migrate_json_to_jsonb, DbType};
15
16pub struct DbConnection {
18 conn: SqliteConnection,
19}
20
21impl DbConnection {
22 pub fn open<P: AsRef<Path>>(path: P, db_type: DbType) -> Result<Self, ConnectionError> {
33 let path_str = path.as_ref().to_string_lossy();
34 debug!(path = %path_str, db_type = ?db_type, "opening database connection");
35
36 let mut conn = SqliteConnection::establish(&path_str)?;
37 trace!("database connection established");
38
39 sql_query("PRAGMA journal_mode = WAL;")
40 .execute(&mut conn)
41 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
42 trace!("WAL journal mode enabled");
43
44 apply_migrations(&mut conn, &db_type)
45 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
46 trace!("migrations applied");
47
48 if matches!(db_type, DbType::Core) {
51 migrate_json_to_jsonb(&mut conn, db_type)
52 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
53 trace!("JSON to JSONB migration completed");
54 }
55
56 debug!(path = %path_str, "database opened successfully");
57 Ok(Self {
58 conn,
59 })
60 }
61
62 pub fn open_without_migrations<P: AsRef<Path>>(path: P) -> Result<Self, ConnectionError> {
66 let path_str = path.as_ref().to_string_lossy();
67 debug!(path = %path_str, "opening database without migrations");
68
69 let mut conn = SqliteConnection::establish(&path_str)?;
70 trace!("database connection established");
71
72 sql_query("PRAGMA journal_mode = WAL;")
74 .execute(&mut conn)
75 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
76 trace!("WAL journal mode enabled");
77
78 debug!(path = %path_str, "database opened successfully");
79 Ok(Self {
80 conn,
81 })
82 }
83
84 pub fn open_metadata<P: AsRef<Path>>(path: P) -> Result<Self, ConnectionError> {
91 let path_str = path.as_ref().to_string_lossy();
92 debug!(path = %path_str, "opening metadata database");
93
94 let mut conn = SqliteConnection::establish(&path_str)?;
95 trace!("metadata database connection established");
96
97 sql_query("PRAGMA journal_mode = WAL;")
99 .execute(&mut conn)
100 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
101 trace!("WAL journal mode enabled");
102
103 migrate_json_to_jsonb(&mut conn, DbType::Metadata)
105 .map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
106 trace!("JSON to JSONB migration completed");
107
108 debug!(path = %path_str, "metadata database opened successfully");
109 Ok(Self {
110 conn,
111 })
112 }
113
114 pub fn conn(&mut self) -> &mut SqliteConnection {
116 &mut self.conn
117 }
118}
119
120impl std::ops::Deref for DbConnection {
121 type Target = SqliteConnection;
122
123 fn deref(&self) -> &Self::Target {
124 &self.conn
125 }
126}
127
128impl std::ops::DerefMut for DbConnection {
129 fn deref_mut(&mut self) -> &mut Self::Target {
130 &mut self.conn
131 }
132}
133
134pub struct DatabaseManager {
156 core: DbConnection,
158 metadata: HashMap<String, DbConnection>,
160}
161
162impl DatabaseManager {
163 pub fn new<P: AsRef<Path>>(base_dir: P) -> Result<Self, ConnectionError> {
174 let base = base_dir.as_ref();
175 debug!(base_dir = %base.display(), "initializing database manager");
176
177 let core_path = base.join("core.db");
178
179 let core = DbConnection::open(&core_path, DbType::Core)?;
180
181 debug!("database manager initialized");
182 Ok(Self {
183 core,
184 metadata: HashMap::new(),
185 })
186 }
187
188 pub fn add_metadata_db<P: AsRef<Path>>(
199 &mut self,
200 repo_name: &str,
201 path: P,
202 ) -> Result<(), ConnectionError> {
203 debug!(repo_name = repo_name, "adding metadata database");
204 let conn = DbConnection::open_metadata(path)?;
205 self.metadata.insert(repo_name.to_string(), conn);
206 trace!(repo_name = repo_name, "metadata database added to manager");
207 Ok(())
208 }
209
210 pub fn core(&mut self) -> &mut DbConnection {
212 &mut self.core
213 }
214
215 pub fn metadata(&mut self, repo_name: &str) -> Option<&mut DbConnection> {
219 self.metadata.get_mut(repo_name)
220 }
221
222 pub fn all_metadata(&mut self) -> impl Iterator<Item = (&String, &mut DbConnection)> {
224 self.metadata.iter_mut()
225 }
226
227 pub fn metadata_names(&self) -> impl Iterator<Item = &String> {
229 self.metadata.keys()
230 }
231
232 pub fn remove_metadata_db(&mut self, repo_name: &str) -> Option<DbConnection> {
234 debug!(repo_name = repo_name, "removing metadata database");
235 self.metadata.remove(repo_name)
236 }
237}