wireframe/message_assembler/types.rs
1//! Public types for message assembly inputs and outputs.
2//!
3//! This module contains `FirstFrameInput`, `FirstFrameInputError`,
4//! `EnvelopeRouting`, and `AssembledMessage`, extracted from `state.rs`
5//! to meet the 400-line file limit.
6
7use thiserror::Error;
8
9use super::{FirstFrameHeader, MessageKey};
10
11/// Envelope identifier from the enclosing transport frame.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
13pub struct EnvelopeId(pub u32);
14
15impl From<u32> for EnvelopeId {
16 fn from(value: u32) -> Self { Self(value) }
17}
18
19impl From<EnvelopeId> for u32 {
20 fn from(value: EnvelopeId) -> Self { value.0 }
21}
22
23/// Correlation identifier from the enclosing transport frame.
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
25pub struct CorrelationId(pub u64);
26
27impl From<u64> for CorrelationId {
28 fn from(value: u64) -> Self { Self(value) }
29}
30
31impl From<CorrelationId> for u64 {
32 fn from(value: CorrelationId) -> Self { value.0 }
33}
34
35/// Routing metadata from the transport envelope that carried a first frame.
36///
37/// Captured at first-frame time so the completed [`AssembledMessage`] can
38/// be dispatched to the correct handler and logged under the original
39/// correlation identifier, regardless of which continuation frame
40/// completed the assembly.
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
42pub struct EnvelopeRouting {
43 /// Envelope identifier from the enclosing transport frame.
44 pub envelope_id: EnvelopeId,
45 /// Correlation identifier from the enclosing transport frame.
46 pub correlation_id: Option<CorrelationId>,
47}
48
49/// Input data for a first frame.
50///
51/// Groups the header and payload components that comprise a first frame,
52/// along with the [`EnvelopeRouting`] metadata so the assembler can
53/// preserve it for the completed message.
54///
55/// # Examples
56///
57/// ```
58/// use wireframe::message_assembler::{
59/// CorrelationId,
60/// EnvelopeId,
61/// EnvelopeRouting,
62/// FirstFrameHeader,
63/// FirstFrameInput,
64/// MessageKey,
65/// };
66///
67/// let header = FirstFrameHeader {
68/// message_key: MessageKey(1),
69/// metadata_len: 2,
70/// body_len: 5,
71/// total_body_len: None,
72/// is_last: false,
73/// };
74/// let routing = EnvelopeRouting {
75/// envelope_id: EnvelopeId(42),
76/// correlation_id: Some(CorrelationId(7)),
77/// };
78/// let input = FirstFrameInput::new(&header, routing, vec![0x01, 0x02], b"hello")
79/// .expect("header lengths match payload sizes");
80/// assert_eq!(input.header.message_key, MessageKey(1));
81/// assert_eq!(input.routing.envelope_id, EnvelopeId(42));
82/// ```
83#[derive(Debug)]
84pub struct FirstFrameInput<'a> {
85 /// The frame header.
86 pub header: &'a FirstFrameHeader,
87 /// Envelope routing metadata from the enclosing transport frame.
88 pub routing: EnvelopeRouting,
89 /// Protocol-specific metadata.
90 pub metadata: Vec<u8>,
91 /// Body payload slice.
92 pub body: &'a [u8],
93}
94
95/// Error returned when [`FirstFrameInput`] validation fails.
96#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)]
97pub enum FirstFrameInputError {
98 /// Metadata length in header does not match actual metadata size.
99 #[error("metadata length mismatch: header declares {header_len} bytes, got {actual_len}")]
100 MetadataLengthMismatch {
101 /// Length declared in header.
102 header_len: usize,
103 /// Actual length of metadata slice.
104 actual_len: usize,
105 },
106 /// Body length in header does not match actual body size.
107 #[error("body length mismatch: header declares {header_len} bytes, got {actual_len}")]
108 BodyLengthMismatch {
109 /// Length declared in header.
110 header_len: usize,
111 /// Actual length of body slice.
112 actual_len: usize,
113 },
114}
115
116impl<'a> FirstFrameInput<'a> {
117 /// Create a new first frame input, validating header lengths against payloads.
118 ///
119 /// # Errors
120 ///
121 /// Returns an error if `header.metadata_len` does not match `metadata.len()`
122 /// or `header.body_len` does not match `body.len()`.
123 pub fn new(
124 header: &'a FirstFrameHeader,
125 routing: EnvelopeRouting,
126 metadata: Vec<u8>,
127 body: &'a [u8],
128 ) -> Result<Self, FirstFrameInputError> {
129 if header.metadata_len != metadata.len() {
130 return Err(FirstFrameInputError::MetadataLengthMismatch {
131 header_len: header.metadata_len,
132 actual_len: metadata.len(),
133 });
134 }
135 if header.body_len != body.len() {
136 return Err(FirstFrameInputError::BodyLengthMismatch {
137 header_len: header.body_len,
138 actual_len: body.len(),
139 });
140 }
141 Ok(Self {
142 header,
143 routing,
144 metadata,
145 body,
146 })
147 }
148}
149
150/// Container for a fully assembled message.
151///
152/// Preserves [`EnvelopeRouting`] from the first frame so that completed
153/// messages are dispatched to the correct handler and logged under the
154/// original correlation identifier.
155///
156/// # Examples
157///
158/// ```
159/// use wireframe::message_assembler::{
160/// AssembledMessage,
161/// CorrelationId,
162/// EnvelopeId,
163/// EnvelopeRouting,
164/// MessageKey,
165/// };
166///
167/// // Normally obtained from MessageAssemblyState::accept_first_frame or
168/// // accept_continuation_frame when a message completes.
169/// let routing = EnvelopeRouting {
170/// envelope_id: EnvelopeId(42),
171/// correlation_id: Some(CorrelationId(7)),
172/// };
173/// let msg = AssembledMessage::new(MessageKey(1), routing, vec![0x01], vec![0x02, 0x03]);
174/// assert_eq!(msg.message_key(), MessageKey(1));
175/// assert_eq!(msg.routing().envelope_id, EnvelopeId(42));
176/// assert_eq!(msg.routing().correlation_id, Some(CorrelationId(7)));
177/// assert_eq!(msg.metadata(), &[0x01]);
178/// assert_eq!(msg.body(), &[0x02, 0x03]);
179/// ```
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct AssembledMessage {
182 message_key: MessageKey,
183 routing: EnvelopeRouting,
184 metadata: Vec<u8>,
185 body: Vec<u8>,
186}
187
188impl AssembledMessage {
189 /// Create a new assembled message.
190 #[must_use]
191 pub fn new(
192 message_key: MessageKey,
193 routing: EnvelopeRouting,
194 metadata: Vec<u8>,
195 body: Vec<u8>,
196 ) -> Self {
197 Self {
198 message_key,
199 routing,
200 metadata,
201 body,
202 }
203 }
204
205 /// Message key that correlated the frames.
206 #[must_use]
207 pub const fn message_key(&self) -> MessageKey { self.message_key }
208
209 /// Envelope routing metadata from the first frame.
210 #[must_use]
211 pub const fn routing(&self) -> EnvelopeRouting { self.routing }
212
213 /// Protocol-specific metadata from the first frame.
214 #[must_use]
215 pub fn metadata(&self) -> &[u8] { &self.metadata }
216
217 /// Reassembled body bytes.
218 #[must_use]
219 pub fn body(&self) -> &[u8] { &self.body }
220
221 /// Consume and return the body bytes.
222 #[must_use]
223 pub fn into_body(self) -> Vec<u8> { self.body }
224}