use super::HotAction;
use liminal::protocol::FrameType;
use liminal_protocol::wire::{ClientDiscriminant, PARTICIPANT_FRAME_TYPE};
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SubjectSource {
FrameSubject,
SubscriptionBinding,
ConversationBinding,
WorkerBinding,
PushBinding,
ParticipantBinding,
FrameLink,
LinkBinding,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "class", rename_all = "snake_case")]
pub enum RouteClassification {
Hot {
action: HotAction,
source: SubjectSource,
},
Cleanup {
action: HotAction,
source: SubjectSource,
},
Deliberate {
source: SubjectSource,
},
Crossing {
source: SubjectSource,
},
Admission,
Control,
ServerOnly,
ParticipantEnvelope,
Unknown,
}
const fn hot(action: HotAction, source: SubjectSource) -> RouteClassification {
RouteClassification::Hot { action, source }
}
const fn cleanup(action: HotAction, source: SubjectSource) -> RouteClassification {
RouteClassification::Cleanup { action, source }
}
#[must_use]
pub const fn classify_wire_route(frame: FrameType) -> RouteClassification {
use SubjectSource as Source;
match frame {
FrameType::Connect => RouteClassification::Admission,
FrameType::Disconnect | FrameType::Ping => RouteClassification::Control,
FrameType::Publish => hot(HotAction::Publish, Source::FrameSubject),
FrameType::Subscribe => hot(HotAction::Subscribe, Source::FrameSubject),
FrameType::Unsubscribe => cleanup(HotAction::Subscribe, Source::SubscriptionBinding),
FrameType::ConversationOpen => hot(HotAction::Call, Source::FrameSubject),
FrameType::ConversationMessage => hot(HotAction::Call, Source::ConversationBinding),
FrameType::ConversationClose => cleanup(HotAction::Call, Source::ConversationBinding),
FrameType::Accept | FrameType::Defer | FrameType::Reject => {
hot(HotAction::Subscribe, Source::SubscriptionBinding)
}
FrameType::PushReply => hot(HotAction::Serve, Source::PushBinding),
FrameType::WorkerRegister => hot(HotAction::Serve, Source::WorkerBinding),
FrameType::LinkOpen => RouteClassification::Deliberate {
source: Source::FrameLink,
},
FrameType::LinkMessage => RouteClassification::Crossing {
source: Source::LinkBinding,
},
FrameType::ConnectAck
| FrameType::ConnectError
| FrameType::SubscribeAck
| FrameType::SubscribeError
| FrameType::PublishAck
| FrameType::PublishError
| FrameType::ConversationError
| FrameType::Pong
| FrameType::Push
| FrameType::WorkerRegisterAck
| FrameType::LinkOpenAck
| FrameType::DisconnectReason
| FrameType::Deliver => RouteClassification::ServerOnly,
FrameType::Unknown(tag) => {
if tag == PARTICIPANT_FRAME_TYPE {
RouteClassification::ParticipantEnvelope
} else {
RouteClassification::Unknown
}
}
}
}
#[must_use]
pub const fn classify_participant_route(route: ClientDiscriminant) -> RouteClassification {
use SubjectSource::ParticipantBinding as Source;
match route {
ClientDiscriminant::EnrollmentRequest
| ClientDiscriminant::CredentialAttachRequest
| ClientDiscriminant::ParticipantAck
| ClientDiscriminant::MarkerAck
| ClientDiscriminant::ObserverRecoveryHandshake
| ClientDiscriminant::SettlementQuery => hot(HotAction::Subscribe, Source),
ClientDiscriminant::RecordAdmission => hot(HotAction::Publish, Source),
ClientDiscriminant::DetachRequest | ClientDiscriminant::LeaveRequest => {
cleanup(HotAction::Subscribe, Source)
}
}
}