wsprism_gateway/transport/
codec.rs

1//! Decode-once codec for the transport layer.
2//!
3//! - Text frames => Envelope (lazy `RawValue` for data)
4//! - Binary frames => HotFrame (panic-free bytes::Buf parsing)
5//! - Ping/Pong/Close are surfaced for lifecycle management
6
7use axum::extract::ws::Message;
8use wsprism_core::{
9    error::{Result, WsPrismError},
10    protocol::{hot, text},
11};
12
13#[derive(Debug)]
14pub enum Inbound {
15    /// Ext lane JSON envelope with captured byte length (for policy).
16    Text { env: text::Envelope, bytes_len: usize },
17    /// Hot lane binary frame with captured byte length (for policy).
18    Hot { frame: hot::HotFrame, bytes_len: usize },
19    /// WS ping payload.
20    Ping(Vec<u8>),
21    /// WS pong payload.
22    Pong(Vec<u8>),
23    /// WS close control frame (no payload surfaced).
24    Close,
25}
26
27pub fn decode(msg: Message) -> Result<Inbound> {
28    match msg {
29        Message::Text(s) => {
30            let bytes_len = s.as_bytes().len();
31            let env: text::Envelope = serde_json::from_str(&s)
32                .map_err(|e| WsPrismError::BadRequest(format!("invalid envelope json: {e}")))?;
33            Ok(Inbound::Text { env, bytes_len })
34        }
35        Message::Binary(b) => {
36            let bytes_len = b.len();
37            let frame = hot::decode_hot_frame(bytes::Bytes::from(b))?;
38            Ok(Inbound::Hot { frame, bytes_len })
39        }
40        Message::Ping(v) => Ok(Inbound::Ping(v)),
41        Message::Pong(v) => Ok(Inbound::Pong(v)),
42        Message::Close(_) => Ok(Inbound::Close),
43    }
44}