1#[cfg(feature = "alloc")]
13use alloc::vec::Vec;
14
15use crate::{Verb, Payload, END, ESC, ProtocolError, ProtocolResult, MAX_FRAME_SIZE};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Frame {
20 verb: Verb,
21 payload: Payload,
22}
23
24impl Frame {
25 pub fn new(verb: Verb, payload: Payload) -> Self {
27 Frame { verb, payload }
28 }
29
30 pub fn simple(verb: Verb) -> Self {
32 Frame {
33 verb,
34 payload: Payload::empty(),
35 }
36 }
37
38 pub fn verb(&self) -> Verb {
40 self.verb
41 }
42
43 pub fn payload(&self) -> &Payload {
45 &self.payload
46 }
47
48 pub fn into_payload(self) -> Payload {
50 self.payload
51 }
52
53 #[cfg(any(feature = "std", feature = "alloc"))]
55 pub fn encode(&self) -> Vec<u8> {
56 let encoded_payload = self.payload.encode();
57 let mut out = Vec::with_capacity(encoded_payload.len() + 2);
58
59 out.push(self.verb.as_byte());
60 out.extend_from_slice(&encoded_payload);
61 out.push(END);
62
63 out
64 }
65
66 pub fn decode(data: &[u8]) -> ProtocolResult<Self> {
68 if data.len() < 2 {
70 return Err(ProtocolError::FrameTooShort);
71 }
72
73 if data.len() > MAX_FRAME_SIZE {
74 return Err(ProtocolError::FrameTooLarge);
75 }
76
77 if data[data.len() - 1] != END {
79 return Err(ProtocolError::MissingEndMarker);
80 }
81
82 let verb = Verb::from_byte(data[0]).ok_or(ProtocolError::InvalidVerb(data[0]))?;
84
85 let payload_data = &data[1..data.len() - 1];
87 let payload = Payload::decode(payload_data)?;
88
89 Ok(Frame { verb, payload })
90 }
91
92 pub fn is_valid(data: &[u8]) -> bool {
94 if data.len() < 2 {
95 return false;
96 }
97 if data[data.len() - 1] != END {
98 return false;
99 }
100 Verb::from_byte(data[0]).is_some()
101 }
102
103 pub fn find_end(data: &[u8]) -> Option<usize> {
105 let mut i = 1; while i < data.len() {
108 match data[i] {
109 END => return Some(i + 1),
110 ESC if i + 1 < data.len() => i += 2, ESC => return None, _ => i += 1,
113 }
114 }
115
116 None }
118}
119
120pub struct FrameBuilder {
122 verb: Verb,
123 payload: Payload,
124}
125
126impl FrameBuilder {
127 pub fn new(verb: Verb) -> Self {
128 FrameBuilder {
129 verb,
130 payload: Payload::new(),
131 }
132 }
133
134 pub fn byte(mut self, b: u8) -> Self {
136 self.payload.push_byte(b);
137 self
138 }
139
140 pub fn string(mut self, s: &str) -> Self {
142 self.payload.push_str(s);
143 self
144 }
145
146 pub fn bytes(mut self, data: &[u8]) -> Self {
148 for &b in data {
149 self.payload.push_byte(b);
150 }
151 self
152 }
153
154 pub fn u16_le(mut self, v: u16) -> Self {
156 self.payload.push_u16_le(v);
157 self
158 }
159
160 pub fn u32_le(mut self, v: u32) -> Self {
162 self.payload.push_u32_le(v);
163 self
164 }
165
166 pub fn build(self) -> Frame {
168 Frame {
169 verb: self.verb,
170 payload: self.payload,
171 }
172 }
173}
174
175impl Frame {
177 pub fn ping() -> Self {
179 Frame::simple(Verb::Ping)
180 }
181
182 pub fn stats() -> Self {
184 Frame::simple(Verb::Stats)
185 }
186
187 pub fn ok() -> Self {
189 Frame::simple(Verb::Ok)
190 }
191
192 pub fn error(message: &str) -> Self {
194 Frame::new(Verb::Error, Payload::from_string(message))
195 }
196
197 pub fn scan(path: &str, depth: u8) -> Self {
199 let mut payload = Payload::new();
200 let len = path.len();
202 if len <= 126 {
203 payload.push_byte((len as u8) + 0x80);
204 } else {
205 payload.push_byte(0xFF);
206 payload.push_u16_le(len as u16);
207 }
208 payload.push_str(path);
209 payload.push_byte(depth);
210
211 Frame::new(Verb::Scan, payload)
212 }
213
214 pub fn search(pattern: &str) -> Self {
216 Frame::new(Verb::Search, Payload::from_string(pattern))
217 }
218
219 pub fn search_path(path: &str, pattern: &str, max_results: u8) -> Self {
221 let mut payload = Payload::new();
222
223 let path_len = path.len();
225 if path_len <= 126 {
226 payload.push_byte((path_len as u8) + 0x80);
227 } else {
228 payload.push_byte(0xFF);
229 payload.push_u16_le(path_len as u16);
230 }
231 payload.push_str(path);
232
233 let pattern_len = pattern.len();
235 if pattern_len <= 126 {
236 payload.push_byte((pattern_len as u8) + 0x80);
237 } else {
238 payload.push_byte(0xFF);
239 payload.push_u16_le(pattern_len as u16);
240 }
241 payload.push_str(pattern);
242
243 payload.push_byte(max_results);
245
246 Frame::new(Verb::Search, payload)
247 }
248
249 pub fn format(mode: &str) -> Self {
251 Frame::new(Verb::Format, Payload::from_string(mode))
252 }
253
254 pub fn format_path(mode: &str, path: &str, depth: u8) -> Self {
256 let mut payload = Payload::new();
257
258 let mode_len = mode.len();
260 if mode_len <= 126 {
261 payload.push_byte((mode_len as u8) + 0x80);
262 } else {
263 payload.push_byte(0xFF);
264 payload.push_u16_le(mode_len as u16);
265 }
266 payload.push_str(mode);
267
268 let path_len = path.len();
270 if path_len <= 126 {
271 payload.push_byte((path_len as u8) + 0x80);
272 } else {
273 payload.push_byte(0xFF);
274 payload.push_u16_le(path_len as u16);
275 }
276 payload.push_str(path);
277
278 payload.push_byte(depth);
280
281 Frame::new(Verb::Format, payload)
282 }
283
284 pub fn remember(content: &str, keywords: &str, memory_type: &str) -> Self {
286 let mut payload = Payload::new();
287
288 let content_len = content.len();
290 if content_len <= 126 {
291 payload.push_byte((content_len as u8) + 0x80);
292 } else {
293 payload.push_byte(0xFF);
294 payload.push_u16_le(content_len as u16);
295 }
296 payload.push_str(content);
297
298 let keywords_len = keywords.len();
300 if keywords_len <= 126 {
301 payload.push_byte((keywords_len as u8) + 0x80);
302 } else {
303 payload.push_byte(0xFF);
304 payload.push_u16_le(keywords_len as u16);
305 }
306 payload.push_str(keywords);
307
308 let type_len = memory_type.len();
310 if type_len <= 126 {
311 payload.push_byte((type_len as u8) + 0x80);
312 } else {
313 payload.push_byte(0xFF);
314 payload.push_u16_le(type_len as u16);
315 }
316 payload.push_str(memory_type);
317
318 payload.push_byte(128); payload.push_byte(128); Frame::new(Verb::Remember, payload)
323 }
324
325 pub fn recall(keywords: &str, max_results: u8) -> Self {
327 let mut payload = Payload::new();
328
329 let keywords_len = keywords.len();
331 if keywords_len <= 126 {
332 payload.push_byte((keywords_len as u8) + 0x80);
333 } else {
334 payload.push_byte(0xFF);
335 payload.push_u16_le(keywords_len as u16);
336 }
337 payload.push_str(keywords);
338
339 payload.push_byte(max_results);
340
341 Frame::new(Verb::Recall, payload)
342 }
343
344 pub fn forget(memory_id: &str) -> Self {
346 Frame::new(Verb::Forget, Payload::from_string(memory_id))
347 }
348
349 pub fn m8_wave() -> Self {
351 Frame::simple(Verb::M8Wave)
352 }
353
354 pub fn audio(acoustic_bytes: &[u8]) -> Self {
359 let mut payload = Payload::new();
360 for &b in acoustic_bytes {
361 payload.push_byte(b);
362 }
363 Frame::new(Verb::Audio, payload)
364 }
365
366 pub fn audio_simple(text: &str, valence: f32, arousal: f32) -> Self {
370 let mut payload = Payload::new();
371
372 let text_len = text.len();
374 if text_len <= 126 {
375 payload.push_byte((text_len as u8) + 0x80);
376 } else {
377 payload.push_byte(0xFF);
378 payload.push_u16_le(text_len as u16);
379 }
380 payload.push_str(text);
381
382 payload.push_byte(((valence + 1.0) * 127.5) as u8);
386 payload.push_byte((arousal * 255.0) as u8);
387
388 Frame::new(Verb::Audio, payload)
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395
396 #[test]
397 fn test_ping_frame() {
398 let frame = Frame::ping();
399 let encoded = frame.encode();
400 assert_eq!(encoded, vec![0x05, 0x00]); }
402
403 #[test]
404 fn test_error_frame() {
405 let frame = Frame::error("not found");
406 let encoded = frame.encode();
407 assert_eq!(encoded[0], 0x15); assert_eq!(encoded[encoded.len() - 1], 0x00); }
410
411 #[test]
412 fn test_frame_roundtrip() {
413 let original = Frame::scan("/home/hue", 3);
414 let encoded = original.encode();
415 let decoded = Frame::decode(&encoded).unwrap();
416
417 assert_eq!(decoded.verb(), Verb::Scan);
418 }
419
420 #[test]
421 fn test_find_end() {
422 let data = vec![0x01, 0x41, 0x42, 0x00]; assert_eq!(Frame::find_end(&data), Some(4));
424 }
425
426 #[test]
427 fn test_find_end_with_escape() {
428 let data = vec![0x01, 0x1B, 0x00, 0x00]; assert_eq!(Frame::find_end(&data), Some(4));
430 }
431
432 #[test]
433 fn test_builder() {
434 let frame = FrameBuilder::new(Verb::Search)
435 .string("*.rs")
436 .build();
437
438 assert_eq!(frame.verb(), Verb::Search);
439 assert_eq!(frame.payload().as_str(), Some("*.rs"));
440 }
441}