use std::{error::Error, fmt::Display};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[serde_with::skip_serializing_none]
#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
pub struct Exception {
pub r#type: String,
pub title: Option<String>,
pub status: Option<u16>,
pub detail: Option<String>,
pub instance: Option<String>,
#[serde(flatten, default, skip_serializing_if = "Map::is_empty")]
pub additional_properties: Map<String, Value>,
}
impl Exception {
pub fn new(r#type: impl ToString) -> Self {
Exception {
r#type: r#type.to_string(),
..Default::default()
}
}
pub fn new_from_status(status: u16) -> Self {
let exception = Exception::new(format!(
"https://httpwg.org/specs/rfc7231.html#status.{}",
status
));
exception.status(status)
}
pub fn title(mut self, title: impl ToString) -> Self {
self.title = Some(title.to_string());
self
}
pub fn status(mut self, status: u16) -> Self {
self.status = Some(status);
self
}
pub fn detail(mut self, detail: impl ToString) -> Self {
self.detail = Some(detail.to_string());
self
}
pub fn instance(mut self, instance: impl ToString) -> Self {
self.instance = Some(instance.to_string());
self
}
}
impl Display for Exception {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", serde_json::to_string_pretty(self).unwrap())
}
}
impl Error for Exception {}
#[cfg(test)]
mod tests {
use super::Exception;
#[test]
fn exception() {
let e = Exception::new_from_status(500);
println!("{:#?}", e);
println!("{}", serde_json::to_string_pretty(&e).unwrap());
}
}