summaryrefslogtreecommitdiff
path: root/src/handlers.rs
diff options
context:
space:
mode:
authorbatsumaru <>2026-10-16 11:57:17 +0900
committerbatsumaru <>2026-10-16 11:57:17 +0900
commita69e78b7482227f22e7ddbfaa7dc7ee31f29a99b (patch)
treee829a766484e1937bcbcd4e97668e9d674ea6099 /src/handlers.rs
parent7dc5038a7438d0eaf63d4bf65f88f6f5ab6b3207 (diff)
Add enum fetchers
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/handlers.rs b/src/handlers.rs
new file mode 100644
index 0000000..3f815a5
--- /dev/null
+++ b/src/handlers.rs
@@ -0,0 +1,94 @@
+use axum::{
+ extract::{Query, State},
+ http::StatusCode,
+ response::Json,
+};
+use chrono::NaiveTime;
+use sqlx::PgPool;
+use uuid::Uuid;
+
+use crate::error::AppError;
+use crate::models::{DateQuery, MeasurementData, MeasurementView, Unit};
+
+pub async fn index() -> &'static str {
+ "Hello World!"
+}
+
+pub async fn all(State(pool): State<PgPool>) -> Result<Json<Vec<MeasurementView>>, AppError> {
+ let rows = sqlx::query_as!(
+ MeasurementView,
+ "SELECT id, measured_at, metric_type, value, unit FROM measurement"
+ )
+ .fetch_all(&pool)
+ .await?;
+ Ok(Json(rows))
+}
+
+pub async fn add(
+ State(pool): State<PgPool>,
+ Json(data): Json<MeasurementData>,
+) -> Result<(StatusCode, Json<Uuid>), AppError> {
+ let rec = sqlx::query!(
+ "
+ INSERT INTO measurement (id, measured_at, metric_type, value, unit)
+ VALUES ($1, $2, $3, $4, $5)
+ RETURNING id
+ ",
+ Uuid::now_v7(),
+ data.measured_at,
+ data.metric_type,
+ data.value,
+ data.unit
+ )
+ .fetch_one(&pool)
+ .await?;
+ Ok((StatusCode::CREATED, Json(rec.id)))
+}
+
+pub async fn search(
+ State(pool): State<PgPool>,
+ Query(date_query): Query<DateQuery>,
+) -> Result<Json<Vec<MeasurementView>>, AppError> {
+ let (from, to) = match (date_query.date, date_query.from, date_query.to) {
+ (Some(date), None, None) => (Some(date), Some(date)),
+ (None, from, to) => (from, to),
+ _ => return Err(AppError::BadRequest),
+ };
+ if from.is_none() && to.is_none() {
+ return Err(AppError::BadRequest);
+ }
+ if let (Some(from), Some(to)) = (from, to)
+ && from > to
+ {
+ return Err(AppError::BadRequest);
+ }
+ let start = from.map(|from| from.and_time(NaiveTime::MIN).and_utc());
+ let end = match to {
+ Some(to) => {
+ let Some(next_day) = to.succ_opt() else {
+ return Err(AppError::BadRequest);
+ };
+ Some(next_day.and_time(NaiveTime::MIN).and_utc())
+ }
+ None => None,
+ };
+ let rows = sqlx::query_as!(
+ MeasurementView,
+ "SELECT id, measured_at, metric_type, value, unit FROM measurement
+ WHERE ($1::timestamptz IS NULL OR measured_at >= $1)
+ AND ($2::timestamptz IS NULL OR measured_at < $2)",
+ start,
+ end
+ )
+ .fetch_all(&pool)
+ .await?;
+ Ok(Json(rows))
+}
+
+pub async fn units(State(pool): State<PgPool>) -> Result<Json<Vec<Unit>>, AppError> {
+ Ok(Json(
+ sqlx::query_scalar("SELECT enum_range(NULL::unit)")
+ .fetch_one(&pool)
+ .await?,
+ ))
+}