use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use crate::RetryDeclaration;
use crate::runtime::router::{AttachDeclaration, RouterCommit};
use crate::runtime::slot::{
AdmitsAt, BatchTransformLast, BindAt, CodecLast, MapPolicyLast, NoReply, Reply, ReplyLast,
SlotPos, TransactionalLast, TransformLast,
};
use super::{Retried, RetryOpen};
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Absent;
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Present;
#[doc(hidden)]
pub struct Declaring<Under, Cap = Absent, Dead = Absent> {
under: Under,
declaration: RetryDeclaration,
_steps: PhantomData<fn() -> (Cap, Dead)>,
}
impl<Under, Cap, Dead> fmt::Debug for Declaring<Under, Cap, Dead> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Declaring")
.field("declaration", &self.declaration)
.finish_non_exhaustive()
}
}
impl<Under, Cap, Dead> Declaring<Under, Cap, Dead> {
fn new(under: Under, declaration: RetryDeclaration) -> Self {
Self {
under,
declaration,
_steps: PhantomData,
}
}
fn map<NewUnder>(self, f: impl FnOnce(Under) -> NewUnder) -> Declaring<NewUnder, Cap, Dead> {
Declaring {
under: f(self.under),
declaration: self.declaration,
_steps: PhantomData,
}
}
fn declare<NewCap, NewDead>(
self,
f: impl FnOnce(RetryDeclaration) -> RetryDeclaration,
) -> Declaring<Under, NewCap, NewDead> {
Declaring {
under: self.under,
declaration: f(self.declaration),
_steps: PhantomData,
}
}
}
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct StepOpen;
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct StepTaken;
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "`max_attempts(..)` has nothing to declare here",
label = "`{Self}`: the cap was declared already, or the chain has moved past the \
declaration",
note = "a registration declares `max_attempts(n)` once, right after `include(..)` and before \
`out_retry(policy)`: \
`b.include(reconcile).max_attempts(nonzero!(5)).dead_letter(\"orders.dead\")`"
)]
pub trait CapOpen {}
impl CapOpen for StepOpen {}
#[doc(hidden)]
pub trait DeclareCap {
type Step;
type Out;
fn declare_cap(self, attempts: NonZeroU32) -> Self::Out;
}
impl<Rep, Slots> DeclareCap for (Rep, Slots) {
type Step = StepOpen;
type Out = Declaring<Self, Present, Absent>;
fn declare_cap(self, attempts: NonZeroU32) -> Self::Out {
Declaring::new(self, RetryDeclaration::new().with_max_attempts(attempts))
}
}
impl<Under, Dead> DeclareCap for Declaring<Under, Absent, Dead> {
type Step = StepOpen;
type Out = Declaring<Under, Present, Dead>;
fn declare_cap(self, attempts: NonZeroU32) -> Self::Out {
self.declare(|declaration| declaration.with_max_attempts(attempts))
}
}
impl<Under, Dead> DeclareCap for Declaring<Under, Present, Dead> {
type Step = StepTaken;
type Out = Self;
fn declare_cap(self, _attempts: NonZeroU32) -> Self {
self
}
}
impl<Under, Attachment, Dest> DeclareCap for Retried<Under, Attachment, Dest> {
type Step = StepTaken;
type Out = Self;
fn declare_cap(self, _attempts: NonZeroU32) -> Self {
self
}
}
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "`dead_letter(..)` has nothing to declare here",
label = "`{Self}`: the destination was declared already, or the chain has moved past the \
declaration",
note = "a registration declares `dead_letter(name)` once, right after `include(..)` and \
before `out_retry(policy)`: \
`b.include(reconcile).max_attempts(nonzero!(5)).dead_letter(\"orders.dead\")`"
)]
pub trait DeadLetterOpen {}
impl DeadLetterOpen for StepOpen {}
#[doc(hidden)]
pub trait DeclareDeadLetter {
type Step;
type Out;
fn declare_dead_letter(self, destination: Cow<'static, str>) -> Self::Out;
}
impl<Rep, Slots> DeclareDeadLetter for (Rep, Slots) {
type Step = StepOpen;
type Out = Declaring<Self, Absent, Present>;
fn declare_dead_letter(self, destination: Cow<'static, str>) -> Self::Out {
Declaring::new(self, RetryDeclaration::new().with_dead_letter(destination))
}
}
impl<Under, Cap> DeclareDeadLetter for Declaring<Under, Cap, Absent> {
type Step = StepOpen;
type Out = Declaring<Under, Cap, Present>;
fn declare_dead_letter(self, destination: Cow<'static, str>) -> Self::Out {
self.declare(|declaration| declaration.with_dead_letter(destination))
}
}
impl<Under, Cap> DeclareDeadLetter for Declaring<Under, Cap, Present> {
type Step = StepTaken;
type Out = Self;
fn declare_dead_letter(self, _destination: Cow<'static, str>) -> Self {
self
}
}
impl<Under, Attachment, Dest> DeclareDeadLetter for Retried<Under, Attachment, Dest> {
type Step = StepTaken;
type Out = Self;
fn declare_dead_letter(self, _destination: Cow<'static, str>) -> Self {
self
}
}
#[doc(hidden)]
pub type RouteDeclaring<Cap, Dead> = Declaring<(NoReply, ()), Cap, Dead>;
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct DeclareMount;
impl<R> RouterCommit<DeclareMount, R, ()> for (NoReply, ()) {
type Out = R;
fn commit(self, (): (), router: R) -> R {
router
}
}
impl<Under: RetryOpen, Cap, Dead> RetryOpen for Declaring<Under, Cap, Dead> {}
impl<Mount, Policy, Under, Cap, Dead> BindAt<Mount, Reply, Policy, ReplyLast>
for Declaring<Under, Cap, Dead>
where
Under: BindAt<Mount, Reply, Policy, ReplyLast>,
{
type Out = Declaring<Under::Out, Cap, Dead>;
fn bind_at(self, policy: Policy) -> Self::Out {
self.map(|under| under.bind_at(policy))
}
}
impl<Mount, M, Policy, Under, Cap, Dead, const POS: usize> BindAt<Mount, M, Policy, SlotPos<POS>>
for Declaring<Under, Cap, Dead>
where
Under: BindAt<Mount, M, Policy, SlotPos<POS>>,
{
type Out = Declaring<Under::Out, Cap, Dead>;
fn bind_at(self, policy: Policy) -> Self::Out {
self.map(|under| under.bind_at(policy))
}
}
macro_rules! forward_steps {
($([$($extra:tt)*] $index:ty),+ $(,)?) => {$(
impl<Cd, Under, Cap, Dead, $($extra)*> CodecLast<Cd, $index> for Declaring<Under, Cap, Dead>
where
Under: CodecLast<Cd, $index>,
{
type Step = Under::Step;
type Out = Declaring<Under::Out, Cap, Dead>;
fn codec_last(self, codec: Cd) -> Self::Out {
self.map(|under| under.codec_last(codec))
}
}
impl<N, Under, Cap, Dead, $($extra)*> TransformLast<N, $index>
for Declaring<Under, Cap, Dead>
where
Under: TransformLast<N, $index>,
{
type Step = Under::Step;
type Out = Declaring<Under::Out, Cap, Dead>;
fn transform_last(self, transform: N) -> Self::Out {
self.map(|under| under.transform_last(transform))
}
}
impl<N, Under, Cap, Dead, Mount, Def, B, $($extra)*> AdmitsAt<N, $index, Mount, Def, B>
for Declaring<Under, Cap, Dead>
where
Under: AdmitsAt<N, $index, Mount, Def, B>,
{
}
impl<N, Under, Cap, Dead, $($extra)*> BatchTransformLast<N, $index>
for Declaring<Under, Cap, Dead>
where
Under: BatchTransformLast<N, $index>,
{
type Step = Under::Step;
type Out = Declaring<Under::Out, Cap, Dead>;
fn batch_transform_last(self, transform: N) -> Self::Out {
self.map(|under| under.batch_transform_last(transform))
}
}
impl<Under, Cap, Dead, $($extra)*> TransactionalLast<$index>
for Declaring<Under, Cap, Dead>
where
Under: TransactionalLast<$index>,
{
type Step = Under::Step;
type Out = Declaring<Under::Out, Cap, Dead>;
fn transactional_last(self) -> Self::Out {
self.map(TransactionalLast::transactional_last)
}
}
impl<Under, Cap, Dead, $($extra)*> MapPolicyLast<$index> for Declaring<Under, Cap, Dead>
where
Under: MapPolicyLast<$index>,
{
type Step = Under::Step;
type Policy = Under::Policy;
fn map_policy_last(self, f: impl FnOnce(Self::Policy) -> Self::Policy) -> Self {
self.map(|under| under.map_policy_last(f))
}
}
)+};
}
forward_steps!([] ReplyLast, [const POS: usize] SlotPos<POS>);
impl<Mount, R, Def, Under, Cap, Dead> RouterCommit<Mount, R, Def> for Declaring<Under, Cap, Dead>
where
Under: RouterCommit<Mount, R, Def>,
Under::Out: AttachDeclaration,
{
type Out = <Under::Out as AttachDeclaration>::Out;
fn commit(self, def: Def, router: R) -> Self::Out {
self.under
.commit(def, router)
.attach_declaration(self.declaration)
}
}