mod db; mod server; use std::sync::Arc; use r2d2_sqlite::{rusqlite, SqliteConnectionManager}; use tera::Tera; use tower_http::compression::CompressionLayer; const MIGRATIONS: [&str; 2] = [ include_str!("migrations/000_initial.sql"), include_str!("migrations/001_plants.sql"), ]; const STATIC_FILES: [(&str, &'static str); 1] = [( "htmx_2.0.4.min.js", include_str!("static/htmx_2.0.4.min.js"), )]; #[derive(Clone)] pub struct Context { pool: db::DbPool, tera: Arc, } #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); let manager = SqliteConnectionManager::file("db.sqlite"); let pool = r2d2::Pool::new(manager).unwrap(); run_migrations(&pool).unwrap(); let tera = load_templates(); let ctx = Context { pool, tera: Arc::new(tera), }; let app = server::app(ctx).layer(CompressionLayer::new().br(true).gzip(true)); let address = "0.0.0.0:8000"; tracing::info!("Starting server on {address}"); let listener = tokio::net::TcpListener::bind(address).await.unwrap(); axum::serve(listener, app.into_make_service()) .await .unwrap(); } fn run_migrations(pool: &db::DbPool) -> rusqlite::Result<()> { let mut conn = pool.get().unwrap(); // If the migration version query fails, we assume it's because the table isn't there yet so we // try to run the first migration let mut next_version = conn .query_row("select max(version) from migration_version", (), |row| { row.get::<_, usize>(0).map(|n| n + 1) }) .unwrap_or(0); while next_version < MIGRATIONS.len() { let tx = conn.transaction()?; tx.execute(MIGRATIONS[next_version], ())?; let cur_time = chrono::Local::now().timestamp(); tx.execute( "insert into migration_version values ($1, $2)", (next_version, cur_time), )?; tx.commit()?; tracing::info!("Applied migration {next_version}"); next_version += 1; } Ok(()) } fn load_templates() -> Tera { let mut tera = Tera::default(); tera.add_raw_templates(vec![ ("base.html", include_str!("templates/base.html")), ("index.html", include_str!("templates/index.html")), ( "partials/plants_ul.html", include_str!("templates/partials/plants_ul.html"), ), ( "partials/plant_li.html", include_str!("templates/partials/plant_li.html"), ), ("plant_page.html", include_str!("templates/plant_page.html")), ( "partials/plant_info.html", include_str!("templates/partials/plant_info.html"), ), ]) .unwrap(); tera }