1use super::Error;
2use crate::{protocol::byte_order::WriteBytesExt, traits::WireFormat};
3
4pub const ENTRY_SIZE: usize = 16;
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum EntryType {
9 FindService,
11 OfferService,
13 StopOfferService,
15 Subscribe,
17 SubscribeAck,
19}
20
21impl TryFrom<u8> for EntryType {
22 type Error = Error;
23 fn try_from(value: u8) -> Result<Self, Error> {
24 match value {
25 0x00 => Ok(EntryType::FindService),
26 0x01 => Ok(EntryType::OfferService),
27 0x02 => Ok(EntryType::StopOfferService),
28 0x06 => Ok(EntryType::Subscribe),
29 0x07 => Ok(EntryType::SubscribeAck),
30 _ => Err(Error::InvalidEntryType(value)),
31 }
32 }
33}
34
35impl From<EntryType> for u8 {
36 fn from(service_entry_type: EntryType) -> u8 {
37 match service_entry_type {
38 EntryType::FindService => 0x00,
39 EntryType::OfferService => 0x01,
40 EntryType::StopOfferService => 0x02,
41 EntryType::Subscribe => 0x06,
42 EntryType::SubscribeAck => 0x07,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub struct OptionsCount {
50 pub first_options_count: u8,
52 pub second_options_count: u8,
54}
55
56impl From<u8> for OptionsCount {
57 fn from(value: u8) -> Self {
58 let first_options_count = (value & 0xf0) >> 4;
59 let second_options_count = value & 0x0f;
60
61 Self {
62 first_options_count,
63 second_options_count,
64 }
65 }
66}
67
68impl From<OptionsCount> for u8 {
69 fn from(options_count: OptionsCount) -> u8 {
70 ((options_count.first_options_count << 4) & 0xf0)
71 | (options_count.second_options_count & 0x0f)
72 }
73}
74
75impl OptionsCount {
76 #[must_use]
79 pub const fn new(first_options_count: u8, second_options_count: u8) -> Self {
80 assert!(first_options_count < 16);
81 assert!(second_options_count < 16);
82 OptionsCount {
83 first_options_count,
84 second_options_count,
85 }
86 }
87}
88
89#[derive(Clone, Debug, Eq, PartialEq)]
91pub struct EventGroupEntry {
92 pub index_first_options_run: u8,
94 pub index_second_options_run: u8,
96 pub options_count: OptionsCount,
98 pub service_id: u16,
100 pub instance_id: u16,
102 pub major_version: u8,
104 pub ttl: u32,
106 pub counter: u16,
108 pub event_group_id: u16,
110}
111
112impl EventGroupEntry {
113 #[must_use]
115 pub const fn new(
116 service_id: u16,
117 instance_id: u16,
118 major_version: u8,
119 ttl: u32,
120 event_group_id: u16,
121 ) -> Self {
122 Self {
123 index_first_options_run: 0,
124 index_second_options_run: 0,
125 options_count: OptionsCount::new(1, 0),
126 service_id,
127 instance_id,
128 major_version,
129 ttl,
130 counter: 0,
131 event_group_id,
132 }
133 }
134}
135
136impl WireFormat for EventGroupEntry {
137 fn required_size(&self) -> usize {
138 16
139 }
140
141 fn encode<T: embedded_io::Write>(
142 &self,
143 writer: &mut T,
144 ) -> Result<usize, crate::protocol::Error> {
145 writer.write_u8(self.index_first_options_run)?;
146 writer.write_u8(self.index_second_options_run)?;
147 writer.write_u8(u8::from(self.options_count))?;
148 writer.write_u16_be(self.service_id)?;
149 writer.write_u16_be(self.instance_id)?;
150 writer.write_u8(self.major_version)?;
151 writer.write_u24_be(self.ttl)?;
152 writer.write_u16_be(self.counter)?;
153 writer.write_u16_be(self.event_group_id)?;
154 Ok(16)
155 }
156}
157
158#[derive(Clone, Debug, Eq, PartialEq)]
160pub struct ServiceEntry {
161 pub index_first_options_run: u8,
163 pub index_second_options_run: u8,
165 pub options_count: OptionsCount,
167 pub service_id: u16,
169 pub instance_id: u16,
171 pub major_version: u8,
173 pub ttl: u32,
175 pub minor_version: u32,
177}
178
179impl ServiceEntry {
180 #[must_use]
182 pub const fn find(service_id: u16) -> Self {
183 Self {
184 index_first_options_run: 0,
185 index_second_options_run: 0,
186 options_count: OptionsCount::new(1, 0),
187 service_id,
188 instance_id: 0xFFFF,
189 major_version: 0xFF,
190 ttl: 0x00FF_FFFF,
191 minor_version: 0xFFFF_FFFF,
192 }
193 }
194}
195
196impl WireFormat for ServiceEntry {
197 fn required_size(&self) -> usize {
198 16
199 }
200
201 fn encode<W: embedded_io::Write>(
202 &self,
203 writer: &mut W,
204 ) -> Result<usize, crate::protocol::Error> {
205 writer.write_u8(self.index_first_options_run)?;
206 writer.write_u8(self.index_second_options_run)?;
207 writer.write_u8(u8::from(self.options_count))?;
208 writer.write_u16_be(self.service_id)?;
209 writer.write_u16_be(self.instance_id)?;
210 writer.write_u8(self.major_version)?;
211 writer.write_u24_be(self.ttl)?;
212 writer.write_u32_be(self.minor_version)?;
213 Ok(16)
214 }
215}
216
217#[derive(Clone, Debug, Eq, PartialEq)]
219pub enum Entry {
220 FindService(ServiceEntry),
222 OfferService(ServiceEntry),
224 StopOfferService(ServiceEntry),
226 SubscribeEventGroup(EventGroupEntry),
228 SubscribeAckEventGroup(EventGroupEntry),
230}
231
232impl Entry {
233 #[must_use]
235 pub fn first_options_count(&self) -> u8 {
236 match self {
237 Entry::FindService(service_entry)
238 | Entry::OfferService(service_entry)
239 | Entry::StopOfferService(service_entry) => {
240 service_entry.options_count.first_options_count
241 }
242 Entry::SubscribeEventGroup(event_group_entry)
243 | Entry::SubscribeAckEventGroup(event_group_entry) => {
244 event_group_entry.options_count.first_options_count
245 }
246 }
247 }
248
249 #[must_use]
251 pub fn second_options_count(&self) -> u8 {
252 match self {
253 Entry::FindService(service_entry)
254 | Entry::OfferService(service_entry)
255 | Entry::StopOfferService(service_entry) => {
256 service_entry.options_count.second_options_count
257 }
258 Entry::SubscribeEventGroup(event_group_entry)
259 | Entry::SubscribeAckEventGroup(event_group_entry) => {
260 event_group_entry.options_count.second_options_count
261 }
262 }
263 }
264
265 #[must_use]
267 pub fn total_options_count(&self) -> u8 {
268 self.first_options_count() + self.second_options_count()
269 }
270}
271
272impl WireFormat for Entry {
273 fn required_size(&self) -> usize {
274 1 + match self {
275 Entry::FindService(service_entry)
276 | Entry::OfferService(service_entry)
277 | Entry::StopOfferService(service_entry) => service_entry.required_size(),
278 Entry::SubscribeEventGroup(event_group_entry)
279 | Entry::SubscribeAckEventGroup(event_group_entry) => event_group_entry.required_size(),
280 }
281 }
282
283 fn encode<W: embedded_io::Write>(
284 &self,
285 writer: &mut W,
286 ) -> Result<usize, crate::protocol::Error> {
287 match self {
288 Entry::FindService(service_entry) => {
289 writer.write_u8(u8::from(EntryType::FindService))?;
290 service_entry.encode(writer)
291 }
292 Entry::OfferService(service_entry) => {
293 writer.write_u8(u8::from(EntryType::OfferService))?;
294 service_entry.encode(writer)
295 }
296 Entry::StopOfferService(service_entry) => {
297 writer.write_u8(u8::from(EntryType::StopOfferService))?;
298 service_entry.encode(writer)
299 }
300 Entry::SubscribeEventGroup(event_group_entry) => {
301 writer.write_u8(u8::from(EntryType::Subscribe))?;
302 event_group_entry.encode(writer)
303 }
304 Entry::SubscribeAckEventGroup(event_group_entry) => {
305 writer.write_u8(u8::from(EntryType::SubscribeAck))?;
306 event_group_entry.encode(writer)
307 }
308 }
309 }
310}
311
312#[derive(Clone, Copy, Debug)]
328pub struct EntryView<'a>(&'a [u8; ENTRY_SIZE]);
329
330impl EntryView<'_> {
331 pub fn entry_type(&self) -> Result<EntryType, Error> {
337 EntryType::try_from(self.0[0])
338 }
339
340 #[must_use]
342 pub fn index_first_options_run(&self) -> u8 {
343 self.0[1]
344 }
345
346 #[must_use]
348 pub fn index_second_options_run(&self) -> u8 {
349 self.0[2]
350 }
351
352 #[must_use]
354 pub fn options_count(&self) -> OptionsCount {
355 OptionsCount::from(self.0[3])
356 }
357
358 #[must_use]
360 pub fn service_id(&self) -> u16 {
361 u16::from_be_bytes([self.0[4], self.0[5]])
362 }
363
364 #[must_use]
366 pub fn instance_id(&self) -> u16 {
367 u16::from_be_bytes([self.0[6], self.0[7]])
368 }
369
370 #[must_use]
372 pub fn major_version(&self) -> u8 {
373 self.0[8]
374 }
375
376 #[must_use]
378 pub fn ttl(&self) -> u32 {
379 u32::from_be_bytes([0, self.0[9], self.0[10], self.0[11]])
380 }
381
382 #[must_use]
384 pub fn minor_version(&self) -> u32 {
385 u32::from_be_bytes([self.0[12], self.0[13], self.0[14], self.0[15]])
386 }
387
388 #[must_use]
390 pub fn counter(&self) -> u16 {
391 u16::from_be_bytes([self.0[12], self.0[13]]) & 0x000f
392 }
393
394 #[must_use]
396 pub fn event_group_id(&self) -> u16 {
397 u16::from_be_bytes([self.0[14], self.0[15]])
398 }
399
400 pub fn to_owned(&self) -> Result<Entry, Error> {
406 let entry_type = self.entry_type()?;
407 match entry_type {
408 EntryType::FindService => Ok(Entry::FindService(self.to_service_entry())),
409 EntryType::OfferService => Ok(Entry::OfferService(self.to_service_entry())),
410 EntryType::StopOfferService => Ok(Entry::StopOfferService(self.to_service_entry())),
411 EntryType::Subscribe => Ok(Entry::SubscribeEventGroup(self.to_event_group_entry())),
412 EntryType::SubscribeAck => {
413 Ok(Entry::SubscribeAckEventGroup(self.to_event_group_entry()))
414 }
415 }
416 }
417
418 fn to_service_entry(self) -> ServiceEntry {
419 ServiceEntry {
420 index_first_options_run: self.index_first_options_run(),
421 index_second_options_run: self.index_second_options_run(),
422 options_count: self.options_count(),
423 service_id: self.service_id(),
424 instance_id: self.instance_id(),
425 major_version: self.major_version(),
426 ttl: self.ttl(),
427 minor_version: self.minor_version(),
428 }
429 }
430
431 fn to_event_group_entry(self) -> EventGroupEntry {
432 EventGroupEntry {
433 index_first_options_run: self.index_first_options_run(),
434 index_second_options_run: self.index_second_options_run(),
435 options_count: self.options_count(),
436 service_id: self.service_id(),
437 instance_id: self.instance_id(),
438 major_version: self.major_version(),
439 ttl: self.ttl(),
440 counter: self.counter(),
441 event_group_id: self.event_group_id(),
442 }
443 }
444}
445
446pub struct EntryIter<'a> {
449 remaining: &'a [u8],
450}
451
452impl<'a> EntryIter<'a> {
453 pub(crate) fn new(buf: &'a [u8]) -> Self {
454 Self { remaining: buf }
455 }
456}
457
458impl<'a> Iterator for EntryIter<'a> {
459 type Item = EntryView<'a>;
460
461 fn next(&mut self) -> Option<Self::Item> {
462 if self.remaining.len() < ENTRY_SIZE {
463 return None;
464 }
465 let entry_bytes: &[u8; ENTRY_SIZE] = self.remaining[..ENTRY_SIZE]
466 .try_into()
467 .expect("length checked above");
468 self.remaining = &self.remaining[ENTRY_SIZE..];
469 Some(EntryView(entry_bytes))
470 }
471
472 fn size_hint(&self) -> (usize, Option<usize>) {
473 let n = self.remaining.len() / ENTRY_SIZE;
474 (n, Some(n))
475 }
476}
477
478impl ExactSizeIterator for EntryIter<'_> {}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483
484 fn encode_entry(entry: &Entry) -> [u8; 17] {
485 let mut buf = [0u8; 17];
486 entry.encode(&mut buf.as_mut_slice()).unwrap();
487 buf
488 }
489
490 fn make_service_entry() -> ServiceEntry {
491 ServiceEntry {
492 index_first_options_run: 1,
493 index_second_options_run: 2,
494 options_count: OptionsCount::new(3, 4),
495 service_id: 0x1234,
496 instance_id: 0x5678,
497 major_version: 0x01,
498 ttl: 0x0000_00FF,
499 minor_version: 0x0000_0002,
500 }
501 }
502
503 fn make_event_group_entry() -> EventGroupEntry {
504 EventGroupEntry {
505 index_first_options_run: 1,
506 index_second_options_run: 2,
507 options_count: OptionsCount::new(3, 4),
508 service_id: 0xABCD,
509 instance_id: 0x0001,
510 major_version: 0x02,
511 ttl: 0x0000_0064,
512 counter: 0x0003,
513 event_group_id: 0x0010,
514 }
515 }
516
517 #[test]
520 fn entry_type_try_from_all_valid_values() {
521 assert_eq!(EntryType::try_from(0x00).unwrap(), EntryType::FindService);
522 assert_eq!(EntryType::try_from(0x01).unwrap(), EntryType::OfferService);
523 assert_eq!(
524 EntryType::try_from(0x02).unwrap(),
525 EntryType::StopOfferService
526 );
527 assert_eq!(EntryType::try_from(0x06).unwrap(), EntryType::Subscribe);
528 assert_eq!(EntryType::try_from(0x07).unwrap(), EntryType::SubscribeAck);
529 }
530
531 #[test]
532 fn entry_type_try_from_invalid_returns_error() {
533 assert!(matches!(
534 EntryType::try_from(0x03),
535 Err(Error::InvalidEntryType(0x03))
536 ));
537 }
538
539 #[test]
540 fn entry_type_into_u8_all_variants() {
541 assert_eq!(u8::from(EntryType::FindService), 0x00);
542 assert_eq!(u8::from(EntryType::OfferService), 0x01);
543 assert_eq!(u8::from(EntryType::StopOfferService), 0x02);
544 assert_eq!(u8::from(EntryType::Subscribe), 0x06);
545 assert_eq!(u8::from(EntryType::SubscribeAck), 0x07);
546 }
547
548 #[test]
551 fn options_count_round_trip() {
552 let oc = OptionsCount::new(3, 7);
553 let byte = u8::from(oc);
554 let decoded = OptionsCount::from(byte);
555 assert_eq!(decoded.first_options_count, 3);
556 assert_eq!(decoded.second_options_count, 7);
557 }
558
559 #[test]
562 fn service_entry_required_size() {
563 assert_eq!(make_service_entry().required_size(), 16);
564 }
565
566 #[test]
567 fn event_group_entry_required_size() {
568 assert_eq!(make_event_group_entry().required_size(), 16);
569 }
570
571 #[test]
572 fn entry_required_size_all_variants() {
573 assert_eq!(Entry::FindService(make_service_entry()).required_size(), 17);
574 assert_eq!(
575 Entry::OfferService(make_service_entry()).required_size(),
576 17
577 );
578 assert_eq!(
579 Entry::StopOfferService(make_service_entry()).required_size(),
580 17
581 );
582 assert_eq!(
583 Entry::SubscribeEventGroup(make_event_group_entry()).required_size(),
584 17
585 );
586 assert_eq!(
587 Entry::SubscribeAckEventGroup(make_event_group_entry()).required_size(),
588 17
589 );
590 }
591
592 #[test]
595 fn entry_options_count_service_variants() {
596 let se = make_service_entry(); for entry in [
598 Entry::FindService(se),
599 Entry::OfferService(make_service_entry()),
600 Entry::StopOfferService(make_service_entry()),
601 ] {
602 assert_eq!(entry.first_options_count(), 3);
603 assert_eq!(entry.second_options_count(), 4);
604 assert_eq!(entry.total_options_count(), 7);
605 }
606 }
607
608 #[test]
609 fn entry_options_count_event_group_variants() {
610 let eg = make_event_group_entry(); for entry in [
612 Entry::SubscribeEventGroup(eg.clone()),
613 Entry::SubscribeAckEventGroup(eg),
614 ] {
615 assert_eq!(entry.first_options_count(), 3);
616 assert_eq!(entry.second_options_count(), 4);
617 assert_eq!(entry.total_options_count(), 7);
618 }
619 }
620
621 #[test]
624 fn find_service_entry_round_trips() {
625 let entry = Entry::FindService(make_service_entry());
626 let buf = encode_entry(&entry);
627 let entry_bytes: &[u8; ENTRY_SIZE] = buf[..ENTRY_SIZE].try_into().unwrap();
630 let view = EntryView(entry_bytes);
631 assert_eq!(view.to_owned().unwrap(), entry);
632 }
633
634 #[test]
635 fn offer_service_entry_round_trips() {
636 let entry = Entry::OfferService(make_service_entry());
637 let buf = encode_entry(&entry);
638 let entry_bytes: &[u8; ENTRY_SIZE] = buf[..ENTRY_SIZE].try_into().unwrap();
639 let view = EntryView(entry_bytes);
640 assert_eq!(view.to_owned().unwrap(), entry);
641 }
642
643 #[test]
644 fn stop_offer_service_entry_round_trips() {
645 let entry = Entry::StopOfferService(make_service_entry());
646 let buf = encode_entry(&entry);
647 let entry_bytes: &[u8; ENTRY_SIZE] = buf[..ENTRY_SIZE].try_into().unwrap();
648 let view = EntryView(entry_bytes);
649 assert_eq!(view.to_owned().unwrap(), entry);
650 }
651
652 #[test]
653 fn subscribe_event_group_entry_round_trips() {
654 let entry = Entry::SubscribeEventGroup(make_event_group_entry());
655 let buf = encode_entry(&entry);
656 let entry_bytes: &[u8; ENTRY_SIZE] = buf[..ENTRY_SIZE].try_into().unwrap();
657 let view = EntryView(entry_bytes);
658 assert_eq!(view.to_owned().unwrap(), entry);
659 }
660
661 #[test]
662 fn subscribe_ack_event_group_entry_round_trips() {
663 let entry = Entry::SubscribeAckEventGroup(make_event_group_entry());
664 let buf = encode_entry(&entry);
665 let entry_bytes: &[u8; ENTRY_SIZE] = buf[..ENTRY_SIZE].try_into().unwrap();
666 let view = EntryView(entry_bytes);
667 assert_eq!(view.to_owned().unwrap(), entry);
668 }
669
670 #[test]
671 fn entry_view_invalid_type_returns_error() {
672 let buf: [u8; ENTRY_SIZE] = [0x03; ENTRY_SIZE]; let view = EntryView(&buf);
674 assert!(matches!(
675 view.to_owned(),
676 Err(Error::InvalidEntryType(0x03))
677 ));
678 }
679
680 #[test]
683 fn entry_iter_empty() {
684 let iter = EntryIter::new(&[]);
685 assert_eq!(iter.len(), 0);
686 }
687
688 #[test]
689 fn entry_iter_two_entries() {
690 let e1 = Entry::FindService(make_service_entry());
691 let e2 = Entry::SubscribeEventGroup(make_event_group_entry());
692 let buf1 = encode_entry(&e1);
693 let buf2 = encode_entry(&e2);
694 let mut combined = [0u8; 32];
696 combined[..16].copy_from_slice(&buf1[..16]);
697 combined[16..32].copy_from_slice(&buf2[..16]);
698
699 let mut iter = EntryIter::new(&combined);
700 assert_eq!(iter.len(), 2);
701 assert_eq!(iter.next().unwrap().to_owned().unwrap(), e1);
702 assert_eq!(iter.next().unwrap().to_owned().unwrap(), e2);
703 assert!(iter.next().is_none());
704 }
705}