use std::{convert::Infallible, error::Error, fmt::Debug};
use crate::{Denied, Exhausted, Fatal, Transient};
mod sealed {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait Slot<M>: From<Infallible> + Error + Clone + Send + Sync + 'static {
fn marker(&self) -> &M;
}
impl<M: From<Infallible> + Error + Clone + Send + Sync + 'static> Slot<M> for M {
fn marker(&self) -> &M {
self
}
}
macro_rules! disabled {
($($marker:ty),*) => {$ (
impl Slot<$marker> for Infallible { fn marker(&self) -> &$marker { match *self {} } }
impl From<Infallible> for $marker { fn from(value: Infallible) -> Self { match value {} } }
)*};
}
disabled!(Denied, Transient, Fatal, Exhausted);
#[doc(hidden)]
pub trait NarrowTransient<F> {
fn narrow(self, attempts: u32) -> F;
}
impl<F> NarrowTransient<F> for Infallible {
fn narrow(self, _: u32) -> F {
match self {}
}
}
impl NarrowTransient<Fatal> for Transient {
fn narrow(self, attempts: u32) -> Fatal {
Fatal::from_error(
crate::FatalKind::Exhausted,
Exhausted {
attempts,
last: self,
},
)
}
}
#[doc(hidden)]
pub trait NarrowDenied<F> {
fn narrow(self) -> F;
}
impl<F> NarrowDenied<F> for Infallible {
fn narrow(self) -> F {
match self {}
}
}
impl NarrowDenied<Fatal> for Denied {
fn narrow(self) -> Fatal {
self.into_fatal()
}
}
pub trait LaneProfile: sealed::Sealed + Debug + Clone + Send + Sync + 'static {
type Denied: Slot<Denied>;
type Transient: Slot<Transient>;
type Fatal: Slot<Fatal>;
type WithoutTransient: LaneProfile<Denied = Self::Denied, Transient = Infallible, Fatal = Self::Fatal>;
type WithoutDenied: LaneProfile<Denied = Infallible, Transient = Self::Transient, Fatal = Self::Fatal>;
}
#[derive(Debug, Clone, Copy)]
pub struct Profile<const D: bool, const T: bool, const F: bool>;
macro_rules! profile {
($d:literal, $t:literal, $f:literal; $denied:ty, $transient:ty, $fatal:ty) => {
impl sealed::Sealed for Profile<$d, $t, $f> {}
impl LaneProfile for Profile<$d, $t, $f> {
type Denied = $denied;
type Transient = $transient;
type Fatal = $fatal;
type WithoutTransient = Profile<$d, false, $f>;
type WithoutDenied = Profile<false, $t, $f>;
}
};
}
profile!(false, false, false; Infallible, Infallible, Infallible);
profile!(false, false, true; Infallible, Infallible, Fatal);
profile!(false, true, false; Infallible, Transient, Infallible);
profile!(false, true, true; Infallible, Transient, Fatal);
profile!(true, false, false; Denied, Infallible, Infallible);
profile!(true, false, true; Denied, Infallible, Fatal);
profile!(true, true, false; Denied, Transient, Infallible);
profile!(true, true, true; Denied, Transient, Fatal);
pub type AllLanes = Profile<true, true, true>;
pub type NoLanes = Profile<false, false, false>;
#[doc(hidden)]
pub trait Union<Other: LaneProfile>: LaneProfile {
type Out: LaneProfile;
}
macro_rules! union_impl {
($d1:literal,$t1:literal,$f1:literal; $d2:literal,$t2:literal,$f2:literal) => {
impl Union<Profile<$d2, $t2, $f2>> for Profile<$d1, $t1, $f1> {
type Out = Profile<{ $d1 || $d2 }, { $t1 || $t2 }, { $f1 || $f2 }>;
}
};
}
union_impl!(false,false,false; false,false,false);
union_impl!(false,false,false; false,false,true);
union_impl!(false,false,false; false,true,false);
union_impl!(false,false,false; false,true,true);
union_impl!(false,false,false; true,false,false);
union_impl!(false,false,false; true,false,true);
union_impl!(false,false,false; true,true,false);
union_impl!(false,false,false; true,true,true);
union_impl!(false,false,true; false,false,false);
union_impl!(false,false,true; false,false,true);
union_impl!(false,false,true; false,true,false);
union_impl!(false,false,true; false,true,true);
union_impl!(false,false,true; true,false,false);
union_impl!(false,false,true; true,false,true);
union_impl!(false,false,true; true,true,false);
union_impl!(false,false,true; true,true,true);
union_impl!(false,true,false; false,false,false);
union_impl!(false,true,false; false,false,true);
union_impl!(false,true,false; false,true,false);
union_impl!(false,true,false; false,true,true);
union_impl!(false,true,false; true,false,false);
union_impl!(false,true,false; true,false,true);
union_impl!(false,true,false; true,true,false);
union_impl!(false,true,false; true,true,true);
union_impl!(false,true,true; false,false,false);
union_impl!(false,true,true; false,false,true);
union_impl!(false,true,true; false,true,false);
union_impl!(false,true,true; false,true,true);
union_impl!(false,true,true; true,false,false);
union_impl!(false,true,true; true,false,true);
union_impl!(false,true,true; true,true,false);
union_impl!(false,true,true; true,true,true);
union_impl!(true,false,false; false,false,false);
union_impl!(true,false,false; false,false,true);
union_impl!(true,false,false; false,true,false);
union_impl!(true,false,false; false,true,true);
union_impl!(true,false,false; true,false,false);
union_impl!(true,false,false; true,false,true);
union_impl!(true,false,false; true,true,false);
union_impl!(true,false,false; true,true,true);
union_impl!(true,false,true; false,false,false);
union_impl!(true,false,true; false,false,true);
union_impl!(true,false,true; false,true,false);
union_impl!(true,false,true; false,true,true);
union_impl!(true,false,true; true,false,false);
union_impl!(true,false,true; true,false,true);
union_impl!(true,false,true; true,true,false);
union_impl!(true,false,true; true,true,true);
union_impl!(true,true,false; false,false,false);
union_impl!(true,true,false; false,false,true);
union_impl!(true,true,false; false,true,false);
union_impl!(true,true,false; false,true,true);
union_impl!(true,true,false; true,false,false);
union_impl!(true,true,false; true,false,true);
union_impl!(true,true,false; true,true,false);
union_impl!(true,true,false; true,true,true);
union_impl!(true,true,true; false,false,false);
union_impl!(true,true,true; false,false,true);
union_impl!(true,true,true; false,true,false);
union_impl!(true,true,true; false,true,true);
union_impl!(true,true,true; true,false,false);
union_impl!(true,true,true; true,false,true);
union_impl!(true,true,true; true,true,false);
union_impl!(true,true,true; true,true,true);
pub type WithoutTransient<L> = <L as LaneProfile>::WithoutTransient;
pub type WithoutDenied<L> = <L as LaneProfile>::WithoutDenied;
#[macro_export]
macro_rules! lanes {
($($lane:ident),* $(,)?) => { $crate::lanes!(@acc [false, false, false] $($lane,)*) };
(@acc [$d:expr, $t:expr, $f:expr]) => { $crate::profile::Profile<{ $d }, { $t }, { $f }> };
(@acc [$d:expr, $t:expr, $f:expr] Denied, $($rest:ident,)*) => {
$crate::lanes!(@acc [true, $t, $f] $($rest,)*)
};
(@acc [$d:expr, $t:expr, $f:expr] Transient, $($rest:ident,)*) => {
$crate::lanes!(@acc [$d, true, $f] $($rest,)*)
};
(@acc [$d:expr, $t:expr, $f:expr] Fatal, $($rest:ident,)*) => {
$crate::lanes!(@acc [$d, $t, true] $($rest,)*)
};
(@acc [$d:expr, $t:expr, $f:expr] Rejected, $($rest:ident,)*) => {
$crate::profile::Profile<{{
compile_error!(
"`Rejected` is not a fault lane: it is selected by the carrier, not the \
profile. Use `Fail<YourRejection, lanes!(..)>` instead of \
`Fault<lanes!(Rejected, ..)>`."
);
$d
}}, { $t }, { $f }>
};
(@acc [$d:expr, $t:expr, $f:expr] $other:ident, $($rest:ident,)*) => {
$crate::profile::Profile<{{
compile_error!(concat!(
"unknown lane `",
stringify!($other),
"`: expected `Denied`, `Transient`, or `Fatal`"
));
$d
}}, { $t }, { $f }>
};
}