elph-agent 0.0.16

Agent runtime for Elph coding assistant
Documentation
use anyhow::Result;
use turso::Connection;

use crate::migration::Migration;

pub async fn run(conn: &Connection, migrations: &[Migration]) -> Result<()> {
    conn.execute(
        "CREATE TABLE IF NOT EXISTS app_migrations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            version INTEGER NOT NULL,
            name TEXT NOT NULL,
            applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        (),
    )
    .await?;

    conn.execute(
        "CREATE UNIQUE INDEX IF NOT EXISTS idx_app_migrations_version
         ON app_migrations(version)",
        (),
    )
    .await?;

    let mut rows = conn
        .query("SELECT COALESCE(MAX(version), 0) FROM app_migrations", ())
        .await?;

    let current_version = if let Some(row) = rows.next().await? {
        row.get::<i64>(0)?
    } else {
        0
    };

    for migration in migrations {
        if migration.version <= current_version {
            continue;
        }

        for statement in split_sql(migration.up) {
            conn.execute(statement, ()).await?;
        }

        conn.execute(
            "INSERT INTO app_migrations (version, name) VALUES (?, ?)",
            (migration.version, migration.name),
        )
        .await?;
    }

    Ok(())
}

fn split_sql(sql: &str) -> Vec<&str> {
    sql.split(';')
        .map(str::trim)
        .filter(|statement| !statement.is_empty())
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn split_sql_handles_multiple_statements() {
        let parts = split_sql("CREATE TABLE a (id INT); CREATE INDEX idx ON a(id);");
        assert_eq!(parts.len(), 2);
    }
}