vnj/src/result.rs
2024-11-17 18:43:15 +03:00

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),
}