1use std::{error::Error, fmt};
9
10use crate::{EnvelopeHeader, Flags, FrameType, MAX_FRAME_BODY_LEN, PROTOCOL_VERSION};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct Frame {
15 pub header: EnvelopeHeader,
16 pub body: Vec<u8>,
17}
18
19impl Frame {
20 pub fn build(
22 ty: FrameType,
23 flags: Flags,
24 channel: u16,
25 corr: u64,
26 body: Vec<u8>,
27 ) -> Result<Self, FrameBuildError> {
28 Self::build_with_version(PROTOCOL_VERSION, ty, flags, channel, corr, body)
29 }
30
31 pub fn build_with_version(
34 ver: u8,
35 ty: FrameType,
36 flags: Flags,
37 channel: u16,
38 corr: u64,
39 body: Vec<u8>,
40 ) -> Result<Self, FrameBuildError> {
41 if body.len() > MAX_FRAME_BODY_LEN as usize {
46 return Err(FrameBuildError::BodyExceedsMax {
47 body_len: body.len(),
48 max: MAX_FRAME_BODY_LEN,
49 });
50 }
51 let len = u32::try_from(body.len()).map_err(|_| FrameBuildError::BodyTooLarge {
52 body_len: body.len(),
53 })?;
54 Ok(Self {
55 header: EnvelopeHeader {
56 len,
57 ver,
58 ty,
59 flags,
60 channel,
61 corr,
62 },
63 body,
64 })
65 }
66
67 pub fn from_wire(header: EnvelopeHeader, body: Vec<u8>) -> Self {
72 debug_assert_eq!(header.len as usize, body.len());
73 Self { header, body }
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum FrameBuildError {
80 BodyTooLarge { body_len: usize },
82 BodyExceedsMax { body_len: usize, max: u32 },
85}
86
87impl fmt::Display for FrameBuildError {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 Self::BodyTooLarge { body_len } => {
91 write!(f, "frame body is too large for u32 len: {body_len} bytes")
92 }
93 Self::BodyExceedsMax { body_len, max } => {
94 write!(f, "frame body {body_len} bytes exceeds max {max} bytes")
95 }
96 }
97 }
98}
99
100impl Error for FrameBuildError {}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn build_rejects_body_over_max_frame_len() {
108 let body = vec![0u8; MAX_FRAME_BODY_LEN as usize + 1];
112 let err = Frame::build(
113 FrameType::Request,
114 Flags::new(false, crate::Priority::Interactive, false),
115 1,
116 7,
117 body,
118 )
119 .expect_err("body over the cap must be rejected");
120 assert!(matches!(
121 err,
122 FrameBuildError::BodyExceedsMax { max, .. } if max == MAX_FRAME_BODY_LEN
123 ));
124 }
125
126 #[test]
127 fn build_accepts_body_at_max_frame_len() {
128 let body = vec![0u8; MAX_FRAME_BODY_LEN as usize];
129 let frame = Frame::build(
130 FrameType::Request,
131 Flags::new(false, crate::Priority::Interactive, false),
132 1,
133 7,
134 body,
135 )
136 .expect("body exactly at the cap is allowed");
137 assert_eq!(frame.header.len, MAX_FRAME_BODY_LEN);
138 }
139}