1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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?,
))
}
|