use crate::outcome::CandidatePhase;
use crate::wire::{
BindingEpoch, CloseCause, ConversationId, DeliverySeq, DetachedCause, DiedCause, ParticipantId,
ParticipantIndex, TransactionOrder,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AdmissionOrder {
transaction_order: TransactionOrder,
candidate_phase: CandidatePhase,
participant_index: ParticipantIndex,
}
impl AdmissionOrder {
#[must_use]
pub const fn new(
transaction_order: TransactionOrder,
candidate_phase: CandidatePhase,
participant_index: ParticipantIndex,
) -> Self {
Self {
transaction_order,
candidate_phase,
participant_index,
}
}
#[must_use]
pub const fn binding_terminal(
transaction_order: TransactionOrder,
participant_id: ParticipantId,
) -> Self {
Self::new(
transaction_order,
CandidatePhase::BindingTerminal,
participant_id,
)
}
#[must_use]
pub const fn transaction_order(self) -> TransactionOrder {
self.transaction_order
}
#[must_use]
pub const fn candidate_phase(self) -> CandidatePhase {
self.candidate_phase
}
#[must_use]
pub const fn participant_index(self) -> ParticipantIndex {
self.participant_index
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ActiveBinding {
pub participant_id: ParticipantId,
pub conversation_id: ConversationId,
pub binding_epoch: BindingEpoch,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CommittedBindingTerminalPosition {
transaction_order: TransactionOrder,
delivery_seq: DeliverySeq,
}
impl CommittedBindingTerminalPosition {
#[must_use]
pub const fn new(transaction_order: TransactionOrder, delivery_seq: DeliverySeq) -> Self {
Self {
transaction_order,
delivery_seq,
}
}
#[must_use]
pub const fn transaction_order(self) -> TransactionOrder {
self.transaction_order
}
#[must_use]
pub const fn delivery_seq(self) -> DeliverySeq {
self.delivery_seq
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PendingBindingTerminalPosition {
transaction_order: TransactionOrder,
}
impl PendingBindingTerminalPosition {
#[must_use]
pub const fn new(transaction_order: TransactionOrder) -> Self {
Self { transaction_order }
}
#[must_use]
pub const fn transaction_order(self) -> TransactionOrder {
self.transaction_order
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BindingTerminalDisposition {
Committed(CommittedBindingTerminalPosition),
Pending(PendingBindingTerminalPosition),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct BindingTerminalIdentity {
participant_id: ParticipantId,
conversation_id: ConversationId,
binding_epoch: BindingEpoch,
admission_order: AdmissionOrder,
}
impl BindingTerminalIdentity {
const fn from_active(binding: ActiveBinding, transaction_order: TransactionOrder) -> Self {
Self {
participant_id: binding.participant_id,
conversation_id: binding.conversation_id,
binding_epoch: binding.binding_epoch,
admission_order: AdmissionOrder::binding_terminal(
transaction_order,
binding.participant_id,
),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CommittedDetachedTerminal {
identity: BindingTerminalIdentity,
cause: DetachedCause,
delivery_seq: DeliverySeq,
}
impl CommittedDetachedTerminal {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
self.identity.participant_id
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
self.identity.conversation_id
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
self.identity.binding_epoch
}
#[must_use]
pub const fn cause(self) -> DetachedCause {
self.cause
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
self.identity.admission_order
}
#[must_use]
pub const fn delivery_seq(self) -> DeliverySeq {
self.delivery_seq
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CommittedDiedTerminal {
identity: BindingTerminalIdentity,
cause: DiedCause,
delivery_seq: DeliverySeq,
}
impl CommittedDiedTerminal {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
self.identity.participant_id
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
self.identity.conversation_id
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
self.identity.binding_epoch
}
#[must_use]
pub const fn cause(self) -> DiedCause {
self.cause
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
self.identity.admission_order
}
#[must_use]
pub const fn delivery_seq(self) -> DeliverySeq {
self.delivery_seq
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommittedBindingTerminal {
Detached(CommittedDetachedTerminal),
Died(CommittedDiedTerminal),
}
impl CommittedBindingTerminal {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
match self {
Self::Detached(value) => value.participant_id(),
Self::Died(value) => value.participant_id(),
}
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
match self {
Self::Detached(value) => value.conversation_id(),
Self::Died(value) => value.conversation_id(),
}
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
match self {
Self::Detached(value) => value.binding_epoch(),
Self::Died(value) => value.binding_epoch(),
}
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
match self {
Self::Detached(value) => value.admission_order(),
Self::Died(value) => value.admission_order(),
}
}
#[must_use]
pub const fn delivery_seq(self) -> DeliverySeq {
match self {
Self::Detached(value) => value.delivery_seq(),
Self::Died(value) => value.delivery_seq(),
}
}
#[must_use]
pub const fn kind(self) -> BindingTerminalKind {
match self {
Self::Detached(_) => BindingTerminalKind::Detached,
Self::Died(_) => BindingTerminalKind::Died,
}
}
#[must_use]
pub const fn close_cause(self) -> CloseCause {
match self {
Self::Detached(value) => value.cause().close_cause(),
Self::Died(value) => value.cause().close_cause(),
}
}
#[must_use]
pub const fn detached_cause(self) -> Option<DetachedCause> {
match self {
Self::Detached(value) => Some(value.cause()),
Self::Died(_) => None,
}
}
#[must_use]
pub const fn died_cause(self) -> Option<DiedCause> {
match self {
Self::Detached(_) => None,
Self::Died(value) => Some(value.cause()),
}
}
}
impl From<CommittedDetachedTerminal> for CommittedBindingTerminal {
fn from(value: CommittedDetachedTerminal) -> Self {
Self::Detached(value)
}
}
impl From<CommittedDiedTerminal> for CommittedBindingTerminal {
fn from(value: CommittedDiedTerminal) -> Self {
Self::Died(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PendingDetachedFinalization {
identity: BindingTerminalIdentity,
cause: DetachedCause,
}
impl PendingDetachedFinalization {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
self.identity.participant_id
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
self.identity.conversation_id
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
self.identity.binding_epoch
}
#[must_use]
pub const fn cause(self) -> DetachedCause {
self.cause
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
self.identity.admission_order
}
#[must_use]
pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedDetachedTerminal {
CommittedDetachedTerminal {
identity: self.identity,
cause: self.cause,
delivery_seq,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PendingDiedFinalization {
identity: BindingTerminalIdentity,
cause: DiedCause,
}
impl PendingDiedFinalization {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
self.identity.participant_id
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
self.identity.conversation_id
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
self.identity.binding_epoch
}
#[must_use]
pub const fn cause(self) -> DiedCause {
self.cause
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
self.identity.admission_order
}
#[must_use]
pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedDiedTerminal {
CommittedDiedTerminal {
identity: self.identity,
cause: self.cause,
delivery_seq,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BindingTerminalKind {
Detached,
Died,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PendingFinalization {
Detached(PendingDetachedFinalization),
Died(PendingDiedFinalization),
}
impl PendingFinalization {
#[must_use]
pub const fn participant_id(self) -> ParticipantId {
match self {
Self::Detached(value) => value.participant_id(),
Self::Died(value) => value.participant_id(),
}
}
#[must_use]
pub const fn conversation_id(self) -> ConversationId {
match self {
Self::Detached(value) => value.conversation_id(),
Self::Died(value) => value.conversation_id(),
}
}
#[must_use]
pub const fn binding_epoch(self) -> BindingEpoch {
match self {
Self::Detached(value) => value.binding_epoch(),
Self::Died(value) => value.binding_epoch(),
}
}
#[must_use]
pub const fn admission_order(self) -> AdmissionOrder {
match self {
Self::Detached(value) => value.admission_order(),
Self::Died(value) => value.admission_order(),
}
}
#[must_use]
pub const fn kind(self) -> BindingTerminalKind {
match self {
Self::Detached(_) => BindingTerminalKind::Detached,
Self::Died(_) => BindingTerminalKind::Died,
}
}
#[must_use]
pub const fn close_cause(self) -> CloseCause {
match self {
Self::Detached(value) => value.cause().close_cause(),
Self::Died(value) => value.cause().close_cause(),
}
}
#[must_use]
pub const fn detached_cause(self) -> Option<DetachedCause> {
match self {
Self::Detached(value) => Some(value.cause()),
Self::Died(_) => None,
}
}
#[must_use]
pub const fn died_cause(self) -> Option<DiedCause> {
match self {
Self::Detached(_) => None,
Self::Died(value) => Some(value.cause()),
}
}
#[must_use]
pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedBindingTerminal {
match self {
Self::Detached(value) => CommittedBindingTerminal::Detached(value.commit(delivery_seq)),
Self::Died(value) => CommittedBindingTerminal::Died(value.commit(delivery_seq)),
}
}
}
impl From<PendingDetachedFinalization> for PendingFinalization {
fn from(value: PendingDetachedFinalization) -> Self {
Self::Detached(value)
}
}
impl From<PendingDiedFinalization> for PendingFinalization {
fn from(value: PendingDiedFinalization) -> Self {
Self::Died(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DetachedBindingTransition {
Committed(CommittedDetachedTerminal),
Pending(PendingDetachedFinalization),
}
impl DetachedBindingTransition {
#[must_use]
pub const fn observer_progress_projection(&self) -> Option<super::ObserverProgressProjection> {
let Self::Committed(terminal) = self else {
return None;
};
Some(super::ObserverProgressProjection::new(
terminal.conversation_id(),
terminal.delivery_seq(),
))
}
#[must_use]
pub const fn binding_state(self) -> BindingState {
match self {
Self::Committed(_) => BindingState::Detached,
Self::Pending(value) => {
BindingState::PendingFinalization(PendingFinalization::Detached(value))
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DiedBindingTransition {
Committed(CommittedDiedTerminal),
Pending(PendingDiedFinalization),
}
impl DiedBindingTransition {
#[must_use]
pub const fn observer_progress_projection(&self) -> Option<super::ObserverProgressProjection> {
let Self::Committed(terminal) = self else {
return None;
};
Some(super::ObserverProgressProjection::new(
terminal.conversation_id(),
terminal.delivery_seq(),
))
}
#[must_use]
pub const fn binding_state(self) -> BindingState {
match self {
Self::Committed(_) => BindingState::Detached,
Self::Pending(value) => {
BindingState::PendingFinalization(PendingFinalization::Died(value))
}
}
}
}
impl ActiveBinding {
const fn finish_detached(
self,
cause: DetachedCause,
disposition: BindingTerminalDisposition,
) -> DetachedBindingTransition {
match disposition {
BindingTerminalDisposition::Committed(position) => {
DetachedBindingTransition::Committed(CommittedDetachedTerminal {
identity: BindingTerminalIdentity::from_active(
self,
position.transaction_order,
),
cause,
delivery_seq: position.delivery_seq,
})
}
BindingTerminalDisposition::Pending(position) => {
DetachedBindingTransition::Pending(PendingDetachedFinalization {
identity: BindingTerminalIdentity::from_active(
self,
position.transaction_order,
),
cause,
})
}
}
}
const fn finish_died(
self,
cause: DiedCause,
disposition: BindingTerminalDisposition,
) -> DiedBindingTransition {
match disposition {
BindingTerminalDisposition::Committed(position) => {
DiedBindingTransition::Committed(CommittedDiedTerminal {
identity: BindingTerminalIdentity::from_active(
self,
position.transaction_order,
),
cause,
delivery_seq: position.delivery_seq,
})
}
BindingTerminalDisposition::Pending(position) => {
DiedBindingTransition::Pending(PendingDiedFinalization {
identity: BindingTerminalIdentity::from_active(
self,
position.transaction_order,
),
cause,
})
}
}
}
#[must_use]
pub const fn clean_deregister(
self,
disposition: BindingTerminalDisposition,
) -> DetachedBindingTransition {
self.finish_detached(DetachedCause::CleanDeregister, disposition)
}
#[must_use]
pub const fn commit_clean_deregister(
self,
position: CommittedBindingTerminalPosition,
) -> CommittedDetachedTerminal {
CommittedDetachedTerminal {
identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
cause: DetachedCause::CleanDeregister,
delivery_seq: position.delivery_seq,
}
}
#[must_use]
pub const fn pending_clean_deregister(
self,
position: PendingBindingTerminalPosition,
) -> PendingDetachedFinalization {
PendingDetachedFinalization {
identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
cause: DetachedCause::CleanDeregister,
}
}
#[must_use]
pub const fn clean_disconnect(
self,
disposition: BindingTerminalDisposition,
) -> DetachedBindingTransition {
self.clean_deregister(disposition)
}
#[must_use]
pub const fn superseded(
self,
position: CommittedBindingTerminalPosition,
) -> CommittedDetachedTerminal {
CommittedDetachedTerminal {
identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
cause: DetachedCause::Superseded,
delivery_seq: position.delivery_seq,
}
}
#[must_use]
pub const fn server_shutdown(
self,
disposition: BindingTerminalDisposition,
) -> DetachedBindingTransition {
self.finish_detached(DetachedCause::ServerShutdown, disposition)
}
#[must_use]
pub const fn connection_lost(
self,
disposition: BindingTerminalDisposition,
) -> DiedBindingTransition {
self.finish_died(DiedCause::ConnectionLost, disposition)
}
#[must_use]
pub const fn process_killed(
self,
disposition: BindingTerminalDisposition,
) -> DiedBindingTransition {
self.finish_died(DiedCause::ProcessKilled, disposition)
}
#[must_use]
pub const fn protocol_error(
self,
disposition: BindingTerminalDisposition,
) -> DiedBindingTransition {
self.finish_died(DiedCause::ProtocolError, disposition)
}
#[must_use]
pub const fn unclean_server_restart(
self,
disposition: BindingTerminalDisposition,
) -> DiedBindingTransition {
self.finish_died(
DiedCause::UncleanServerRestart {
prior_server_incarnation: self
.binding_epoch
.connection_incarnation
.server_incarnation,
},
disposition,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BindingState {
Detached,
Bound(ActiveBinding),
PendingFinalization(PendingFinalization),
}
pub(super) const fn restore_pending_finalization(
binding: ActiveBinding,
cause: CloseCause,
transaction_order: TransactionOrder,
) -> Option<PendingFinalization> {
let position =
BindingTerminalDisposition::Pending(PendingBindingTerminalPosition::new(transaction_order));
match cause {
CloseCause::CleanDeregister => match binding.clean_deregister(position) {
DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
DetachedBindingTransition::Committed(_) => None,
},
CloseCause::ServerShutdown => match binding.server_shutdown(position) {
DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
DetachedBindingTransition::Committed(_) => None,
},
CloseCause::Superseded => None,
CloseCause::ConnectionLost => match binding.connection_lost(position) {
DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
DiedBindingTransition::Committed(_) => None,
},
CloseCause::ProcessKilled => match binding.process_killed(position) {
DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
DiedBindingTransition::Committed(_) => None,
},
CloseCause::ProtocolError => match binding.protocol_error(position) {
DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
DiedBindingTransition::Committed(_) => None,
},
CloseCause::UncleanServerRestart {
prior_server_incarnation,
} => {
if binding
.binding_epoch
.connection_incarnation
.server_incarnation
!= prior_server_incarnation
{
return None;
}
match binding.unclean_server_restart(position) {
DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
DiedBindingTransition::Committed(_) => None,
}
}
}
}
pub(super) const fn restore_committed_terminal(
binding: ActiveBinding,
cause: CloseCause,
transaction_order: TransactionOrder,
delivery_seq: DeliverySeq,
) -> Option<CommittedBindingTerminal> {
let position = BindingTerminalDisposition::Committed(CommittedBindingTerminalPosition::new(
transaction_order,
delivery_seq,
));
match cause {
CloseCause::CleanDeregister => match binding.clean_deregister(position) {
DetachedBindingTransition::Committed(value) => {
Some(CommittedBindingTerminal::Detached(value))
}
DetachedBindingTransition::Pending(_) => None,
},
CloseCause::Superseded => Some(CommittedBindingTerminal::Detached(binding.superseded(
CommittedBindingTerminalPosition::new(transaction_order, delivery_seq),
))),
CloseCause::ServerShutdown => match binding.server_shutdown(position) {
DetachedBindingTransition::Committed(value) => {
Some(CommittedBindingTerminal::Detached(value))
}
DetachedBindingTransition::Pending(_) => None,
},
CloseCause::ConnectionLost => match binding.connection_lost(position) {
DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
DiedBindingTransition::Pending(_) => None,
},
CloseCause::ProcessKilled => match binding.process_killed(position) {
DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
DiedBindingTransition::Pending(_) => None,
},
CloseCause::ProtocolError => match binding.protocol_error(position) {
DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
DiedBindingTransition::Pending(_) => None,
},
CloseCause::UncleanServerRestart {
prior_server_incarnation,
} => {
if binding
.binding_epoch
.connection_incarnation
.server_incarnation
!= prior_server_incarnation
{
return None;
}
match binding.unclean_server_restart(position) {
DiedBindingTransition::Committed(value) => {
Some(CommittedBindingTerminal::Died(value))
}
DiedBindingTransition::Pending(_) => None,
}
}
}
}