use core::num::NonZeroU64;
use crate::wire::{
AttachEnvelope, CredentialAttachRequest, CredentialAttachResponse, EnrollmentEnvelope,
EnrollmentRequest, EnrollmentResponse, IdentityCapacityExceeded, IdentityCapacityScope,
ParticipantId,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CapacityCounterInvariantError {
ZeroLimit,
OccupiedExceedsLimit {
occupied: u64,
limit: u64,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CapacityCounter {
limit: NonZeroU64,
occupied: u64,
}
impl CapacityCounter {
pub const fn try_new(limit: u64, occupied: u64) -> Result<Self, CapacityCounterInvariantError> {
let Some(limit) = NonZeroU64::new(limit) else {
return Err(CapacityCounterInvariantError::ZeroLimit);
};
if occupied > limit.get() {
return Err(CapacityCounterInvariantError::OccupiedExceedsLimit {
occupied,
limit: limit.get(),
});
}
Ok(Self { limit, occupied })
}
#[must_use]
pub const fn limit(self) -> u64 {
self.limit.get()
}
#[must_use]
pub const fn occupied(self) -> u64 {
self.occupied
}
#[must_use]
pub const fn is_full(self) -> bool {
self.occupied == self.limit.get()
}
const fn incremented(self) -> Option<Self> {
if self.is_full() {
return None;
}
Some(Self {
limit: self.limit,
occupied: self.occupied + 1,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FreshParticipantCapacityCounterInvariantError {
Capacity(CapacityCounterInvariantError),
Nonempty {
occupied: u64,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FreshParticipantCapacityCounter {
counter: CapacityCounter,
}
impl FreshParticipantCapacityCounter {
pub const fn try_new(
limit: u64,
occupied: u64,
) -> Result<Self, FreshParticipantCapacityCounterInvariantError> {
let counter = match CapacityCounter::try_new(limit, occupied) {
Ok(counter) => counter,
Err(error) => {
return Err(FreshParticipantCapacityCounterInvariantError::Capacity(
error,
));
}
};
if occupied != 0 {
return Err(FreshParticipantCapacityCounterInvariantError::Nonempty { occupied });
}
Ok(Self { counter })
}
#[must_use]
pub const fn limit(self) -> u64 {
self.counter.limit()
}
#[must_use]
pub const fn occupied(self) -> u64 {
self.counter.occupied()
}
const fn reserved(self) -> CapacityCounter {
CapacityCounter {
limit: self.counter.limit,
occupied: 1,
}
}
const fn unfilled(self) -> CapacityCounter {
self.counter
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ParticipantWindowAdmission {
Landed,
Displaced,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ParticipantWindowCommit {
admission: ParticipantWindowAdmission,
resulting: CapacityCounter,
}
impl ParticipantWindowCommit {
#[must_use]
pub const fn admission(self) -> ParticipantWindowAdmission {
self.admission
}
#[must_use]
pub const fn displaced(self) -> bool {
matches!(self.admission, ParticipantWindowAdmission::Displaced)
}
#[must_use]
pub const fn resulting(self) -> CapacityCounter {
self.resulting
}
}
#[must_use]
pub const fn select_participant_window(current: CapacityCounter) -> ParticipantWindowCommit {
match current.incremented() {
Some(resulting) => ParticipantWindowCommit {
admission: ParticipantWindowAdmission::Landed,
resulting,
},
None => ParticipantWindowCommit {
admission: ParticipantWindowAdmission::Displaced,
resulting: current,
},
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConnectionConversationTracking {
AlreadyTracked,
Untracked,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ConnectionConversationCapacityCommit {
resulting: CapacityCounter,
newly_tracked: bool,
}
impl ConnectionConversationCapacityCommit {
#[must_use]
pub const fn resulting(self) -> CapacityCounter {
self.resulting
}
#[must_use]
pub const fn newly_tracked(self) -> bool {
self.newly_tracked
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SemanticConnectionCapacityDecision {
Commit(ConnectionConversationCapacityCommit),
Respond {
limit: u64,
},
}
#[must_use]
pub const fn select_semantic_connection_capacity(
tracking: ConnectionConversationTracking,
current: CapacityCounter,
) -> SemanticConnectionCapacityDecision {
match tracking {
ConnectionConversationTracking::AlreadyTracked => {
SemanticConnectionCapacityDecision::Commit(ConnectionConversationCapacityCommit {
resulting: current,
newly_tracked: false,
})
}
ConnectionConversationTracking::Untracked => {
let Some(resulting) = current.incremented() else {
return SemanticConnectionCapacityDecision::Respond {
limit: current.limit(),
};
};
SemanticConnectionCapacityDecision::Commit(ConnectionConversationCapacityCommit {
resulting,
newly_tracked: true,
})
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BindingSlotOccupancy {
Empty,
Occupied {
participant_id: ParticipantId,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BindingSlotDecision<R> {
Available,
Respond(R),
}
#[must_use]
pub const fn select_enrollment_binding_slot(
request: &EnrollmentRequest,
occupancy: BindingSlotOccupancy,
) -> BindingSlotDecision<EnrollmentResponse> {
match occupancy {
BindingSlotOccupancy::Empty => BindingSlotDecision::Available,
BindingSlotOccupancy::Occupied { .. } => BindingSlotDecision::Respond(
EnrollmentResponse::connection_conversation_binding_occupied(&enrollment_envelope(
request,
)),
),
}
}
#[must_use]
pub const fn select_credential_attach_binding_slot(
request: &CredentialAttachRequest,
occupancy: BindingSlotOccupancy,
) -> BindingSlotDecision<CredentialAttachResponse> {
match occupancy {
BindingSlotOccupancy::Empty => BindingSlotDecision::Available,
BindingSlotOccupancy::Occupied { participant_id }
if participant_id == request.participant_id =>
{
BindingSlotDecision::Available
}
BindingSlotOccupancy::Occupied { .. } => BindingSlotDecision::Respond(
CredentialAttachResponse::connection_conversation_binding_occupied(&attach_envelope(
request,
)),
),
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EnrollmentCapacityCounters {
identity_server: CapacityCounter,
identity_conversation: CapacityCounter,
live_receipt_participant: FreshParticipantCapacityCounter,
provenance_participant: FreshParticipantCapacityCounter,
}
impl EnrollmentCapacityCounters {
#[must_use]
pub const fn new(
identity_server: CapacityCounter,
identity_conversation: CapacityCounter,
live_receipt_participant: FreshParticipantCapacityCounter,
provenance_participant: FreshParticipantCapacityCounter,
) -> Self {
Self {
identity_server,
identity_conversation,
live_receipt_participant,
provenance_participant,
}
}
#[must_use]
pub const fn identity_server(self) -> CapacityCounter {
self.identity_server
}
#[must_use]
pub const fn identity_conversation(self) -> CapacityCounter {
self.identity_conversation
}
#[must_use]
pub const fn live_receipt_participant(self) -> FreshParticipantCapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_participant(self) -> FreshParticipantCapacityCounter {
self.provenance_participant
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ResultingEnrollmentCapacityCounters {
identity_server: CapacityCounter,
identity_conversation: CapacityCounter,
live_receipt_participant: CapacityCounter,
provenance_participant: CapacityCounter,
}
impl ResultingEnrollmentCapacityCounters {
#[must_use]
pub const fn identity_server(self) -> CapacityCounter {
self.identity_server
}
#[must_use]
pub const fn identity_conversation(self) -> CapacityCounter {
self.identity_conversation
}
#[must_use]
pub const fn live_receipt_participant(self) -> CapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_participant(self) -> CapacityCounter {
self.provenance_participant
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EnrollmentCapacityCommit {
resulting: ResultingEnrollmentCapacityCounters,
}
impl EnrollmentCapacityCommit {
#[must_use]
pub const fn resulting(self) -> ResultingEnrollmentCapacityCounters {
self.resulting
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EnrollmentCapacityDecision {
Commit(EnrollmentCapacityCommit),
Respond(EnrollmentResponse),
}
#[must_use]
pub const fn select_enrollment_capacity(
request: &EnrollmentRequest,
current: EnrollmentCapacityCounters,
) -> EnrollmentCapacityDecision {
let Some(identity_server) = current.identity_server.incremented() else {
return enrollment_identity_refusal(
request,
IdentityCapacityScope::Server,
current.identity_server,
);
};
let Some(identity_conversation) = current.identity_conversation.incremented() else {
return enrollment_identity_refusal(
request,
IdentityCapacityScope::Conversation,
current.identity_conversation,
);
};
EnrollmentCapacityDecision::Commit(EnrollmentCapacityCommit {
resulting: ResultingEnrollmentCapacityCounters {
identity_server,
identity_conversation,
live_receipt_participant: current.live_receipt_participant.reserved(),
provenance_participant: current.provenance_participant.unfilled(),
},
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CredentialAttachCapacityCounters {
live_receipt_participant: CapacityCounter,
provenance_participant: CapacityCounter,
}
impl CredentialAttachCapacityCounters {
#[must_use]
pub const fn new(
live_receipt_participant: CapacityCounter,
provenance_participant: CapacityCounter,
) -> Self {
Self {
live_receipt_participant,
provenance_participant,
}
}
#[must_use]
pub const fn live_receipt_participant(self) -> CapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_participant(self) -> CapacityCounter {
self.provenance_participant
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CredentialAttachCapacityCommit {
live_receipt_participant: ParticipantWindowCommit,
provenance_participant: ParticipantWindowCommit,
}
impl CredentialAttachCapacityCommit {
#[must_use]
pub const fn live_receipt_participant(self) -> ParticipantWindowCommit {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_participant(self) -> ParticipantWindowCommit {
self.provenance_participant
}
}
#[must_use]
pub const fn select_credential_attach_capacity(
current: CredentialAttachCapacityCounters,
) -> CredentialAttachCapacityCommit {
CredentialAttachCapacityCommit {
live_receipt_participant: select_participant_window(current.live_receipt_participant),
provenance_participant: select_participant_window(current.provenance_participant),
}
}
const fn enrollment_identity_refusal(
request: &EnrollmentRequest,
scope: IdentityCapacityScope,
counter: CapacityCounter,
) -> EnrollmentCapacityDecision {
EnrollmentCapacityDecision::Respond(EnrollmentResponse::identity_capacity_exceeded(
IdentityCapacityExceeded {
request: enrollment_envelope(request),
scope,
limit: counter.limit(),
occupied: counter.occupied(),
},
))
}
const fn enrollment_envelope(request: &EnrollmentRequest) -> EnrollmentEnvelope {
EnrollmentEnvelope {
conversation_id: request.conversation_id,
enrollment_token: request.enrollment_token,
}
}
const fn attach_envelope(request: &CredentialAttachRequest) -> AttachEnvelope {
AttachEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
attach_attempt_token: request.attach_attempt_token,
accept_marker_delivery_seq: request.accept_marker_delivery_seq,
}
}