use axum::{body::Body, extract::FromRequest, response::IntoResponse}; use serde::{Deserialize, Serialize}; pub type ApiResult = Result, Err>; #[derive(Debug, Clone)] pub struct FromJson(pub T); #[axum::async_trait] impl Deserialize<'de>, S: Send + Sync> FromRequest for FromJson { type Rejection = Err; async fn from_request( request: axum::http::Request, s: &S, ) -> Result { let js = axum::Json::from_request(request, s) .await .map_err(|rej| eyre::eyre!("failed to parse json: {}", rej.to_string())) .inspect_err(|e| tracing::error!(error = %e, "got error"))?; Ok(Self(js.0)) } } #[derive(Debug, Serialize, Deserialize)] pub struct Ok { pub ok: T, } impl From for Ok { fn from(value: T) -> Self { Self { ok: value } } } impl IntoResponse for Ok { fn into_response(self) -> axum::response::Response { axum::Json(self).into_response() } } #[derive(Debug, Serialize, Deserialize)] pub struct Err { pub error: ApiError, } impl From for Err { fn from(value: eyre::Report) -> Self { Self { error: ApiError::Custom(format!("{value:?}")), } } } impl IntoResponse for Err { fn into_response(self) -> axum::response::Response { axum::Json(self).into_response() } } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ApiError { Custom(String), }