use crate::{ErrorSetValue, StatusWrapper};
use axum_core::response::{IntoResponse, Response};
use http::StatusCode;
use std::{fmt::Debug, hash::Hash, marker::PhantomData};
use type_sets::{Contains, SupersetOf};
pub struct ErrorSet<T, E> {
value: T,
code: StatusCode,
_e: PhantomData<fn() -> E>,
}
impl<T, E> ErrorSet<T, E> {
pub fn new_with<R: StatusWrapper>(value: T) -> Self
where
E: Contains<R>,
{
Self::new_unchecked(value, R::STATUS_CODE)
}
pub fn new<R: StatusWrapper>(value: R) -> Self
where
E: Contains<R::Pure>,
R::Inner: Into<T>,
{
Self::new_with::<R::Pure>(value.into_inner().into())
}
pub fn new_unchecked(value: T, code: StatusCode) -> Self {
ErrorSet {
value,
code,
_e: PhantomData,
}
}
pub fn into_parts(self) -> (T, StatusCode) {
(self.value, self.code)
}
pub fn into_superset<E2>(self) -> ErrorSet<T, E2>
where
E2: SupersetOf<E>,
{
ErrorSet::new_unchecked(self.value, self.code)
}
pub fn map_value<F, U>(self, f: F) -> ErrorSet<U, E>
where
F: FnOnce(T) -> U,
{
ErrorSet::new_unchecked(f(self.value), self.code)
}
pub fn value(&self) -> &T {
&self.value
}
pub fn value_mut(&mut self) -> &mut T {
&mut self.value
}
pub fn status_code(&self) -> StatusCode {
self.code
}
}
impl<T: ErrorSetValue, R> IntoResponse for ErrorSet<T, R> {
fn into_response(self) -> Response {
self.value.into_response_with(self.code)
}
}
impl<T: Debug, R> Debug for ErrorSet<T, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ApiError")
.field("value", &self.value)
.field("code", &self.code)
.finish()
}
}
impl<T: Clone, R> Clone for ErrorSet<T, R> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
code: self.code,
_e: PhantomData,
}
}
}
impl<T: Hash, R> Hash for ErrorSet<T, R> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
self.code.hash(state);
}
}
impl<T: PartialEq, R> PartialEq for ErrorSet<T, R> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value && self.code == other.code
}
}
impl<T: Eq, R> Eq for ErrorSet<T, R> {}