// Copyright (C) 2024-2026 Whiterun LLC // // This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later. // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html use anyhow::{bail, Result}; use rusqlite::{params, Connection}; pub const DB_VERSION: u32 = 5; const DB_VERSION_KEY: &str = "db_version"; /// Create the config table pub fn create_table(conn: &Connection) { conn.execute( "CREATE TABLE config ( key TEXT PRIMARY KEY, value TEXT )", [], ) .expect("failed to create config table"); } /// Add or update config entry pub fn config_set(conn: &Connection, key: &str, value: &str) { let mut stmt = conn .prepare("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)") .unwrap(); stmt.execute(params![key, value]).unwrap(); } /// Get config entry pub fn config_get(conn: &Connection, key: &str) -> Result> { let mut stmt = conn.prepare("SELECT value FROM config WHERE key = ?")?; let mut row = stmt.query([key])?; if let Some(r) = row.next()? { Ok(r.get(0)?) } else { Ok(None) } } /// Check database version and panic if it doesn't match expected version pub fn check_db_version(conn: &Connection) -> Result<()> { let version_str = config_get(conn, DB_VERSION_KEY)?; match version_str { Some(version_str) => { let version: u32 = version_str.parse().unwrap_or(0); if version != DB_VERSION { bail!( "Database version mismatch. Expected {}, got {}", DB_VERSION, version ); } } None => { bail!( "Database version not found. Expected version {}", DB_VERSION ); } } Ok(()) } /// Set database version pub fn set_db_version(conn: &Connection) { config_set(conn, DB_VERSION_KEY, &DB_VERSION.to_string()); } #[cfg(test)] mod tests { use super::*; use rusqlite::Connection; #[test] fn test_db_version_functionality() { // Create a new in-memory database let conn = Connection::open_in_memory().unwrap(); // Create the config table using the shared function create_table(&conn); // Test setting the version set_db_version(&conn); // Test getting the version let version_str = config_get(&conn, DB_VERSION_KEY).unwrap().unwrap(); let version: u32 = version_str.parse().unwrap(); assert_eq!(version, DB_VERSION); // Test version check should pass check_db_version(&conn).unwrap(); // Test with wrong version config_set(&conn, DB_VERSION_KEY, "1"); let result = check_db_version(&conn); assert!(result.is_err()); assert!(result .unwrap_err() .to_string() .contains("Database version mismatch")); // Test with missing version conn.execute("DELETE FROM config WHERE key = ?", [DB_VERSION_KEY]) .unwrap(); let result = check_db_version(&conn); assert!(result.is_err()); assert!(result .unwrap_err() .to_string() .contains("Database version not found")); } }