Serde errors

This commit is contained in:
Igor Katson 2022-12-08 20:43:02 +00:00
parent 6968a4e449
commit aacb8caaa3
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
5 changed files with 47 additions and 27 deletions

1
Cargo.lock generated
View file

@ -811,6 +811,7 @@ dependencies = [
"dht", "dht",
"futures", "futures",
"hex 0.4.3", "hex 0.4.3",
"http",
"librqbit_core", "librqbit_core",
"log", "log",
"openssl", "openssl",

View file

@ -31,6 +31,7 @@ serde_json = "1"
serde_urlencoded = "*" serde_urlencoded = "*"
anyhow = "1" anyhow = "1"
http = "0.2"
regex = "1" regex = "1"
reqwest = {version="0.11", default-features=false} reqwest = {version="0.11", default-features=false}
urlencoding = "2" urlencoding = "2"

View file

@ -1,8 +1,8 @@
use anyhow::Context; use anyhow::Context;
use axum::extract::{Path, Query, State}; use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use buffers::ByteString; use buffers::ByteString;
use dht::{Dht, DhtStats}; use dht::{Dht, DhtStats};
use http::StatusCode;
use librqbit_core::id20::Id20; use librqbit_core::id20::Id20;
use librqbit_core::torrent_metainfo::TorrentMetaV1Info; use librqbit_core::torrent_metainfo::TorrentMetaV1Info;
use log::warn; use log::warn;

View file

@ -19,7 +19,18 @@ async fn check_response(r: reqwest::Response) -> anyhow::Result<reqwest::Respons
.text() .text()
.await .await
.with_context(|| format!("cannot read response body for request to {url} ({status})"))?; .with_context(|| format!("cannot read response body for request to {url} ({status})"))?;
anyhow::bail!("{} -> {}: {}", url, status, body)
#[derive(Deserialize)]
struct HumanReadableError<'a> {
human_readable: Option<&'a str>,
}
let human_readable_internal_error = serde_json::from_str::<HumanReadableError<'_>>(&body)
.ok()
.and_then(|e| e.human_readable);
let body_display = human_readable_internal_error.unwrap_or(&body);
anyhow::bail!("{} -> {}: {}", url, status, body_display)
} }
#[derive(Deserialize)] #[derive(Deserialize)]

View file

@ -1,9 +1,7 @@
use std::ops::Deref; use std::ops::Deref;
use axum::{ use axum::response::{IntoResponse, Response};
http::StatusCode, use http::StatusCode;
response::{IntoResponse, Response},
};
use serde::{ser::SerializeMap, Serialize, Serializer}; use serde::{ser::SerializeMap, Serialize, Serializer};
// Convenience error type. // Convenience error type.
@ -60,31 +58,40 @@ where
} }
} }
impl Serialize for ApiErrorKind { impl Serialize for ApiError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
match self { #[derive(Serialize, Default)]
ApiErrorKind::TorrentNotFound(id) => { struct SerializedError<'a> {
let mut m = serializer.serialize_map(None)?; error_kind: &'a str,
m.serialize_entry("error_kind", "torrent_not_found")?; human_readable: String,
m.serialize_entry("id", id)?; status: u16,
m.end() #[serde(skip_serializing_if = "Option::is_none")]
} id: Option<usize>,
ApiErrorKind::DhtDisabled => { #[serde(skip_serializing_if = "Option::is_none")]
let mut m = serializer.serialize_map(None)?; error_chain: Option<ErrWrap<'a, dyn std::error::Error>>,
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()
}
} }
let mut serr: SerializedError = SerializedError {
error_kind: match self.kind {
ApiErrorKind::TorrentNotFound(_) => "torrent_not_found",
ApiErrorKind::DhtDisabled => "dht_disabled",
ApiErrorKind::Other(_) => "internal_error",
},
human_readable: format!("{self}"),
status: self
.status
.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
.as_u16(),
..Default::default()
};
match &self.kind {
ApiErrorKind::TorrentNotFound(id) => serr.id = Some(*id),
ApiErrorKind::Other(err) => serr.error_chain = Some(ErrWrap(err.deref())),
_ => {}
}
serr.serialize(serializer)
} }
} }
@ -118,7 +125,7 @@ impl std::fmt::Display for ApiError {
impl IntoResponse for ApiError { impl IntoResponse for ApiError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
let mut response = axum::Json(&self.kind).into_response(); let mut response = axum::Json(&self).into_response();
*response.status_mut() = match self.status { *response.status_mut() = match self.status {
Some(s) => s, Some(s) => s,
None => StatusCode::INTERNAL_SERVER_ERROR, None => StatusCode::INTERNAL_SERVER_ERROR,