90 lines
2.6 KiB
Rust
90 lines
2.6 KiB
Rust
mod euph;
|
|
mod migrate;
|
|
mod prepare;
|
|
|
|
use std::path::Path;
|
|
use std::{fs, thread};
|
|
|
|
use rusqlite::Connection;
|
|
use tokio::sync::{mpsc, oneshot};
|
|
|
|
use self::euph::EuphRequest;
|
|
pub use self::euph::EuphVault;
|
|
|
|
enum Request {
|
|
Close(oneshot::Sender<()>),
|
|
Gc(oneshot::Sender<()>),
|
|
Euph(EuphRequest),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Vault {
|
|
pub(self) tx: mpsc::UnboundedSender<Request>,
|
|
}
|
|
|
|
impl Vault {
|
|
pub async fn close(&self) {
|
|
let (tx, rx) = oneshot::channel();
|
|
let _ = self.tx.send(Request::Close(tx));
|
|
let _ = rx.await;
|
|
}
|
|
|
|
pub async fn gc(&self) {
|
|
let (tx, rx) = oneshot::channel();
|
|
let _ = self.tx.send(Request::Gc(tx));
|
|
let _ = rx.await;
|
|
}
|
|
|
|
pub fn euph(&self, room: String) -> EuphVault {
|
|
EuphVault {
|
|
vault: self.clone(),
|
|
room,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn run(mut conn: Connection, mut rx: mpsc::UnboundedReceiver<Request>) {
|
|
while let Some(request) = rx.blocking_recv() {
|
|
match request {
|
|
Request::Close(tx) => {
|
|
println!("Closing vault");
|
|
let _ = conn.execute_batch("PRAGMA optimize");
|
|
// Ensure `Vault::close` exits only after the sqlite connection
|
|
// has been closed properly.
|
|
drop(conn);
|
|
drop(tx);
|
|
break;
|
|
}
|
|
Request::Gc(tx) => {
|
|
let _ = conn.execute_batch("ANALYZE; VACUUM;");
|
|
drop(tx);
|
|
}
|
|
Request::Euph(r) => r.perform(&mut conn),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn launch(path: &Path) -> rusqlite::Result<Vault> {
|
|
// If this fails, rusqlite will complain about not being able to open the db
|
|
// file, which saves me from adding a separate vault error type.
|
|
let _ = fs::create_dir_all(path.parent().expect("path to file"));
|
|
|
|
let mut conn = Connection::open(path)?;
|
|
|
|
// Setting locking mode before journal mode so no shared memory files
|
|
// (*-shm) need to be created by sqlite. Apparently, setting the journal
|
|
// mode is also enough to immediately acquire the exclusive lock even if the
|
|
// database was already using WAL.
|
|
// https://sqlite.org/pragma.html#pragma_locking_mode
|
|
conn.pragma_update(None, "locking_mode", "exclusive")?;
|
|
conn.pragma_update(None, "journal_mode", "wal")?;
|
|
conn.pragma_update(None, "foreign_keys", true)?;
|
|
conn.pragma_update(None, "trusted_schema", false)?;
|
|
|
|
migrate::migrate(&mut conn)?;
|
|
prepare::prepare(&mut conn)?;
|
|
|
|
let (tx, rx) = mpsc::unbounded_channel();
|
|
thread::spawn(move || run(conn, rx));
|
|
Ok(Vault { tx })
|
|
}
|