Skip to main content

melin_app/
decoder.rs

1//! Wire-side request decoder seam.
2//!
3//! The server runtime (accept loop, frame reader, DPDK transport)
4//! consumes incoming frames from the network and needs to turn them
5//! into application events to publish to the pipeline. The decoding
6//! itself — pattern-matching on the wire enum, mapping per-variant
7//! fields, enforcing per-connection permission policy — is
8//! application-shaped: a trading server decodes order submissions, a
9//! payments server decodes transfers, a logistics server decodes
10//! shipment events. This trait is the seam that lets the runtime
11//! delegate that decoding to the application without ever naming the
12//! concrete wire enum.
13//!
14//! The runtime calls [`RequestDecoder::decode`] once per incoming
15//! frame; the [`Decoded`] return value encodes exactly the four
16//! outcomes the runtime acts on (drop, publish, reject with reason,
17//! log decode error).
18
19use crate::AppEvent;
20use crate::auth::Permission;
21
22/// Decode an authenticated client frame into an application event the
23/// runtime can publish to the pipeline.
24///
25/// Stateless on the connection (the runtime carries connection-level
26/// state — `Permission`, `key_hash`, etc. — and feeds the relevant
27/// piece in per call). Implementors are typically zero-sized types.
28pub trait RequestDecoder: Send + Sync {
29    /// Application event type produced on a successful decode. The
30    /// runtime wraps this in a transport-level envelope (e.g.
31    /// `JournalEvent::App`) before publishing.
32    type Event: AppEvent;
33
34    /// Decode a wire frame. `bytes` is the framed payload (length
35    /// prefix already stripped by the caller). `permission` is the
36    /// role established during the auth handshake and stored on the
37    /// connection.
38    fn decode(&self, bytes: &[u8], permission: Permission) -> Decoded<Self::Event>;
39}
40
41/// Outcome of a single [`RequestDecoder::decode`] call. The runtime
42/// branches on this and never needs to know the underlying wire enum.
43pub enum Decoded<E: AppEvent> {
44    /// Drop the frame silently. Used for transport-level messages
45    /// (heartbeats, post-auth handshakes, subscription control) that
46    /// the runtime never publishes to the pipeline.
47    Filter,
48    /// Frame OK and authorized. Caller publishes `event` with the
49    /// per-key sequence `request_seq`. Whether the event needs a
50    /// timestamp is derived by the runtime from [`AppEvent::is_query`]
51    /// — query events bypass the journal and skip the wall-clock
52    /// stamp.
53    Permitted {
54        /// Per-key idempotency sequence carried in the wire frame.
55        request_seq: u64,
56        /// Decoded application event.
57        event: E,
58    },
59    /// Authenticated connection lacks the permission level for this
60    /// operation. The static string is logged at debug level on the
61    /// reader thread; the runtime drops the frame.
62    PermissionDenied(&'static str),
63    /// Wire-level decode failure (malformed length, unknown variant
64    /// tag, invalid field). The runtime logs at debug level and drops
65    /// the frame; the connection is not closed (a misbehaving client
66    /// drops itself on the next read timeout).
67    DecodeError(&'static str),
68}