use std::collections::HashMap;
use std::str::FromStr;
use axum::extract::FromRequestParts;
use axum::response::{IntoResponse, Response};
use crate::api::{ProblemBuilder, ProblemKind};
use crate::axum::extract::Path;
use crate::db::Db;
use crate::dx::db_from_state::DbFromState;
use crate::dx::route_model::RouteModel;
pub struct Bound<T>(pub T);
impl<T> Bound<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T, S> FromRequestParts<S> for Bound<T>
where
T: RouteModel,
S: Send + Sync,
Db: DbFromState<S>,
{
type Rejection = Response;
async fn from_request_parts(
parts: &mut axum::http::request::Parts,
state: &S,
) -> Result<Self, Self::Rejection> {
let db = Db::db_from_state(state);
let path_params = Path::<HashMap<String, String>>::from_request_parts(parts, state)
.await
.map_err(|_| {
ProblemBuilder::new(ProblemKind::BadRequest)
.detail("missing route parameters")
.build()
.into_response()
})?;
let key_str = path_params.get(T::KEY_PARAM).ok_or_else(|| {
ProblemBuilder::new(ProblemKind::BadRequest)
.detail(format!("missing route parameter `{}`", T::KEY_PARAM))
.build()
.into_response()
})?;
let key = T::Key::from_str(key_str).map_err(|_| {
ProblemBuilder::new(ProblemKind::BadRequest)
.detail(format!(
"invalid route parameter `{}`: expected {}",
T::KEY_PARAM,
std::any::type_name::<T::Key>()
))
.build()
.into_response()
})?;
let model = T::load(key, &db).await.map_err(|err| {
ProblemBuilder::new(ProblemKind::Internal)
.detail(format!("database error: {err}"))
.build()
.into_response()
})?;
let model = model.ok_or_else(|| {
ProblemBuilder::new(ProblemKind::NotFound)
.build()
.into_response()
})?;
Ok(Bound(model))
}
}