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 observer_progress_projection(&self) -> Option<super::ObserverProgressProjection> {
587 let Self::Committed(terminal) = self else {
588 return None;
589 };
590 Some(super::ObserverProgressProjection::new(
591 terminal.conversation_id(),
592 terminal.delivery_seq(),
593 ))
594 }
595
596 #[must_use]
598 pub const fn binding_state(self) -> BindingState {
599 match self {
600 Self::Committed(_) => BindingState::Detached,
601 Self::Pending(value) => {
602 BindingState::PendingFinalization(PendingFinalization::Detached(value))
603 }
604 }
605 }
606}
607
608#[derive(Clone, Copy, Debug, PartialEq, Eq)]
610pub enum DiedBindingTransition {
611 Committed(CommittedDiedTerminal),
613 Pending(PendingDiedFinalization),
615}
616
617impl DiedBindingTransition {
618 #[must_use]
620 pub const fn observer_progress_projection(&self) -> Option<super::ObserverProgressProjection> {
621 let Self::Committed(terminal) = self else {
622 return None;
623 };
624 Some(super::ObserverProgressProjection::new(
625 terminal.conversation_id(),
626 terminal.delivery_seq(),
627 ))
628 }
629
630 #[must_use]
632 pub const fn binding_state(self) -> BindingState {
633 match self {
634 Self::Committed(_) => BindingState::Detached,
635 Self::Pending(value) => {
636 BindingState::PendingFinalization(PendingFinalization::Died(value))
637 }
638 }
639 }
640}
641
642impl ActiveBinding {
643 const fn finish_detached(
644 self,
645 cause: DetachedCause,
646 disposition: BindingTerminalDisposition,
647 ) -> DetachedBindingTransition {
648 match disposition {
649 BindingTerminalDisposition::Committed(position) => {
650 DetachedBindingTransition::Committed(CommittedDetachedTerminal {
651 identity: BindingTerminalIdentity::from_active(
652 self,
653 position.transaction_order,
654 ),
655 cause,
656 delivery_seq: position.delivery_seq,
657 })
658 }
659 BindingTerminalDisposition::Pending(position) => {
660 DetachedBindingTransition::Pending(PendingDetachedFinalization {
661 identity: BindingTerminalIdentity::from_active(
662 self,
663 position.transaction_order,
664 ),
665 cause,
666 })
667 }
668 }
669 }
670
671 const fn finish_died(
672 self,
673 cause: DiedCause,
674 disposition: BindingTerminalDisposition,
675 ) -> DiedBindingTransition {
676 match disposition {
677 BindingTerminalDisposition::Committed(position) => {
678 DiedBindingTransition::Committed(CommittedDiedTerminal {
679 identity: BindingTerminalIdentity::from_active(
680 self,
681 position.transaction_order,
682 ),
683 cause,
684 delivery_seq: position.delivery_seq,
685 })
686 }
687 BindingTerminalDisposition::Pending(position) => {
688 DiedBindingTransition::Pending(PendingDiedFinalization {
689 identity: BindingTerminalIdentity::from_active(
690 self,
691 position.transaction_order,
692 ),
693 cause,
694 })
695 }
696 }
697 }
698
699 #[must_use]
701 pub const fn clean_deregister(
702 self,
703 disposition: BindingTerminalDisposition,
704 ) -> DetachedBindingTransition {
705 self.finish_detached(DetachedCause::CleanDeregister, disposition)
706 }
707
708 #[must_use]
710 pub const fn commit_clean_deregister(
711 self,
712 position: CommittedBindingTerminalPosition,
713 ) -> CommittedDetachedTerminal {
714 CommittedDetachedTerminal {
715 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
716 cause: DetachedCause::CleanDeregister,
717 delivery_seq: position.delivery_seq,
718 }
719 }
720
721 #[must_use]
723 pub const fn pending_clean_deregister(
724 self,
725 position: PendingBindingTerminalPosition,
726 ) -> PendingDetachedFinalization {
727 PendingDetachedFinalization {
728 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
729 cause: DetachedCause::CleanDeregister,
730 }
731 }
732
733 #[must_use]
735 pub const fn clean_disconnect(
736 self,
737 disposition: BindingTerminalDisposition,
738 ) -> DetachedBindingTransition {
739 self.clean_deregister(disposition)
740 }
741
742 #[must_use]
748 pub const fn superseded(
749 self,
750 position: CommittedBindingTerminalPosition,
751 ) -> CommittedDetachedTerminal {
752 CommittedDetachedTerminal {
753 identity: BindingTerminalIdentity::from_active(self, position.transaction_order),
754 cause: DetachedCause::Superseded,
755 delivery_seq: position.delivery_seq,
756 }
757 }
758
759 #[must_use]
761 pub const fn server_shutdown(
762 self,
763 disposition: BindingTerminalDisposition,
764 ) -> DetachedBindingTransition {
765 self.finish_detached(DetachedCause::ServerShutdown, disposition)
766 }
767
768 #[must_use]
770 pub const fn connection_lost(
771 self,
772 disposition: BindingTerminalDisposition,
773 ) -> DiedBindingTransition {
774 self.finish_died(DiedCause::ConnectionLost, disposition)
775 }
776
777 #[must_use]
779 pub const fn process_killed(
780 self,
781 disposition: BindingTerminalDisposition,
782 ) -> DiedBindingTransition {
783 self.finish_died(DiedCause::ProcessKilled, disposition)
784 }
785
786 #[must_use]
788 pub const fn protocol_error(
789 self,
790 disposition: BindingTerminalDisposition,
791 ) -> DiedBindingTransition {
792 self.finish_died(DiedCause::ProtocolError, disposition)
793 }
794
795 #[must_use]
800 pub const fn unclean_server_restart(
801 self,
802 disposition: BindingTerminalDisposition,
803 ) -> DiedBindingTransition {
804 self.finish_died(
805 DiedCause::UncleanServerRestart {
806 prior_server_incarnation: self
807 .binding_epoch
808 .connection_incarnation
809 .server_incarnation,
810 },
811 disposition,
812 )
813 }
814}
815
816#[derive(Clone, Copy, Debug, PartialEq, Eq)]
818pub enum BindingState {
819 Detached,
821 Bound(ActiveBinding),
823 PendingFinalization(PendingFinalization),
825}
826
827pub(super) const fn restore_pending_finalization(
828 binding: ActiveBinding,
829 cause: CloseCause,
830 transaction_order: TransactionOrder,
831) -> Option<PendingFinalization> {
832 let position =
833 BindingTerminalDisposition::Pending(PendingBindingTerminalPosition::new(transaction_order));
834 match cause {
835 CloseCause::CleanDeregister => match binding.clean_deregister(position) {
836 DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
837 DetachedBindingTransition::Committed(_) => None,
838 },
839 CloseCause::ServerShutdown => match binding.server_shutdown(position) {
840 DetachedBindingTransition::Pending(value) => Some(PendingFinalization::Detached(value)),
841 DetachedBindingTransition::Committed(_) => None,
842 },
843 CloseCause::Superseded => None,
845 CloseCause::ConnectionLost => match binding.connection_lost(position) {
846 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
847 DiedBindingTransition::Committed(_) => None,
848 },
849 CloseCause::ProcessKilled => match binding.process_killed(position) {
850 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
851 DiedBindingTransition::Committed(_) => None,
852 },
853 CloseCause::ProtocolError => match binding.protocol_error(position) {
854 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
855 DiedBindingTransition::Committed(_) => None,
856 },
857 CloseCause::UncleanServerRestart {
858 prior_server_incarnation,
859 } => {
860 if binding
861 .binding_epoch
862 .connection_incarnation
863 .server_incarnation
864 != prior_server_incarnation
865 {
866 return None;
867 }
868 match binding.unclean_server_restart(position) {
869 DiedBindingTransition::Pending(value) => Some(PendingFinalization::Died(value)),
870 DiedBindingTransition::Committed(_) => None,
871 }
872 }
873 }
874}
875
876pub(super) const fn restore_committed_terminal(
877 binding: ActiveBinding,
878 cause: CloseCause,
879 transaction_order: TransactionOrder,
880 delivery_seq: DeliverySeq,
881) -> Option<CommittedBindingTerminal> {
882 let position = BindingTerminalDisposition::Committed(CommittedBindingTerminalPosition::new(
883 transaction_order,
884 delivery_seq,
885 ));
886 match cause {
887 CloseCause::CleanDeregister => match binding.clean_deregister(position) {
888 DetachedBindingTransition::Committed(value) => {
889 Some(CommittedBindingTerminal::Detached(value))
890 }
891 DetachedBindingTransition::Pending(_) => None,
892 },
893 CloseCause::Superseded => Some(CommittedBindingTerminal::Detached(binding.superseded(
894 CommittedBindingTerminalPosition::new(transaction_order, delivery_seq),
895 ))),
896 CloseCause::ServerShutdown => match binding.server_shutdown(position) {
897 DetachedBindingTransition::Committed(value) => {
898 Some(CommittedBindingTerminal::Detached(value))
899 }
900 DetachedBindingTransition::Pending(_) => None,
901 },
902 CloseCause::ConnectionLost => match binding.connection_lost(position) {
903 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
904 DiedBindingTransition::Pending(_) => None,
905 },
906 CloseCause::ProcessKilled => match binding.process_killed(position) {
907 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
908 DiedBindingTransition::Pending(_) => None,
909 },
910 CloseCause::ProtocolError => match binding.protocol_error(position) {
911 DiedBindingTransition::Committed(value) => Some(CommittedBindingTerminal::Died(value)),
912 DiedBindingTransition::Pending(_) => None,
913 },
914 CloseCause::UncleanServerRestart {
915 prior_server_incarnation,
916 } => {
917 if binding
918 .binding_epoch
919 .connection_incarnation
920 .server_incarnation
921 != prior_server_incarnation
922 {
923 return None;
924 }
925 match binding.unclean_server_restart(position) {
926 DiedBindingTransition::Committed(value) => {
927 Some(CommittedBindingTerminal::Died(value))
928 }
929 DiedBindingTransition::Pending(_) => None,
930 }
931 }
932 }
933}