use rusqlite::Connection;
use rusqlite::ToSql;
use std::cell::RefCell;
use std::ops::Range;
use std::rc::Rc;
use ic_stable_structures::memory_manager::MemoryId;
use ic_stable_structures::{DefaultMemoryImpl, memory_manager::MemoryManager};
const FS_MEMORY_RANGE: Range<u8> = 200..210;
const DEFAULT_MOUNTED_DB_ID: u8 = 120;
const DB_FILE_NAME: &str = "main.db";
pub use ic_wasi_polyfill;
pub use rusqlite;
thread_local! {
pub static CONNECTION: RefCell<Option<Rc<Connection>>> = const { RefCell::new(None) };
pub static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> = {
let m = MemoryManager::init(DefaultMemoryImpl::default());
ic_wasi_polyfill::init_with_memory_manager(&[0u8; 32], &[], &m, FS_MEMORY_RANGE);
RefCell::new(m)
};
}
fn set_pragmas(conn: &Connection) {
conn.pragma_update(None, "journal_mode", &"PERSIST" as &dyn ToSql)
.unwrap();
conn.pragma_update(None, "synchronous", &0 as &dyn ToSql)
.unwrap();
conn.pragma_update(None, "locking_mode", &"EXCLUSIVE" as &dyn ToSql)
.unwrap();
conn.pragma_update(None, "temp_store", &"MEMORY" as &dyn ToSql)
.unwrap();
conn.pragma_update(None, "cache_size", &1000000 as &dyn ToSql)
.unwrap();
}
fn init_db() -> Rc<Connection> {
let memory = MEMORY_MANAGER.with_borrow(|m| m.get(MemoryId::new(DEFAULT_MOUNTED_DB_ID)));
ic_wasi_polyfill::mount_memory_file(DB_FILE_NAME, Box::new(memory));
let _ = std::fs::remove_dir_all(format!("{DB_FILE_NAME}.lock"));
let conn = rusqlite::Connection::open(DB_FILE_NAME).expect("Failed opening the database!");
set_pragmas(&conn);
CONNECTION.with_borrow_mut(|c| {
*c = Some(Rc::new(conn));
});
CONNECTION.with_borrow(|c| c.clone().unwrap())
}
pub fn get_connection() -> Rc<Connection> {
if let Some(conn) = CONNECTION.with_borrow(|c| c.clone()) {
Rc::clone(&conn)
} else {
init_db()
}
}