1use std::fmt;
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
35pub struct Header(u32);
36
37impl Header {
38 pub const SIZE: usize = 4;
39
40 pub fn new(ty: FrameType, len: u16) -> Self {
41 match ty {
42 FrameType::Data => Self::data(len),
43 FrameType::Ack => Self::ack(len),
44 }
45 }
46
47 pub fn unvalidated(n: u32) -> Self {
49 Self(n)
50 }
51
52 pub fn data(len: u16) -> Self {
54 Self(len as u32)
55 }
56
57 pub fn ack(len: u16) -> Self {
59 Self(0x1000000 | len as u32)
60 }
61
62 pub fn frame_type(self) -> Result<FrameType, u8> {
64 match (self.0 & 0xF000000) >> 24 {
65 0 => Ok(FrameType::Data),
66 1 => Ok(FrameType::Ack),
67 t => Err(t as u8),
68 }
69 }
70
71 pub fn partial(self) -> Self {
73 Self(self.0 | 0x800000)
74 }
75
76 pub fn is_data(self) -> bool {
78 self.0 & 0xF000000 == 0
79 }
80
81 pub fn is_ack(self) -> bool {
83 self.0 & 0xF000000 == 0x1000000
84 }
85
86 pub fn is_partial(self) -> bool {
88 self.0 & 0x800000 == 0x800000
89 }
90
91 pub fn len(self) -> u16 {
93 (self.0 & 0xFFFF) as u16
94 }
95
96 pub fn is_empty(self) -> bool {
98 self.len() == 0
99 }
100
101 pub fn to_bytes(self) -> [u8; Self::SIZE] {
103 self.0.to_be_bytes()
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum FrameType {
110 Data,
111 Ack,
112}
113
114impl From<Header> for [u8; Header::SIZE] {
115 fn from(val: Header) -> Self {
116 val.to_bytes()
117 }
118}
119
120impl fmt::Display for Header {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_struct("Header")
123 .field("type", &self.frame_type())
124 .field("len", &self.len())
125 .field("partial", &self.is_partial())
126 .finish()
127 }
128}
129
130#[derive(Debug, thiserror::Error)]
131#[error("invalid header: {0}")]
132pub struct InvalidHeader(&'static str);
133
134#[cfg(test)]
135mod tests {
136 use quickcheck::quickcheck;
137
138 use super::{FrameType, Header};
139
140 quickcheck! {
141 fn data(len: u16) -> bool {
142 let hdr = Header::data(len);
143 hdr.is_data() && !hdr.is_partial() && hdr.frame_type() == Ok(FrameType::Data)
144 }
145
146 fn ack(len: u16) -> bool {
147 let hdr = Header::ack(len);
148 hdr.is_ack() && !hdr.is_partial() && hdr.frame_type() == Ok(FrameType::Ack)
149 }
150
151 fn partial_data(len: u16) -> bool {
152 Header::data(len).partial().is_partial()
153 }
154
155 fn partial_ack(len: u16) -> bool {
156 Header::ack(len).partial().is_partial()
157 }
158
159 fn data_len(len: u16) -> bool {
160 Header::data(len).len() == len
161 }
162
163 fn ack_len(len: u16) -> bool {
164 Header::ack(len).len() == len
165 }
166 }
167}