use anyhow::Context; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; use sqlx::SqlitePool; use std::str::FromStr; pub async fn init_db(database_url: &str) -> anyhow::Result { let options = SqliteConnectOptions::from_str(database_url) .context("invalid DATABASE_URL")? .create_if_missing(true) .journal_mode(sqlx::sqlite::SqliteJournalMode::Wal) .foreign_keys(true); let pool = SqlitePoolOptions::new() .max_connections(5) .connect_with(options) .await .context("failed to connect to SQLite")?; run_schema(&pool).await?; seed_horizon(&pool).await?; Ok(pool) } async fn run_schema(pool: &SqlitePool) -> anyhow::Result<()> { let schema = include_str!("schema.sql"); // Execute each statement separately for statement in schema.split(';') { let s = statement.trim(); if !s.is_empty() { sqlx::query(s).execute(pool).await?; } } Ok(()) } async fn seed_horizon(pool: &SqlitePool) -> anyhow::Result<()> { let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM horizon") .fetch_one(pool) .await?; if count == 0 { let mut tx = pool.begin().await?; for az in 0..360i32 { sqlx::query("INSERT OR IGNORE INTO horizon (az_deg, alt_deg) VALUES (?, 15.0)") .bind(az) .execute(&mut *tx) .await?; } tx.commit().await?; tracing::info!("Seeded horizon table with 360 flat points at 15°"); } Ok(()) }