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, NoBinding, ObserverBackpressure, ObserverBackpressureState,
10    ParticipantId, ParticipantReferenceEnvelope, ParticipantUnknown, ResponseEnvelope, Retired,
11    ServerDiscriminant, ServerValue, 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    /// Borrows the bound wire value for encoding or inspection.
120    #[must_use]
121    pub const fn server_value(&self) -> &ServerValue {
122        &self.value
123    }
124
125    /// Returns the bound value's exact wire discriminant.
126    #[must_use]
127    pub const fn discriminant(&self) -> ServerDiscriminant {
128        self.value.discriminant()
129    }
130
131    /// Moves the bound wire value out for transmission.
132    #[must_use]
133    pub fn into_server_value(self) -> ServerValue {
134        self.value
135    }
136}
137
138/// Server response bound to one terminal Leave request.
139///
140/// Constructors exist only for the outcomes the frozen R-D1 register admits
141/// for Leave; every other pairing is a compile error by construction.
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct LeaveResponse {
144    value: ServerValue,
145}
146
147impl LeaveResponse {
148    /// Exact committed Leave token with verified secret but a changed
149    /// canonical non-secret body; Leave can select only the generation
150    /// conflict (register row 5639).
151    #[must_use]
152    pub const fn attempt_token_body_conflict(
153        token: LeaveAttemptToken,
154        conversation_id: ConversationId,
155        presented_participant_id: ParticipantId,
156        presented_generation: Generation,
157    ) -> Self {
158        Self {
159            value: ServerValue::AttemptTokenBodyConflict(AttemptTokenBodyConflict::Leave {
160                token,
161                conversation_id,
162                presented_participant_id,
163                presented_generation,
164            }),
165        }
166    }
167
168    /// First decoded semantic operation for an untracked conversation
169    /// exceeded the connection-conversation limit (register row 5641).
170    #[must_use]
171    pub const fn connection_conversation_capacity_exceeded(
172        request: LeaveEnvelope,
173        limit: u64,
174    ) -> Self {
175        Self {
176            value: ServerValue::ConnectionConversationCapacityExceeded(
177                ConnectionConversationCapacityExceeded::SemanticRequest {
178                    request: ResponseEnvelope::Leave(request),
179                    limit,
180                },
181            ),
182        }
183    }
184
185    /// Presented participant is unknown (register row 5645).
186    #[must_use]
187    pub const fn participant_unknown(request: LeaveEnvelope) -> Self {
188        Self {
189            value: ServerValue::ParticipantUnknown(ParticipantUnknown {
190                request: ParticipantReferenceEnvelope::Leave(request),
191            }),
192        }
193    }
194
195    /// Leave while a different live binding epoch exists (register row 5646).
196    #[must_use]
197    pub const fn no_binding(request: LeaveEnvelope) -> Self {
198        Self {
199            value: ServerValue::NoBinding(NoBinding {
200                request: BindingRequiredEnvelope::Leave(request),
201            }),
202        }
203    }
204
205    /// Leave-specific stale authority: live mismatch or the exact committed
206    /// Leave token with a wrong secret (register rows 5647, 5680).
207    #[must_use]
208    pub const fn stale_authority(value: LeaveStaleAuthority) -> Self {
209        Self {
210            value: ServerValue::StaleAuthority(StaleAuthority::Leave(value)),
211        }
212    }
213
214    /// Presented id has a tombstone under a different token (register rows
215    /// 5648, 5680).
216    #[must_use]
217    pub const fn retired(request: LeaveEnvelope, retired_generation: Generation) -> Self {
218        Self {
219            value: ServerValue::Retired(Retired::Participant {
220                request: ParticipantReferenceEnvelope::Leave(request),
221                retired_generation,
222            }),
223        }
224    }
225
226    /// Closure-checked Leave admission exceeded marker-closure capacity
227    /// (register row 5649).
228    #[must_use]
229    pub fn marker_closure_capacity_exceeded(
230        request: LeaveEnvelope,
231        snapshot: ClosureSnapshot,
232        reason: ClosureRefusalReason,
233    ) -> Self {
234        Self {
235            value: ServerValue::MarkerClosureCapacityExceeded(Box::new(
236                MarkerClosureCapacityExceeded {
237                    request: ClosureCheckedEnvelope::Leave(request),
238                    snapshot,
239                    reason,
240                },
241            )),
242        }
243    }
244
245    /// Terminal Leave success for the bound or detached exact-secret arms
246    /// (register rows 5678, 5679).
247    #[must_use]
248    pub const fn leave_committed(value: LeaveCommitted) -> Self {
249        Self {
250            value: ServerValue::LeaveCommitted(value),
251        }
252    }
253
254    /// Hard-observer retention refused the Leave append (register row 5681).
255    #[must_use]
256    pub const fn observer_backpressure(
257        request: LeaveEnvelope,
258        state: ObserverBackpressureState,
259        prior_terminal_cell_exists: bool,
260    ) -> Self {
261        Self {
262            value: ServerValue::ObserverBackpressure(ObserverBackpressure::Leave {
263                request,
264                state,
265                prior_terminal_cell_exists,
266            }),
267        }
268    }
269
270    /// Borrows the bound wire value for encoding or inspection.
271    #[must_use]
272    pub const fn server_value(&self) -> &ServerValue {
273        &self.value
274    }
275
276    /// Returns the bound value's exact wire discriminant.
277    #[must_use]
278    pub const fn discriminant(&self) -> ServerDiscriminant {
279        self.value.discriminant()
280    }
281
282    /// Moves the bound wire value out for transmission.
283    #[must_use]
284    pub fn into_server_value(self) -> ServerValue {
285        self.value
286    }
287}