Skip to main content

liminal_protocol/wire/authority/
lifecycle.rs

1//! Response authorities bound to `ClientRequest::Detach` (`0x0003`) and
2//! `ClientRequest::Leave` (`0x0005`).
3
4use super::super::{
5    AttemptTokenBodyConflict, BindingEpoch, BindingRequiredEnvelope, ClosureCheckedEnvelope,
6    ClosureRefusalReason, ClosureSnapshot, ConnectionConversationCapacityExceeded, ConversationId,
7    DetachCommitted, DetachEnvelope, DetachInProgress, DetachStaleAuthority, Generation,
8    LeaveAttemptToken, LeaveCommitted, LeaveEnvelope, LeaveStaleAuthority,
9    MarkerClosureCapacityExceeded, MarkerSettlementBackpressure, NoBinding, ObserverBackpressure,
10    ObserverBackpressureState, ParticipantId, ParticipantReferenceEnvelope, ParticipantUnknown,
11    ResponseEnvelope, Retired, ServerDiscriminant, ServerValue, SettlementEpoch, StaleAuthority,
12};
13
14use alloc::boxed::Box;
15
16/// Server response bound to one explicit detach request.
17///
18/// Constructors exist only for the outcomes the frozen R-D1 register admits
19/// for detach; every other pairing is a compile error by construction.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct DetachResponse {
22    value: ServerValue,
23}
24
25impl DetachResponse {
26    /// First decoded semantic operation for an untracked conversation
27    /// exceeded the connection-conversation limit (register row 5641).
28    #[must_use]
29    pub const fn connection_conversation_capacity_exceeded(
30        request: DetachEnvelope,
31        limit: u64,
32    ) -> Self {
33        Self {
34            value: ServerValue::ConnectionConversationCapacityExceeded(
35                ConnectionConversationCapacityExceeded::SemanticRequest {
36                    request: ResponseEnvelope::Detach(request),
37                    limit,
38                },
39            ),
40        }
41    }
42
43    /// Presented participant is unknown (register row 5645).
44    #[must_use]
45    pub const fn participant_unknown(request: DetachEnvelope) -> Self {
46        Self {
47            value: ServerValue::ParticipantUnknown(ParticipantUnknown {
48                request: ParticipantReferenceEnvelope::Detach(request),
49            }),
50        }
51    }
52
53    /// New detach with no Pending cell found no current binding (register
54    /// row 5646).
55    #[must_use]
56    pub const fn no_binding(request: DetachEnvelope) -> Self {
57        Self {
58            value: ServerValue::NoBinding(NoBinding {
59                request: BindingRequiredEnvelope::Detach(request),
60            }),
61        }
62    }
63
64    /// Detach-specific stale authority: live mismatch or a verified exact old
65    /// token resolved to a terminalized detach cell (register rows 5647,
66    /// 5671).
67    #[must_use]
68    pub const fn stale_authority(value: DetachStaleAuthority) -> Self {
69        Self {
70            value: ServerValue::StaleAuthority(StaleAuthority::Detach(value)),
71        }
72    }
73
74    /// Presented id has a tombstone after Leave (register rows 5648, 5672).
75    #[must_use]
76    pub const fn retired(request: DetachEnvelope, retired_generation: Generation) -> Self {
77        Self {
78            value: ServerValue::Retired(Retired::Participant {
79                request: ParticipantReferenceEnvelope::Detach(request),
80                retired_generation,
81            }),
82        }
83    }
84
85    /// Stable committed detach result (register row 5668).
86    #[must_use]
87    pub const fn detach_committed(value: DetachCommitted) -> Self {
88        Self {
89            value: ServerValue::DetachCommitted(value),
90        }
91    }
92
93    /// A different detach token encountered an existing Pending cell
94    /// (register row 5670).
95    #[must_use]
96    pub const fn detach_in_progress(value: DetachInProgress) -> Self {
97        Self {
98            value: ServerValue::DetachInProgress(value),
99        }
100    }
101
102    /// Detach append is blocked or an exact-token Pending replay returned its
103    /// current cell epoch (register rows 5669, 5673).
104    #[must_use]
105    pub const fn observer_backpressure(
106        request: DetachEnvelope,
107        committed_binding_epoch: BindingEpoch,
108        state: ObserverBackpressureState,
109    ) -> Self {
110        Self {
111            value: ServerValue::ObserverBackpressure(ObserverBackpressure::Detach {
112                request,
113                committed_binding_epoch,
114                state,
115            }),
116        }
117    }
118
119    /// A marker candidate is awaiting its drain (participant contract §0.16
120    /// condition 2, detach wrapper — amendment A5).
121    ///
122    /// Detach requires an existing attached binding, a membership predicate
123    /// strictly stronger than attach's, so this row carries the settlement
124    /// epoch and is paired with the `0x0202 MarkerSettled` wake. Answering the
125    /// condition with [`Self::observer_backpressure`] is OUTLAWED: it would
126    /// promise an `ObserverProgressed` that nothing sends.
127    #[must_use]
128    pub const fn marker_settlement_backpressure(
129        request: &DetachEnvelope,
130        refused_epoch: SettlementEpoch,
131    ) -> Self {
132        Self {
133            value: ServerValue::MarkerSettlementBackpressure(
134                MarkerSettlementBackpressure::Detach {
135                    conversation_id: request.conversation_id,
136                    refused_epoch,
137                },
138            ),
139        }
140    }
141
142    /// Borrows the bound wire value for encoding or inspection.
143    #[must_use]
144    pub const fn server_value(&self) -> &ServerValue {
145        &self.value
146    }
147
148    /// Returns the bound value's exact wire discriminant.
149    #[must_use]
150    pub const fn discriminant(&self) -> ServerDiscriminant {
151        self.value.discriminant()
152    }
153
154    /// Moves the bound wire value out for transmission.
155    #[must_use]
156    pub fn into_server_value(self) -> ServerValue {
157        self.value
158    }
159}
160
161/// Server response bound to one terminal Leave request.
162///
163/// Constructors exist only for the outcomes the frozen R-D1 register admits
164/// for Leave; every other pairing is a compile error by construction.
165#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct LeaveResponse {
167    value: ServerValue,
168}
169
170impl LeaveResponse {
171    /// Exact committed Leave token with verified secret but a changed
172    /// canonical non-secret body; Leave can select only the generation
173    /// conflict (register row 5639).
174    #[must_use]
175    pub const fn attempt_token_body_conflict(
176        token: LeaveAttemptToken,
177        conversation_id: ConversationId,
178        presented_participant_id: ParticipantId,
179        presented_generation: Generation,
180    ) -> Self {
181        Self {
182            value: ServerValue::AttemptTokenBodyConflict(AttemptTokenBodyConflict::Leave {
183                token,
184                conversation_id,
185                presented_participant_id,
186                presented_generation,
187            }),
188        }
189    }
190
191    /// First decoded semantic operation for an untracked conversation
192    /// exceeded the connection-conversation limit (register row 5641).
193    #[must_use]
194    pub const fn connection_conversation_capacity_exceeded(
195        request: LeaveEnvelope,
196        limit: u64,
197    ) -> Self {
198        Self {
199            value: ServerValue::ConnectionConversationCapacityExceeded(
200                ConnectionConversationCapacityExceeded::SemanticRequest {
201                    request: ResponseEnvelope::Leave(request),
202                    limit,
203                },
204            ),
205        }
206    }
207
208    /// Presented participant is unknown (register row 5645).
209    #[must_use]
210    pub const fn participant_unknown(request: LeaveEnvelope) -> Self {
211        Self {
212            value: ServerValue::ParticipantUnknown(ParticipantUnknown {
213                request: ParticipantReferenceEnvelope::Leave(request),
214            }),
215        }
216    }
217
218    /// Leave while a different live binding epoch exists (register row 5646).
219    #[must_use]
220    pub const fn no_binding(request: LeaveEnvelope) -> Self {
221        Self {
222            value: ServerValue::NoBinding(NoBinding {
223                request: BindingRequiredEnvelope::Leave(request),
224            }),
225        }
226    }
227
228    /// Leave-specific stale authority: live mismatch or the exact committed
229    /// Leave token with a wrong secret (register rows 5647, 5680).
230    #[must_use]
231    pub const fn stale_authority(value: LeaveStaleAuthority) -> Self {
232        Self {
233            value: ServerValue::StaleAuthority(StaleAuthority::Leave(value)),
234        }
235    }
236
237    /// Presented id has a tombstone under a different token (register rows
238    /// 5648, 5680).
239    #[must_use]
240    pub const fn retired(request: LeaveEnvelope, retired_generation: Generation) -> Self {
241        Self {
242            value: ServerValue::Retired(Retired::Participant {
243                request: ParticipantReferenceEnvelope::Leave(request),
244                retired_generation,
245            }),
246        }
247    }
248
249    /// Closure-checked Leave admission exceeded marker-closure capacity
250    /// (register row 5649).
251    #[must_use]
252    pub fn marker_closure_capacity_exceeded(
253        request: LeaveEnvelope,
254        snapshot: ClosureSnapshot,
255        reason: ClosureRefusalReason,
256    ) -> Self {
257        Self {
258            value: ServerValue::MarkerClosureCapacityExceeded(Box::new(
259                MarkerClosureCapacityExceeded {
260                    request: ClosureCheckedEnvelope::Leave(request),
261                    snapshot,
262                    reason,
263                },
264            )),
265        }
266    }
267
268    /// Terminal Leave success for the bound or detached exact-secret arms
269    /// (register rows 5678, 5679).
270    #[must_use]
271    pub const fn leave_committed(value: LeaveCommitted) -> Self {
272        Self {
273            value: ServerValue::LeaveCommitted(value),
274        }
275    }
276
277    /// Hard-observer retention refused the Leave append (register row 5681).
278    #[must_use]
279    pub const fn observer_backpressure(
280        request: LeaveEnvelope,
281        state: ObserverBackpressureState,
282        prior_terminal_cell_exists: bool,
283    ) -> Self {
284        Self {
285            value: ServerValue::ObserverBackpressure(ObserverBackpressure::Leave {
286                request,
287                state,
288                prior_terminal_cell_exists,
289            }),
290        }
291    }
292
293    /// Borrows the bound wire value for encoding or inspection.
294    #[must_use]
295    pub const fn server_value(&self) -> &ServerValue {
296        &self.value
297    }
298
299    /// Returns the bound value's exact wire discriminant.
300    #[must_use]
301    pub const fn discriminant(&self) -> ServerDiscriminant {
302        self.value.discriminant()
303    }
304
305    /// Moves the bound wire value out for transmission.
306    #[must_use]
307    pub fn into_server_value(self) -> ServerValue {
308        self.value
309    }
310}