use alloc::boxed::Box;
use crate::wire::{BindingEpoch, ConversationId, DeliverySeq, ParticipantId};
#[cfg(test)]
use super::FencedAttachCommit;
use super::{
CommittedDiedTerminal, Event, OrdinaryBindingFate, RecoveredBindingFate, SealedBindingFateToken,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SealedBindingFateIntent {
Ordinary,
Recovered {
prior_binding_epoch: BindingEpoch,
marker_delivery_seq: DeliverySeq,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::lifecycle) struct BindingFateMeasurementContext {
pub(in crate::lifecycle) conversation_id: ConversationId,
pub(in crate::lifecycle) participant_id: ParticipantId,
pub(in crate::lifecycle) binding_epoch: BindingEpoch,
pub(in crate::lifecycle) cursor: DeliverySeq,
}
impl SealedBindingFateToken {
#[must_use]
pub const fn is_recovered(&self) -> bool {
self.recovered.is_some()
}
#[must_use]
pub const fn intent(&self) -> Option<SealedBindingFateIntent> {
match (&self.ordinary, &self.recovered) {
(Some(_), None) => Some(SealedBindingFateIntent::Ordinary),
(None, Some(proof)) => Some(SealedBindingFateIntent::Recovered {
prior_binding_epoch: proof.prior_binding_epoch(),
marker_delivery_seq: proof.marker_delivery_seq(),
}),
(None, None) | (Some(_), Some(_)) => None,
}
}
#[cfg(test)]
pub(in crate::lifecycle) const fn from_recovered_for_test(
recovered: FencedAttachCommit,
) -> Self {
let cursor = recovered.marker_delivery_seq();
Self {
ordinary: None,
recovered: Some(recovered),
cursor,
}
}
pub(in crate::lifecycle) fn participant_ack_progressed(
mut self,
conversation_id: ConversationId,
participant_id: ParticipantId,
binding_epoch: BindingEpoch,
previous_cursor: DeliverySeq,
through_seq: DeliverySeq,
) -> Result<Self, Box<Self>> {
if previous_cursor != self.cursor || through_seq <= previous_cursor {
return Err(Box::new(self));
}
match (self.ordinary.take(), self.recovered.as_ref()) {
(Some(authority), None) => match authority.participant_ack_progressed(
conversation_id,
participant_id,
binding_epoch,
previous_cursor,
through_seq,
) {
Ok(authority) => self.ordinary = Some(authority),
Err(authority) => {
self.ordinary = Some(authority);
return Err(Box::new(self));
}
},
(None, Some(proof))
if proof.conversation_id() == conversation_id
&& proof.participant_id() == participant_id
&& proof.new_binding_epoch() == binding_epoch => {}
(ordinary, _) => {
self.ordinary = ordinary;
return Err(Box::new(self));
}
}
self.cursor = through_seq;
Ok(self)
}
pub(in crate::lifecycle) const fn measurement_context(
&self,
) -> Option<BindingFateMeasurementContext> {
match (&self.ordinary, &self.recovered) {
(Some(authority), None) if authority.through_seq() == self.cursor => {
let binding = authority.binding();
Some(BindingFateMeasurementContext {
conversation_id: binding.conversation_id,
participant_id: binding.participant_id,
binding_epoch: binding.binding_epoch,
cursor: self.cursor,
})
}
(None, Some(proof)) => Some(BindingFateMeasurementContext {
conversation_id: proof.conversation_id(),
participant_id: proof.participant_id(),
binding_epoch: proof.new_binding_epoch(),
cursor: self.cursor,
}),
(None | Some(_), None) | (Some(_), Some(_)) => None,
}
}
pub(in crate::lifecycle) fn ordinary_binding_fate(
mut self,
terminal: CommittedDiedTerminal,
resulting_floor: DeliverySeq,
) -> Result<OrdinaryBindingFate, Box<Self>> {
if self.recovered.is_some() {
return Err(Box::new(self));
}
let Some(authority) = self.ordinary.take() else {
return Err(Box::new(self));
};
match authority.binding_fate(terminal, resulting_floor) {
Ok(fate) => Ok(fate),
Err(authority) => {
self.ordinary = Some(authority);
Err(Box::new(self))
}
}
}
pub(in crate::lifecycle) fn recovered_binding_fate_measured(
self,
resulting_floor: DeliverySeq,
) -> Result<RecoveredBindingFate, Box<Self>> {
let Some(context) = self.measurement_context() else {
return Err(Box::new(self));
};
self.recovered_binding_fate(Event::binding_fate_observed(
context.participant_id,
context.binding_epoch,
resulting_floor,
))
}
pub(in crate::lifecycle) fn recovered_binding_fate(
mut self,
event: Event,
) -> Result<RecoveredBindingFate, Box<Self>> {
if self.ordinary.is_some() {
return Err(Box::new(self));
}
let Some(proof) = self.recovered.take() else {
return Err(Box::new(self));
};
match proof.recovered_binding_fate(event) {
Ok(fate) => Ok(fate),
Err(proof) => {
self.recovered = Some(*proof);
Err(Box::new(self))
}
}
}
}