1use crate::outcome::CandidatePhase;
2use crate::wire::{
3 BindingEpoch, CloseCause, ConversationId, DeliverySeq, DetachedCause, DiedCause, ParticipantId,
4 ParticipantIndex, TransactionOrder,
5};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
14pub struct AdmissionOrder {
15 transaction_order: TransactionOrder,
16 candidate_phase: CandidatePhase,
17 participant_index: ParticipantIndex,
18}
19
20impl AdmissionOrder {
21 #[must_use]
23 pub const fn new(
24 transaction_order: TransactionOrder,
25 candidate_phase: CandidatePhase,
26 participant_index: ParticipantIndex,
27 ) -> Self {
28 Self {
29 transaction_order,
30 candidate_phase,
31 participant_index,
32 }
33 }
34
35 #[must_use]
37 pub const fn binding_terminal(
38 transaction_order: TransactionOrder,
39 participant_id: ParticipantId,
40 ) -> Self {
41 Self::new(
42 transaction_order,
43 CandidatePhase::BindingTerminal,
44 participant_id,
45 )
46 }
47
48 #[must_use]
50 pub const fn transaction_order(self) -> TransactionOrder {
51 self.transaction_order
52 }
53
54 #[must_use]
56 pub const fn candidate_phase(self) -> CandidatePhase {
57 self.candidate_phase
58 }
59
60 #[must_use]
62 pub const fn participant_index(self) -> ParticipantIndex {
63 self.participant_index
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct ActiveBinding {
70 pub participant_id: ParticipantId,
72 pub conversation_id: ConversationId,
74 pub binding_epoch: BindingEpoch,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub struct CommittedBindingTerminalPosition {
81 transaction_order: TransactionOrder,
82 delivery_seq: DeliverySeq,
83}
84
85impl CommittedBindingTerminalPosition {
86 #[must_use]
88 pub const fn new(transaction_order: TransactionOrder, delivery_seq: DeliverySeq) -> Self {
89 Self {
90 transaction_order,
91 delivery_seq,
92 }
93 }
94
95 #[must_use]
97 pub const fn transaction_order(self) -> TransactionOrder {
98 self.transaction_order
99 }
100
101 #[must_use]
103 pub const fn delivery_seq(self) -> DeliverySeq {
104 self.delivery_seq
105 }
106}
107
108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
110pub struct PendingBindingTerminalPosition {
111 transaction_order: TransactionOrder,
112}
113
114impl PendingBindingTerminalPosition {
115 #[must_use]
117 pub const fn new(transaction_order: TransactionOrder) -> Self {
118 Self { transaction_order }
119 }
120
121 #[must_use]
123 pub const fn transaction_order(self) -> TransactionOrder {
124 self.transaction_order
125 }
126}
127
128#[derive(Clone, Copy, Debug, PartialEq, Eq)]
130pub enum BindingTerminalDisposition {
131 Committed(CommittedBindingTerminalPosition),
133 Pending(PendingBindingTerminalPosition),
135}
136
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138struct BindingTerminalIdentity {
139 participant_id: ParticipantId,
140 conversation_id: ConversationId,
141 binding_epoch: BindingEpoch,
142 admission_order: AdmissionOrder,
143}
144
145impl BindingTerminalIdentity {
146 const fn from_active(binding: ActiveBinding, transaction_order: TransactionOrder) -> Self {
147 Self {
148 participant_id: binding.participant_id,
149 conversation_id: binding.conversation_id,
150 binding_epoch: binding.binding_epoch,
151 admission_order: AdmissionOrder::binding_terminal(
152 transaction_order,
153 binding.participant_id,
154 ),
155 }
156 }
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq)]
161pub struct CommittedDetachedTerminal {
162 identity: BindingTerminalIdentity,
163 cause: DetachedCause,
164 delivery_seq: DeliverySeq,
165}
166
167impl CommittedDetachedTerminal {
168 #[must_use]
170 pub const fn participant_id(self) -> ParticipantId {
171 self.identity.participant_id
172 }
173
174 #[must_use]
176 pub const fn conversation_id(self) -> ConversationId {
177 self.identity.conversation_id
178 }
179
180 #[must_use]
182 pub const fn binding_epoch(self) -> BindingEpoch {
183 self.identity.binding_epoch
184 }
185
186 #[must_use]
188 pub const fn cause(self) -> DetachedCause {
189 self.cause
190 }
191
192 #[must_use]
194 pub const fn admission_order(self) -> AdmissionOrder {
195 self.identity.admission_order
196 }
197
198 #[must_use]
200 pub const fn delivery_seq(self) -> DeliverySeq {
201 self.delivery_seq
202 }
203}
204
205#[derive(Clone, Copy, Debug, PartialEq, Eq)]
207pub struct CommittedDiedTerminal {
208 identity: BindingTerminalIdentity,
209 cause: DiedCause,
210 delivery_seq: DeliverySeq,
211}
212
213impl CommittedDiedTerminal {
214 #[must_use]
216 pub const fn participant_id(self) -> ParticipantId {
217 self.identity.participant_id
218 }
219
220 #[must_use]
222 pub const fn conversation_id(self) -> ConversationId {
223 self.identity.conversation_id
224 }
225
226 #[must_use]
228 pub const fn binding_epoch(self) -> BindingEpoch {
229 self.identity.binding_epoch
230 }
231
232 #[must_use]
234 pub const fn cause(self) -> DiedCause {
235 self.cause
236 }
237
238 #[must_use]
240 pub const fn admission_order(self) -> AdmissionOrder {
241 self.identity.admission_order
242 }
243
244 #[must_use]
246 pub const fn delivery_seq(self) -> DeliverySeq {
247 self.delivery_seq
248 }
249}
250
251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
256pub enum CommittedBindingTerminal {
257 Detached(CommittedDetachedTerminal),
259 Died(CommittedDiedTerminal),
261}
262
263impl CommittedBindingTerminal {
264 #[must_use]
266 pub const fn participant_id(self) -> ParticipantId {
267 match self {
268 Self::Detached(value) => value.participant_id(),
269 Self::Died(value) => value.participant_id(),
270 }
271 }
272
273 #[must_use]
275 pub const fn conversation_id(self) -> ConversationId {
276 match self {
277 Self::Detached(value) => value.conversation_id(),
278 Self::Died(value) => value.conversation_id(),
279 }
280 }
281
282 #[must_use]
284 pub const fn binding_epoch(self) -> BindingEpoch {
285 match self {
286 Self::Detached(value) => value.binding_epoch(),
287 Self::Died(value) => value.binding_epoch(),
288 }
289 }
290
291 #[must_use]
293 pub const fn admission_order(self) -> AdmissionOrder {
294 match self {
295 Self::Detached(value) => value.admission_order(),
296 Self::Died(value) => value.admission_order(),
297 }
298 }
299
300 #[must_use]
302 pub const fn delivery_seq(self) -> DeliverySeq {
303 match self {
304 Self::Detached(value) => value.delivery_seq(),
305 Self::Died(value) => value.delivery_seq(),
306 }
307 }
308
309 #[must_use]
311 pub const fn kind(self) -> BindingTerminalKind {
312 match self {
313 Self::Detached(_) => BindingTerminalKind::Detached,
314 Self::Died(_) => BindingTerminalKind::Died,
315 }
316 }
317
318 #[must_use]
320 pub const fn close_cause(self) -> CloseCause {
321 match self {
322 Self::Detached(value) => value.cause().close_cause(),
323 Self::Died(value) => value.cause().close_cause(),
324 }
325 }
326
327 #[must_use]
329 pub const fn detached_cause(self) -> Option<DetachedCause> {
330 match self {
331 Self::Detached(value) => Some(value.cause()),
332 Self::Died(_) => None,
333 }
334 }
335
336 #[must_use]
338 pub const fn died_cause(self) -> Option<DiedCause> {
339 match self {
340 Self::Detached(_) => None,
341 Self::Died(value) => Some(value.cause()),
342 }
343 }
344}
345
346impl From<CommittedDetachedTerminal> for CommittedBindingTerminal {
347 fn from(value: CommittedDetachedTerminal) -> Self {
348 Self::Detached(value)
349 }
350}
351
352impl From<CommittedDiedTerminal> for CommittedBindingTerminal {
353 fn from(value: CommittedDiedTerminal) -> Self {
354 Self::Died(value)
355 }
356}
357
358#[derive(Clone, Copy, Debug, PartialEq, Eq)]
360pub struct PendingDetachedFinalization {
361 identity: BindingTerminalIdentity,
362 cause: DetachedCause,
363}
364
365impl PendingDetachedFinalization {
366 #[must_use]
368 pub const fn participant_id(self) -> ParticipantId {
369 self.identity.participant_id
370 }
371
372 #[must_use]
374 pub const fn conversation_id(self) -> ConversationId {
375 self.identity.conversation_id
376 }
377
378 #[must_use]
380 pub const fn binding_epoch(self) -> BindingEpoch {
381 self.identity.binding_epoch
382 }
383
384 #[must_use]
386 pub const fn cause(self) -> DetachedCause {
387 self.cause
388 }
389
390 #[must_use]
392 pub const fn admission_order(self) -> AdmissionOrder {
393 self.identity.admission_order
394 }
395
396 #[must_use]
398 pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedDetachedTerminal {
399 CommittedDetachedTerminal {
400 identity: self.identity,
401 cause: self.cause,
402 delivery_seq,
403 }
404 }
405}
406
407#[derive(Clone, Copy, Debug, PartialEq, Eq)]
409pub struct PendingDiedFinalization {
410 identity: BindingTerminalIdentity,
411 cause: DiedCause,
412}
413
414impl PendingDiedFinalization {
415 #[must_use]
417 pub const fn participant_id(self) -> ParticipantId {
418 self.identity.participant_id
419 }
420
421 #[must_use]
423 pub const fn conversation_id(self) -> ConversationId {
424 self.identity.conversation_id
425 }
426
427 #[must_use]
429 pub const fn binding_epoch(self) -> BindingEpoch {
430 self.identity.binding_epoch
431 }
432
433 #[must_use]
435 pub const fn cause(self) -> DiedCause {
436 self.cause
437 }
438
439 #[must_use]
441 pub const fn admission_order(self) -> AdmissionOrder {
442 self.identity.admission_order
443 }
444
445 #[must_use]
447 pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedDiedTerminal {
448 CommittedDiedTerminal {
449 identity: self.identity,
450 cause: self.cause,
451 delivery_seq,
452 }
453 }
454}
455
456#[derive(Clone, Copy, Debug, PartialEq, Eq)]
458pub enum BindingTerminalKind {
459 Detached,
461 Died,
463}
464
465#[derive(Clone, Copy, Debug, PartialEq, Eq)]
472pub enum PendingFinalization {
473 Detached(PendingDetachedFinalization),
475 Died(PendingDiedFinalization),
477}
478
479impl PendingFinalization {
480 #[must_use]
482 pub const fn participant_id(self) -> ParticipantId {
483 match self {
484 Self::Detached(value) => value.participant_id(),
485 Self::Died(value) => value.participant_id(),
486 }
487 }
488
489 #[must_use]
491 pub const fn conversation_id(self) -> ConversationId {
492 match self {
493 Self::Detached(value) => value.conversation_id(),
494 Self::Died(value) => value.conversation_id(),
495 }
496 }
497
498 #[must_use]
500 pub const fn binding_epoch(self) -> BindingEpoch {
501 match self {
502 Self::Detached(value) => value.binding_epoch(),
503 Self::Died(value) => value.binding_epoch(),
504 }
505 }
506
507 #[must_use]
509 pub const fn admission_order(self) -> AdmissionOrder {
510 match self {
511 Self::Detached(value) => value.admission_order(),
512 Self::Died(value) => value.admission_order(),
513 }
514 }
515
516 #[must_use]
518 pub const fn kind(self) -> BindingTerminalKind {
519 match self {
520 Self::Detached(_) => BindingTerminalKind::Detached,
521 Self::Died(_) => BindingTerminalKind::Died,
522 }
523 }
524
525 #[must_use]
527 pub const fn close_cause(self) -> CloseCause {
528 match self {
529 Self::Detached(value) => value.cause().close_cause(),
530 Self::Died(value) => value.cause().close_cause(),
531 }
532 }
533
534 #[must_use]
536 pub const fn detached_cause(self) -> Option<DetachedCause> {
537 match self {
538 Self::Detached(value) => Some(value.cause()),
539 Self::Died(_) => None,
540 }
541 }
542
543 #[must_use]
545 pub const fn died_cause(self) -> Option<DiedCause> {
546 match self {
547 Self::Detached(_) => None,
548 Self::Died(value) => Some(value.cause()),
549 }
550 }
551
552 #[must_use]
554 pub const fn commit(self, delivery_seq: DeliverySeq) -> CommittedBindingTerminal {
555 match self {
556 Self::Detached(value) => CommittedBindingTerminal::Detached(value.commit(delivery_seq)),
557 Self::Died(value) => CommittedBindingTerminal::Died(value.commit(delivery_seq)),
558 }
559 }
560}
561
562impl From<PendingDetachedFinalization> for PendingFinalization {
563 fn from(value: PendingDetachedFinalization) -> Self {
564 Self::Detached(value)
565 }
566}
567
568impl From<PendingDiedFinalization> for PendingFinalization {
569 fn from(value: PendingDiedFinalization) -> Self {
570 Self::Died(value)
571 }
572}
573
574#[derive(Clone, Copy, Debug, PartialEq, Eq)]
576pub enum DetachedBindingTransition {
577 Committed(CommittedDetachedTerminal),
579 Pending(PendingDetachedFinalization),
581}
582
583impl DetachedBindingTransition {
584 #[must_use]
586 pub const fn binding_state(self) -> BindingState {
587 match self {
588 Self::Committed(_) => BindingState::Detached,
589 Self::Pending(value) => {
590 BindingState::PendingFinalization(PendingFinalization::Detached(value))
591 }
592 }
593 }
594}
595
596#[derive(Clone, Copy, Debug, PartialEq, Eq)]
598pub enum DiedBindingTransition {
599 Committed(CommittedDiedTerminal),
601 Pending(PendingDiedFinalization),
603}
604
605impl DiedBindingTransition {
606 #[must_use]
608 pub const fn observer_progress_projection(&self) -> Option<super::ObserverProgressProjection> {
609 let Self::Committed(terminal) = self else {
610 return None;
611 };
612 Some(super::ObserverProgressProjection::new(
613 terminal.conversation_id(),
614 terminal.delivery_seq(),
615 ))
616 }
617
618 #[must_use]
620 pub const fn binding_state(self) -> BindingState {
621 match self {
622 Self::Committed(_) => BindingState::Detached,
623 Self::Pending(value) => {
624 BindingState::PendingFinalization(PendingFinalization::Died(value))
625 }
626 }
627 }
628}
629
630impl ActiveBinding {
631 const fn finish_detached(
632 self,
633 cause: DetachedCause,
634 disposition: BindingTerminalDisposition,
635 ) -> DetachedBindingTransition {
636 match disposition {
637 BindingTerminalDisposition::Committed(position) => {
638 DetachedBindingTransition::Committed(CommittedDetachedTerminal {
639 identity: BindingTerminalIdentity::from_active(
640 self,
641 position.transaction_order,
642 ),
643 cause,
644 delivery_seq: position.delivery_seq,
645 })
646 }
647 BindingTerminalDisposition::Pending(position) => {
648 DetachedBindingTransition::Pending(PendingDetachedFinalization {
649 identity: BindingTerminalIdentity::from_active(
650 self,
651 position.transaction_order,
652 ),
653 cause,
654 })
655 }
656 }
657 }
658
659 const fn finish_died(
660 self,
661 cause: DiedCause,
662 disposition: BindingTerminalDisposition,
663 ) -> DiedBindingTransition {
664 match disposition {
665 BindingTerminalDisposition::Committed(position) => {
666 DiedBindingTransition::Committed(CommittedDiedTerminal {
667 identity: BindingTerminalIdentity::from_active(
668 self,
669 position.transaction_order,
670 ),
671 cause,
672 delivery_seq: position.delivery_seq,
673 })
674 }
675 BindingTerminalDisposition::Pending(position) => {
676 DiedBindingTransition::Pending(PendingDiedFinalization {
677 identity: BindingTerminalIdentity::from_active(
678 self,
679 position.transaction_order,
680 ),
681 cause,
682 })
683 }
684 }
685 }
686
687 #[must_use]
689 pub const fn clean_deregister(
690 self,
691 disposition: BindingTerminalDisposition,
692 ) -> DetachedBindingTransition {
693 self.finish_detached(DetachedCause::CleanDeregister, disposition)
694 }
695
696 #[must_use]
698 pub const fn commit_clean_deregister(
699 self,
700 position: CommittedBindingTerminalPosition,
701 ) -> CommittedDetachedTerminal {
702 CommittedDetachedTerminal {
703 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
704 cause: DetachedCause::CleanDeregister,
705 delivery_seq: position.delivery_seq,
706 }
707 }
708
709 #[must_use]
711 pub const fn pending_clean_deregister(
712 self,
713 position: PendingBindingTerminalPosition,
714 ) -> PendingDetachedFinalization {
715 PendingDetachedFinalization {
716 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
717 cause: DetachedCause::CleanDeregister,
718 }
719 }
720
721 #[must_use]
723 pub const fn clean_disconnect(
724 self,
725 disposition: BindingTerminalDisposition,
726 ) -> DetachedBindingTransition {
727 self.clean_deregister(disposition)
728 }
729
730 #[must_use]
736 pub const fn superseded(
737 self,
738 position: CommittedBindingTerminalPosition,
739 ) -> CommittedDetachedTerminal {
740 CommittedDetachedTerminal {
741 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
742 cause: DetachedCause::Superseded,
743 delivery_seq: position.delivery_seq,
744 }
745 }
746
747 #[must_use]
749 pub const fn server_shutdown(
750 self,
751 disposition: BindingTerminalDisposition,
752 ) -> DetachedBindingTransition {
753 self.finish_detached(DetachedCause::ServerShutdown, disposition)
754 }
755
756 #[must_use]
758 pub const fn connection_lost(
759 self,
760 disposition: BindingTerminalDisposition,
761 ) -> DiedBindingTransition {
762 self.finish_died(DiedCause::ConnectionLost, disposition)
763 }
764
765 #[must_use]
767 pub const fn process_killed(
768 self,
769 disposition: BindingTerminalDisposition,
770 ) -> DiedBindingTransition {
771 self.finish_died(DiedCause::ProcessKilled, disposition)
772 }
773
774 #[must_use]
776 pub const fn protocol_error(
777 self,
778 disposition: BindingTerminalDisposition,
779 ) -> DiedBindingTransition {
780 self.finish_died(DiedCause::ProtocolError, disposition)
781 }
782
783 #[must_use]
788 pub const fn unclean_server_restart(
789 self,
790 disposition: BindingTerminalDisposition,
791 ) -> DiedBindingTransition {
792 self.finish_died(
793 DiedCause::UncleanServerRestart {
794 prior_server_incarnation: self
795 .binding_epoch
796 .connection_incarnation
797 .server_incarnation,
798 },
799 disposition,
800 )
801 }
802}
803
804#[derive(Clone, Copy, Debug, PartialEq, Eq)]
806pub enum BindingState {
807 Detached,
809 Bound(ActiveBinding),
811 PendingFinalization(PendingFinalization),
813}
814
815pub(super) const fn restore_pending_finalization(
816 binding: ActiveBinding,
817 cause: CloseCause,
818 transaction_order: TransactionOrder,
819) -> Option<PendingFinalization> {
820 let position =
821 BindingTerminalDisposition::Pending(PendingBindingTerminalPosition::new(transaction_order));
822 match cause {
823 CloseCause::CleanDeregister => match binding.clean_deregister(position) {
824 DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
825 DetachedBindingTransition::Committed(_) => None,
826 },
827 CloseCause::ServerShutdown => match binding.server_shutdown(position) {
828 DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
829 DetachedBindingTransition::Committed(_) => None,
830 },
831 CloseCause::Superseded => None,
833 CloseCause::ConnectionLost => match binding.connection_lost(position) {
834 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
835 DiedBindingTransition::Committed(_) => None,
836 },
837 CloseCause::ProcessKilled => match binding.process_killed(position) {
838 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
839 DiedBindingTransition::Committed(_) => None,
840 },
841 CloseCause::ProtocolError => match binding.protocol_error(position) {
842 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
843 DiedBindingTransition::Committed(_) => None,
844 },
845 CloseCause::UncleanServerRestart {
846 prior_server_incarnation,
847 } => {
848 if binding
849 .binding_epoch
850 .connection_incarnation
851 .server_incarnation
852 != prior_server_incarnation
853 {
854 return None;
855 }
856 match binding.unclean_server_restart(position) {
857 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
858 DiedBindingTransition::Committed(_) => None,
859 }
860 }
861 }
862}
863
864pub(super) const fn restore_committed_terminal(
865 binding: ActiveBinding,
866 cause: CloseCause,
867 transaction_order: TransactionOrder,
868 delivery_seq: DeliverySeq,
869) -> Option<CommittedBindingTerminal> {
870 let position = BindingTerminalDisposition::Committed(CommittedBindingTerminalPosition::new(
871 transaction_order,
872 delivery_seq,
873 ));
874 match cause {
875 CloseCause::CleanDeregister => match binding.clean_deregister(position) {
876 DetachedBindingTransition::Committed(value) => {
877 Some(CommittedBindingTerminal::Detached(value))
878 }
879 DetachedBindingTransition::Pending(_) => None,
880 },
881 CloseCause::Superseded => Some(CommittedBindingTerminal::Detached(binding.superseded(
882 CommittedBindingTerminalPosition::new(transaction_order, delivery_seq),
883 ))),
884 CloseCause::ServerShutdown => match binding.server_shutdown(position) {
885 DetachedBindingTransition::Committed(value) => {
886 Some(CommittedBindingTerminal::Detached(value))
887 }
888 DetachedBindingTransition::Pending(_) => None,
889 },
890 CloseCause::ConnectionLost => match binding.connection_lost(position) {
891 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
892 DiedBindingTransition::Pending(_) => None,
893 },
894 CloseCause::ProcessKilled => match binding.process_killed(position) {
895 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
896 DiedBindingTransition::Pending(_) => None,
897 },
898 CloseCause::ProtocolError => match binding.protocol_error(position) {
899 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
900 DiedBindingTransition::Pending(_) => None,
901 },
902 CloseCause::UncleanServerRestart {
903 prior_server_incarnation,
904 } => {
905 if binding
906 .binding_epoch
907 .connection_incarnation
908 .server_incarnation
909 != prior_server_incarnation
910 {
911 return None;
912 }
913 match binding.unclean_server_restart(position) {
914 DiedBindingTransition::Committed(value) => {
915 Some(CommittedBindingTerminal::Died(value))
916 }
917 DiedBindingTransition::Pending(_) => None,
918 }
919 }
920 }
921}