1use bytes::{Buf, BufMut, BytesMut};
2
3pub const HEADER_SIZE: usize = 44;
15
16pub(crate) const MAGIC: [u8; 2] = *b"FT";
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 Self::peek_slice(src.as_ref())
61 }
62
63 pub fn peek_slice(src: &[u8]) -> Result<Option<Self>, futu_core::error::FutuError> {
68 if src.len() < HEADER_SIZE {
69 return Ok(None);
70 }
71
72 if src[0] != MAGIC[0] || src[1] != MAGIC[1] {
74 return Err(futu_core::error::FutuError::InvalidHeader);
75 }
76
77 let proto_id = u32::from_le_bytes([src[2], src[3], src[4], src[5]]);
78 let proto_fmt_type = ProtoFmtType::try_from(src[6]).map_err(|v| {
79 futu_core::error::FutuError::Codec(format!("unknown proto fmt type: {v}"))
80 })?;
81 let proto_ver = src[7];
82 let serial_no = u32::from_le_bytes([src[8], src[9], src[10], src[11]]);
83 let body_len = u32::from_le_bytes([src[12], src[13], src[14], src[15]]);
84
85 let mut body_sha1 = [0u8; 20];
86 body_sha1.copy_from_slice(&src[16..36]);
87
88 Ok(Some(Self {
89 proto_id,
90 proto_fmt_type,
91 proto_ver,
92 serial_no,
93 body_len,
94 body_sha1,
95 }))
96 }
97
98 pub fn decode(src: &mut BytesMut) -> Result<Option<Self>, futu_core::error::FutuError> {
100 let header = Self::peek(src)?;
101 if header.is_some() {
102 src.advance(HEADER_SIZE);
103 }
104 Ok(header)
105 }
106
107 pub fn encode(&self, dst: &mut BytesMut) {
109 dst.reserve(HEADER_SIZE);
110 dst.put_slice(&MAGIC);
111 dst.put_u32_le(self.proto_id);
112 dst.put_u8(self.proto_fmt_type as u8);
113 dst.put_u8(self.proto_ver);
114 dst.put_u32_le(self.serial_no);
115 dst.put_u32_le(self.body_len);
116 dst.put_slice(&self.body_sha1);
117 dst.put_slice(&[0u8; 8]); }
119}
120
121#[cfg(test)]
122mod tests;