65 lines
1.5 KiB
Rust
65 lines
1.5 KiB
Rust
use axum::{body::Body, extract::FromRequest, response::IntoResponse};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
pub type ApiResult<T> = Result<Ok<T>, Err>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct FromJson<T>(pub T);
|
|
|
|
#[axum::async_trait]
|
|
impl<T: for<'de> Deserialize<'de>, S: Send + Sync> FromRequest<S> for FromJson<T> {
|
|
type Rejection = Err;
|
|
|
|
async fn from_request(
|
|
request: axum::http::Request<Body>,
|
|
s: &S,
|
|
) -> Result<Self, Self::Rejection> {
|
|
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<T> {
|
|
pub ok: T,
|
|
}
|
|
|
|
impl<T> From<T> for Ok<T> {
|
|
fn from(value: T) -> Self {
|
|
Self { ok: value }
|
|
}
|
|
}
|
|
|
|
impl<T: Serialize> IntoResponse for Ok<T> {
|
|
fn into_response(self) -> axum::response::Response {
|
|
axum::Json(self).into_response()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct Err {
|
|
pub error: ApiError,
|
|
}
|
|
|
|
impl From<eyre::Report> 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),
|
|
}
|