liminal_server/auth_membership/
refusal.rs1use super::{
2 AtomicU64, CONTRACT_UNSUPPORTED, CONTRACT_VERSION, CURSOR_FOREIGN, HotAction, LOG_MISMATCH,
3 Mode, Ordering, ServerError,
4};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum MembershipAction {
10 Read,
13 Post,
15}
16
17impl MembershipAction {
18 #[must_use]
20 pub const fn as_str(self) -> &'static str {
21 match self {
22 Self::Read => "read",
23 Self::Post => "post",
24 }
25 }
26
27 #[must_use]
32 pub const fn for_hot(action: HotAction) -> Self {
33 match action {
34 HotAction::Publish | HotAction::Call => Self::Post,
35 HotAction::Subscribe | HotAction::Serve => Self::Read,
36 }
37 }
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
46pub enum MembershipRefusal {
47 HolderUnverified,
49 HolderKindUnknown,
51 HolderMalformed,
54 AuthorityMissing,
57 NotReady,
59 Unavailable {
61 cause: String,
63 },
64 Gap {
66 after: u64,
68 missing: u64,
70 },
71 Reset,
73 ResumePending,
75 ChangeApplying,
77 LogMismatch,
79 ContractUnsupported {
81 contract: u32,
83 },
84 AnswerSubstituted,
87 RevisionStale {
89 required: u64,
91 decided: Option<u64>,
93 },
94 ScopeOutsideMapping,
96 Refused {
98 refusal: String,
100 },
101 Held {
103 mode: Mode,
105 },
106 CapacityExceeded {
108 limit: usize,
110 },
111 IndexPoisoned,
113 WorkspaceNotMapped,
115 ChannelNotMapped,
117 CursorForeign,
119 CursorStale,
121 PageOverBound {
123 rows: u64,
125 bytes: u64,
127 },
128 PageForeignRow,
130 PageDuplicateRow,
132 PageInconsistent,
134}
135
136impl MembershipRefusal {
137 #[must_use]
142 pub const fn holds(&self) -> bool {
143 matches!(
144 self,
145 Self::AuthorityMissing
146 | Self::NotReady
147 | Self::Unavailable { .. }
148 | Self::Gap { .. }
149 | Self::Reset
150 | Self::ResumePending
151 | Self::ChangeApplying
152 | Self::IndexPoisoned
153 )
154 }
155
156 #[must_use]
158 pub fn name(&self) -> &str {
159 match self {
160 Self::HolderUnverified => "membership_holder_unverified",
161 Self::HolderKindUnknown => "membership_holder_kind_unknown",
162 Self::HolderMalformed => "membership_holder_malformed",
163 Self::AuthorityMissing => "membership_authority_missing",
164 Self::NotReady => "membership_not_ready",
165 Self::Unavailable { .. } => "membership_unavailable",
166 Self::Gap { .. } => "membership_gap",
167 Self::Reset => "membership_log_reset",
168 Self::ResumePending => "membership_resume_pending",
169 Self::ChangeApplying => "membership_change_applying",
170 Self::LogMismatch => LOG_MISMATCH,
171 Self::ContractUnsupported { .. } => CONTRACT_UNSUPPORTED,
172 Self::AnswerSubstituted => "membership_answer_substituted",
173 Self::RevisionStale { .. } => "membership_revision_stale",
174 Self::ScopeOutsideMapping => "membership_scope_outside_mapping",
175 Self::Refused { refusal } => refusal,
176 Self::Held { .. } => "membership_held",
177 Self::CapacityExceeded { .. } => "membership_capacity_exceeded",
178 Self::IndexPoisoned => "membership_index_poisoned",
179 Self::WorkspaceNotMapped => "membership_workspace_not_mapped",
180 Self::ChannelNotMapped => "membership_channel_not_mapped",
181 Self::CursorForeign => CURSOR_FOREIGN,
182 Self::CursorStale => "membership_cursor_stale",
183 Self::PageOverBound { .. } => "membership_page_over_bound",
184 Self::PageForeignRow => "membership_page_foreign_row",
185 Self::PageDuplicateRow => "membership_page_duplicate_row",
186 Self::PageInconsistent => "membership_page_inconsistent",
187 }
188 }
189}
190
191impl std::fmt::Display for MembershipRefusal {
192 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 match self {
194 Self::Unavailable { cause } => write!(formatter, "{}: {cause}", self.name()),
195 Self::Gap { after, missing } => write!(
196 formatter,
197 "{}: {missing} revisions missing after {after}",
198 self.name()
199 ),
200 Self::ContractUnsupported { contract } => write!(
201 formatter,
202 "{}: contract {contract}; this liminal speaks {CONTRACT_VERSION}",
203 self.name()
204 ),
205 Self::RevisionStale { required, decided } => write!(
206 formatter,
207 "{}: required {required}, decided {decided:?}",
208 self.name()
209 ),
210 Self::Held { mode } => write!(formatter, "{}: {mode:?}", self.name()),
211 Self::CapacityExceeded { limit } => {
212 write!(formatter, "{}: limit {limit}", self.name())
213 }
214 Self::PageOverBound { rows, bytes } => {
215 write!(formatter, "{}: {rows} rows, {bytes} bytes", self.name())
216 }
217 _ => formatter.write_str(self.name()),
218 }
219 }
220}
221
222impl std::error::Error for MembershipRefusal {}
223
224pub(super) fn malformed(value: &str) -> bool {
225 value.is_empty() || value.chars().any(char::is_control)
226}
227
228pub(super) fn invalid(message: impl Into<String>) -> ServerError {
229 ServerError::ConfigValidation {
230 message: message.into(),
231 }
232}
233
234pub(super) fn bump(counter: &AtomicU64) {
235 counter.fetch_add(1, Ordering::Relaxed);
236}
237
238pub(super) fn bump_by(counter: &AtomicU64, count: u64) {
239 counter.fetch_add(count, Ordering::Relaxed);
240}