#![warn(missing_docs)]
#![deny(unused_qualifications)]
pub mod type_;
use std::{
error::Error,
fmt::{Debug, Display},
ops::{Deref, DerefMut},
};
use http::{Extensions, StatusCode};
use http_api_problem::{ApiError, ApiErrorBuilder};
#[cfg(feature = "ext-typed-record")]
use typed_record::{TypedRecord, TypedRecordKey};
pub struct Problem(ApiError);
pub type ProbResult<T> = Result<T, Problem>;
#[cfg(feature = "alias-future")]
pub type ProbFuture<'a, T> = futures::future::BoxFuture<'a, Result<T, Problem>>;
#[cfg(feature = "alias-future")]
pub type ProbStream<'a, T> = futures::stream::BoxStream<'a, Result<T, Problem>>;
impl Default for Problem {
#[inline]
fn default() -> Self {
Self(ApiError::new(StatusCode::IM_A_TEAPOT))
}
}
impl Problem {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn builder() -> ProblemBuilder {
ProblemBuilder(ApiError::builder(StatusCode::IM_A_TEAPOT))
}
}
impl Display for Problem {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match (self.title().as_ref(), self.detail_message()) {
(Some(title), Some(detail)) => return write!(f, " - {} - {}", title, detail),
(Some(title), None) => return write!(f, " - {}", title),
(None, Some(detail)) => return write!(f, " - {}", detail),
(None, None) => (),
}
if let Some(type_url) = self.type_url().as_ref() {
return write!(f, " of type {}", type_url);
}
if let Some(instance) = self.instance().as_ref() {
return write!(f, " on {}", instance);
}
Ok(())
}
}
impl Debug for Problem {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self, f)
}
}
impl Error for Problem {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.0.source()
}
}
impl Deref for Problem {
type Target = ApiError;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Problem {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<ApiError> for Problem {
#[inline]
fn from(inner: ApiError) -> Self {
Self(inner)
}
}
pub struct ProblemBuilder(ApiErrorBuilder);
impl ProblemBuilder {
#[inline]
pub fn title<T: Display>(mut self, title: T) -> Self {
self.0 = self.0.title(title);
self
}
#[inline]
pub fn message<M: Display>(mut self, message: M) -> Self {
self.0 = self.0.message(message);
self
}
#[inline]
pub fn type_url<U: Display>(mut self, type_url: U) -> Self {
self.0 = self.0.type_url(type_url);
self
}
#[inline]
pub fn instance<T: Display>(mut self, instance: T) -> Self {
self.0 = self.0.instance(instance);
self
}
#[inline]
pub fn extension<T: Send + Sync + 'static>(mut self, val: T) -> Self {
self.0 = self.0.extension(val);
self
}
#[inline]
pub fn with_extensions<F>(mut self, f: F) -> Self
where
F: FnOnce(Extensions) -> Extensions,
{
self.0 = self.0.with_extensions(f);
self
}
#[inline]
pub fn source<E: Error + Send + Sync + 'static>(mut self, source: E) -> Self {
self.0 = self.0.source(source);
self
}
#[inline]
pub fn source_in_a_box<E: Into<Box<dyn Error + Send + Sync + 'static>>>(
mut self,
source: E,
) -> Self {
self.0 = self.0.source_in_a_box(source);
self
}
#[inline]
pub fn finish(self) -> Problem {
Problem(self.0.finish())
}
}
impl Deref for ProblemBuilder {
type Target = ApiErrorBuilder;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ProblemBuilder {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<ApiErrorBuilder> for ProblemBuilder {
#[inline]
fn from(inner: ApiErrorBuilder) -> Self {
Self(inner)
}
}
impl From<ProblemBuilder> for ApiErrorBuilder {
#[inline]
fn from(builder: ProblemBuilder) -> Self {
builder.0
}
}
#[cfg(feature = "ext-typed-record")]
pub trait ProblemBuilderExt: Sized {
fn extend_with<K: TypedRecordKey>(self, v: K::Value) -> Self;
fn extend_with_opt<K: TypedRecordKey>(self, v: Option<K::Value>) -> Self;
}
#[cfg(feature = "ext-typed-record")]
impl ProblemBuilderExt for ApiErrorBuilder {
#[inline]
fn extend_with<K: TypedRecordKey>(mut self, v: K::Value) -> Self {
self.extensions.insert_rec_item::<K>(v);
self
}
#[inline]
fn extend_with_opt<K: TypedRecordKey>(mut self, v: Option<K::Value>) -> Self {
if let Some(v) = v {
self.extensions.insert_rec_item::<K>(v);
}
self
}
}
#[cfg(feature = "ext-typed-record")]
impl ProblemBuilderExt for ProblemBuilder {
#[inline]
fn extend_with<K: TypedRecordKey>(mut self, v: K::Value) -> Self {
self.0 = self.0.extend_with::<K>(v);
self
}
#[inline]
fn extend_with_opt<K: TypedRecordKey>(mut self, v: Option<K::Value>) -> Self {
self.0 = self.0.extend_with_opt::<K>(v);
self
}
}