calories/server/src/main.rs

248 lines
7.4 KiB
Rust
Raw Normal View History

2024-08-13 20:11:12 +02:00
#![feature(iter_intersperse)]
use axum::{
extract::State,
2024-08-14 17:39:47 +02:00
http::{
// header,
StatusCode,
Uri,
},
2024-08-13 20:11:12 +02:00
response::{Html, IntoResponse},
routing::{get, get_service},
Json, Router,
2024-08-13 20:11:12 +02:00
};
2024-08-14 17:39:47 +02:00
// use image::ImageFormat;
// use mysql::prelude::*;
2024-08-13 20:11:12 +02:00
use mysql::*;
2024-08-14 17:39:47 +02:00
// use plotters::prelude::*;
// use std::io::{BufWriter, Cursor};
2024-08-13 20:11:12 +02:00
use std::sync::Arc;
2024-08-14 17:39:47 +02:00
mod math;
2024-08-13 20:11:12 +02:00
mod my_structs;
use my_structs::MyError;
use my_structs::Purchase;
2024-08-13 20:11:12 +02:00
mod plotting;
2024-08-14 17:39:47 +02:00
// use crate::plotting::Plotter;
2024-08-13 21:50:17 +02:00
use std::{thread, time};
use tower_http::services::ServeDir;
2024-08-13 20:11:12 +02:00
use shared::structs::Nutrition;
2024-08-13 20:11:12 +02:00
#[derive(Clone)]
struct AppState {
sql_pool: Pool,
}
2024-08-21 23:00:06 +02:00
fn get_sql_pool() -> Pool {
let db_user = std::env::var("DB_USER").expect("DB_USER environment variable not set!");
let db_password = std::env::var("DB_PASSWD").expect("DB_PASSWD environment variable not set!");
let db_host = std::env::var("DB_HOST").expect("DB_HOST environment variable not set!");
let db_name = std::env::var("DB_NAME").expect("DB_NAME environment variable not set!");
let url = format!(
"mysql://{}:{}@{}:3306/{}",
db_user, db_password, db_host, db_name
);
2024-08-13 20:11:12 +02:00
loop {
2024-08-21 23:00:06 +02:00
let sql_pool_attempt = Pool::new(url.as_str());
match sql_pool_attempt {
Ok(sql_pool) => return sql_pool,
Err(e) => {
println!("Cannot connect to SQL database. Retrying. {:?}", e);
thread::sleep(time::Duration::from_millis(1000));
}
2024-08-13 20:11:12 +02:00
}
}
}
#[tokio::main]
async fn main() {
//Initialize state
2024-08-21 23:00:06 +02:00
let sql_pool = get_sql_pool();
2024-08-13 20:11:12 +02:00
let shared_state = Arc::new(AppState { sql_pool });
// build our application with a single route
let app = Router::new()
.route("/get_rows", get(get_rows))
.route("/html_demo", get(html_demo))
.route("/html_plotter", get(html_plotter))
.route("/image.png", get(png_image))
.route("/image.svg", get(svg_image))
2024-08-14 17:39:47 +02:00
.route("/calorie_intake.svg", get(svg_calorie_intake))
.route("/calorie_intake", get(calorie_intake))
.route("/wasm_test", get(wasm_test))
2024-08-19 18:11:59 +02:00
.route("/camera_test", get(camera_test))
.route("/get_product_options", get(get_product_options))
.nest_service("/pkg", get_service(ServeDir::new("../browser/pkg")))
.nest_service("/css", get_service(ServeDir::new("../css")))
.nest_service("/js", get_service(ServeDir::new("../javascript")))
.nest_service("/assets", get_service(ServeDir::new("../assets")))
2024-08-13 21:50:17 +02:00
.with_state(shared_state)
.fallback(not_found);
2024-08-13 20:11:12 +02:00
// run our app with hyper, listening globally on port 3000
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
2024-08-13 21:50:17 +02:00
async fn not_found(uri: Uri) -> (StatusCode, String) {
2024-08-14 17:39:47 +02:00
(StatusCode::NOT_FOUND, format!("404 not found: {uri}"))
2024-08-13 21:50:17 +02:00
}
async fn get_rows(State(state): State<Arc<AppState>>) -> Result<String, MyError> {
let nutrition_val_promise = Nutrition::get_nutrition_hashmap(state.sql_pool.clone());
let purchases_val_promise = Purchase::get_purchase_rows(state.sql_pool.clone());
2024-08-13 21:50:17 +02:00
let nutrition_val = nutrition_val_promise.await?;
let purchases_val = purchases_val_promise.await?;
2024-08-13 20:11:12 +02:00
println!("Serving: get_rows");
2024-08-14 17:39:47 +02:00
// Ok("See print logs instead".to_string())
2024-08-13 21:50:17 +02:00
Ok(format!("{:#?} {:#?}", nutrition_val, purchases_val))
2024-08-13 20:11:12 +02:00
}
#[axum::debug_handler]
async fn get_product_options(
State(state): State<Arc<AppState>>,
) -> Result<Json<Vec<Nutrition>>, MyError> {
println!("Serving: get_product_options");
let nutrition_val_promise = Nutrition::get_nutrition_rows(state.sql_pool.clone());
let nutrition_val = nutrition_val_promise.await?;
Ok(Json(nutrition_val))
}
2024-08-13 20:11:12 +02:00
async fn html_demo(State(_state): State<Arc<AppState>>) -> Html<String> {
println!("Serving: html_demo");
Html("<body><p>This is HTML</p></body>".to_string())
}
2024-08-14 17:39:47 +02:00
async fn svg_image(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
println!("Serving: svg_image");
let fun = plotting::SVGPlotter::examplefun();
plotting::SVGPlotter::plot(640, 480, fun)
2024-08-13 20:11:12 +02:00
}
2024-08-14 17:39:47 +02:00
async fn svg_calorie_intake(
State(state): State<Arc<AppState>>,
) -> Result<impl IntoResponse, MyError> {
2024-08-14 17:39:47 +02:00
println!("Serving: svg_calorie_intake");
2024-08-13 20:11:12 +02:00
let nutrition_val_promise = Nutrition::get_nutrition_hashmap(state.sql_pool.clone());
let purchases_val_promise = Purchase::get_purchase_rows(state.sql_pool.clone());
2024-08-13 20:11:12 +02:00
2024-08-14 17:39:47 +02:00
let nutrition_val = nutrition_val_promise.await?;
let purchases_val = purchases_val_promise.await?;
2024-08-13 20:11:12 +02:00
2024-08-14 17:39:47 +02:00
let plotfun = math::plot_calories_per_day(nutrition_val, purchases_val);
Ok(plotting::SVGPlotter::plot(640, 480, plotfun))
2024-08-13 20:11:12 +02:00
}
2024-08-14 17:39:47 +02:00
async fn png_image(State(_state): State<Arc<AppState>>) -> impl IntoResponse {
println!("Serving: png_image");
let fun = plotting::BitMapPlotter::examplefun();
plotting::BitMapPlotter::plot(640, 480, fun, plotting::BitMapOutputFormat::PNG)
2024-08-13 20:11:12 +02:00
}
2024-08-14 17:39:47 +02:00
async fn html_plotter(State(_state): State<Arc<AppState>>) -> Html<String> {
println!("Serving: html_plotter");
2024-08-13 20:11:12 +02:00
2024-08-14 17:39:47 +02:00
Html("<body><img src=/image.svg></img><img src=/calorie_intake.svg></img></body>".to_string())
2024-08-13 20:11:12 +02:00
}
2024-08-14 17:39:47 +02:00
async fn calorie_intake(State(_state): State<Arc<AppState>>) -> Html<String> {
println!("Serving: calorie_intake");
2024-08-13 20:11:12 +02:00
2024-08-14 17:39:47 +02:00
Html("<body><img src=/calorie_intake.svg></img></body>".to_string())
2024-08-13 20:11:12 +02:00
}
fn construct_js(path: &str) -> Result<String, MyError> {
// let javascript = std::fs::read_to_string(format!("../javascript/{}", path))?;
// let module = format!(
// "
// <script type=\"module\">
// {}
// </script>
// ",
// javascript
// );
// Ok(module)
Ok(format!(
"<script type=\"module\" src=\"/js/{}\"></script>",
path
))
}
fn construct_css(path: &str) -> Result<String, MyError> {
Ok(format!("<link rel=\"stylesheet\" href=\"/css/{}\"/>", path))
2024-08-19 18:11:59 +02:00
}
fn construct_tmpl(path: &str) -> Result<String, MyError> {
2024-08-19 18:11:59 +02:00
Ok(std::fs::read_to_string(format!("../templates/{}", path))?)
}
fn construct_html(
js_paths: Vec<&str>,
css_paths: Vec<&str>,
2024-08-19 18:11:59 +02:00
tmpl_paths: Vec<&str>,
) -> Result<String, MyError> {
2024-08-19 18:11:59 +02:00
let js_modules: Vec<String> = js_paths
.into_iter()
.map(construct_js)
.collect::<Result<Vec<String>, MyError>>()?;
let css_styling: Vec<String> = css_paths
.into_iter()
.map(construct_css)
.collect::<Result<Vec<String>, MyError>>()?;
2024-08-19 18:11:59 +02:00
let tmpl_snippets: Vec<String> = tmpl_paths
.into_iter()
.map(construct_tmpl)
.collect::<Result<Vec<String>, MyError>>()?;
2024-08-19 18:11:59 +02:00
let html = format!(
"
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
2024-08-19 18:11:59 +02:00
{}
{}
2024-08-19 18:11:59 +02:00
</head>
<body>
{}
</body>
",
js_modules.join(""),
css_styling.join(""),
2024-08-19 18:11:59 +02:00
tmpl_snippets.join("")
);
Ok(html)
}
fn get_template(path: &str) -> Result<String, MyError> {
2024-08-19 18:11:59 +02:00
Ok(std::fs::read_to_string(format!("../templates/{}", path))?)
}
async fn wasm_test(State(_state): State<Arc<AppState>>) -> Result<Html<String>, MyError> {
println!("Serving: wasm_test");
2024-08-19 18:11:59 +02:00
let content = get_template("view_calories.html")?;
Ok(Html(content))
}
async fn camera_test(State(_state): State<Arc<AppState>>) -> Result<Html<String>, MyError> {
2024-08-19 18:11:59 +02:00
println!("Serving: camera_test");
let html = construct_html(
vec!["camera.js"],
vec!["camera_test.css"],
vec!["camera_test.html"],
)?;
2024-08-19 18:11:59 +02:00
Ok(Html(html))
}