#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::{Verb, Payload, END, ESC, ProtocolError, ProtocolResult, MAX_FRAME_SIZE};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Frame {
verb: Verb,
payload: Payload,
}
impl Frame {
pub fn new(verb: Verb, payload: Payload) -> Self {
Frame { verb, payload }
}
pub fn simple(verb: Verb) -> Self {
Frame {
verb,
payload: Payload::empty(),
}
}
pub fn verb(&self) -> Verb {
self.verb
}
pub fn payload(&self) -> &Payload {
&self.payload
}
pub fn into_payload(self) -> Payload {
self.payload
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn encode(&self) -> Vec<u8> {
let encoded_payload = self.payload.encode();
let mut out = Vec::with_capacity(encoded_payload.len() + 2);
out.push(self.verb.as_byte());
out.extend_from_slice(&encoded_payload);
out.push(END);
out
}
pub fn decode(data: &[u8]) -> ProtocolResult<Self> {
if data.len() < 2 {
return Err(ProtocolError::FrameTooShort);
}
if data.len() > MAX_FRAME_SIZE {
return Err(ProtocolError::FrameTooLarge);
}
if data[data.len() - 1] != END {
return Err(ProtocolError::MissingEndMarker);
}
let verb = Verb::from_byte(data[0]).ok_or(ProtocolError::InvalidVerb(data[0]))?;
let payload_data = &data[1..data.len() - 1];
let payload = Payload::decode(payload_data)?;
Ok(Frame { verb, payload })
}
pub fn is_valid(data: &[u8]) -> bool {
if data.len() < 2 {
return false;
}
if data[data.len() - 1] != END {
return false;
}
Verb::from_byte(data[0]).is_some()
}
pub fn find_end(data: &[u8]) -> Option<usize> {
let mut i = 1;
while i < data.len() {
match data[i] {
END => return Some(i + 1),
ESC if i + 1 < data.len() => i += 2, ESC => return None, _ => i += 1,
}
}
None }
}
pub struct FrameBuilder {
verb: Verb,
payload: Payload,
}
impl FrameBuilder {
pub fn new(verb: Verb) -> Self {
FrameBuilder {
verb,
payload: Payload::new(),
}
}
pub fn byte(mut self, b: u8) -> Self {
self.payload.push_byte(b);
self
}
pub fn string(mut self, s: &str) -> Self {
self.payload.push_str(s);
self
}
pub fn bytes(mut self, data: &[u8]) -> Self {
for &b in data {
self.payload.push_byte(b);
}
self
}
pub fn u16_le(mut self, v: u16) -> Self {
self.payload.push_u16_le(v);
self
}
pub fn u32_le(mut self, v: u32) -> Self {
self.payload.push_u32_le(v);
self
}
pub fn build(self) -> Frame {
Frame {
verb: self.verb,
payload: self.payload,
}
}
}
impl Frame {
pub fn ping() -> Self {
Frame::simple(Verb::Ping)
}
pub fn stats() -> Self {
Frame::simple(Verb::Stats)
}
pub fn ok() -> Self {
Frame::simple(Verb::Ok)
}
pub fn error(message: &str) -> Self {
Frame::new(Verb::Error, Payload::from_string(message))
}
pub fn scan(path: &str, depth: u8) -> Self {
let mut payload = Payload::new();
let len = path.len();
if len <= 126 {
payload.push_byte((len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(len as u16);
}
payload.push_str(path);
payload.push_byte(depth);
Frame::new(Verb::Scan, payload)
}
pub fn search(pattern: &str) -> Self {
Frame::new(Verb::Search, Payload::from_string(pattern))
}
pub fn search_path(path: &str, pattern: &str, max_results: u8) -> Self {
let mut payload = Payload::new();
let path_len = path.len();
if path_len <= 126 {
payload.push_byte((path_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(path_len as u16);
}
payload.push_str(path);
let pattern_len = pattern.len();
if pattern_len <= 126 {
payload.push_byte((pattern_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(pattern_len as u16);
}
payload.push_str(pattern);
payload.push_byte(max_results);
Frame::new(Verb::Search, payload)
}
pub fn format(mode: &str) -> Self {
Frame::new(Verb::Format, Payload::from_string(mode))
}
pub fn format_path(mode: &str, path: &str, depth: u8) -> Self {
let mut payload = Payload::new();
let mode_len = mode.len();
if mode_len <= 126 {
payload.push_byte((mode_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(mode_len as u16);
}
payload.push_str(mode);
let path_len = path.len();
if path_len <= 126 {
payload.push_byte((path_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(path_len as u16);
}
payload.push_str(path);
payload.push_byte(depth);
Frame::new(Verb::Format, payload)
}
pub fn remember(content: &str, keywords: &str, memory_type: &str) -> Self {
let mut payload = Payload::new();
let content_len = content.len();
if content_len <= 126 {
payload.push_byte((content_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(content_len as u16);
}
payload.push_str(content);
let keywords_len = keywords.len();
if keywords_len <= 126 {
payload.push_byte((keywords_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(keywords_len as u16);
}
payload.push_str(keywords);
let type_len = memory_type.len();
if type_len <= 126 {
payload.push_byte((type_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(type_len as u16);
}
payload.push_str(memory_type);
payload.push_byte(128); payload.push_byte(128);
Frame::new(Verb::Remember, payload)
}
pub fn recall(keywords: &str, max_results: u8) -> Self {
let mut payload = Payload::new();
let keywords_len = keywords.len();
if keywords_len <= 126 {
payload.push_byte((keywords_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(keywords_len as u16);
}
payload.push_str(keywords);
payload.push_byte(max_results);
Frame::new(Verb::Recall, payload)
}
pub fn forget(memory_id: &str) -> Self {
Frame::new(Verb::Forget, Payload::from_string(memory_id))
}
pub fn m8_wave() -> Self {
Frame::simple(Verb::M8Wave)
}
pub fn audio(acoustic_bytes: &[u8]) -> Self {
let mut payload = Payload::new();
for &b in acoustic_bytes {
payload.push_byte(b);
}
Frame::new(Verb::Audio, payload)
}
pub fn audio_simple(text: &str, valence: f32, arousal: f32) -> Self {
let mut payload = Payload::new();
let text_len = text.len();
if text_len <= 126 {
payload.push_byte((text_len as u8) + 0x80);
} else {
payload.push_byte(0xFF);
payload.push_u16_le(text_len as u16);
}
payload.push_str(text);
payload.push_byte(((valence + 1.0) * 127.5) as u8);
payload.push_byte((arousal * 255.0) as u8);
Frame::new(Verb::Audio, payload)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ping_frame() {
let frame = Frame::ping();
let encoded = frame.encode();
assert_eq!(encoded, vec![0x05, 0x00]); }
#[test]
fn test_error_frame() {
let frame = Frame::error("not found");
let encoded = frame.encode();
assert_eq!(encoded[0], 0x15); assert_eq!(encoded[encoded.len() - 1], 0x00); }
#[test]
fn test_frame_roundtrip() {
let original = Frame::scan("/home/hue", 3);
let encoded = original.encode();
let decoded = Frame::decode(&encoded).unwrap();
assert_eq!(decoded.verb(), Verb::Scan);
}
#[test]
fn test_find_end() {
let data = vec![0x01, 0x41, 0x42, 0x00]; assert_eq!(Frame::find_end(&data), Some(4));
}
#[test]
fn test_find_end_with_escape() {
let data = vec![0x01, 0x1B, 0x00, 0x00]; assert_eq!(Frame::find_end(&data), Some(4));
}
#[test]
fn test_builder() {
let frame = FrameBuilder::new(Verb::Search)
.string("*.rs")
.build();
assert_eq!(frame.verb(), Verb::Search);
assert_eq!(frame.payload().as_str(), Some("*.rs"));
}
}