use bytes::Bytes; use serde::Serialize; use super::frame::{FireFrame, FrameType, PacketOptions}; use tdf::stringify::TdfStringifier; use tdf::reader::TdfDeserializer; /// A fully framed Blaze packet. #[derive(Debug, Clone)] pub struct Packet { /// Decoded Fire2 header. pub frame: FireFrame, /// Raw body bytes (unparsed TDF). pub body: Bytes, } impl Packet { /// Create a raw-mode packet (no frame header — body only). pub fn raw(body: Bytes) -> Self { Packet { frame: FireFrame { component: 0, command: 0, error: 0, ty: FrameType::Request, options: PacketOptions::NONE, seq: 0, }, body, } } /// Build an empty response that mirrors `req`'s routing fields. pub fn response_empty(req: &Packet) -> Self { Packet { frame: req.frame.response(), body: Bytes::new() } } /// Build a response with the given TDF body. pub fn response(req: &Packet, value: &V) -> Self { Packet { frame: req.frame.response(), body: Bytes::from(tdf::serialize_vec(value)), } } /// Try to decode the body as TDF and return a human-readable string. /// On any decode error, return the raw hex. pub fn tdf_string(&self) -> String { if self.body.is_empty() { return String::from("(empty)"); } let r = TdfDeserializer::new(&self.body); let (s, ok) = TdfStringifier::::new_string(r); if ok { s } else { format!("(decode error) raw={}", hex::encode(&self.body)) } } /// Serialize the packet into a JSON-friendly struct for capture logs. pub fn to_capture(&self) -> CaptureRecord { CaptureRecord { component: self.frame.component, command: self.frame.command, error: self.frame.error, ty: self.frame.ty.to_string(), seq: self.frame.seq, body_len: self.body.len(), raw_hex: hex::encode(&self.body), tdf: self.tdf_string(), } } } #[derive(Debug, Serialize)] pub struct CaptureRecord { pub component: u16, pub command: u16, pub error: u16, pub ty: String, pub seq: u16, pub body_len: usize, pub raw_hex: String, pub tdf: String, }