use std::sync::{Arc, Mutex}; use axum::{extract::{Path, State}, response::Html, routing::{get, post}, Router}; use rusqlite::Connection; use askama::Template; #[derive(Template)] #[template(path = "index.html")] struct IndexTemplate { foods: Vec, sum: i32, } #[derive(Template)] #[template(path = "food.html")] struct FoodTemplate { food: Food, } #[derive(Debug, Clone, PartialEq)] struct Food { id: i32, portion: String, name: String, kc_per_serving: i32, target_servings: i32, actual_servings: i32, } #[tokio::main] async fn main() { let db_connecion_str = "./foods.db".to_string(); let conn = Connection::open(db_connecion_str).unwrap(); conn.execute(include_str!("create_tables.sql"), ()).unwrap(); let conn = Arc::new(Mutex::new(conn)); let app = Router::new().route("/", get(root)) .route("/increase/{id}", post(increase)) .route("/decrease/{id}", post(decrease)) .with_state(conn); let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await.unwrap(); println!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); } async fn root( State(conn): State>> ) -> Html { let conn = conn.lock().unwrap(); let mut stmt = conn.prepare("SELECT id, portion, name, kc_per_serving, target_servings, actual_servings FROM food").unwrap(); let foods = stmt.query_map((), |row| { Ok(Food { id: row.get(0).unwrap(), portion: row.get(1).unwrap(), name: row.get(2).unwrap(), kc_per_serving: row.get(3).unwrap(), target_servings: row.get(4).unwrap(), actual_servings: row.get(5).unwrap(), }) }).unwrap().collect::>().unwrap(); let mut stmt = conn.prepare("SELECT SUM(kc_per_serving * actual_servings) as kc FROM food").unwrap(); let sum = stmt.query_one((), |row| row.get(0)).unwrap(); let index = IndexTemplate {foods, sum}; Html( index.render().unwrap() ) } async fn increase( State(conn): State>>, Path(id): Path ) -> Html{ let conn = conn.lock().unwrap(); let mut stmt = conn.prepare("UPDATE food SET actual_servings = (SELECT actual_servings FROM food WHERE id = ?1) + 1 WHERE id = ?1").unwrap(); stmt.execute((id,)).unwrap(); let mut stmt = conn.prepare("SELECT id, portion, name, kc_per_serving, target_servings, actual_servings FROM food WHERE id = ?1").unwrap(); let food = stmt.query_one((id,), |row| Ok(Food { id: row.get(0).unwrap(), portion: row.get(1).unwrap(), name: row.get(2).unwrap(), kc_per_serving: row.get(3).unwrap(), target_servings: row.get(4).unwrap(), actual_servings: row.get(5).unwrap(), })).unwrap(); let food = FoodTemplate {food}; Html( food.render().unwrap() ) } async fn decrease( State(conn): State>>, Path(id): Path ) -> Html{ let conn = conn.lock().unwrap(); let mut stmt = conn.prepare("UPDATE food SET actual_servings = MAX((SELECT actual_servings FROM food WHERE id = ?1) - 1, 0) WHERE id = ?1").unwrap(); stmt.execute((id,)).unwrap(); let mut stmt = conn.prepare("SELECT id, portion, name, kc_per_serving, target_servings, actual_servings FROM food WHERE id = ?1").unwrap(); let food = stmt.query_one((id,), |row| Ok(Food { id: row.get(0).unwrap(), portion: row.get(1).unwrap(), name: row.get(2).unwrap(), kc_per_serving: row.get(3).unwrap(), target_servings: row.get(4).unwrap(), actual_servings: row.get(5).unwrap(), })).unwrap(); let food = FoodTemplate {food}; Html( food.render().unwrap() ) }