1use bytes::{Buf, BufMut, BytesMut};
2
3pub const HEADER_SIZE: usize = 44;
15
16pub(crate) const MAGIC: [u8; 2] = [b'F', b'T'];
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[repr(u8)]
25#[non_exhaustive]
26pub enum ProtoFmtType {
27 Protobuf = 0,
28 Json = 1,
29}
30
31impl TryFrom<u8> for ProtoFmtType {
32 type Error = u8;
33
34 fn try_from(value: u8) -> Result<Self, u8> {
35 match value {
36 0 => Ok(Self::Protobuf),
37 1 => Ok(Self::Json),
38 other => Err(other),
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct FutuHeader {
46 pub proto_id: u32,
47 pub proto_fmt_type: ProtoFmtType,
48 pub proto_ver: u8,
49 pub serial_no: u32,
50 pub body_len: u32,
51 pub body_sha1: [u8; 20],
52}
53
54impl FutuHeader {
55 pub fn peek(src: &BytesMut) -> Result<Option<Self>, futu_core::error::FutuError> {
60 if src.len() < HEADER_SIZE {
61 return Ok(None);
62 }
63
64 if src[0] != MAGIC[0] || src[1] != MAGIC[1] {
66 return Err(futu_core::error::FutuError::InvalidHeader);
67 }
68
69 let proto_id = u32::from_le_bytes([src[2], src[3], src[4], src[5]]);
70 let proto_fmt_type = ProtoFmtType::try_from(src[6]).map_err(|v| {
71 futu_core::error::FutuError::Codec(format!("unknown proto fmt type: {v}"))
72 })?;
73 let proto_ver = src[7];
74 let serial_no = u32::from_le_bytes([src[8], src[9], src[10], src[11]]);
75 let body_len = u32::from_le_bytes([src[12], src[13], src[14], src[15]]);
76
77 let mut body_sha1 = [0u8; 20];
78 body_sha1.copy_from_slice(&src[16..36]);
79
80 Ok(Some(Self {
81 proto_id,
82 proto_fmt_type,
83 proto_ver,
84 serial_no,
85 body_len,
86 body_sha1,
87 }))
88 }
89
90 pub fn decode(src: &mut BytesMut) -> Result<Option<Self>, futu_core::error::FutuError> {
92 let header = Self::peek(src)?;
93 if header.is_some() {
94 src.advance(HEADER_SIZE);
95 }
96 Ok(header)
97 }
98
99 pub fn encode(&self, dst: &mut BytesMut) {
101 dst.reserve(HEADER_SIZE);
102 dst.put_slice(&MAGIC);
103 dst.put_u32_le(self.proto_id);
104 dst.put_u8(self.proto_fmt_type as u8);
105 dst.put_u8(self.proto_ver);
106 dst.put_u32_le(self.serial_no);
107 dst.put_u32_le(self.body_len);
108 dst.put_slice(&self.body_sha1);
109 dst.put_slice(&[0u8; 8]); }
111}
112
113#[cfg(test)]
114mod tests;