248 lines
7.4 KiB
Rust
248 lines
7.4 KiB
Rust
#![feature(iter_intersperse)]
|
|
|
|
use axum::{
|
|
extract::State,
|
|
http::{
|
|
// header,
|
|
StatusCode,
|
|
Uri,
|
|
},
|
|
response::{Html, IntoResponse},
|
|
routing::{get, get_service},
|
|
Json, Router,
|
|
};
|
|
// use image::ImageFormat;
|
|
// use mysql::prelude::*;
|
|
use mysql::*;
|
|
// use plotters::prelude::*;
|
|
// use std::io::{BufWriter, Cursor};
|
|
use std::sync::Arc;
|
|
mod math;
|
|
mod my_structs;
|
|
use my_structs::MyError;
|
|
use my_structs::Purchase;
|
|
mod plotting;
|
|
// use crate::plotting::Plotter;
|
|
use std::{thread, time};
|
|
use tower_http::services::ServeDir;
|
|
|
|
use shared::structs::Nutrition;
|
|
|
|
#[derive(Clone)]
|
|
struct AppState {
|
|
sql_pool: Pool,
|
|
}
|
|
|
|
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
|
|
);
|
|
loop {
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
//Initialize state
|
|
let sql_pool = get_sql_pool();
|
|
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))
|
|
.route("/calorie_intake.svg", get(svg_calorie_intake))
|
|
.route("/calorie_intake", get(calorie_intake))
|
|
.route("/wasm_test", get(wasm_test))
|
|
.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")))
|
|
.with_state(shared_state)
|
|
.fallback(not_found);
|
|
|
|
// 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();
|
|
}
|
|
|
|
async fn not_found(uri: Uri) -> (StatusCode, String) {
|
|
(StatusCode::NOT_FOUND, format!("404 not found: {uri}"))
|
|
}
|
|
|
|
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());
|
|
|
|
let nutrition_val = nutrition_val_promise.await?;
|
|
let purchases_val = purchases_val_promise.await?;
|
|
|
|
println!("Serving: get_rows");
|
|
// Ok("See print logs instead".to_string())
|
|
Ok(format!("{:#?} {:#?}", nutrition_val, purchases_val))
|
|
}
|
|
|
|
#[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))
|
|
}
|
|
|
|
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())
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
async fn svg_calorie_intake(
|
|
State(state): State<Arc<AppState>>,
|
|
) -> Result<impl IntoResponse, MyError> {
|
|
println!("Serving: svg_calorie_intake");
|
|
|
|
let nutrition_val_promise = Nutrition::get_nutrition_hashmap(state.sql_pool.clone());
|
|
let purchases_val_promise = Purchase::get_purchase_rows(state.sql_pool.clone());
|
|
|
|
let nutrition_val = nutrition_val_promise.await?;
|
|
let purchases_val = purchases_val_promise.await?;
|
|
|
|
let plotfun = math::plot_calories_per_day(nutrition_val, purchases_val);
|
|
Ok(plotting::SVGPlotter::plot(640, 480, plotfun))
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
async fn html_plotter(State(_state): State<Arc<AppState>>) -> Html<String> {
|
|
println!("Serving: html_plotter");
|
|
|
|
Html("<body><img src=/image.svg></img><img src=/calorie_intake.svg></img></body>".to_string())
|
|
}
|
|
|
|
async fn calorie_intake(State(_state): State<Arc<AppState>>) -> Html<String> {
|
|
println!("Serving: calorie_intake");
|
|
|
|
Html("<body><img src=/calorie_intake.svg></img></body>".to_string())
|
|
}
|
|
|
|
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))
|
|
}
|
|
|
|
fn construct_tmpl(path: &str) -> Result<String, MyError> {
|
|
Ok(std::fs::read_to_string(format!("../templates/{}", path))?)
|
|
}
|
|
|
|
fn construct_html(
|
|
js_paths: Vec<&str>,
|
|
css_paths: Vec<&str>,
|
|
tmpl_paths: Vec<&str>,
|
|
) -> Result<String, MyError> {
|
|
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>>()?;
|
|
|
|
let tmpl_snippets: Vec<String> = tmpl_paths
|
|
.into_iter()
|
|
.map(construct_tmpl)
|
|
.collect::<Result<Vec<String>, MyError>>()?;
|
|
|
|
let html = format!(
|
|
"
|
|
<!DOCTYPE html>
|
|
<html lang=\"en\">
|
|
<head>
|
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
|
{}
|
|
{}
|
|
</head>
|
|
<body>
|
|
{}
|
|
</body>
|
|
",
|
|
js_modules.join(""),
|
|
css_styling.join(""),
|
|
tmpl_snippets.join("")
|
|
);
|
|
|
|
Ok(html)
|
|
}
|
|
fn get_template(path: &str) -> Result<String, MyError> {
|
|
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");
|
|
|
|
let content = get_template("view_calories.html")?;
|
|
|
|
Ok(Html(content))
|
|
}
|
|
|
|
async fn camera_test(State(_state): State<Arc<AppState>>) -> Result<Html<String>, MyError> {
|
|
println!("Serving: camera_test");
|
|
|
|
let html = construct_html(
|
|
vec!["camera.js"],
|
|
vec!["camera_test.css"],
|
|
vec!["camera_test.html"],
|
|
)?;
|
|
|
|
Ok(Html(html))
|
|
}
|