Use sqlx built-in migration tracking
Replaces hand-rolled raw_sql migration with sqlx::migrate!() macro.
Migrations are now tracked in _sqlx_migrations and only run once.
Existing databases work because our DDL uses IF NOT EXISTS.

Assisted-by: Claude Opus 4.6 via pi
change vytquqykypplqtywtsnllyytvplnwvlu
commit 55c1a487106f60e39dbbde44241fb6d249c14558
author Alpha Chen <alpha@kejadlen.dev>
date
parent lmumzysw
diff --git a/Cargo.toml b/Cargo.toml
index 61ebb6a..b39bfb0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,7 @@ clap = { version = "*", features = ["derive", "env"] }
 rand = "*"
 serde = { version = "*", features = ["derive"] }
 serde_json = "*"
-sqlx = { version = "*", features = ["runtime-tokio", "sqlite"] }
+sqlx = { version = "*", features = ["runtime-tokio", "sqlite", "migrate"] }
 thiserror = "*"
 tokio = { version = "*", features = ["full"] }
 xdg = "*"
diff --git a/src/db.rs b/src/db.rs
index 35d6f6c..5aa5edb 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -20,20 +20,10 @@ pub async fn connect(path: &Path) -> Result<SqlitePool, RangerError> {
         .connect_with(options)
         .await?;
 
-    migrate(&pool).await?;
+    sqlx::migrate!().run(&pool).await?;
     Ok(pool)
 }
 
-async fn migrate(pool: &SqlitePool) -> Result<(), RangerError> {
-    sqlx::raw_sql(include_str!("../migrations/001_initial.sql"))
-        .execute(pool)
-        .await?;
-    sqlx::raw_sql(include_str!("../migrations/002_unique_backlog_name.sql"))
-        .execute(pool)
-        .await?;
-    Ok(())
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/error.rs b/src/error.rs
index 899704e..8df5d6f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,6 +6,8 @@ pub enum RangerError {
     AmbiguousPrefix(String),
     #[error("database error: {0}")]
     Db(#[from] sqlx::Error),
+    #[error("migration error: {0}")]
+    Migrate(#[from] sqlx::migrate::MigrateError),
     #[error("io error: {0}")]
     Io(#[from] std::io::Error),
 }