use serdeconv;
use std::error;
use std::fmt;
use trackable::Trackable;
use url::Url;
use types::HttpStatus;
use RpcResponse;
#[derive(Debug, Serialize, Deserialize)]
pub struct ProblemResponse {
status: Option<u16>,
header: ProblemHeader,
#[serde(skip)]
body: Problem,
}
impl ProblemResponse {
fn new(body: Problem) -> Self {
ProblemResponse {
status: Some(body.get_status_code()),
header: ProblemHeader::new(),
body,
}
}
pub fn problem(&self) -> &Problem {
&self.body
}
pub fn set_problem(&mut self, problem: Problem) {
self.body = problem;
}
}
impl RpcResponse for ProblemResponse {
fn body(&mut self) -> Box<dyn AsRef<[u8]> + Send + 'static> {
let json = serdeconv::to_json_string_pretty(&self.body).expect("Never fails");
Box::new(json.into_bytes())
}
fn set_body(&mut self, body: Vec<u8>) {
if let Ok(body) = serdeconv::from_json_slice(&body) {
self.body = body;
}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct ProblemHeader {
#[serde(rename = "content-type")]
content_type: ContentTypeProblemJson,
}
impl ProblemHeader {
pub fn new() -> Self {
ProblemHeader {
content_type: ContentTypeProblemJson,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Problem {
#[serde(rename = "about:blank")]
AboutBlank(AboutBlankProblem),
#[serde(rename = "https://docs.rs/htrpc/0.0.2/htrpc/rfc7807/struct.TrackableProblem.html")]
Trackable(TrackableProblem),
}
impl Problem {
pub fn about_blank(status: HttpStatus) -> Self {
Problem::AboutBlank(AboutBlankProblem::new(status))
}
pub fn trackable<E>(status: HttpStatus, error: E) -> Self
where
E: error::Error + Trackable,
E::Event: fmt::Display,
{
Problem::Trackable(TrackableProblem::new(status, error))
}
pub fn into_response(self) -> ProblemResponse {
ProblemResponse::new(self)
}
fn get_status_code(&self) -> u16 {
match *self {
Problem::AboutBlank(ref p) => p.status,
Problem::Trackable(ref p) => p.status,
}
}
}
impl Default for Problem {
fn default() -> Self {
Problem::about_blank(HttpStatus::InternalServerError)
}
}
impl From<AboutBlankProblem> for Problem {
fn from(f: AboutBlankProblem) -> Self {
Problem::AboutBlank(f)
}
}
impl From<TrackableProblem> for Problem {
fn from(f: TrackableProblem) -> Self {
Problem::Trackable(f)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename = "application/problem+json")]
struct ContentTypeProblemJson;
#[derive(Debug, Serialize, Deserialize)]
pub struct AboutBlankProblem {
pub title: String,
pub status: u16,
}
impl AboutBlankProblem {
pub fn new(status: HttpStatus) -> Self {
AboutBlankProblem {
title: status.reason_phrase().to_string(),
status: status.code(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TrackableProblem {
pub title: String,
pub status: u16,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub detail: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub instance: Option<Url>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub history: Option<Vec<String>>,
}
impl TrackableProblem {
pub fn new<E>(status: HttpStatus, error: E) -> Self
where
E: error::Error + Trackable,
E::Event: fmt::Display,
{
TrackableProblem {
title: error.to_string(),
status: status.code(),
detail: error.source().map(|c| c.to_string()),
instance: None,
history: error
.history()
.map(|h| h.events().iter().map(|e| e.to_string()).collect()),
}
}
}