liminal_bus_protocol/
error.rs1use super::{FrameType, lifecycle::ConnectionState};
2
3pub const PASS_EXPIRED_CODE: u16 = 0x000A;
5
6#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
8pub enum ProtocolError {
9 #[error("incomplete frame header")]
11 IncompleteHeader { message: Option<String> },
12
13 #[error("truncated frame payload")]
15 TruncatedPayload { message: Option<String> },
16
17 #[error("unknown frame type {type_id}")]
19 UnknownFrameType {
20 type_id: u8,
21 message: Option<String>,
22 },
23
24 #[error("invalid stream {stream_id} for {frame_type:?}")]
26 InvalidStream {
27 frame_type: FrameType,
28 stream_id: u32,
29 message: Option<String>,
30 },
31
32 #[error("invalid protocol state transition from {current_state:?} with {frame_type:?}")]
34 InvalidStateTransition {
35 current_state: ConnectionState,
36 frame_type: FrameType,
37 message: Option<String>,
38 },
39
40 #[error("authentication failure")]
42 AuthenticationFailure { message: Option<String> },
43
44 #[error("pass expired")]
46 PassExpired { message: Option<String> },
47
48 #[error("protocol version mismatch")]
50 VersionMismatch { message: Option<String> },
51
52 #[error("schema incompatible")]
54 SchemaIncompatible { message: Option<String> },
55
56 #[error("protocol codec error")]
58 CodecError { message: Option<String> },
59}
60
61impl ProtocolError {
62 pub const INCOMPLETE_HEADER_CODE: u16 = 0x0001;
64 pub const TRUNCATED_PAYLOAD_CODE: u16 = 0x0002;
66 pub const UNKNOWN_FRAME_TYPE_CODE: u16 = 0x0003;
68 pub const INVALID_STREAM_CODE: u16 = 0x0004;
70 pub const INVALID_STATE_TRANSITION_CODE: u16 = 0x0005;
72 pub const AUTHENTICATION_FAILURE_CODE: u16 = 0x0006;
74 pub const VERSION_MISMATCH_CODE: u16 = 0x0007;
76 pub const SCHEMA_INCOMPATIBLE_CODE: u16 = 0x0008;
78 pub const CODEC_ERROR_CODE: u16 = 0x0009;
80
81 pub const PASS_EXPIRED_CODE: u16 = crate::error::PASS_EXPIRED_CODE;
83
84 #[must_use]
86 pub const fn reason_code(&self) -> u16 {
87 match self {
88 Self::IncompleteHeader { .. } => Self::INCOMPLETE_HEADER_CODE,
89 Self::TruncatedPayload { .. } => Self::TRUNCATED_PAYLOAD_CODE,
90 Self::UnknownFrameType { .. } => Self::UNKNOWN_FRAME_TYPE_CODE,
91 Self::InvalidStream { .. } => Self::INVALID_STREAM_CODE,
92 Self::InvalidStateTransition { .. } => Self::INVALID_STATE_TRANSITION_CODE,
93 Self::AuthenticationFailure { .. } => Self::AUTHENTICATION_FAILURE_CODE,
94 Self::PassExpired { .. } => PASS_EXPIRED_CODE,
95 Self::VersionMismatch { .. } => Self::VERSION_MISMATCH_CODE,
96 Self::SchemaIncompatible { .. } => Self::SCHEMA_INCOMPATIBLE_CODE,
97 Self::CodecError { .. } => Self::CODEC_ERROR_CODE,
98 }
99 }
100
101 #[must_use]
103 pub fn message(&self) -> Option<&str> {
104 match self {
105 Self::IncompleteHeader { message }
106 | Self::TruncatedPayload { message }
107 | Self::UnknownFrameType { message, .. }
108 | Self::InvalidStream { message, .. }
109 | Self::InvalidStateTransition { message, .. }
110 | Self::AuthenticationFailure { message }
111 | Self::PassExpired { message }
112 | Self::VersionMismatch { message }
113 | Self::SchemaIncompatible { message }
114 | Self::CodecError { message } => message.as_deref(),
115 }
116 }
117
118 #[must_use]
119 pub(crate) fn invalid_stream(frame_type: FrameType, stream_id: u32) -> Self {
120 Self::InvalidStream {
121 frame_type,
122 stream_id,
123 message: Some("stream id violates frame type invariants".to_owned()),
124 }
125 }
126
127 #[must_use]
128 pub(crate) fn invalid_state_transition(
129 current_state: ConnectionState,
130 frame_type: FrameType,
131 ) -> Self {
132 Self::InvalidStateTransition {
133 current_state,
134 frame_type,
135 message: Some("frame is invalid for the current connection state".to_owned()),
136 }
137 }
138
139 #[must_use]
140 pub(crate) fn codec(message: impl Into<String>) -> Self {
141 Self::CodecError {
142 message: Some(message.into()),
143 }
144 }
145}
146
147#[cfg(test)]
148#[path = "pass_expired_tests.rs"]
149mod pass_expired_tests;