use std::{
convert::Infallible,
error::Error,
fmt::{self, Debug},
};
use crate::{
classify::Classify,
fail::{Fail, Fault, Level, Lift, Rejection, UnmappedInto},
lane::{Denied, Exhausted, Fatal, FatalKind, Lane, Transient},
profile::{LaneProfile, NoLanes, Profile},
};
#[doc(hidden)]
pub mod kind {
pub struct Plain;
pub struct Carrier;
pub struct Source;
pub struct Payload;
pub struct Fault;
pub struct Fail;
pub struct CarrierShape;
pub trait IntoFault {}
impl IntoFault for Source {}
impl IntoFault for Payload {}
impl IntoFault for CarrierShape {}
pub trait IntoFail {}
impl IntoFail for Source {}
impl IntoFail for Payload {}
impl IntoFail for Fault {}
impl IntoFail for CarrierShape {}
pub trait IntoBareLane {}
impl IntoBareLane for Source {}
impl IntoBareLane for CarrierShape {}
}
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "`{Self}` does not enter the lanes",
note = "implement `errlanes::Classify` (or derive `Rejection` / `Classify`), or declare \
a carrier with `#[derive(errlanes::Carrier)]` on a lane enum"
)]
pub trait IntoLanes: Sized {
type Kind;
type Shape;
type Rejected;
type Lanes: LaneProfile;
fn into_lanes(self) -> Fail<Self::Rejected, Self::Lanes>;
}
impl<W: Classify> IntoLanes for W {
type Kind = kind::Plain;
type Shape = kind::Source;
type Rejected = W::Rejected;
type Lanes = W::Lanes;
fn into_lanes(self) -> Fail<Self::Rejected, Self::Lanes> {
self.classify()
}
}
impl<L: LaneProfile> IntoLanes for Fault<L> {
type Kind = kind::Plain;
type Shape = kind::Fault;
type Rejected = Infallible;
type Lanes = L;
fn into_lanes(self) -> Fail<Infallible, L> {
match self {
Fault::Denied(d) => Fail::Denied(d),
Fault::Transient(t) => Fail::Transient(t),
Fault::Fatal(f) => Fail::Fatal(f),
}
}
}
impl<D, L: LaneProfile> IntoLanes for Fail<D, L> {
type Kind = kind::Plain;
type Shape = kind::Fail;
type Rejected = D;
type Lanes = L;
fn into_lanes(self) -> Fail<D, L> {
self
}
}
macro_rules! payload {
($ty:ty, $d:literal, $t:literal, $f:literal, $variant:ident) => {
impl IntoLanes for $ty {
type Kind = kind::Plain;
type Shape = kind::Payload;
type Rejected = Infallible;
type Lanes = Profile<$d, $t, $f>;
fn into_lanes(self) -> Fail<Infallible, Self::Lanes> {
Fail::$variant(self)
}
}
};
}
payload!(Transient, false, true, false, Transient);
payload!(Fatal, false, false, true, Fatal);
payload!(Denied, true, false, false, Denied);
impl IntoLanes for Exhausted {
type Kind = kind::Plain;
type Shape = kind::Payload;
type Rejected = Infallible;
type Lanes = Profile<false, false, true>;
fn into_lanes(self) -> Fail<Infallible, Self::Lanes> {
Fail::Fatal(Fatal::from_error(FatalKind::Exhausted, self))
}
}
impl IntoLanes for Infallible {
type Kind = kind::Plain;
type Shape = kind::Payload;
type Rejected = Infallible;
type Lanes = NoLanes;
fn into_lanes(self) -> Fail<Infallible, NoLanes> {
match self {}
}
}
#[doc(hidden)]
pub trait BuiltinFor<L: LaneProfile>: Sized {
type Builtin: Error + Send + Sync + 'static;
fn builtin(f: Fail<Self, L>) -> Self::Builtin;
}
impl<L: LaneProfile> BuiltinFor<L> for Infallible {
type Builtin = Fault<L>;
fn builtin(f: Fail<Infallible, L>) -> Fault<L> {
fail_into_fault(f)
}
}
impl<R: Rejection, L: LaneProfile> BuiltinFor<L> for R {
type Builtin = Fail<R, L>;
fn builtin(f: Fail<R, L>) -> Fail<R, L> {
f
}
}
fn fail_into_fault<L: LaneProfile, M: LaneProfile>(f: Fail<Infallible, L>) -> Fault<M>
where
L::Denied: Into<M::Denied>,
L::Transient: Into<M::Transient>,
L::Fatal: Into<M::Fatal>,
{
match f {
Fail::Rejected(never) => match never {},
Fail::Denied(d) => Fault::Denied(d.into()),
Fail::Transient(t) => Fault::Transient(t.into()),
Fail::Fatal(x) => Fault::Fatal(x.into()),
}
}
impl<W: IntoLanes<Rejected = Infallible>, M: LaneProfile> From<W> for Fault<M>
where
W::Shape: kind::IntoFault,
<W::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<W::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<W::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
{
fn from(w: W) -> Self {
fail_into_fault(w.into_lanes())
}
}
impl<W: IntoLanes, D: Lift<W::Rejected, Unmapped = Infallible>, M: LaneProfile> From<W>
for Fail<D, M>
where
W::Shape: kind::IntoFail,
<W::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<W::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<W::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
{
fn from(w: W) -> Self {
fail_absorb(w.into_lanes())
}
}
fn fail_absorb<R, L: LaneProfile, D: Lift<R, Unmapped = Infallible>, M: LaneProfile>(
f: Fail<R, L>,
) -> Fail<D, M>
where
L::Denied: Into<M::Denied>,
L::Transient: Into<M::Transient>,
L::Fatal: Into<M::Fatal>,
{
match f {
Fail::Rejected(r) => match D::lift(r) {
Ok(d) => Fail::Rejected(d),
Err(never) => match never {},
},
Fail::Denied(d) => Fail::Denied(d.into()),
Fail::Transient(t) => Fail::Transient(t.into()),
Fail::Fatal(x) => Fail::Fatal(x.into()),
}
}
impl<W: IntoLanes<Rejected = Infallible, Lanes = Profile<false, false, true>>> From<W> for Fatal
where
W::Shape: kind::IntoBareLane,
{
fn from(w: W) -> Self {
match w.into_lanes() {
Fail::Rejected(never) => match never {},
Fail::Denied(never) => match never {},
Fail::Transient(never) => match never {},
Fail::Fatal(x) => x,
}
}
}
impl<W: IntoLanes<Rejected = Infallible, Lanes = Profile<false, true, false>>> From<W> for Transient
where
W::Shape: kind::IntoBareLane,
{
fn from(w: W) -> Self {
match w.into_lanes() {
Fail::Rejected(never) => match never {},
Fail::Denied(never) => match never {},
Fail::Transient(t) => t,
Fail::Fatal(never) => match never {},
}
}
}
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "`{W}` cannot be absorbed into this carrier by `?`",
note = "a rejection needs a total `Lift`; a lane the carrier does not declare must be \
narrowed first; another carrier must be listed in `from(..)`"
)]
pub trait Absorb<W> {
fn absorb(w: W) -> Self;
}
impl<W: IntoLanes<Rejected = Infallible>, M: LaneProfile> Absorb<W> for Fault<M>
where
<W::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<W::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<W::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
{
fn absorb(w: W) -> Self {
fail_into_fault(w.into_lanes())
}
}
impl<W: IntoLanes, P: Lift<W::Rejected, Unmapped = Infallible>, M: LaneProfile> Absorb<W>
for Fail<P, M>
where
<W::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<W::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<W::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
{
fn absorb(w: W) -> Self {
fail_absorb(w.into_lanes())
}
}
pub enum LaneRef<'a, R> {
Rejected(&'a R),
Denied(&'a Denied),
Transient(&'a Transient),
Fatal(&'a Fatal),
}
impl<R> Clone for LaneRef<'_, R> {
fn clone(&self) -> Self {
*self
}
}
impl<R> Copy for LaneRef<'_, R> {}
impl<R: Debug> Debug for LaneRef<'_, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LaneRef::Rejected(r) => f.debug_tuple("Rejected").field(r).finish(),
LaneRef::Denied(d) => f.debug_tuple("Denied").field(d).finish(),
LaneRef::Transient(t) => f.debug_tuple("Transient").field(t).finish(),
LaneRef::Fatal(x) => f.debug_tuple("Fatal").field(x).finish(),
}
}
}
impl<R> LaneRef<'_, R> {
pub fn lane(&self) -> Lane {
match self {
LaneRef::Rejected(_) => Lane::Rejected,
LaneRef::Denied(_) => Lane::Denied,
LaneRef::Transient(_) => Lane::Transient,
LaneRef::Fatal(_) => Lane::Fatal,
}
}
}
impl<R: LaneRejected> LaneRef<'_, R> {
pub fn message(&self) -> String {
match self {
LaneRef::Rejected(d) => d.code_string(),
LaneRef::Denied(d) => d.to_string(),
LaneRef::Transient(t) => crate::dynamic::message_chain(*t),
LaneRef::Fatal(x) => crate::dynamic::message_chain(*x),
}
}
pub fn level(&self) -> Level {
match self {
LaneRef::Rejected(d) => d.level(),
other => other.lane().level(),
}
}
#[cfg(feature = "tracing")]
pub(crate) fn record(&self, span: &tracing::Span) {
crate::record::record_lanes(span, *self);
}
#[cfg(feature = "tracing")]
pub(crate) fn emit(&self) {
crate::record::emit_lanes(*self);
}
}
#[doc(hidden)]
pub trait LaneRejected {
fn code_string(&self) -> String;
fn code_str(&self) -> &'static str;
fn level(&self) -> Level;
}
impl<R: Rejection> LaneRejected for R {
fn code_string(&self) -> String {
self.code().to_string()
}
fn code_str(&self) -> &'static str {
Into::<&'static str>::into(self.code())
}
fn level(&self) -> Level {
Rejection::level(self)
}
}
impl LaneRejected for Infallible {
fn code_string(&self) -> String {
match *self {}
}
fn code_str(&self) -> &'static str {
match *self {}
}
fn level(&self) -> Level {
match *self {}
}
}
impl<L: LaneProfile> Fault<L> {
pub fn lanes(&self) -> LaneRef<'_, Infallible> {
use crate::profile::Slot;
match self {
Fault::Denied(d) => LaneRef::Denied(d.marker()),
Fault::Transient(t) => LaneRef::Transient(t.marker()),
Fault::Fatal(x) => LaneRef::Fatal(x.marker()),
}
}
}
impl<D, L: LaneProfile> Fail<D, L> {
pub fn lanes(&self) -> LaneRef<'_, D> {
use crate::profile::Slot;
match self {
Fail::Rejected(d) => LaneRef::Rejected(d),
Fail::Denied(d) => LaneRef::Denied(d.marker()),
Fail::Transient(t) => LaneRef::Transient(t.marker()),
Fail::Fatal(x) => LaneRef::Fatal(x.marker()),
}
}
}
mod repr_sealed {
use super::{Fail, Fault, LaneProfile};
pub trait Sealed {}
impl<L: LaneProfile> Sealed for Fault<L> {}
impl<D, L: LaneProfile> Sealed for Fail<D, L> {}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not `Fault<L>` or `Fail<R, L>`",
note = "a carrier's `Repr` is the built-in it stands in for"
)]
pub trait Repr: repr_sealed::Sealed + IntoLanes<Kind = kind::Plain> {}
impl<L: LaneProfile> Repr for Fault<L> {}
impl<D, L: LaneProfile> Repr for Fail<D, L> {}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a carrier",
note = "declare one with `#[derive(errlanes::Carrier)]` on a lane enum"
)]
pub trait Carrier:
Error + Send + Sync + Sized + 'static + IntoLanes<Kind = kind::Carrier, Shape = kind::CarrierShape>
{
type Repr: Repr<Rejected = <Self as IntoLanes>::Rejected, Lanes = <Self as IntoLanes>::Lanes>;
fn from_repr(r: Self::Repr) -> Self;
fn into_repr(self) -> Self::Repr;
fn lanes(&self) -> LaneRef<'_, <Self as IntoLanes>::Rejected>;
}
#[doc(hidden)]
pub trait WidenBy<S, E2> {
fn widen_err(s: S) -> E2;
}
impl<L: LaneProfile, M: LaneProfile> WidenBy<Fault<L>, Fault<M>> for kind::Fault
where
L::Denied: Into<M::Denied>,
L::Transient: Into<M::Transient>,
L::Fatal: Into<M::Fatal>,
{
fn widen_err(s: Fault<L>) -> Fault<M> {
s.widen()
}
}
impl<R, P: Lift<R>, L: LaneProfile, M: LaneProfile> WidenBy<Fail<R, L>, Fail<P, M>> for kind::Fail
where
L::Denied: Into<M::Denied>,
L::Transient: Into<M::Transient>,
L::Fatal: Into<M::Fatal>,
P::Unmapped: UnmappedInto<M::Fatal>,
{
fn widen_err(s: Fail<R, L>) -> Fail<P, M> {
s.widen()
}
}
impl<C: IntoLanes, P: Lift<C::Rejected>, M: LaneProfile> WidenBy<C, Fail<P, M>> for kind::Source
where
<C::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<C::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<C::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
P::Unmapped: UnmappedInto<M::Fatal>,
{
fn widen_err(s: C) -> Fail<P, M> {
s.into_lanes().widen()
}
}
impl<C: IntoLanes<Rejected = Infallible>, M: LaneProfile> WidenBy<C, Fault<M>> for kind::Source
where
<C::Lanes as LaneProfile>::Denied: Into<M::Denied>,
<C::Lanes as LaneProfile>::Transient: Into<M::Transient>,
<C::Lanes as LaneProfile>::Fatal: Into<M::Fatal>,
{
fn widen_err(s: C) -> Fault<M> {
fail_into_fault(s.into_lanes())
}
}
impl<C: Carrier, E2> WidenBy<C, E2> for kind::CarrierShape
where
<C::Repr as IntoLanes>::Shape: WidenBy<C::Repr, E2>,
{
fn widen_err(s: C) -> E2 {
<<C::Repr as IntoLanes>::Shape as WidenBy<C::Repr, E2>>::widen_err(s.into_repr())
}
}