1use axum::extract::{Path, State};
2use axum::routing::get;
3use axum::{Json, Router};
4use serde::de::DeserializeOwned;
5use serde::Serialize;
6
7use crate::error::Result;
8
9#[async_trait::async_trait]
17pub trait Resource: Send + Sync + 'static {
18 type State: Clone + Send + Sync + 'static;
19 type Id: DeserializeOwned + Send + Sync + 'static;
20 type Item: Serialize + Send + Sync + 'static;
21 type Create: DeserializeOwned + Send + Sync + 'static;
22 type Update: DeserializeOwned + Send + Sync + 'static;
23
24 async fn index(state: &Self::State) -> Result<Vec<Self::Item>>;
25 async fn show(state: &Self::State, id: Self::Id) -> Result<Self::Item>;
26 async fn create(state: &Self::State, body: Self::Create) -> Result<Self::Item>;
27 async fn update(state: &Self::State, id: Self::Id, body: Self::Update) -> Result<Self::Item>;
28 async fn destroy(state: &Self::State, id: Self::Id) -> Result<()>;
29}
30
31pub fn resource<R: Resource>(path: &str) -> Router<R::State> {
37 let item_path = format!("{path}/{{id}}");
38 Router::new()
39 .route(path, get(index::<R>).post(create::<R>))
40 .route(&item_path, get(show::<R>).put(update::<R>).delete(destroy::<R>))
41}
42
43async fn index<R: Resource>(State(state): State<R::State>) -> Result<Json<Vec<R::Item>>> {
44 Ok(Json(R::index(&state).await?))
45}
46
47async fn show<R: Resource>(
48 State(state): State<R::State>,
49 Path(id): Path<R::Id>,
50) -> Result<Json<R::Item>> {
51 Ok(Json(R::show(&state, id).await?))
52}
53
54async fn create<R: Resource>(
55 State(state): State<R::State>,
56 Json(body): Json<R::Create>,
57) -> Result<Json<R::Item>> {
58 Ok(Json(R::create(&state, body).await?))
59}
60
61async fn update<R: Resource>(
62 State(state): State<R::State>,
63 Path(id): Path<R::Id>,
64 Json(body): Json<R::Update>,
65) -> Result<Json<R::Item>> {
66 Ok(Json(R::update(&state, id, body).await?))
67}
68
69async fn destroy<R: Resource>(
70 State(state): State<R::State>,
71 Path(id): Path<R::Id>,
72) -> Result<axum::http::StatusCode> {
73 R::destroy(&state, id).await?;
74 Ok(axum::http::StatusCode::NO_CONTENT)
75}