From 871d92759672a847f86b6fbd970e8acc869f79b3 Mon Sep 17 00:00:00 2001 From: Igor Katson Date: Thu, 8 Dec 2022 20:13:01 +0000 Subject: [PATCH] JSON error (playing with serde API) --- crates/librqbit/src/http_api_error.rs | 52 ++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/librqbit/src/http_api_error.rs b/crates/librqbit/src/http_api_error.rs index 044596e..07c3633 100644 --- a/crates/librqbit/src/http_api_error.rs +++ b/crates/librqbit/src/http_api_error.rs @@ -1,7 +1,10 @@ +use std::ops::Deref; + use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; +use serde::{ser::SerializeMap, Serialize, Serializer}; // Convenience error type. #[derive(Debug)] @@ -38,6 +41,53 @@ enum ApiErrorKind { Other(anyhow::Error), } +struct ErrWrap<'a, E: ?Sized>(&'a E); + +impl<'a, E> Serialize for ErrWrap<'a, E> +where + E: std::error::Error + ?Sized, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut m = serializer.serialize_map(None)?; + m.serialize_entry("description", &format!("{}", self.0))?; + if let Some(source) = self.0.source() { + m.serialize_entry("source", &ErrWrap(source))?; + } + m.end() + } +} + +impl Serialize for ApiErrorKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ApiErrorKind::TorrentNotFound(id) => { + let mut m = serializer.serialize_map(None)?; + m.serialize_entry("error_kind", "torrent_not_found")?; + m.serialize_entry("id", id)?; + m.end() + } + ApiErrorKind::DhtDisabled => { + let mut m = serializer.serialize_map(None)?; + m.serialize_entry("error_kind", "dht_disabled")?; + m.end() + } + ApiErrorKind::Other(err) => { + let mut m = serializer.serialize_map(None)?; + m.serialize_entry("error_kind", "internal_error")?; + m.serialize_entry("human_readable", &format!("{err:#}"))?; + m.serialize_entry("error_chain", &ErrWrap(err.deref()))?; + m.end() + } + } + } +} + impl From for ApiError { fn from(value: anyhow::Error) -> Self { Self { @@ -68,7 +118,7 @@ impl std::fmt::Display for ApiError { impl IntoResponse for ApiError { fn into_response(self) -> Response { - let mut response = format!("{self}").into_response(); + let mut response = axum::Json(&self.kind).into_response(); *response.status_mut() = match self.status { Some(s) => s, None => StatusCode::INTERNAL_SERVER_ERROR,