elph_agent/datastore/
mod.rs1mod lazy;
4mod migrations;
5
6use std::path::Path;
7
8use anyhow::Result;
9use turso::Builder;
10
11pub use crate::migration::Migration;
12pub use lazy::ensure_databases_once;
13pub use migrations::run as run_migrations;
14
15pub struct DatabaseSpec<'a> {
17 pub path: &'a Path,
18 pub migrations: &'static [Migration],
19}
20
21pub async fn ensure_database(path: &Path, migrations: &'static [Migration]) -> Result<()> {
23 ensure_parent_dir(path)?;
24 open_and_migrate(path, migrations).await
25}
26
27pub async fn ensure_databases(specs: &[DatabaseSpec<'_>]) -> Result<()> {
29 for spec in specs {
30 ensure_database(spec.path, spec.migrations).await?;
31 }
32 Ok(())
33}
34
35async fn open_and_migrate(path: &Path, migrations: &'static [Migration]) -> Result<()> {
36 let db = Builder::new_local(&path.to_string_lossy()).build().await?;
37 let conn = db.connect()?;
38 migrations::run(&conn, migrations).await?;
39 Ok(())
40}
41
42fn ensure_parent_dir(path: &Path) -> Result<()> {
43 if let Some(parent) = path.parent() {
44 std::fs::create_dir_all(parent)?;
45 }
46 Ok(())
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 static TEST_MIGRATIONS: [Migration; 1] = [Migration {
54 version: 1,
55 name: "create_notes",
56 up: "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL)",
57 }];
58
59 #[tokio::test]
60 async fn ensure_database_applies_migrations() {
61 let tmp = tempfile::tempdir().expect("tempdir");
62 let db_path = tmp.path().join("test.db");
63
64 ensure_database(&db_path, &TEST_MIGRATIONS)
65 .await
66 .expect("ensure database");
67
68 assert!(db_path.exists());
69 }
70}