use core::num::NonZeroU64;
use crate::wire::{
AttachEnvelope, CredentialAttachRequest, CredentialAttachResponse, EnrollmentEnvelope,
EnrollmentReceiptCapacityScope, EnrollmentRequest, EnrollmentResponse,
IdentityCapacityExceeded, IdentityCapacityScope, ParticipantId, ReceiptCapacityScope,
};
#[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,
}
}
}
#[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_server: CapacityCounter,
live_receipt_participant: FreshParticipantCapacityCounter,
provenance_server: CapacityCounter,
provenance_conversation: CapacityCounter,
provenance_participant: FreshParticipantCapacityCounter,
}
impl EnrollmentCapacityCounters {
#[must_use]
pub const fn new(
identity_server: CapacityCounter,
identity_conversation: CapacityCounter,
live_receipt_server: CapacityCounter,
live_receipt_participant: FreshParticipantCapacityCounter,
provenance_server: CapacityCounter,
provenance_conversation: CapacityCounter,
provenance_participant: FreshParticipantCapacityCounter,
) -> Self {
Self {
identity_server,
identity_conversation,
live_receipt_server,
live_receipt_participant,
provenance_server,
provenance_conversation,
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_server(self) -> CapacityCounter {
self.live_receipt_server
}
#[must_use]
pub const fn live_receipt_participant(self) -> FreshParticipantCapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_server(self) -> CapacityCounter {
self.provenance_server
}
#[must_use]
pub const fn provenance_conversation(self) -> CapacityCounter {
self.provenance_conversation
}
#[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_server: CapacityCounter,
live_receipt_participant: CapacityCounter,
provenance_server: CapacityCounter,
provenance_conversation: 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_server(self) -> CapacityCounter {
self.live_receipt_server
}
#[must_use]
pub const fn live_receipt_participant(self) -> CapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_server(self) -> CapacityCounter {
self.provenance_server
}
#[must_use]
pub const fn provenance_conversation(self) -> CapacityCounter {
self.provenance_conversation
}
#[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,
);
};
let Some(live_receipt_server) = current.live_receipt_server.incremented() else {
return enrollment_receipt_refusal(
request,
EnrollmentReceiptCapacityScope::LiveReceiptServer,
current.live_receipt_server,
);
};
let Some(provenance_server) = current.provenance_server.incremented() else {
return enrollment_receipt_refusal(
request,
EnrollmentReceiptCapacityScope::ProvenanceServer,
current.provenance_server,
);
};
let Some(provenance_conversation) = current.provenance_conversation.incremented() else {
return enrollment_receipt_refusal(
request,
EnrollmentReceiptCapacityScope::ProvenanceConversation,
current.provenance_conversation,
);
};
EnrollmentCapacityDecision::Commit(EnrollmentCapacityCommit {
resulting: ResultingEnrollmentCapacityCounters {
identity_server,
identity_conversation,
live_receipt_server,
live_receipt_participant: current.live_receipt_participant.reserved(),
provenance_server,
provenance_conversation,
provenance_participant: current.provenance_participant.reserved(),
},
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CredentialAttachCapacityCounters {
live_receipt_server: CapacityCounter,
live_receipt_participant: CapacityCounter,
provenance_server: CapacityCounter,
provenance_conversation: CapacityCounter,
provenance_participant: CapacityCounter,
}
impl CredentialAttachCapacityCounters {
#[must_use]
pub const fn new(
live_receipt_server: CapacityCounter,
live_receipt_participant: CapacityCounter,
provenance_server: CapacityCounter,
provenance_conversation: CapacityCounter,
provenance_participant: CapacityCounter,
) -> Self {
Self {
live_receipt_server,
live_receipt_participant,
provenance_server,
provenance_conversation,
provenance_participant,
}
}
#[must_use]
pub const fn live_receipt_server(self) -> CapacityCounter {
self.live_receipt_server
}
#[must_use]
pub const fn live_receipt_participant(self) -> CapacityCounter {
self.live_receipt_participant
}
#[must_use]
pub const fn provenance_server(self) -> CapacityCounter {
self.provenance_server
}
#[must_use]
pub const fn provenance_conversation(self) -> CapacityCounter {
self.provenance_conversation
}
#[must_use]
pub const fn provenance_participant(self) -> CapacityCounter {
self.provenance_participant
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CredentialAttachCapacityCommit {
resulting: CredentialAttachCapacityCounters,
}
impl CredentialAttachCapacityCommit {
#[must_use]
pub const fn resulting(self) -> CredentialAttachCapacityCounters {
self.resulting
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CredentialAttachCapacityDecision {
Commit(CredentialAttachCapacityCommit),
Respond(CredentialAttachResponse),
}
#[must_use]
pub const fn select_credential_attach_capacity(
request: &CredentialAttachRequest,
current: CredentialAttachCapacityCounters,
) -> CredentialAttachCapacityDecision {
let Some(live_receipt_server) = current.live_receipt_server.incremented() else {
return credential_attach_receipt_refusal(
request,
ReceiptCapacityScope::LiveReceiptServer,
current.live_receipt_server,
);
};
let Some(live_receipt_participant) = current.live_receipt_participant.incremented() else {
return credential_attach_receipt_refusal(
request,
ReceiptCapacityScope::LiveReceiptParticipant,
current.live_receipt_participant,
);
};
let Some(provenance_server) = current.provenance_server.incremented() else {
return credential_attach_receipt_refusal(
request,
ReceiptCapacityScope::ProvenanceServer,
current.provenance_server,
);
};
let Some(provenance_conversation) = current.provenance_conversation.incremented() else {
return credential_attach_receipt_refusal(
request,
ReceiptCapacityScope::ProvenanceConversation,
current.provenance_conversation,
);
};
let Some(provenance_participant) = current.provenance_participant.incremented() else {
return credential_attach_receipt_refusal(
request,
ReceiptCapacityScope::ProvenanceParticipant,
current.provenance_participant,
);
};
CredentialAttachCapacityDecision::Commit(CredentialAttachCapacityCommit {
resulting: CredentialAttachCapacityCounters {
live_receipt_server,
live_receipt_participant,
provenance_server,
provenance_conversation,
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_receipt_refusal(
request: &EnrollmentRequest,
scope: EnrollmentReceiptCapacityScope,
counter: CapacityCounter,
) -> EnrollmentCapacityDecision {
EnrollmentCapacityDecision::Respond(EnrollmentResponse::receipt_capacity_exceeded(
enrollment_envelope(request),
scope,
counter.limit(),
counter.occupied(),
))
}
const fn credential_attach_receipt_refusal(
request: &CredentialAttachRequest,
scope: ReceiptCapacityScope,
counter: CapacityCounter,
) -> CredentialAttachCapacityDecision {
CredentialAttachCapacityDecision::Respond(CredentialAttachResponse::receipt_capacity_exceeded(
attach_envelope(request),
scope,
counter.limit(),
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,
}
}