diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 86 |
1 files changed, 84 insertions, 2 deletions
diff --git a/src/main.rs b/src/main.rs index e7a11a9..31a10cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,85 @@ -fn main() { - println!("Hello, world!"); +use axum::{ + Router, + extract::State, + http::{Response, StatusCode}, + response::Json, + routing::{get, post}, +}; +use chrono::{DateTime, Utc}; +use dotenvy::dotenv; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::{Value, from_value, json}; +use sqlx::{PgPool, postgres::PgPoolOptions}; +use std::env; +use uuid::Uuid; + +const DATABASE_URL_FALLBACK: &str = "postgres://health:health@127.0.0.1:5432/health"; + +#[derive(sqlx::Type, Serialize, Deserialize)] +#[sqlx(type_name = "metric_type")] +pub enum MetricType { + BodyWeight, + Hip, + Waist, +} + +#[derive(sqlx::Type, Serialize, Deserialize)] +#[sqlx(type_name = "unit")] +pub enum Unit { + Kilogram, + Centimeter, +} + +#[derive(Serialize, Deserialize)] +pub struct Row { + id: Uuid, + measured_at: DateTime<Utc>, + metric_type: MetricType, + value: f64, + unit: Unit, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt::init(); + dotenv().ok(); + let database_url = + env::var("DATABASE_URL").unwrap_or_else(|_| DATABASE_URL_FALLBACK.to_owned()); + let pool = PgPoolOptions::new() + .max_connections(5) + .connect(&database_url) + .await?; + let app = Router::new() + .route("/", get(index)) + .route("/all", get(all)) + .route("/add", post(add)) + .with_state(pool); + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; + tracing::info!("listening at 0.0.0.0:3000"); + axum::serve(listener, app).await?; + Ok(()) +} + +async fn index() -> &'static str { + "Hello World!" +} + +async fn all(State(pool): State<PgPool>) -> Json<Value> { + let rows = sqlx::query_as!(Row, "SELECT * FROM measurement") + .fetch_all(&pool) + .await + .unwrap_or_else(|_| Vec::new()); + Json(json!(rows)) +} + +async fn add(State(pool): State<PgPool>, Json(row): Json<Value>) -> StatusCode { + if let Ok(insert_row) = from_value::<Row>(row) { + match sqlx::query!("INSERT INTO measurement (id, measured_at, metric_type, value, unit) VALUES ($1, $2, $3, $4, $5)", insert_row.id, insert_row.measured_at, insert_row.metric_type, insert_row.value, insert_row.unit).execute(&pool).await { + Ok(_) => {return StatusCode::CREATED;}, + Err(sqlx::Error::Database(e)) if e.is_unique_violation() => StatusCode::CONFLICT, + Err(_) => StatusCode::INTERNAL_SERVER_ERROR + } + } else { + return StatusCode::UNPROCESSABLE_ENTITY; + } } |
