liminal_server/auth_lys/
routes.rs1use super::HotAction;
4use liminal::protocol::FrameType;
5use liminal_protocol::wire::{ClientDiscriminant, PARTICIPANT_FRAME_TYPE};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum SubjectSource {
12 FrameSubject,
14 SubscriptionBinding,
16 ConversationBinding,
18 WorkerBinding,
20 PushBinding,
22 ParticipantBinding,
24 FrameLink,
26 LinkBinding,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(tag = "class", rename_all = "snake_case")]
33pub enum RouteClassification {
34 Hot {
36 action: HotAction,
38 source: SubjectSource,
40 },
41 Cleanup {
43 action: HotAction,
45 source: SubjectSource,
47 },
48 Deliberate {
52 source: SubjectSource,
54 },
55 Crossing {
59 source: SubjectSource,
61 },
62 Admission,
64 Control,
66 ServerOnly,
68 ParticipantEnvelope,
70 Unknown,
72}
73
74const fn hot(action: HotAction, source: SubjectSource) -> RouteClassification {
75 RouteClassification::Hot { action, source }
76}
77const fn cleanup(action: HotAction, source: SubjectSource) -> RouteClassification {
78 RouteClassification::Cleanup { action, source }
79}
80
81#[must_use]
86pub const fn classify_wire_route(frame: FrameType) -> RouteClassification {
87 use SubjectSource as Source;
88 match frame {
89 FrameType::Connect => RouteClassification::Admission,
90 FrameType::Disconnect | FrameType::Ping => RouteClassification::Control,
91 FrameType::Publish => hot(HotAction::Publish, Source::FrameSubject),
92 FrameType::Subscribe => hot(HotAction::Subscribe, Source::FrameSubject),
93 FrameType::Unsubscribe => cleanup(HotAction::Subscribe, Source::SubscriptionBinding),
94 FrameType::ConversationOpen => hot(HotAction::Call, Source::FrameSubject),
95 FrameType::ConversationMessage => hot(HotAction::Call, Source::ConversationBinding),
96 FrameType::ConversationClose => cleanup(HotAction::Call, Source::ConversationBinding),
97 FrameType::Accept | FrameType::Defer | FrameType::Reject => {
98 hot(HotAction::Subscribe, Source::SubscriptionBinding)
99 }
100 FrameType::PushReply => hot(HotAction::Serve, Source::PushBinding),
101 FrameType::WorkerRegister => hot(HotAction::Serve, Source::WorkerBinding),
102 FrameType::LinkOpen => RouteClassification::Deliberate {
103 source: Source::FrameLink,
104 },
105 FrameType::LinkMessage => RouteClassification::Crossing {
106 source: Source::LinkBinding,
107 },
108 FrameType::ConnectAck
109 | FrameType::ConnectError
110 | FrameType::SubscribeAck
111 | FrameType::SubscribeError
112 | FrameType::PublishAck
113 | FrameType::PublishError
114 | FrameType::ConversationError
115 | FrameType::Pong
116 | FrameType::Push
117 | FrameType::WorkerRegisterAck
118 | FrameType::LinkOpenAck
119 | FrameType::DisconnectReason
120 | FrameType::Deliver => RouteClassification::ServerOnly,
121 FrameType::Unknown(tag) => {
122 if tag == PARTICIPANT_FRAME_TYPE {
123 RouteClassification::ParticipantEnvelope
124 } else {
125 RouteClassification::Unknown
126 }
127 }
128 }
129}
130
131#[must_use]
136pub const fn classify_participant_route(route: ClientDiscriminant) -> RouteClassification {
137 use SubjectSource::ParticipantBinding as Source;
138 match route {
139 ClientDiscriminant::EnrollmentRequest
140 | ClientDiscriminant::CredentialAttachRequest
141 | ClientDiscriminant::ParticipantAck
142 | ClientDiscriminant::MarkerAck
143 | ClientDiscriminant::ObserverRecoveryHandshake
144 | ClientDiscriminant::SettlementQuery => hot(HotAction::Subscribe, Source),
145 ClientDiscriminant::RecordAdmission => hot(HotAction::Publish, Source),
146 ClientDiscriminant::DetachRequest | ClientDiscriminant::LeaveRequest => {
147 cleanup(HotAction::Subscribe, Source)
148 }
149 }
150}