summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs86
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;
+ }
}