1#![allow(clippy::enum_variant_names)]
9#![allow(clippy::len_without_is_empty)]
10#![allow(clippy::len_zero)]
11
12use bitfield_struct::bitfield;
13use bitflags::bitflags;
14#[cfg(test)]
15use linux_cec_macros::opcode_test;
16use linux_cec_macros::{BitfieldSpecifier, Operand};
17use num_enum::{IntoPrimitive, TryFromPrimitive};
18use std::convert::TryFrom;
19use std::str::FromStr;
20use strum::{Display, EnumString};
21
22use crate::{constants, Error, PhysicalAddress, Range, Result};
23
24pub type AnalogueFrequency = u16; pub type DurationHours = BcdByte;
26pub type Hour = BcdByte<0, 23>;
27pub type Minute = BcdByte<0, 59>;
28pub type ShortAudioDescriptor = [u8; 3];
29pub type UiFunctionMedia = u8;
30pub type UiFunctionSelectAudioInput = u8;
31pub type UiFunctionSelectAvInput = u8;
32
33pub trait OperandEncodable: Sized {
34 fn to_bytes(&self, buf: &mut impl Extend<u8>);
35 fn try_from_bytes(bytes: &[u8]) -> Result<Self>;
36 #[must_use]
37 fn len(&self) -> usize;
38 #[must_use]
39 fn expected_len() -> Range<usize>;
40}
41
42impl OperandEncodable for PhysicalAddress {
43 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
44 <u16 as OperandEncodable>::to_bytes(&self.0, buf);
45 }
46
47 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
48 Ok(PhysicalAddress(u16::try_from_bytes(bytes)?))
49 }
50
51 fn len(&self) -> usize {
52 2
53 }
54
55 fn expected_len() -> Range<usize> {
56 Range::AtLeast(2)
57 }
58}
59
60#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
61#[repr(transparent)]
62pub struct Delay(u8);
63
64impl From<Delay> for u8 {
65 #[inline]
66 fn from(val: Delay) -> u8 {
67 val.0
68 }
69}
70
71impl Delay {
72 const MIN: u8 = 1;
73 const MAX: u8 = 251;
74
75 #[must_use]
76 pub fn is_valid(&self) -> bool {
77 Range::Interval {
78 min: Delay::MIN as usize,
79 max: Delay::MAX as usize,
80 }
81 .check(self.0, "value")
82 .is_ok()
83 }
84}
85
86impl TryFrom<u8> for Delay {
87 type Error = Error;
88
89 fn try_from(val: u8) -> Result<Delay> {
90 Range::Interval {
91 min: Delay::MIN as usize,
92 max: Delay::MAX as usize,
93 }
94 .check(val, "value")?;
95 Ok(Delay(val))
96 }
97}
98
99impl OperandEncodable for Delay {
100 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
101 buf.extend([self.0]);
102 }
103
104 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
105 Self::expected_len().check(bytes.len(), "bytes")?;
106 Delay::try_from(bytes[0])
107 }
108
109 fn len(&self) -> usize {
110 1
111 }
112
113 fn expected_len() -> Range<usize> {
114 Range::AtLeast(1)
115 }
116}
117
118#[cfg(test)]
119mod test_delay {
120 use super::*;
121
122 opcode_test! {
123 name: _min,
124 ty: Delay,
125 instance: Delay(1),
126 bytes: [1],
127 extra: [Overfull],
128 }
129
130 opcode_test! {
131 name: _max,
132 ty: Delay,
133 instance: Delay(251),
134 bytes: [251],
135 extra: [Overfull],
136 }
137
138 #[test]
139 fn test_decode_out_of_range() {
140 assert_eq!(
141 <Delay as OperandEncodable>::try_from_bytes(&[0]),
142 Err(Error::OutOfRange {
143 got: 0,
144 expected: Range::from(1..=251),
145 quantity: "value"
146 })
147 );
148
149 assert_eq!(
150 <Delay as OperandEncodable>::try_from_bytes(&[252]),
151 Err(Error::OutOfRange {
152 got: 252,
153 expected: Range::from(1..=251),
154 quantity: "value"
155 })
156 );
157 }
158
159 #[test]
160 fn test_encode_out_of_range() {
161 let mut buf = Vec::new();
162 Delay(0).to_bytes(&mut buf);
163 assert_eq!(&buf, &[0]);
164
165 let mut buf = Vec::new();
166 Delay(255).to_bytes(&mut buf);
167 assert_eq!(&buf, &[255]);
168 }
169
170 #[test]
171 fn test_decode_empty() {
172 assert_eq!(
173 <Delay as OperandEncodable>::try_from_bytes(&[]),
174 Err(Error::OutOfRange {
175 got: 0,
176 expected: Range::AtLeast(1),
177 quantity: "bytes"
178 })
179 );
180 }
181}
182
183#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
184#[repr(transparent)]
185pub struct AudioVolumeLevel(u8);
186
187impl From<AudioVolumeLevel> for u8 {
188 #[inline]
189 fn from(val: AudioVolumeLevel) -> u8 {
190 val.0
191 }
192}
193
194impl AudioVolumeLevel {
195 const MIN: u8 = 0;
196 const MAX: u8 = 100;
197 const NO_CHANGE_VAL: u8 = 0x7F;
198 pub const NO_CHANGE: AudioVolumeLevel = AudioVolumeLevel(Self::NO_CHANGE_VAL);
199
200 #[must_use]
201 pub fn is_valid(&self) -> bool {
202 if self.0 == AudioVolumeLevel::NO_CHANGE_VAL {
203 return true;
204 }
205
206 Range::Interval {
207 min: AudioVolumeLevel::MIN as usize,
208 max: AudioVolumeLevel::MAX as usize,
209 }
210 .check(self.0, "value")
211 .is_ok()
212 }
213}
214
215impl TryFrom<u8> for AudioVolumeLevel {
216 type Error = Error;
217
218 fn try_from(val: u8) -> Result<AudioVolumeLevel> {
219 Range::Interval {
220 min: AudioVolumeLevel::MIN as usize,
221 max: AudioVolumeLevel::MAX as usize,
222 }
223 .check(val, "value")?;
224 Ok(AudioVolumeLevel(val))
225 }
226}
227
228impl OperandEncodable for AudioVolumeLevel {
229 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
230 buf.extend([self.0]);
231 }
232
233 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
234 Self::expected_len().check(bytes.len(), "bytes")?;
235 if bytes[0] == AudioVolumeLevel::NO_CHANGE_VAL {
236 return Ok(AudioVolumeLevel(bytes[0]));
237 }
238 AudioVolumeLevel::try_from(bytes[0])
239 }
240
241 fn len(&self) -> usize {
242 1
243 }
244
245 fn expected_len() -> Range<usize> {
246 Range::AtLeast(1)
247 }
248}
249
250#[cfg(test)]
251mod test_audio_volume_level {
252 use super::*;
253
254 opcode_test! {
255 name: _min,
256 ty: AudioVolumeLevel,
257 instance: AudioVolumeLevel(0),
258 bytes: [0],
259 extra: [Overfull],
260 }
261
262 opcode_test! {
263 name: _max,
264 ty: AudioVolumeLevel,
265 instance: AudioVolumeLevel(100),
266 bytes: [100],
267 extra: [Overfull],
268 }
269
270 opcode_test! {
271 name: _keep_current,
272 ty: AudioVolumeLevel,
273 instance: AudioVolumeLevel(0x7f),
274 bytes: [0x7f],
275 extra: [Overfull],
276 }
277
278 #[test]
279 fn test_decode_out_of_range() {
280 assert_eq!(
281 <AudioVolumeLevel as OperandEncodable>::try_from_bytes(&[101]),
282 Err(Error::OutOfRange {
283 got: 101,
284 expected: Range::from(0..=100),
285 quantity: "value"
286 })
287 );
288
289 assert_eq!(
290 <AudioVolumeLevel as OperandEncodable>::try_from_bytes(&[128]),
291 Err(Error::OutOfRange {
292 got: 128,
293 expected: Range::from(0..=100),
294 quantity: "value"
295 })
296 );
297 }
298
299 #[test]
300 fn test_encode_out_of_range() {
301 let mut buf = Vec::new();
302 AudioVolumeLevel(101).to_bytes(&mut buf);
303 assert_eq!(&buf, &[101]);
304
305 let mut buf = Vec::new();
306 AudioVolumeLevel(128).to_bytes(&mut buf);
307 assert_eq!(&buf, &[128]);
308 }
309
310 #[test]
311 fn test_decode_empty() {
312 assert_eq!(
313 <AudioVolumeLevel as OperandEncodable>::try_from_bytes(&[]),
314 Err(Error::OutOfRange {
315 got: 0,
316 expected: Range::AtLeast(1),
317 quantity: "bytes"
318 })
319 );
320 }
321}
322
323impl OperandEncodable for u8 {
324 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
325 buf.extend([*self]);
326 }
327
328 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
329 Self::expected_len().check(bytes.len(), "bytes")?;
330 Ok(bytes[0])
331 }
332
333 fn len(&self) -> usize {
334 1
335 }
336
337 fn expected_len() -> Range<usize> {
338 Range::AtLeast(1)
339 }
340}
341
342#[cfg(test)]
343mod test_u8 {
344 use super::*;
345
346 opcode_test! {
347 ty: u8,
348 instance: 0x56,
349 bytes: [0x56],
350 extra: [Overfull],
351 }
352
353 #[test]
354 fn test_decode_empty() {
355 assert_eq!(
356 <u8 as OperandEncodable>::try_from_bytes(&[]),
357 Err(Error::OutOfRange {
358 got: 0,
359 expected: Range::AtLeast(1),
360 quantity: "bytes"
361 })
362 );
363 }
364}
365
366impl<T: OperandEncodable> OperandEncodable for Option<T> {
367 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
368 if let Some(data) = self {
369 data.to_bytes(buf);
370 }
371 }
372
373 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
374 if bytes.len() < 1 {
375 Ok(None)
376 } else {
377 Ok(Some(T::try_from_bytes(bytes)?))
378 }
379 }
380
381 fn len(&self) -> usize {
382 if let Some(ref data) = self {
383 data.len()
384 } else {
385 0
386 }
387 }
388
389 fn expected_len() -> Range<usize> {
390 Range::AtLeast(0)
391 }
392}
393
394#[cfg(test)]
395mod test_option {
396 use super::*;
397
398 opcode_test! {
399 name: _none,
400 ty: Option<u8>,
401 instance: None,
402 bytes: [],
403 }
404
405 opcode_test! {
406 name: _some,
407 ty: Option<u8>,
408 instance: Some(0x56),
409 bytes: [0x56],
410 extra: [Overfull],
411 }
412}
413
414impl<const S: usize> OperandEncodable for [u8; S] {
415 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
416 buf.extend(*self);
417 }
418
419 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
420 Self::expected_len().check(bytes.len(), "bytes")?;
421 Ok(bytes[..S].try_into().unwrap())
422 }
423
424 fn len(&self) -> usize {
425 S
426 }
427
428 fn expected_len() -> Range<usize> {
429 Range::AtLeast(S)
430 }
431}
432
433#[cfg(test)]
434mod test_array {
435 use super::*;
436
437 opcode_test! {
438 name: _1,
439 ty: [u8; 1],
440 instance: [0x56],
441 bytes: [0x56],
442 extra: [Overfull],
443 }
444
445 opcode_test! {
446 name: _2,
447 ty: [u8; 2],
448 instance: [0x56, 0x78],
449 bytes: [0x56, 0x78],
450 extra: [Overfull],
451 }
452
453 opcode_test! {
454 name: _3,
455 ty: [u8; 3],
456 instance: [0x56, 0x78, 0x9A],
457 bytes: [0x56, 0x78, 0x9A],
458 extra: [Overfull],
459 }
460
461 #[test]
462 fn test_decode_missing_byte() {
463 assert_eq!(
464 <[u8; 2] as OperandEncodable>::try_from_bytes(&[0x12]),
465 Err(Error::OutOfRange {
466 got: 1,
467 expected: Range::AtLeast(2),
468 quantity: "bytes"
469 })
470 );
471 }
472
473 #[test]
474 fn test_decode_missing_bytes() {
475 assert_eq!(
476 <[u8; 3] as OperandEncodable>::try_from_bytes(&[0x12]),
477 Err(Error::OutOfRange {
478 got: 1,
479 expected: Range::AtLeast(3),
480 quantity: "bytes"
481 })
482 );
483 }
484
485 #[test]
486 fn test_decode_empty() {
487 assert_eq!(
488 <[u8; 1] as OperandEncodable>::try_from_bytes(&[]),
489 Err(Error::OutOfRange {
490 got: 0,
491 expected: Range::AtLeast(1),
492 quantity: "bytes"
493 })
494 );
495 }
496}
497
498impl OperandEncodable for u16 {
499 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
500 buf.extend([
501 u8::try_from(*self >> 8).unwrap(),
502 u8::try_from(*self & 0xFF).unwrap(),
503 ]);
504 }
505
506 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
507 Self::expected_len().check(bytes.len(), "bytes")?;
508 Ok((u16::from(bytes[0]) << 8) | u16::from(bytes[1]))
509 }
510
511 fn len(&self) -> usize {
512 2
513 }
514
515 fn expected_len() -> Range<usize> {
516 Range::AtLeast(2)
517 }
518}
519
520#[cfg(test)]
521mod test_u16 {
522 use super::*;
523
524 opcode_test! {
525 ty: u16,
526 instance: 0x5678,
527 bytes: [0x56, 0x78],
528 extra: [Overfull],
529 }
530
531 #[test]
532 fn test_decode_underfull() {
533 assert_eq!(
534 <u16 as OperandEncodable>::try_from_bytes(&[0x56]),
535 Err(Error::OutOfRange {
536 got: 1,
537 expected: Range::AtLeast(2),
538 quantity: "bytes"
539 })
540 );
541 }
542
543 #[test]
544 fn test_decode_empty() {
545 assert_eq!(
546 <u16 as OperandEncodable>::try_from_bytes(&[]),
547 Err(Error::OutOfRange {
548 got: 0,
549 expected: Range::AtLeast(2),
550 quantity: "bytes"
551 })
552 );
553 }
554}
555
556impl OperandEncodable for bool {
557 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
558 buf.extend([u8::from(*self)]);
559 }
560
561 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
562 Self::expected_len().check(bytes.len(), "bytes")?;
563 Ok(bytes[0] != 0)
564 }
565
566 fn len(&self) -> usize {
567 1
568 }
569
570 fn expected_len() -> Range<usize> {
571 Range::AtLeast(1)
572 }
573}
574
575#[cfg(test)]
576mod test_bool {
577 use super::*;
578
579 opcode_test! {
580 name: _true,
581 ty: bool,
582 instance: true,
583 bytes: [0x01],
584 extra: [Overfull],
585 }
586
587 opcode_test! {
588 name: _false,
589 ty: bool,
590 instance: false,
591 bytes: [0x00],
592 extra: [Overfull],
593 }
594
595 #[test]
596 fn test_decode_nb() {
597 assert_eq!(
598 <bool as OperandEncodable>::try_from_bytes(&[0x02]),
599 Ok(true)
600 );
601 }
602
603 #[test]
604 fn test_decode_empty() {
605 assert_eq!(
606 <bool as OperandEncodable>::try_from_bytes(&[]),
607 Err(Error::OutOfRange {
608 got: 0,
609 expected: Range::AtLeast(1),
610 quantity: "bytes"
611 })
612 );
613 }
614}
615
616pub trait TaggedLengthBuffer: Sized {
617 type FixedParam: Into<u8> + TryFrom<u8> + Copy;
618
619 fn try_new(first: Self::FixedParam, extra: &[u8]) -> Result<Self>;
620
621 #[must_use]
622 fn fixed_param(&self) -> Self::FixedParam;
623
624 #[must_use]
625 fn extra_params(&self) -> &[u8];
626
627 #[must_use]
628 fn expected_len() -> Range<usize> {
629 Range::AtLeast(1)
630 }
631}
632
633impl<T: TryFrom<u8> + Into<u8> + Copy, U: TaggedLengthBuffer<FixedParam = T>> OperandEncodable for U
634where
635 Error: From<<T as TryFrom<u8>>::Error>,
636{
637 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
638 let head: u8 = self.fixed_param().into();
639 let extra_params = self.extra_params();
640 if extra_params.is_empty() {
641 buf.extend([head & 0x7F]);
642 } else {
643 buf.extend([head | 0x80]);
644 buf.extend(
645 extra_params
646 .iter()
647 .take(extra_params.len() - 1)
648 .map(|b| b | 0x80),
649 );
650 buf.extend([extra_params.last().unwrap() & 0x7F]);
651 }
652 }
653
654 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
655 Range::AtLeast(1).check(bytes.len(), "bytes")?;
656 let first = T::try_from(bytes[0] & 0x7F)?;
657 let mut extra = Vec::new();
658 if bytes[0] & 0x80 == 0x80 {
659 let mut offset = 1;
660 while offset < bytes.len() {
661 let byte = bytes[offset];
662 extra.push(byte & 0x7F);
663 if (byte & 0x80) == 0 {
664 break;
665 }
666 offset += 1;
667 }
668 }
669 Self::try_new(first, &extra)
670 }
671
672 fn len(&self) -> usize {
673 1 + self.extra_params().len()
674 }
675
676 fn expected_len() -> Range<usize> {
677 Range::AtLeast(1)
678 }
679}
680
681#[cfg(test)]
682mod test_tagged_length_buffer {
683 use super::*;
684
685 #[derive(PartialEq, Debug, Copy, Clone)]
686 #[repr(transparent)]
687 struct U8(pub u8);
688
689 impl Into<u8> for U8 {
690 fn into(self) -> u8 {
691 self.0
692 }
693 }
694
695 impl TryFrom<u8> for U8 {
696 type Error = Error;
697 fn try_from(val: u8) -> Result<U8> {
698 Range::AtMost(0x7F).check(val, "value")?;
699 Ok(U8(val))
700 }
701 }
702
703 #[derive(PartialEq, Debug, Copy, Clone)]
704 struct U8Buffer {
705 first: U8,
706 rest: [u8; 13],
707 len: usize,
708 }
709
710 impl TaggedLengthBuffer for U8Buffer {
711 type FixedParam = U8;
712
713 fn try_new(first: Self::FixedParam, extra: &[u8]) -> Result<Self> {
714 let len = extra.len();
715 Range::AtMost(13).check(len, "bytes")?;
716 let mut rest = [0; 13];
717 rest[..len].copy_from_slice(&extra[..len]);
718 Ok(U8Buffer { first, rest, len })
719 }
720
721 fn fixed_param(&self) -> Self::FixedParam {
722 self.first
723 }
724
725 fn extra_params(&self) -> &[u8] {
726 &self.rest[..self.len]
727 }
728 }
729
730 #[test]
731 fn test_u8_fixed_param() {
732 assert_eq!(
733 U8Buffer {
734 first: U8(0x56),
735 rest: [0; 13],
736 len: 0,
737 }
738 .fixed_param(),
739 U8(0x56)
740 );
741 }
742
743 #[test]
744 fn test_u8_extra_params() {
745 assert_eq!(
746 U8Buffer {
747 first: U8(0),
748 rest: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
749 len: 2,
750 }
751 .extra_params(),
752 &[1, 2]
753 );
754 }
755
756 #[test]
757 fn test_u8_try_new() {
758 assert_eq!(
759 Ok(U8Buffer {
760 first: U8(0x56),
761 rest: [0x78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
762 len: 1,
763 }),
764 U8Buffer::try_new(U8(0x56), &[0x78])
765 );
766 }
767
768 opcode_test! {
769 name: _fixed_only,
770 ty: U8Buffer,
771 instance: U8Buffer {
772 first: U8(0x12),
773 rest: [0; 13],
774 len: 0,
775 },
776 bytes: [0x12],
777 }
778
779 opcode_test! {
780 name: _fixed_and_one_byte,
781 ty: U8Buffer,
782 instance: U8Buffer {
783 first: U8(0x12),
784 rest: [0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
785 len: 1,
786 },
787 bytes: [0x92, 0x34],
788 }
789
790 opcode_test! {
791 name: _fixed_and_two_bytes,
792 ty: U8Buffer,
793 instance: U8Buffer {
794 first: U8(0x12),
795 rest: [0x34, 0x56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
796 len: 2,
797 },
798 bytes: [0x92, 0xB4, 0x56],
799 }
800
801 #[test]
802 fn test_decode_zero_bytes() {
803 assert_eq!(
804 U8Buffer::try_from_bytes(&[]),
805 Err(Error::OutOfRange {
806 expected: Range::AtLeast(1),
807 got: 0,
808 quantity: "bytes",
809 })
810 );
811 }
812
813 #[test]
814 fn test_decode_one_byte_junk() {
815 assert_eq!(
816 U8Buffer::try_from_bytes(&[0x12, 0x34]),
817 Ok(U8Buffer {
818 first: U8(0x12),
819 rest: [0; 13],
820 len: 0,
821 })
822 );
823 }
824
825 #[test]
826 fn test_decode_two_bytes_junk() {
827 assert_eq!(
828 U8Buffer::try_from_bytes(&[0x92, 0x34, 0x56]),
829 Ok(U8Buffer {
830 first: U8(0x12),
831 rest: [0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
832 len: 1,
833 })
834 );
835 }
836
837 #[test]
838 fn test_decode_missing_byte() {
839 assert_eq!(
840 U8Buffer::try_from_bytes(&[0x92]),
841 Ok(U8Buffer {
842 first: U8(0x12),
843 rest: [0; 13],
844 len: 0,
845 })
846 );
847 }
848
849 #[test]
850 fn test_decode_missing_byte_2() {
851 assert_eq!(
852 U8Buffer::try_from_bytes(&[0x92, 0xB4]),
853 Ok(U8Buffer {
854 first: U8(0x12),
855 rest: [0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
856 len: 1,
857 })
858 );
859 }
860}
861
862#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
863pub struct BoundedBufferOperand<const S: usize, T: OperandEncodable + Default + Copy> {
864 pub(crate) buffer: [T; S],
865 pub(crate) len: usize,
866}
867
868impl<const S: usize, T: OperandEncodable + Default + Copy> OperandEncodable
869 for BoundedBufferOperand<S, T>
870{
871 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
872 for elem in &self.buffer[..self.len] {
873 elem.to_bytes(buf);
874 }
875 }
876
877 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
878 let mut buf = Vec::new();
879 let mut offset = 0;
880 let mut len = 0;
881 while offset < S * size_of::<T>() && offset + size_of::<T>() <= bytes.len() {
882 buf.push(
883 T::try_from_bytes(&bytes[offset..]).map_err(crate::Error::add_offset(offset))?,
884 );
885 offset += size_of::<T>();
886 len += 1;
887 }
888 buf.resize(S, T::default());
889 Ok(Self {
890 buffer: *buf.first_chunk().unwrap(),
891 len,
892 })
893 }
894
895 fn len(&self) -> usize {
896 usize::min(self.len, S) * size_of::<T>()
897 }
898
899 fn expected_len() -> Range<usize> {
900 Range::AtLeast(0)
901 }
902}
903
904impl<const S: usize, T: OperandEncodable + Copy + Default> Default for BoundedBufferOperand<S, T> {
905 fn default() -> BoundedBufferOperand<S, T> {
906 BoundedBufferOperand {
907 buffer: [T::default(); S],
908 len: 0,
909 }
910 }
911}
912
913impl<const S: usize> FromStr for BoundedBufferOperand<S, u8> {
914 type Err = Error;
915
916 fn from_str(s: &str) -> Result<Self> {
917 let bytes = s.as_bytes();
918 Range::AtMost(S).check(bytes.len(), "bytes")?;
919 let mut buffer = [0; S];
920 buffer[..bytes.len()].copy_from_slice(bytes);
921 Ok(BoundedBufferOperand::<S, u8> {
922 buffer,
923 len: bytes.len(),
924 })
925 }
926}
927
928impl<const S: usize, T: OperandEncodable + Default + Copy> TryFrom<&[T]>
929 for BoundedBufferOperand<S, T>
930{
931 type Error = Error;
932
933 fn try_from(arr: &[T]) -> Result<Self> {
934 Range::AtMost(S).check(arr.len(), "elements")?;
935 let mut buffer = [T::default(); S];
936 buffer[..arr.len()].copy_from_slice(arr);
937 Ok(BoundedBufferOperand::<S, T> {
938 buffer,
939 len: arr.len(),
940 })
941 }
942}
943
944impl<const S: usize, T: OperandEncodable + Default + Copy> BoundedBufferOperand<S, T> {
945 #[must_use]
946 pub fn new() -> BoundedBufferOperand<S, T> {
947 BoundedBufferOperand::default()
948 }
949
950 #[must_use]
951 pub fn as_bytes(&self) -> &[T] {
952 &self.buffer[..self.len]
953 }
954}
955
956pub type BufferOperand = BoundedBufferOperand<14, u8>;
957
958#[cfg(test)]
959mod test_buffer_operand {
960 use super::*;
961
962 opcode_test! {
963 name: _empty,
964 ty: BoundedBufferOperand::<2, u8>,
965 instance: BoundedBufferOperand::<2, u8> {
966 buffer: [0; 2],
967 len: 0,
968 },
969 bytes: [],
970 }
971
972 opcode_test! {
973 name: _underfull,
974 ty: BoundedBufferOperand::<2, u8>,
975 instance: BoundedBufferOperand::<2, u8> {
976 buffer: [0x12, 0],
977 len: 1,
978 },
979 bytes: [0x12],
980 }
981
982 opcode_test! {
983 name: _full,
984 ty: BoundedBufferOperand::<2, u8>,
985 instance: BoundedBufferOperand::<2, u8> {
986 buffer: [0x12, 0x34],
987 len: 2,
988 },
989 bytes: [0x12, 0x34],
990 }
991
992 opcode_test! {
993 name: _u16_empty,
994 ty: BoundedBufferOperand::<2, u16>,
995 instance: BoundedBufferOperand::<2, u16> {
996 buffer: [0; 2],
997 len: 0,
998 },
999 bytes: [],
1000 }
1001
1002 opcode_test! {
1003 name: _u16_underfull,
1004 ty: BoundedBufferOperand::<2, u16>,
1005 instance: BoundedBufferOperand::<2, u16> {
1006 buffer: [0x1234, 0],
1007 len: 1,
1008 },
1009 bytes: [0x12, 0x34],
1010 }
1011
1012 opcode_test! {
1013 name: _u16_full,
1014 ty: BoundedBufferOperand::<2, u16>,
1015 instance: BoundedBufferOperand::<2, u16> {
1016 buffer: [0x1234, 0x5678],
1017 len: 2,
1018 },
1019 bytes: [0x12, 0x34, 0x56, 0x78],
1020 }
1021
1022 #[test]
1023 fn test_encode_underfull_with_junk() {
1024 let mut buf = Vec::new();
1025
1026 BoundedBufferOperand::<2, u8> {
1027 buffer: [0x12, 0x34],
1028 len: 1,
1029 }
1030 .to_bytes(&mut buf);
1031 assert_eq!(&buf, &[0x12]);
1032 }
1033
1034 #[test]
1035 fn test_decode_overfull() {
1036 assert_eq!(
1037 BoundedBufferOperand::<2, u8>::try_from_bytes(&[0x12, 0x34, 0x56]).unwrap(),
1038 BoundedBufferOperand::<2, u8> {
1039 buffer: [0x12, 0x34],
1040 len: 2,
1041 }
1042 );
1043 }
1044
1045 #[test]
1046 fn test_decode_u16_misaligned() {
1047 assert_eq!(
1048 BoundedBufferOperand::<2, u16>::try_from_bytes(&[0x12, 0x34, 0x56]).unwrap(),
1049 BoundedBufferOperand::<2, u16> {
1050 buffer: [0x1234, 0],
1051 len: 1,
1052 }
1053 );
1054 }
1055
1056 #[test]
1057 fn test_from_string_fit() {
1058 let s = "abc";
1059 let buffer = BoundedBufferOperand::<3, u8>::from_str(s).unwrap();
1060 assert_eq!(buffer.len, 3);
1061 assert_eq!(&buffer.buffer, s.as_bytes());
1062 }
1063
1064 #[test]
1065 fn test_from_string_underfull() {
1066 let s = "abc";
1067 let buffer = BoundedBufferOperand::<4, u8>::from_str(s).unwrap();
1068 assert_eq!(buffer.len, 3);
1069 assert_ne!(&buffer.buffer, s.as_bytes());
1070 assert_eq!(&buffer.buffer, &['a' as u8, 'b' as u8, 'c' as u8, 0]);
1071 }
1072
1073 #[test]
1074 fn test_from_string_overfull() {
1075 let s = "abc";
1076 let buffer = BoundedBufferOperand::<2, u8>::from_str(s);
1077 assert_eq!(
1078 buffer,
1079 Err(Error::OutOfRange {
1080 expected: Range::AtMost(2),
1081 got: 3,
1082 quantity: "bytes",
1083 })
1084 );
1085 }
1086
1087 #[test]
1088 fn test_from_string_overfull_utf8() {
1089 let s = "💩";
1090 let buffer = BoundedBufferOperand::<2, u8>::from_str(s);
1091 assert_eq!(
1092 buffer,
1093 Err(Error::OutOfRange {
1094 expected: Range::AtMost(2),
1095 got: 4,
1096 quantity: "bytes",
1097 })
1098 );
1099 }
1100}
1101
1102#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1103#[repr(u8)]
1104#[non_exhaustive]
1105pub enum AbortReason {
1106 UnrecognizedOp = constants::CEC_OP_ABORT_UNRECOGNIZED_OP,
1108 IncorrectMode = constants::CEC_OP_ABORT_INCORRECT_MODE,
1110 NoSource = constants::CEC_OP_ABORT_NO_SOURCE,
1112 InvalidOp = constants::CEC_OP_ABORT_INVALID_OP,
1114 Refused = constants::CEC_OP_ABORT_REFUSED,
1116 Undetermined = constants::CEC_OP_ABORT_UNDETERMINED,
1118}
1119
1120#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1121#[repr(u8)]
1122#[non_exhaustive]
1123pub enum AnalogueBroadcastType {
1124 Cable = constants::CEC_OP_ANA_BCAST_TYPE_CABLE,
1125 Satellite = constants::CEC_OP_ANA_BCAST_TYPE_SATELLITE,
1126 Terrestrial = constants::CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL,
1127}
1128
1129#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1130#[repr(u8)]
1131#[non_exhaustive]
1132pub enum AudioRate {
1133 Off = constants::CEC_OP_AUD_RATE_OFF,
1135 WideStandard = constants::CEC_OP_AUD_RATE_WIDE_STD,
1137 WideFast = constants::CEC_OP_AUD_RATE_WIDE_FAST,
1139 WideSlow = constants::CEC_OP_AUD_RATE_WIDE_SLOW,
1141 NarrowStandard = constants::CEC_OP_AUD_RATE_NARROW_STD,
1143 NarrowFast = constants::CEC_OP_AUD_RATE_NARROW_FAST,
1145 NarrowSlow = constants::CEC_OP_AUD_RATE_NARROW_SLOW,
1147}
1148
1149#[derive(BitfieldSpecifier, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1150#[bits = 2]
1151#[repr(u8)]
1152pub enum AudioFormatId {
1153 CEA861 = constants::CEC_OP_AUD_FMT_ID_CEA861,
1155 CEA861Cxt = constants::CEC_OP_AUD_FMT_ID_CEA861_CXT,
1157 #[default]
1158 Invalid(u8),
1159}
1160
1161#[derive(BitfieldSpecifier, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1162#[bits = 2]
1163#[repr(u8)]
1164pub enum AudioOutputCompensated {
1165 NotApplicable = constants::CEC_OP_AUD_OUT_COMPENSATED_NA,
1166 Delay = constants::CEC_OP_AUD_OUT_COMPENSATED_DELAY,
1167 NoDelay = constants::CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY,
1168 PartialDelay = constants::CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY,
1169}
1170
1171#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1172#[repr(u8)]
1173#[non_exhaustive]
1174pub enum BroadcastSystem {
1175 PalBG = constants::CEC_OP_BCAST_SYSTEM_PAL_BG,
1177 SecamLq = constants::CEC_OP_BCAST_SYSTEM_SECAM_LQ,
1179 PalM = constants::CEC_OP_BCAST_SYSTEM_PAL_M,
1181 NtscM = constants::CEC_OP_BCAST_SYSTEM_NTSC_M,
1183 PalI = constants::CEC_OP_BCAST_SYSTEM_PAL_I,
1185 SecamDK = constants::CEC_OP_BCAST_SYSTEM_SECAM_DK,
1187 SecamBG = constants::CEC_OP_BCAST_SYSTEM_SECAM_BG,
1189 SecamL = constants::CEC_OP_BCAST_SYSTEM_SECAM_L,
1191 PalDK = constants::CEC_OP_BCAST_SYSTEM_PAL_DK,
1193 Other = constants::CEC_OP_BCAST_SYSTEM_OTHER,
1194}
1195
1196#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
1197#[repr(u8)]
1198#[non_exhaustive]
1199enum ChannelNumberFormat {
1200 Fmt1Part = constants::CEC_OP_CHANNEL_NUMBER_FMT_1_PART,
1201 Fmt2Part = constants::CEC_OP_CHANNEL_NUMBER_FMT_2_PART,
1202}
1203
1204#[repr(u8)]
1205#[derive(
1206 Debug,
1207 Copy,
1208 Clone,
1209 PartialEq,
1210 Eq,
1211 PartialOrd,
1212 Ord,
1213 Hash,
1214 IntoPrimitive,
1215 TryFromPrimitive,
1216 Operand,
1217)]
1218pub enum DayOfMonth {
1219 Day1 = 1,
1220 Day2 = 2,
1221 Day3 = 3,
1222 Day4 = 4,
1223 Day5 = 5,
1224 Day6 = 6,
1225 Day7 = 7,
1226 Day8 = 8,
1227 Day9 = 9,
1228 Day10 = 10,
1229 Day11 = 11,
1230 Day12 = 12,
1231 Day13 = 13,
1232 Day14 = 14,
1233 Day15 = 15,
1234 Day16 = 16,
1235 Day17 = 17,
1236 Day18 = 18,
1237 Day19 = 19,
1238 Day20 = 20,
1239 Day21 = 21,
1240 Day22 = 22,
1241 Day23 = 23,
1242 Day24 = 24,
1243 Day25 = 25,
1244 Day26 = 26,
1245 Day27 = 27,
1246 Day28 = 28,
1247 Day29 = 29,
1248 Day30 = 30,
1249 Day31 = 31,
1250}
1251
1252#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1253#[repr(u8)]
1254#[non_exhaustive]
1255pub enum DeckControlMode {
1256 SkipForward = constants::CEC_OP_DECK_CTL_MODE_SKIP_FWD,
1257 SkipReverse = constants::CEC_OP_DECK_CTL_MODE_SKIP_REV,
1258 Stop = constants::CEC_OP_DECK_CTL_MODE_STOP,
1259 Eject = constants::CEC_OP_DECK_CTL_MODE_EJECT,
1260}
1261
1262#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1263#[repr(u8)]
1264#[non_exhaustive]
1265pub enum DeckInfo {
1266 Play = constants::CEC_OP_DECK_INFO_PLAY,
1267 Record = constants::CEC_OP_DECK_INFO_RECORD,
1268 PlayReverse = constants::CEC_OP_DECK_INFO_PLAY_REV,
1269 Still = constants::CEC_OP_DECK_INFO_STILL,
1270 Slow = constants::CEC_OP_DECK_INFO_SLOW,
1271 SlowReverse = constants::CEC_OP_DECK_INFO_SLOW_REV,
1272 FastForward = constants::CEC_OP_DECK_INFO_FAST_FWD,
1273 FastReverse = constants::CEC_OP_DECK_INFO_FAST_REV,
1274 NoMedia = constants::CEC_OP_DECK_INFO_NO_MEDIA,
1275 Stop = constants::CEC_OP_DECK_INFO_STOP,
1276 SkipForward = constants::CEC_OP_DECK_INFO_SKIP_FWD,
1277 SkipReverse = constants::CEC_OP_DECK_INFO_SKIP_REV,
1278 IndexSearchForward = constants::CEC_OP_DECK_INFO_INDEX_SEARCH_FWD,
1279 IndexSearchReverse = constants::CEC_OP_DECK_INFO_INDEX_SEARCH_REV,
1280 Other = constants::CEC_OP_DECK_INFO_OTHER,
1281}
1282
1283#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1284#[repr(u8)]
1285#[non_exhaustive]
1286pub enum DigitalServiceBroadcastSystem {
1287 AribGeneric = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN,
1288 AtscGeneric = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN,
1289 DvbGeneric = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN,
1290 AribBs = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS,
1291 AribCs = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS,
1292 AribT = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T,
1293 AtscCable = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE,
1294 AtscSatellite = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT,
1295 AtscTerrestrial = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T,
1296 DvbC = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C,
1297 DvbS = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S,
1298 DvbS2 = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2,
1299 DvbT = constants::CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T,
1300}
1301
1302#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1303#[repr(u8)]
1304#[non_exhaustive]
1305pub enum DisplayControl {
1306 Default = constants::CEC_OP_DISP_CTL_DEFAULT,
1307 UntilCleared = constants::CEC_OP_DISP_CTL_UNTIL_CLEARED,
1308 Clear = constants::CEC_OP_DISP_CTL_CLEAR,
1309}
1310
1311#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1312#[repr(u8)]
1313#[non_exhaustive]
1314pub enum ExternalSourceSpecifier {
1315 ExternalPlug = constants::CEC_OP_EXT_SRC_PLUG,
1316 ExternalPhysicalAddress = constants::CEC_OP_EXT_SRC_PHYS_ADDR,
1317}
1318
1319#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1320#[repr(u8)]
1321#[non_exhaustive]
1322pub enum MediaInfo {
1323 UnprotectedMedia = constants::CEC_OP_MEDIA_INFO_UNPROT_MEDIA,
1324 ProtectedMedia = constants::CEC_OP_MEDIA_INFO_PROT_MEDIA,
1325 NoMedia = constants::CEC_OP_MEDIA_INFO_NO_MEDIA,
1326}
1327
1328#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1329#[repr(u8)]
1330#[non_exhaustive]
1331pub enum MenuRequestType {
1332 Activate = constants::CEC_OP_MENU_REQUEST_ACTIVATE,
1333 Deactivate = constants::CEC_OP_MENU_REQUEST_DEACTIVATE,
1334 Query = constants::CEC_OP_MENU_REQUEST_QUERY,
1335}
1336
1337#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1338#[repr(u8)]
1339#[non_exhaustive]
1340pub enum MenuState {
1341 Activated = constants::CEC_OP_MENU_STATE_ACTIVATED,
1342 Deactivated = constants::CEC_OP_MENU_STATE_DEACTIVATED,
1343}
1344
1345#[repr(u8)]
1346#[derive(
1347 Debug,
1348 Copy,
1349 Clone,
1350 PartialEq,
1351 Eq,
1352 PartialOrd,
1353 Ord,
1354 Hash,
1355 IntoPrimitive,
1356 TryFromPrimitive,
1357 Operand,
1358)]
1359pub enum MonthOfYear {
1360 January = 1,
1361 February = 2,
1362 March = 3,
1363 April = 4,
1364 May = 5,
1365 June = 6,
1366 July = 7,
1367 August = 8,
1368 September = 9,
1369 October = 10,
1370 November = 11,
1371 December = 12,
1372}
1373
1374#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1375#[repr(u8)]
1376#[non_exhaustive]
1377pub enum PlayMode {
1378 Forward = constants::CEC_OP_PLAY_MODE_PLAY_FWD,
1379 Reverse = constants::CEC_OP_PLAY_MODE_PLAY_REV,
1380 Still = constants::CEC_OP_PLAY_MODE_PLAY_STILL,
1381 FastForwardMinimum = constants::CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN,
1382 FastForwardMedium = constants::CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED,
1383 FastForwardMaximum = constants::CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX,
1384 FastReverseMinimum = constants::CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN,
1385 FastReverseMedium = constants::CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED,
1386 FastReverseMaximum = constants::CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX,
1387 SlowForwardMinimum = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN,
1388 SlowForwardMedium = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED,
1389 SlowForwardMaximum = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX,
1390 SlowReverseMinimum = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN,
1391 SlowReverseMedium = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED,
1392 SlowReverseMaximum = constants::CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX,
1393}
1394
1395#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1396#[repr(u8)]
1397#[non_exhaustive]
1398pub enum PowerStatus {
1399 On = constants::CEC_OP_POWER_STATUS_ON,
1401 Standby = constants::CEC_OP_POWER_STATUS_STANDBY,
1403 ToOn = constants::CEC_OP_POWER_STATUS_TO_ON,
1405 ToStandby = constants::CEC_OP_POWER_STATUS_TO_STANDBY,
1407}
1408
1409#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1410#[repr(u8)]
1411#[non_exhaustive]
1412pub enum PrimaryDeviceType {
1413 Tv = constants::CEC_OP_PRIM_DEVTYPE_TV,
1414 Recording = constants::CEC_OP_PRIM_DEVTYPE_RECORD,
1415 Tuner = constants::CEC_OP_PRIM_DEVTYPE_TUNER,
1416 Playback = constants::CEC_OP_PRIM_DEVTYPE_PLAYBACK,
1417 Audio = constants::CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM,
1418 Switch = constants::CEC_OP_PRIM_DEVTYPE_SWITCH,
1419 Processor = constants::CEC_OP_PRIM_DEVTYPE_PROCESSOR,
1420}
1421
1422#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1423#[repr(u8)]
1424#[non_exhaustive]
1425pub enum RecordSourceType {
1426 Own = constants::CEC_OP_RECORD_SRC_OWN,
1427 Digital = constants::CEC_OP_RECORD_SRC_DIGITAL,
1428 Analogue = constants::CEC_OP_RECORD_SRC_ANALOG,
1429 ExternalPlug = constants::CEC_OP_RECORD_SRC_EXT_PLUG,
1430 ExternalPhysicalAddress = constants::CEC_OP_RECORD_SRC_EXT_PHYS_ADDR,
1431}
1432
1433#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1434#[repr(u8)]
1435#[non_exhaustive]
1436pub enum RecordStatusInfo {
1437 CurrentSource = constants::CEC_OP_RECORD_STATUS_CUR_SRC,
1438 DigitalService = constants::CEC_OP_RECORD_STATUS_DIG_SERVICE,
1439 AnalogueService = constants::CEC_OP_RECORD_STATUS_ANA_SERVICE,
1440 ExternalInput = constants::CEC_OP_RECORD_STATUS_EXT_INPUT,
1441 NoDigitalService = constants::CEC_OP_RECORD_STATUS_NO_DIG_SERVICE,
1442 NoAnalogueService = constants::CEC_OP_RECORD_STATUS_NO_ANA_SERVICE,
1443 NoService = constants::CEC_OP_RECORD_STATUS_NO_SERVICE,
1444 InvalidExternalPlug = constants::CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG,
1445 InvalidExternalPhysicalAddress = constants::CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR,
1446 CaUnsupported = constants::CEC_OP_RECORD_STATUS_UNSUP_CA,
1447 InsufficientCaEntitlements = constants::CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS,
1448 DisallowedCopySource = constants::CEC_OP_RECORD_STATUS_CANT_COPY_SRC,
1449 NoFurtherCopies = constants::CEC_OP_RECORD_STATUS_NO_MORE_COPIES,
1450 NoMedia = constants::CEC_OP_RECORD_STATUS_NO_MEDIA,
1451 Playing = constants::CEC_OP_RECORD_STATUS_PLAYING,
1452 AlreadyRecording = constants::CEC_OP_RECORD_STATUS_ALREADY_RECORDING,
1453 MediaProtected = constants::CEC_OP_RECORD_STATUS_MEDIA_PROT,
1454 NoSignal = constants::CEC_OP_RECORD_STATUS_NO_SIGNAL,
1455 MediaProblem = constants::CEC_OP_RECORD_STATUS_MEDIA_PROBLEM,
1456 NotEnoughSpace = constants::CEC_OP_RECORD_STATUS_NO_SPACE,
1457 ParentalLock = constants::CEC_OP_RECORD_STATUS_PARENTAL_LOCK,
1458 TerminatedOk = constants::CEC_OP_RECORD_STATUS_TERMINATED_OK,
1459 AlreadyTerminated = constants::CEC_OP_RECORD_STATUS_ALREADY_TERM,
1460 Other = constants::CEC_OP_RECORD_STATUS_OTHER,
1461}
1462
1463#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1464#[repr(u8)]
1465#[non_exhaustive]
1466pub enum RcProfileId {
1467 ProfileNone = constants::CEC_OP_FEAT_RC_TV_PROFILE_NONE,
1468 Profile1 = constants::CEC_OP_FEAT_RC_TV_PROFILE_1,
1469 Profile2 = constants::CEC_OP_FEAT_RC_TV_PROFILE_2,
1470 Profile3 = constants::CEC_OP_FEAT_RC_TV_PROFILE_3,
1471 Profile4 = constants::CEC_OP_FEAT_RC_TV_PROFILE_4,
1472}
1473
1474#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
1475#[repr(u8)]
1476#[non_exhaustive]
1477enum ServiceIdMethod {
1478 ByDigitalId = constants::CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID,
1479 ByChannel = constants::CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL,
1480}
1481
1482#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
1483pub enum ServiceId {
1484 Digital(DigitalServiceId),
1485 Analogue(AnalogueServiceId),
1486}
1487
1488impl From<DigitalServiceId> for ServiceId {
1489 fn from(val: DigitalServiceId) -> ServiceId {
1490 ServiceId::Digital(val)
1491 }
1492}
1493
1494impl From<AnalogueServiceId> for ServiceId {
1495 fn from(val: AnalogueServiceId) -> ServiceId {
1496 ServiceId::Analogue(val)
1497 }
1498}
1499
1500#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1501#[repr(u8)]
1502#[non_exhaustive]
1503pub enum StatusRequest {
1504 On = constants::CEC_OP_STATUS_REQ_ON,
1505 Off = constants::CEC_OP_STATUS_REQ_OFF,
1506 Once = constants::CEC_OP_STATUS_REQ_ONCE,
1507}
1508
1509#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1510#[repr(u8)]
1511#[non_exhaustive]
1512pub enum TimerClearedStatusData {
1513 Recording = constants::CEC_OP_TIMER_CLR_STAT_RECORDING,
1514 NoMatching = constants::CEC_OP_TIMER_CLR_STAT_NO_MATCHING,
1515 NoInfo = constants::CEC_OP_TIMER_CLR_STAT_NO_INFO,
1516 Cleared = constants::CEC_OP_TIMER_CLR_STAT_CLEARED,
1517}
1518
1519#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1520#[repr(u8)]
1521#[non_exhaustive]
1522pub enum TunerDisplayInfo {
1523 Digital = constants::CEC_OP_TUNER_DISPLAY_INFO_DIGITAL,
1524 None = constants::CEC_OP_TUNER_DISPLAY_INFO_NONE,
1525 Analogue = constants::CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE,
1526}
1527
1528#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1529#[repr(u8)]
1530#[non_exhaustive]
1531pub enum UiBroadcastType {
1532 ToggleAll = constants::CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL,
1533 ToggleDigitalAnalogue = constants::CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA,
1534 Analogue = constants::CEC_OP_UI_BCAST_TYPE_ANALOGUE,
1535 AnalogueTerrestrial = constants::CEC_OP_UI_BCAST_TYPE_ANALOGUE_T,
1536 AnalogueCable = constants::CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE,
1537 AnalogueSatellite = constants::CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT,
1538 Digital = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL,
1539 DigitalTerrestrial = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL_T,
1540 DigitalCable = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE,
1541 DigitalSatellite = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT,
1542 DigitalCommsSatellite = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT,
1543 DigitalCommsSatellite2 = constants::CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2,
1544 Ip = constants::CEC_OP_UI_BCAST_TYPE_IP,
1545}
1546
1547#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Display, EnumString)]
1548#[repr(u8)]
1549#[strum(serialize_all = "kebab-case")]
1550#[non_exhaustive]
1551pub enum UiCommand {
1552 #[strum(serialize = "select", serialize = "ok")]
1553 Select = constants::CEC_OP_UI_CMD_SELECT,
1554 Up = constants::CEC_OP_UI_CMD_UP,
1555 Down = constants::CEC_OP_UI_CMD_DOWN,
1556 Left = constants::CEC_OP_UI_CMD_LEFT,
1557 Right = constants::CEC_OP_UI_CMD_RIGHT,
1558 RightUp = constants::CEC_OP_UI_CMD_RIGHT_UP,
1559 RightDown = constants::CEC_OP_UI_CMD_RIGHT_DOWN,
1560 LeftUp = constants::CEC_OP_UI_CMD_LEFT_UP,
1561 LeftDown = constants::CEC_OP_UI_CMD_LEFT_DOWN,
1562 DeviceRootMenu = constants::CEC_OP_UI_CMD_DEVICE_ROOT_MENU,
1563 DeviceSetupMenu = constants::CEC_OP_UI_CMD_DEVICE_SETUP_MENU,
1564 ContentsMenu = constants::CEC_OP_UI_CMD_CONTENTS_MENU,
1565 FavoriteMenu = constants::CEC_OP_UI_CMD_FAVORITE_MENU,
1566 #[strum(serialize = "back", serialize = "exit")]
1567 Back = constants::CEC_OP_UI_CMD_BACK,
1568 MediaTopMenu = constants::CEC_OP_UI_CMD_MEDIA_TOP_MENU,
1569 MediaContextSensitiveMenu = constants::CEC_OP_UI_CMD_MEDIA_CONTEXT_SENSITIVE_MENU,
1570 NumberEntryMode = constants::CEC_OP_UI_CMD_NUMBER_ENTRY_MODE,
1571 #[strum(serialize = "11")]
1572 Number11 = constants::CEC_OP_UI_CMD_NUMBER_11,
1573 #[strum(serialize = "12")]
1574 Number12 = constants::CEC_OP_UI_CMD_NUMBER_12,
1575 #[strum(serialize = "0", serialize = "10")]
1576 Number0OrNumber10 = constants::CEC_OP_UI_CMD_NUMBER_0_OR_NUMBER_10,
1577 #[strum(serialize = "1")]
1578 Number1 = constants::CEC_OP_UI_CMD_NUMBER_1,
1579 #[strum(serialize = "2")]
1580 Number2 = constants::CEC_OP_UI_CMD_NUMBER_2,
1581 #[strum(serialize = "3")]
1582 Number3 = constants::CEC_OP_UI_CMD_NUMBER_3,
1583 #[strum(serialize = "4")]
1584 Number4 = constants::CEC_OP_UI_CMD_NUMBER_4,
1585 #[strum(serialize = "5")]
1586 Number5 = constants::CEC_OP_UI_CMD_NUMBER_5,
1587 #[strum(serialize = "6")]
1588 Number6 = constants::CEC_OP_UI_CMD_NUMBER_6,
1589 #[strum(serialize = "7")]
1590 Number7 = constants::CEC_OP_UI_CMD_NUMBER_7,
1591 #[strum(serialize = "8")]
1592 Number8 = constants::CEC_OP_UI_CMD_NUMBER_8,
1593 #[strum(serialize = "9")]
1594 Number9 = constants::CEC_OP_UI_CMD_NUMBER_9,
1595 Dot = constants::CEC_OP_UI_CMD_DOT,
1596 Enter = constants::CEC_OP_UI_CMD_ENTER,
1597 Clear = constants::CEC_OP_UI_CMD_CLEAR,
1598 NextFavorite = constants::CEC_OP_UI_CMD_NEXT_FAVORITE,
1599 ChannelUp = constants::CEC_OP_UI_CMD_CHANNEL_UP,
1600 ChannelDown = constants::CEC_OP_UI_CMD_CHANNEL_DOWN,
1601 PreviousChannel = constants::CEC_OP_UI_CMD_PREVIOUS_CHANNEL,
1602 SoundSelect = constants::CEC_OP_UI_CMD_SOUND_SELECT,
1603 InputSelect = constants::CEC_OP_UI_CMD_INPUT_SELECT,
1604 DisplayInformation = constants::CEC_OP_UI_CMD_DISPLAY_INFORMATION,
1605 Help = constants::CEC_OP_UI_CMD_HELP,
1606 PageUp = constants::CEC_OP_UI_CMD_PAGE_UP,
1607 PageDown = constants::CEC_OP_UI_CMD_PAGE_DOWN,
1608 Power = constants::CEC_OP_UI_CMD_POWER,
1609 VolumeUp = constants::CEC_OP_UI_CMD_VOLUME_UP,
1610 VolumeDown = constants::CEC_OP_UI_CMD_VOLUME_DOWN,
1611 Mute = constants::CEC_OP_UI_CMD_MUTE,
1612 Play = constants::CEC_OP_UI_CMD_PLAY,
1613 Stop = constants::CEC_OP_UI_CMD_STOP,
1614 Pause = constants::CEC_OP_UI_CMD_PAUSE,
1615 Record = constants::CEC_OP_UI_CMD_RECORD,
1616 Rewind = constants::CEC_OP_UI_CMD_REWIND,
1617 FastForward = constants::CEC_OP_UI_CMD_FAST_FORWARD,
1618 Eject = constants::CEC_OP_UI_CMD_EJECT,
1619 SkipForward = constants::CEC_OP_UI_CMD_SKIP_FORWARD,
1620 SkipBackward = constants::CEC_OP_UI_CMD_SKIP_BACKWARD,
1621 StopRecord = constants::CEC_OP_UI_CMD_STOP_RECORD,
1622 PauseRecord = constants::CEC_OP_UI_CMD_PAUSE_RECORD,
1623 Angle = constants::CEC_OP_UI_CMD_ANGLE,
1624 SubPicture = constants::CEC_OP_UI_CMD_SUB_PICTURE,
1625 VideoOnDemand = constants::CEC_OP_UI_CMD_VIDEO_ON_DEMAND,
1626 ElectronicProgramGuide = constants::CEC_OP_UI_CMD_ELECTRONIC_PROGRAM_GUIDE,
1627 TimerProgramming = constants::CEC_OP_UI_CMD_TIMER_PROGRAMMING,
1628 InitialConfiguration = constants::CEC_OP_UI_CMD_INITIAL_CONFIGURATION,
1629 SelectBroadcastType(Option<UiBroadcastType>) = constants::CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE,
1630 SelectSoundPresentation(Option<UiSoundPresentationControl>) =
1631 constants::CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION,
1632 AudioDescription = constants::CEC_OP_UI_CMD_AUDIO_DESCRIPTION,
1633 Internet = constants::CEC_OP_UI_CMD_INTERNET,
1634 #[strum(serialize = "3d-mode")]
1635 ThreeDMode = constants::CEC_OP_UI_CMD_3D_MODE,
1636 PlayFunction(Option<PlayMode>) = constants::CEC_OP_UI_CMD_PLAY_FUNCTION,
1637 PausePlayFunction = constants::CEC_OP_UI_CMD_PAUSE_PLAY_FUNCTION,
1638 RecordFunction = constants::CEC_OP_UI_CMD_RECORD_FUNCTION,
1639 PauseRecordFunction = constants::CEC_OP_UI_CMD_PAUSE_RECORD_FUNCTION,
1640 StopFunction = constants::CEC_OP_UI_CMD_STOP_FUNCTION,
1641 MuteFunction = constants::CEC_OP_UI_CMD_MUTE_FUNCTION,
1642 RestoreVolumeFunction = constants::CEC_OP_UI_CMD_RESTORE_VOLUME_FUNCTION,
1643 TuneFunction(Option<ChannelId>) = constants::CEC_OP_UI_CMD_TUNE_FUNCTION,
1644 SelectMediaFunction(Option<UiFunctionMedia>) = constants::CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION,
1645 SelectAvInputFunction(Option<UiFunctionSelectAvInput>) =
1646 constants::CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION,
1647 SelectAudioInputFunction(Option<UiFunctionSelectAudioInput>) =
1648 constants::CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION,
1649 PowerToggleFunction = constants::CEC_OP_UI_CMD_POWER_TOGGLE_FUNCTION,
1650 PowerOffFunction = constants::CEC_OP_UI_CMD_POWER_OFF_FUNCTION,
1651 PowerOnFunction = constants::CEC_OP_UI_CMD_POWER_ON_FUNCTION,
1652 #[strum(serialize = "f1", serialize = "blue")]
1653 F1Blue = constants::CEC_OP_UI_CMD_F1_BLUE,
1654 #[strum(serialize = "f2", serialize = "red")]
1655 F2Red = constants::CEC_OP_UI_CMD_F2_RED,
1656 #[strum(serialize = "f3", serialize = "green")]
1657 F3Green = constants::CEC_OP_UI_CMD_F3_GREEN,
1658 #[strum(serialize = "f4", serialize = "yellow")]
1659 F4Yellow = constants::CEC_OP_UI_CMD_F4_YELLOW,
1660 F5 = constants::CEC_OP_UI_CMD_F5,
1661 Data = constants::CEC_OP_UI_CMD_DATA,
1662}
1663
1664impl OperandEncodable for UiCommand {
1665 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
1666 buf.extend([unsafe { *<*const _>::from(self).cast::<u8>() }]);
1667 match self {
1668 UiCommand::SelectBroadcastType(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1669 UiCommand::SelectSoundPresentation(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1670 UiCommand::PlayFunction(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1671 UiCommand::TuneFunction(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1672 UiCommand::SelectMediaFunction(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1673 UiCommand::SelectAvInputFunction(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1674 UiCommand::SelectAudioInputFunction(x) => <_ as OperandEncodable>::to_bytes(x, buf),
1675 _ => (),
1676 }
1677 }
1678
1679 fn try_from_bytes(bytes: &[u8]) -> Result<UiCommand> {
1680 use constants::*;
1681 use UiCommand::*;
1682
1683 Range::AtLeast(1).check(bytes.len(), "bytes")?;
1684 Ok(match bytes[0] {
1685 CEC_OP_UI_CMD_SELECT => Select,
1686 CEC_OP_UI_CMD_UP => Up,
1687 CEC_OP_UI_CMD_DOWN => Down,
1688 CEC_OP_UI_CMD_LEFT => Left,
1689 CEC_OP_UI_CMD_RIGHT => Right,
1690 CEC_OP_UI_CMD_RIGHT_UP => RightUp,
1691 CEC_OP_UI_CMD_RIGHT_DOWN => RightDown,
1692 CEC_OP_UI_CMD_LEFT_UP => LeftUp,
1693 CEC_OP_UI_CMD_LEFT_DOWN => LeftDown,
1694 CEC_OP_UI_CMD_DEVICE_ROOT_MENU => DeviceRootMenu,
1695 CEC_OP_UI_CMD_DEVICE_SETUP_MENU => DeviceSetupMenu,
1696 CEC_OP_UI_CMD_CONTENTS_MENU => ContentsMenu,
1697 CEC_OP_UI_CMD_FAVORITE_MENU => FavoriteMenu,
1698 CEC_OP_UI_CMD_BACK => Back,
1699 CEC_OP_UI_CMD_MEDIA_TOP_MENU => MediaTopMenu,
1700 CEC_OP_UI_CMD_MEDIA_CONTEXT_SENSITIVE_MENU => MediaContextSensitiveMenu,
1701 CEC_OP_UI_CMD_NUMBER_ENTRY_MODE => NumberEntryMode,
1702 CEC_OP_UI_CMD_NUMBER_11 => Number11,
1703 CEC_OP_UI_CMD_NUMBER_12 => Number12,
1704 CEC_OP_UI_CMD_NUMBER_0_OR_NUMBER_10 => Number0OrNumber10,
1705 CEC_OP_UI_CMD_NUMBER_1 => Number1,
1706 CEC_OP_UI_CMD_NUMBER_2 => Number2,
1707 CEC_OP_UI_CMD_NUMBER_3 => Number3,
1708 CEC_OP_UI_CMD_NUMBER_4 => Number4,
1709 CEC_OP_UI_CMD_NUMBER_5 => Number5,
1710 CEC_OP_UI_CMD_NUMBER_6 => Number6,
1711 CEC_OP_UI_CMD_NUMBER_7 => Number7,
1712 CEC_OP_UI_CMD_NUMBER_8 => Number8,
1713 CEC_OP_UI_CMD_NUMBER_9 => Number9,
1714 CEC_OP_UI_CMD_DOT => Dot,
1715 CEC_OP_UI_CMD_ENTER => Enter,
1716 CEC_OP_UI_CMD_CLEAR => Clear,
1717 CEC_OP_UI_CMD_NEXT_FAVORITE => NextFavorite,
1718 CEC_OP_UI_CMD_CHANNEL_UP => ChannelUp,
1719 CEC_OP_UI_CMD_CHANNEL_DOWN => ChannelDown,
1720 CEC_OP_UI_CMD_PREVIOUS_CHANNEL => PreviousChannel,
1721 CEC_OP_UI_CMD_SOUND_SELECT => SoundSelect,
1722 CEC_OP_UI_CMD_INPUT_SELECT => InputSelect,
1723 CEC_OP_UI_CMD_DISPLAY_INFORMATION => DisplayInformation,
1724 CEC_OP_UI_CMD_HELP => Help,
1725 CEC_OP_UI_CMD_PAGE_UP => PageUp,
1726 CEC_OP_UI_CMD_PAGE_DOWN => PageDown,
1727 CEC_OP_UI_CMD_POWER => Power,
1728 CEC_OP_UI_CMD_VOLUME_UP => VolumeUp,
1729 CEC_OP_UI_CMD_VOLUME_DOWN => VolumeDown,
1730 CEC_OP_UI_CMD_MUTE => Mute,
1731 CEC_OP_UI_CMD_PLAY => Play,
1732 CEC_OP_UI_CMD_STOP => Stop,
1733 CEC_OP_UI_CMD_PAUSE => Pause,
1734 CEC_OP_UI_CMD_RECORD => Record,
1735 CEC_OP_UI_CMD_REWIND => Rewind,
1736 CEC_OP_UI_CMD_FAST_FORWARD => FastForward,
1737 CEC_OP_UI_CMD_EJECT => Eject,
1738 CEC_OP_UI_CMD_SKIP_FORWARD => SkipForward,
1739 CEC_OP_UI_CMD_SKIP_BACKWARD => SkipBackward,
1740 CEC_OP_UI_CMD_STOP_RECORD => StopRecord,
1741 CEC_OP_UI_CMD_PAUSE_RECORD => PauseRecord,
1742 CEC_OP_UI_CMD_ANGLE => Angle,
1743 CEC_OP_UI_CMD_SUB_PICTURE => SubPicture,
1744 CEC_OP_UI_CMD_VIDEO_ON_DEMAND => VideoOnDemand,
1745 CEC_OP_UI_CMD_ELECTRONIC_PROGRAM_GUIDE => ElectronicProgramGuide,
1746 CEC_OP_UI_CMD_TIMER_PROGRAMMING => TimerProgramming,
1747 CEC_OP_UI_CMD_INITIAL_CONFIGURATION => InitialConfiguration,
1748 CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE => {
1749 SelectBroadcastType(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1750 }
1751 CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION => {
1752 SelectSoundPresentation(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1753 }
1754 CEC_OP_UI_CMD_AUDIO_DESCRIPTION => AudioDescription,
1755 CEC_OP_UI_CMD_INTERNET => Internet,
1756 CEC_OP_UI_CMD_3D_MODE => ThreeDMode,
1757 CEC_OP_UI_CMD_PLAY_FUNCTION => {
1758 PlayFunction(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1759 }
1760 CEC_OP_UI_CMD_PAUSE_PLAY_FUNCTION => PausePlayFunction,
1761 CEC_OP_UI_CMD_RECORD_FUNCTION => RecordFunction,
1762 CEC_OP_UI_CMD_PAUSE_RECORD_FUNCTION => PauseRecordFunction,
1763 CEC_OP_UI_CMD_STOP_FUNCTION => StopFunction,
1764 CEC_OP_UI_CMD_MUTE_FUNCTION => MuteFunction,
1765 CEC_OP_UI_CMD_RESTORE_VOLUME_FUNCTION => RestoreVolumeFunction,
1766 CEC_OP_UI_CMD_TUNE_FUNCTION => {
1767 TuneFunction(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1768 }
1769 CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION => {
1770 SelectMediaFunction(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1771 }
1772 CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION => {
1773 SelectAvInputFunction(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1774 }
1775 CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION => {
1776 SelectAudioInputFunction(<_ as OperandEncodable>::try_from_bytes(&bytes[1..])?)
1777 }
1778 CEC_OP_UI_CMD_POWER_TOGGLE_FUNCTION => PowerToggleFunction,
1779 CEC_OP_UI_CMD_POWER_OFF_FUNCTION => PowerOffFunction,
1780 CEC_OP_UI_CMD_POWER_ON_FUNCTION => PowerOnFunction,
1781 CEC_OP_UI_CMD_F1_BLUE => F1Blue,
1782 CEC_OP_UI_CMD_F2_RED => F2Red,
1783 CEC_OP_UI_CMD_F3_GREEN => F3Green,
1784 CEC_OP_UI_CMD_F4_YELLOW => F4Yellow,
1785 CEC_OP_UI_CMD_F5 => F5,
1786 CEC_OP_UI_CMD_DATA => Data,
1787 x => {
1788 return Err(Error::InvalidValueForType {
1789 ty: "UiCommand",
1790 value: x.to_string(),
1791 })
1792 }
1793 })
1794 }
1795
1796 fn len(&self) -> usize {
1797 (match self {
1798 UiCommand::SelectBroadcastType(x) => <_ as OperandEncodable>::len(x),
1799 UiCommand::SelectSoundPresentation(x) => <_ as OperandEncodable>::len(x),
1800 UiCommand::PlayFunction(x) => <_ as OperandEncodable>::len(x),
1801 UiCommand::TuneFunction(x) => <_ as OperandEncodable>::len(x),
1802 UiCommand::SelectMediaFunction(x) => <_ as OperandEncodable>::len(x),
1803 UiCommand::SelectAvInputFunction(x) => <_ as OperandEncodable>::len(x),
1804 UiCommand::SelectAudioInputFunction(x) => <_ as OperandEncodable>::len(x),
1805 _ => 0,
1806 }) + 1
1807 }
1808
1809 fn expected_len() -> Range<usize> {
1810 Range::AtLeast(1)
1811 }
1812}
1813
1814#[cfg(test)]
1815mod test_ui_command {
1816 use super::*;
1817
1818 opcode_test! {
1819 ty: UiCommand,
1820 instance: UiCommand::Play,
1821 bytes: [
1822 constants::CEC_OP_UI_CMD_PLAY
1823 ],
1824 extra: [Overfull],
1825 }
1826
1827 opcode_test! {
1828 name: _select_broadcast_type_none,
1829 ty: UiCommand,
1830 instance: UiCommand::SelectBroadcastType(None),
1831 bytes: [
1832 constants::CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE,
1833 ],
1834 }
1835
1836 opcode_test! {
1837 name: _select_broadcast_type_some,
1838 ty: UiCommand,
1839 instance: UiCommand::SelectBroadcastType(Some(UiBroadcastType::Ip)),
1840 bytes: [
1841 constants::CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE,
1842 UiBroadcastType::Ip as u8,
1843 ],
1844 extra: [Overfull],
1845 }
1846
1847 opcode_test! {
1848 name: _select_sound_presentation_none,
1849 ty: UiCommand,
1850 instance: UiCommand::SelectSoundPresentation(None),
1851 bytes: [
1852 constants::CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION,
1853 ],
1854 }
1855
1856 opcode_test! {
1857 name: _select_sound_presentation_some,
1858 ty: UiCommand,
1859 instance: UiCommand::SelectSoundPresentation(Some(UiSoundPresentationControl::Karaoke)),
1860 bytes: [
1861 constants::CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION,
1862 UiSoundPresentationControl::Karaoke as u8,
1863 ],
1864 extra: [Overfull],
1865 }
1866
1867 opcode_test! {
1868 name: _play_function_none,
1869 ty: UiCommand,
1870 instance: UiCommand::PlayFunction(None),
1871 bytes: [
1872 constants::CEC_OP_UI_CMD_PLAY_FUNCTION,
1873 ],
1874 }
1875
1876 opcode_test! {
1877 name: _play_function_some,
1878 ty: UiCommand,
1879 instance: UiCommand::PlayFunction(Some(PlayMode::Still)),
1880 bytes: [
1881 constants::CEC_OP_UI_CMD_PLAY_FUNCTION,
1882 PlayMode::Still as u8,
1883 ],
1884 extra: [Overfull],
1885 }
1886
1887 opcode_test! {
1888 name: _tune_function_none,
1889 ty: UiCommand,
1890 instance: UiCommand::TuneFunction(None),
1891 bytes: [
1892 constants::CEC_OP_UI_CMD_TUNE_FUNCTION,
1893 ],
1894 }
1895
1896 opcode_test! {
1897 name: _tune_function_one_part,
1898 ty: UiCommand,
1899 instance: UiCommand::TuneFunction(Some(ChannelId::OnePart(0x1234))),
1900 bytes: [
1901 constants::CEC_OP_UI_CMD_TUNE_FUNCTION,
1902 0x04,
1903 0x00,
1904 0x12,
1905 0x34,
1906 ],
1907 extra: [Overfull],
1908 }
1909
1910 opcode_test! {
1911 name: _tune_function_two_part,
1912 ty: UiCommand,
1913 instance: UiCommand::TuneFunction(Some(ChannelId::TwoPart(0x0234, 0x5678))),
1914 bytes: [
1915 constants::CEC_OP_UI_CMD_TUNE_FUNCTION,
1916 0x0A,
1917 0x34,
1918 0x56,
1919 0x78,
1920 ],
1921 extra: [Overfull],
1922 }
1923
1924 opcode_test! {
1925 name: _select_media_function_none,
1926 ty: UiCommand,
1927 instance: UiCommand::SelectMediaFunction(None),
1928 bytes: [
1929 constants::CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION,
1930 ],
1931 }
1932
1933 opcode_test! {
1934 name: _select_media_function_some,
1935 ty: UiCommand,
1936 instance: UiCommand::SelectMediaFunction(Some(0x12)),
1937 bytes: [
1938 constants::CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION,
1939 0x12,
1940 ],
1941 extra: [Overfull],
1942 }
1943
1944 opcode_test! {
1945 name: _select_av_input_function_none,
1946 ty: UiCommand,
1947 instance: UiCommand::SelectAvInputFunction(None),
1948 bytes: [
1949 constants::CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION,
1950 ],
1951 }
1952
1953 opcode_test! {
1954 name: _select_av_input_function_some,
1955 ty: UiCommand,
1956 instance: UiCommand::SelectAvInputFunction(Some(0x12)),
1957 bytes: [
1958 constants::CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION,
1959 0x12,
1960 ],
1961 extra: [Overfull],
1962 }
1963
1964 opcode_test! {
1965 name: _select_audio_input_function_none,
1966 ty: UiCommand,
1967 instance: UiCommand::SelectAudioInputFunction(None),
1968 bytes: [
1969 constants::CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION,
1970 ],
1971 }
1972
1973 opcode_test! {
1974 name: _select_audio_input_function_some,
1975 ty: UiCommand,
1976 instance: UiCommand::SelectAudioInputFunction(Some(0x12)),
1977 bytes: [
1978 constants::CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION,
1979 0x12,
1980 ],
1981 extra: [Overfull],
1982 }
1983}
1984
1985#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
1986#[repr(u8)]
1987#[non_exhaustive]
1988pub enum Version {
1989 V1_1 = 0,
1992 V1_2 = 1,
1993 V1_2a = 2,
1994 V1_3 = 3,
1995 V1_3a = constants::CEC_OP_CEC_VERSION_1_3A,
1996 V1_4 = constants::CEC_OP_CEC_VERSION_1_4,
1997 V2_0 = constants::CEC_OP_CEC_VERSION_2_0,
1998}
1999
2000#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive, Operand)]
2001#[repr(u8)]
2002#[non_exhaustive]
2003pub enum UiSoundPresentationControl {
2004 DualMono = constants::CEC_OP_UI_SND_PRES_CTL_DUAL_MONO,
2005 Karaoke = constants::CEC_OP_UI_SND_PRES_CTL_KARAOKE,
2006 Downmix = constants::CEC_OP_UI_SND_PRES_CTL_DOWNMIX,
2007 Reverb = constants::CEC_OP_UI_SND_PRES_CTL_REVERB,
2008 Equalizer = constants::CEC_OP_UI_SND_PRES_CTL_EQUALIZER,
2009 BassUp = constants::CEC_OP_UI_SND_PRES_CTL_BASS_UP,
2010 BassNeutral = constants::CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL,
2011 BassDown = constants::CEC_OP_UI_SND_PRES_CTL_BASS_DOWN,
2012 TrebleUp = constants::CEC_OP_UI_SND_PRES_CTL_TREBLE_UP,
2013 TrebleNeutral = constants::CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL,
2014 TrebleDown = constants::CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN,
2015}
2016
2017bitflags! {
2018 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2019 pub struct AllDeviceTypes: u8 {
2020 const TV = constants::CEC_OP_ALL_DEVTYPE_TV;
2021 const RECORDING = constants::CEC_OP_ALL_DEVTYPE_RECORD;
2022 const TUNER = constants::CEC_OP_ALL_DEVTYPE_TUNER;
2023 const PLAYBACK = constants::CEC_OP_ALL_DEVTYPE_PLAYBACK;
2024 const AUDIOSYSTEM = constants::CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
2025 const SWITCH = constants::CEC_OP_ALL_DEVTYPE_SWITCH;
2026 }
2027}
2028
2029bitflags! {
2030 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2031 pub struct DeviceFeatures1: u8 {
2032 const HAS_RECORD_TV_SCREEN = constants::CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN;
2033 const HAS_SET_OSD_STRING = constants::CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING;
2034 const HAS_DECK_CONTROL = constants::CEC_OP_FEAT_DEV_HAS_DECK_CONTROL;
2035 const HAS_SET_AUDIO_RATE = constants::CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE;
2036 const SINK_HAS_ARC_TX = constants::CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX;
2037 const SOURCE_HAS_ARC_RX = constants::CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX;
2038 const HAS_SET_AUDIO_VOLUME_LEVEL = constants::CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL;
2039 }
2040}
2041
2042impl From<DeviceFeatures1> for u8 {
2043 #[inline]
2044 fn from(flags: DeviceFeatures1) -> u8 {
2045 flags.bits()
2046 }
2047}
2048
2049impl TryFrom<u8> for DeviceFeatures1 {
2050 type Error = Error;
2051
2052 fn try_from(flags: u8) -> Result<DeviceFeatures1> {
2053 Ok(DeviceFeatures1::from_bits_retain(flags))
2054 }
2055}
2056
2057bitflags! {
2058 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2059 pub struct RcProfileSource: u8 {
2060 const HAS_DEV_ROOT_MENU = constants::CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU;
2061 const HAS_DEV_SETUP_MENU = constants::CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU;
2062 const HAS_CONTENTS_MENU = constants::CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU;
2063 const HAS_MEDIA_TOP_MENU = constants::CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU;
2064 const HAS_MEDIA_CONTEXT_MENU = constants::CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU;
2065 }
2066}
2067
2068bitflags! {
2069 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2070 pub struct RecordingSequence: u8 {
2071 const SUNDAY = constants::CEC_OP_REC_SEQ_SUNDAY;
2072 const MONDAY = constants::CEC_OP_REC_SEQ_MONDAY;
2073 const TUESDAY = constants::CEC_OP_REC_SEQ_TUESDAY;
2074 const WEDNESDAY = constants::CEC_OP_REC_SEQ_WEDNESDAY;
2075 const THURSDAY = constants::CEC_OP_REC_SEQ_THURSDAY;
2076 const FRIDAY = constants::CEC_OP_REC_SEQ_FRIDAY;
2077 const SATURDAY = constants::CEC_OP_REC_SEQ_SATURDAY;
2078 }
2079}
2080
2081impl RecordingSequence {
2082 pub const ONCE_ONLY: RecordingSequence = RecordingSequence::empty();
2083
2084 #[must_use]
2085 pub fn is_once_only(&self) -> bool {
2086 self.is_empty()
2087 }
2088}
2089
2090#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2091pub struct AnalogueServiceId {
2092 pub broadcast_type: AnalogueBroadcastType,
2093 pub frequency: AnalogueFrequency,
2094 pub broadcast_system: BroadcastSystem,
2095}
2096
2097#[cfg(test)]
2098mod test_analogue_service_id {
2099 use super::*;
2100
2101 opcode_test! {
2102 ty: AnalogueServiceId,
2103 instance: AnalogueServiceId {
2104 broadcast_type: AnalogueBroadcastType::Terrestrial,
2105 frequency: 0x1234,
2106 broadcast_system: BroadcastSystem::PalBG,
2107 },
2108 bytes: [
2109 AnalogueBroadcastType::Terrestrial as u8,
2110 0x12,
2111 0x34,
2112 BroadcastSystem::PalBG as u8
2113 ],
2114 extra: [Overfull],
2115 }
2116
2117 #[test]
2118 fn test_decode_missing_opcodes_1() {
2119 assert_eq!(
2120 AnalogueServiceId::try_from_bytes(&[
2121 AnalogueBroadcastType::Terrestrial as u8,
2122 0x12,
2123 0x34
2124 ]),
2125 Err(Error::OutOfRange {
2126 expected: Range::AtLeast(4),
2127 got: 3,
2128 quantity: "bytes"
2129 })
2130 );
2131 }
2132
2133 #[test]
2134 fn test_decode_missing_opcodes_1_and_byte() {
2135 assert_eq!(
2136 AnalogueServiceId::try_from_bytes(&[AnalogueBroadcastType::Terrestrial as u8, 0x12]),
2137 Err(Error::OutOfRange {
2138 expected: Range::AtLeast(4),
2139 got: 2,
2140 quantity: "bytes"
2141 })
2142 );
2143 }
2144
2145 #[test]
2146 fn test_decode_missing_opcodes_2() {
2147 assert_eq!(
2148 AnalogueServiceId::try_from_bytes(&[AnalogueBroadcastType::Terrestrial as u8]),
2149 Err(Error::OutOfRange {
2150 expected: Range::AtLeast(4),
2151 got: 1,
2152 quantity: "bytes"
2153 })
2154 );
2155 }
2156
2157 #[test]
2158 fn test_decode_missing_opcodes_3() {
2159 assert_eq!(
2160 AnalogueServiceId::try_from_bytes(&[]),
2161 Err(Error::OutOfRange {
2162 expected: Range::AtLeast(4),
2163 got: 0,
2164 quantity: "bytes"
2165 })
2166 );
2167 }
2168}
2169
2170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2171#[non_exhaustive]
2172pub enum DigitalServiceId {
2173 AribGeneric(AribData),
2174 AtscGeneric(AtscData),
2175 DvbGeneric(DvbData),
2176 AribBs(AribData),
2177 AribCs(AribData),
2178 AribT(AribData),
2179 AtscCable(AtscData),
2180 AtscSatellite(AtscData),
2181 AtscTerrestrial(AtscData),
2182 DvbC(DvbData),
2183 DvbS(DvbData),
2184 DvbS2(DvbData),
2185 DvbT(DvbData),
2186 Channel {
2187 broadcast_system: DigitalServiceBroadcastSystem,
2188 channel_id: ChannelId,
2189 reserved: u16,
2190 },
2191}
2192
2193impl DigitalServiceId {
2194 #[must_use]
2195 pub fn arib_data(&self) -> Option<&'_ AribData> {
2196 match self {
2197 DigitalServiceId::AribGeneric(ref data)
2198 | DigitalServiceId::AribBs(ref data)
2199 | DigitalServiceId::AribCs(ref data)
2200 | DigitalServiceId::AribT(ref data) => Some(data),
2201 _ => None,
2202 }
2203 }
2204
2205 #[must_use]
2206 pub fn atsc_data(&self) -> Option<&'_ AtscData> {
2207 match self {
2208 DigitalServiceId::AtscGeneric(ref data)
2209 | DigitalServiceId::AtscCable(ref data)
2210 | DigitalServiceId::AtscSatellite(ref data)
2211 | DigitalServiceId::AtscTerrestrial(ref data) => Some(data),
2212 _ => None,
2213 }
2214 }
2215
2216 #[must_use]
2217 pub fn dvb_data(&self) -> Option<&'_ DvbData> {
2218 match self {
2219 DigitalServiceId::DvbGeneric(ref data)
2220 | DigitalServiceId::DvbC(ref data)
2221 | DigitalServiceId::DvbS(ref data)
2222 | DigitalServiceId::DvbS2(ref data)
2223 | DigitalServiceId::DvbT(ref data) => Some(data),
2224 _ => None,
2225 }
2226 }
2227
2228 #[must_use]
2229 pub fn broadcast_system(&self) -> DigitalServiceBroadcastSystem {
2230 use DigitalServiceBroadcastSystem as System;
2231 use DigitalServiceId as Id;
2232
2233 match self {
2234 Id::AribGeneric(_) => System::AribGeneric,
2235 Id::AtscGeneric(_) => System::AtscGeneric,
2236 Id::DvbGeneric(_) => System::DvbGeneric,
2237 Id::AribBs(_) => System::AribBs,
2238 Id::AribCs(_) => System::AribCs,
2239 Id::AribT(_) => System::AribT,
2240 Id::AtscCable(_) => System::AtscCable,
2241 Id::AtscSatellite(_) => System::AtscSatellite,
2242 Id::AtscTerrestrial(_) => System::AtscTerrestrial,
2243 Id::DvbC(_) => System::DvbC,
2244 Id::DvbS(_) => System::DvbS,
2245 Id::DvbS2(_) => System::DvbS2,
2246 Id::DvbT(_) => System::DvbT,
2247 Id::Channel {
2248 broadcast_system, ..
2249 } => *broadcast_system,
2250 }
2251 }
2252}
2253
2254impl OperandEncodable for DigitalServiceId {
2255 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
2256 use DigitalServiceId as Id;
2257
2258 let service_id_method = match self {
2259 Id::Channel { .. } => ServiceIdMethod::ByChannel,
2260 _ => ServiceIdMethod::ByDigitalId,
2261 };
2262 let broadcast_system = self.broadcast_system();
2263 buf.extend([broadcast_system as u8 | ((service_id_method as u8) << 7)]);
2264 match self {
2265 Id::AribGeneric(data) | Id::AribBs(data) | Id::AribCs(data) | Id::AribT(data) => {
2266 data.to_bytes(buf);
2267 }
2268 Id::AtscGeneric(data)
2269 | Id::AtscCable(data)
2270 | Id::AtscSatellite(data)
2271 | Id::AtscTerrestrial(data) => {
2272 data.to_bytes(buf);
2273 }
2274 Id::DvbGeneric(data)
2275 | Id::DvbC(data)
2276 | Id::DvbS(data)
2277 | Id::DvbS2(data)
2278 | Id::DvbT(data) => {
2279 data.to_bytes(buf);
2280 }
2281 Id::Channel {
2282 channel_id,
2283 reserved,
2284 ..
2285 } => {
2286 channel_id.to_bytes(buf);
2287 <u16 as OperandEncodable>::to_bytes(reserved, buf);
2288 }
2289 }
2290 }
2291
2292 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
2293 use DigitalServiceBroadcastSystem as System;
2294 use DigitalServiceId as Id;
2295
2296 Self::expected_len().check(bytes.len(), "bytes")?;
2297 let head = bytes[0];
2298 let service_id_method = ServiceIdMethod::try_from_primitive(head >> 7)?;
2299 let broadcast_system = System::try_from_primitive(head & 0x7F)?;
2300 if service_id_method == ServiceIdMethod::ByChannel {
2301 let channel_id = <ChannelId as OperandEncodable>::try_from_bytes(&bytes[1..])
2302 .map_err(Error::add_offset(1))?;
2303 let reserved = <u16 as OperandEncodable>::try_from_bytes(&bytes[5..])
2304 .map_err(Error::add_offset(5))?;
2305 Ok(Id::Channel {
2306 broadcast_system,
2307 channel_id,
2308 reserved,
2309 })
2310 } else {
2311 Ok(match broadcast_system {
2312 System::AribGeneric => Id::AribGeneric(
2313 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2314 ),
2315 System::AtscGeneric => Id::AtscGeneric(
2316 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2317 ),
2318 System::DvbGeneric => Id::DvbGeneric(
2319 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2320 ),
2321 System::AribCs => Id::AribCs(
2322 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2323 ),
2324 System::AribBs => Id::AribBs(
2325 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2326 ),
2327 System::AribT => Id::AribT(
2328 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2329 ),
2330 System::AtscCable => Id::AtscCable(
2331 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2332 ),
2333 System::AtscSatellite => Id::AtscSatellite(
2334 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2335 ),
2336 System::AtscTerrestrial => Id::AtscTerrestrial(
2337 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2338 ),
2339 System::DvbC => Id::DvbC(
2340 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2341 ),
2342 System::DvbS => Id::DvbS(
2343 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2344 ),
2345 System::DvbS2 => Id::DvbS2(
2346 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2347 ),
2348 System::DvbT => Id::DvbT(
2349 OperandEncodable::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
2350 ),
2351 })
2352 }
2353 }
2354
2355 fn len(&self) -> usize {
2356 7
2357 }
2358
2359 fn expected_len() -> Range<usize> {
2360 Range::AtLeast(7)
2361 }
2362}
2363
2364#[cfg(test)]
2365mod test_digital_service_id {
2366 use super::*;
2367
2368 opcode_test! {
2369 name: _arib_generic,
2370 ty: DigitalServiceId,
2371 instance: DigitalServiceId::AribGeneric(AribData {
2372 transport_stream_id: 0x1234,
2373 service_id: 0x5678,
2374 original_network_id: 0xABCD,
2375 }),
2376 bytes: [
2377 DigitalServiceBroadcastSystem::AribGeneric as u8,
2378 0x12,
2379 0x34,
2380 0x56,
2381 0x78,
2382 0xAB,
2383 0xCD,
2384 ],
2385 extra: [Overfull],
2386 }
2387
2388 opcode_test! {
2389 name: _atsc_generic,
2390 ty: DigitalServiceId,
2391 instance: DigitalServiceId::AtscGeneric(AtscData {
2392 transport_stream_id: 0x1234,
2393 program_number: 0x5678,
2394 }),
2395 bytes: [
2396 DigitalServiceBroadcastSystem::AtscGeneric as u8,
2397 0x12,
2398 0x34,
2399 0x56,
2400 0x78,
2401 0x00,
2402 0x00,
2403 ],
2404 extra: [Overfull],
2405 }
2406
2407 opcode_test! {
2408 name: _dvb_generic,
2409 ty: DigitalServiceId,
2410 instance: DigitalServiceId::DvbGeneric(DvbData {
2411 transport_stream_id: 0x1234,
2412 service_id: 0x5678,
2413 original_network_id: 0xABCD,
2414 }),
2415 bytes: [
2416 DigitalServiceBroadcastSystem::DvbGeneric as u8,
2417 0x12,
2418 0x34,
2419 0x56,
2420 0x78,
2421 0xAB,
2422 0xCD,
2423 ],
2424 extra: [Overfull],
2425 }
2426
2427 opcode_test! {
2428 name: _arib_bs,
2429 ty: DigitalServiceId,
2430 instance: DigitalServiceId::AribBs(AribData {
2431 transport_stream_id: 0x1234,
2432 service_id: 0x5678,
2433 original_network_id: 0xABCD,
2434 }),
2435 bytes: [
2436 DigitalServiceBroadcastSystem::AribBs as u8,
2437 0x12,
2438 0x34,
2439 0x56,
2440 0x78,
2441 0xAB,
2442 0xCD,
2443 ],
2444 extra: [Overfull],
2445 }
2446
2447 opcode_test! {
2448 name: _arib_cs,
2449 ty: DigitalServiceId,
2450 instance: DigitalServiceId::AribCs(AribData {
2451 transport_stream_id: 0x1234,
2452 service_id: 0x5678,
2453 original_network_id: 0xABCD,
2454 }),
2455 bytes: [
2456 DigitalServiceBroadcastSystem::AribCs as u8,
2457 0x12,
2458 0x34,
2459 0x56,
2460 0x78,
2461 0xAB,
2462 0xCD,
2463 ],
2464 extra: [Overfull],
2465 }
2466
2467 opcode_test! {
2468 name: _arib_t,
2469 ty: DigitalServiceId,
2470 instance: DigitalServiceId::AribT(AribData {
2471 transport_stream_id: 0x1234,
2472 service_id: 0x5678,
2473 original_network_id: 0xABCD,
2474 }),
2475 bytes: [
2476 DigitalServiceBroadcastSystem::AribT as u8,
2477 0x12,
2478 0x34,
2479 0x56,
2480 0x78,
2481 0xAB,
2482 0xCD,
2483 ],
2484 extra: [Overfull],
2485 }
2486
2487 opcode_test! {
2488 name: _atsc_cable,
2489 ty: DigitalServiceId,
2490 instance: DigitalServiceId::AtscCable(AtscData {
2491 transport_stream_id: 0x1234,
2492 program_number: 0x5678,
2493 }),
2494 bytes: [
2495 DigitalServiceBroadcastSystem::AtscCable as u8,
2496 0x12,
2497 0x34,
2498 0x56,
2499 0x78,
2500 0x00,
2501 0x00,
2502 ],
2503 extra: [Overfull],
2504 }
2505
2506 opcode_test! {
2507 name: _atsc_satellite,
2508 ty: DigitalServiceId,
2509 instance: DigitalServiceId::AtscSatellite(AtscData {
2510 transport_stream_id: 0x1234,
2511 program_number: 0x5678,
2512 }),
2513 bytes: [
2514 DigitalServiceBroadcastSystem::AtscSatellite as u8,
2515 0x12,
2516 0x34,
2517 0x56,
2518 0x78,
2519 0x00,
2520 0x00,
2521 ],
2522 extra: [Overfull],
2523 }
2524
2525 opcode_test! {
2526 name: _atsc_terrestrial,
2527 ty: DigitalServiceId,
2528 instance: DigitalServiceId::AtscTerrestrial(AtscData {
2529 transport_stream_id: 0x1234,
2530 program_number: 0x5678,
2531 }),
2532 bytes: [
2533 DigitalServiceBroadcastSystem::AtscTerrestrial as u8,
2534 0x12,
2535 0x34,
2536 0x56,
2537 0x78,
2538 0x00,
2539 0x00,
2540 ],
2541 extra: [Overfull],
2542 }
2543
2544 opcode_test! {
2545 name: _dvb_c,
2546 ty: DigitalServiceId,
2547 instance: DigitalServiceId::DvbC(DvbData {
2548 transport_stream_id: 0x1234,
2549 service_id: 0x5678,
2550 original_network_id: 0xABCD,
2551 }),
2552 bytes: [
2553 DigitalServiceBroadcastSystem::DvbC as u8,
2554 0x12,
2555 0x34,
2556 0x56,
2557 0x78,
2558 0xAB,
2559 0xCD,
2560 ],
2561 extra: [Overfull],
2562 }
2563
2564 opcode_test! {
2565 name: _dvb_s,
2566 ty: DigitalServiceId,
2567 instance: DigitalServiceId::DvbS(DvbData {
2568 transport_stream_id: 0x1234,
2569 service_id: 0x5678,
2570 original_network_id: 0xABCD,
2571 }),
2572 bytes: [
2573 DigitalServiceBroadcastSystem::DvbS as u8,
2574 0x12,
2575 0x34,
2576 0x56,
2577 0x78,
2578 0xAB,
2579 0xCD,
2580 ],
2581 extra: [Overfull],
2582 }
2583
2584 opcode_test! {
2585 name: _dvb_s2,
2586 ty: DigitalServiceId,
2587 instance: DigitalServiceId::DvbS2(DvbData {
2588 transport_stream_id: 0x1234,
2589 service_id: 0x5678,
2590 original_network_id: 0xABCD,
2591 }),
2592 bytes: [
2593 DigitalServiceBroadcastSystem::DvbS2 as u8,
2594 0x12,
2595 0x34,
2596 0x56,
2597 0x78,
2598 0xAB,
2599 0xCD,
2600 ],
2601 extra: [Overfull],
2602 }
2603
2604 opcode_test! {
2605 name: _dvb_t,
2606 ty: DigitalServiceId,
2607 instance: DigitalServiceId::DvbT(DvbData {
2608 transport_stream_id: 0x1234,
2609 service_id: 0x5678,
2610 original_network_id: 0xABCD,
2611 }),
2612 bytes: [
2613 DigitalServiceBroadcastSystem::DvbT as u8,
2614 0x12,
2615 0x34,
2616 0x56,
2617 0x78,
2618 0xAB,
2619 0xCD,
2620 ],
2621 extra: [Overfull],
2622 }
2623
2624 opcode_test! {
2625 name: _channel,
2626 ty: DigitalServiceId,
2627 instance: DigitalServiceId::Channel {
2628 broadcast_system: DigitalServiceBroadcastSystem::DvbGeneric,
2629 channel_id: ChannelId::TwoPart(0x0234, 0x5678),
2630 reserved: 0xABCD,
2631 },
2632 bytes: [
2633 DigitalServiceBroadcastSystem::DvbGeneric as u8 | 0x80,
2634 0x0A,
2635 0x34,
2636 0x56,
2637 0x78,
2638 0xAB,
2639 0xCD,
2640 ],
2641 extra: [Overfull],
2642 }
2643
2644 #[test]
2645 fn test_decode_empty() {
2646 assert_eq!(
2647 DigitalServiceId::try_from_bytes(&[]),
2648 Err(Error::OutOfRange {
2649 got: 0,
2650 expected: Range::AtLeast(7),
2651 quantity: "bytes"
2652 })
2653 );
2654 }
2655}
2656
2657pub trait DurationAvailable {
2658 fn duration_available(&self) -> Option<Duration>;
2659}
2660
2661#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2662#[non_exhaustive]
2663pub enum ProgrammedInfo {
2664 EnoughSpace,
2665 NotEnoughSpace {
2666 duration_available: Option<Duration>,
2667 },
2668 MayNotBeEnoughSpace {
2669 duration_available: Option<Duration>,
2670 },
2671 NoneAvailable,
2672}
2673
2674impl DurationAvailable for ProgrammedInfo {
2675 fn duration_available(&self) -> Option<Duration> {
2676 match self {
2677 ProgrammedInfo::NotEnoughSpace { duration_available }
2678 | ProgrammedInfo::MayNotBeEnoughSpace { duration_available } => *duration_available,
2679 _ => None,
2680 }
2681 }
2682}
2683
2684#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2685#[non_exhaustive]
2686pub enum NotProgrammedErrorInfo {
2687 NoFreeTimer,
2688 DateOutOfRange,
2689 RecordingSequenceError,
2690 InvalidExternalPlug,
2691 InvalidExternalPhysicalAddress,
2692 CaUnsupported,
2693 InsufficientCaEntitlements,
2694 ResolutionUnsupported,
2695 ParentalLock,
2696 ClockFailure,
2697 Duplicate {
2698 duration_available: Option<Duration>,
2699 },
2700}
2701
2702impl DurationAvailable for NotProgrammedErrorInfo {
2703 fn duration_available(&self) -> Option<Duration> {
2704 match self {
2705 NotProgrammedErrorInfo::Duplicate { duration_available } => *duration_available,
2706 _ => None,
2707 }
2708 }
2709}
2710
2711#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
2712pub enum TimerProgrammedInfo {
2713 Programmed(ProgrammedInfo),
2714 NotProgrammed(NotProgrammedErrorInfo),
2715}
2716
2717impl From<ProgrammedInfo> for TimerProgrammedInfo {
2718 fn from(val: ProgrammedInfo) -> TimerProgrammedInfo {
2719 TimerProgrammedInfo::Programmed(val)
2720 }
2721}
2722
2723impl From<NotProgrammedErrorInfo> for TimerProgrammedInfo {
2724 fn from(val: NotProgrammedErrorInfo) -> TimerProgrammedInfo {
2725 TimerProgrammedInfo::NotProgrammed(val)
2726 }
2727}
2728
2729#[bitfield(u8)]
2730#[derive(PartialEq, Eq, Hash, Operand)]
2731pub struct AudioFormatIdAndCode {
2732 #[bits(6)]
2733 pub code: usize,
2734 #[bits(2)]
2735 pub id: AudioFormatId,
2736}
2737
2738#[cfg(test)]
2739mod test_audio_format_id_and_code {
2740 use super::*;
2741
2742 opcode_test! {
2743 ty: AudioFormatIdAndCode,
2744 instance: AudioFormatIdAndCode::new()
2745 .with_code(0x05)
2746 .with_id(AudioFormatId::CEA861Cxt),
2747 bytes: [0x45],
2748 extra: [Overfull],
2749 }
2750
2751 #[test]
2752 fn test_decode_empty() {
2753 assert_eq!(
2754 AudioFormatIdAndCode::try_from_bytes(&[]),
2755 Err(Error::OutOfRange {
2756 got: 0,
2757 expected: Range::AtLeast(1),
2758 quantity: "bytes"
2759 })
2760 );
2761 }
2762}
2763
2764#[bitfield(u8)]
2765#[derive(PartialEq, Eq, Hash, Operand)]
2766pub struct AudioStatus {
2767 #[bits(7)]
2768 pub volume: usize,
2769 pub mute: bool,
2770}
2771
2772#[cfg(test)]
2773mod test_audio_status {
2774 use super::*;
2775
2776 opcode_test! {
2777 ty: AudioStatus,
2778 instance: AudioStatus::new().with_volume(0x09).with_mute(true),
2779 bytes: [0x89],
2780 extra: [Overfull],
2781 }
2782
2783 #[test]
2784 fn test_decode_empty() {
2785 assert_eq!(
2786 AudioStatus::try_from_bytes(&[]),
2787 Err(Error::OutOfRange {
2788 got: 0,
2789 expected: Range::AtLeast(1),
2790 quantity: "bytes"
2791 })
2792 );
2793 }
2794}
2795
2796#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Hash)]
2797pub struct BcdByte<const MIN: u8 = 0, const MAX: u8 = 99>(u8);
2798
2799impl<const MIN: u8, const MAX: u8> OperandEncodable for BcdByte<MIN, MAX> {
2800 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
2801 buf.extend([self.0]);
2802 }
2803
2804 fn try_from_bytes(bytes: &[u8]) -> Result<BcdByte<MIN, MAX>> {
2805 Self::expected_len().check(bytes.len(), "bytes")?;
2806 let byte = bytes[0];
2807 Range::from(0..=9).check(byte & 0xF, "low bits")?;
2808 Range::from(0..=9).check(byte >> 4, "high bits")?;
2809 Range::Interval {
2810 min: MIN as usize,
2811 max: MAX as usize,
2812 }
2813 .check((byte >> 4) * 10 + (byte & 0xF), "value")?;
2814 Ok(BcdByte(byte))
2815 }
2816
2817 fn len(&self) -> usize {
2818 1
2819 }
2820
2821 fn expected_len() -> Range<usize> {
2822 Range::AtLeast(1)
2823 }
2824}
2825
2826impl<const MIN: u8, const MAX: u8> From<BcdByte<MIN, MAX>> for u8 {
2827 #[inline]
2828 fn from(bcd: BcdByte<MIN, MAX>) -> u8 {
2829 (bcd.0 >> 4) * 10 + (bcd.0 & 0xF)
2830 }
2831}
2832
2833impl<const MIN: u8, const MAX: u8> TryFrom<u8> for BcdByte<MIN, MAX> {
2834 type Error = Error;
2835
2836 fn try_from(byte: u8) -> Result<BcdByte<MIN, MAX>> {
2837 Range::Interval {
2838 min: MIN as usize,
2839 max: MAX as usize,
2840 }
2841 .check(byte, "value")?;
2842 Ok(BcdByte(((byte / 10) << 4) + (byte % 10)))
2843 }
2844}
2845
2846#[cfg(test)]
2847mod test_bcd_byte {
2848 use super::*;
2849
2850 opcode_test! {
2851 ty: BcdByte::<0, 99>,
2852 instance: BcdByte::<0, 99>::try_from(12).unwrap(),
2853 bytes: [0x12],
2854 extra: [Overfull],
2855 }
2856
2857 #[test]
2858 fn test_create_range() {
2859 assert_eq!(
2860 BcdByte::<10, 20>::try_from(0),
2861 Err(Error::OutOfRange {
2862 expected: Range::from(10..=20),
2863 got: 0,
2864 quantity: "value",
2865 })
2866 );
2867
2868 assert_eq!(
2869 BcdByte::<10, 20>::try_from(9),
2870 Err(Error::OutOfRange {
2871 expected: Range::from(10..=20),
2872 got: 9,
2873 quantity: "value",
2874 })
2875 );
2876
2877 assert!(BcdByte::<10, 20>::try_from(10).is_ok());
2878
2879 assert!(BcdByte::<10, 20>::try_from(20).is_ok());
2880
2881 assert_eq!(
2882 BcdByte::<10, 20>::try_from(21),
2883 Err(Error::OutOfRange {
2884 expected: Range::from(10..=20),
2885 got: 21,
2886 quantity: "value",
2887 })
2888 );
2889
2890 assert_eq!(
2891 BcdByte::<10, 20>::try_from(30),
2892 Err(Error::OutOfRange {
2893 expected: Range::from(10..=20),
2894 got: 30,
2895 quantity: "value",
2896 })
2897 );
2898 }
2899
2900 #[test]
2901 fn test_decode_range() {
2902 assert_eq!(
2903 BcdByte::<10, 20>::try_from_bytes(&[0]),
2904 Err(Error::OutOfRange {
2905 expected: Range::from(10..=20),
2906 got: 0,
2907 quantity: "value",
2908 })
2909 );
2910
2911 assert_eq!(
2912 BcdByte::<10, 20>::try_from_bytes(&[9]),
2913 Err(Error::OutOfRange {
2914 expected: Range::from(10..=20),
2915 got: 9,
2916 quantity: "value",
2917 })
2918 );
2919
2920 assert_eq!(
2921 BcdByte::<10, 20>::try_from_bytes(&[0xA]),
2922 Err(Error::OutOfRange {
2923 expected: Range::from(0..=9),
2924 got: 10,
2925 quantity: "low bits",
2926 })
2927 );
2928
2929 assert_eq!(
2930 BcdByte::<10, 20>::try_from_bytes(&[0xA0]),
2931 Err(Error::OutOfRange {
2932 expected: Range::from(0..=9),
2933 got: 10,
2934 quantity: "high bits",
2935 })
2936 );
2937
2938 assert!(BcdByte::<10, 20>::try_from_bytes(&[0x10]).is_ok());
2939
2940 assert!(BcdByte::<10, 20>::try_from_bytes(&[0x20]).is_ok());
2941
2942 assert_eq!(
2943 BcdByte::<10, 20>::try_from_bytes(&[0x21]),
2944 Err(Error::OutOfRange {
2945 expected: Range::from(10..=20),
2946 got: 21,
2947 quantity: "value",
2948 })
2949 );
2950
2951 assert_eq!(
2952 BcdByte::<10, 20>::try_from_bytes(&[0x30]),
2953 Err(Error::OutOfRange {
2954 expected: Range::from(10..=20),
2955 got: 30,
2956 quantity: "value",
2957 })
2958 );
2959 }
2960
2961 #[test]
2962 fn test_into_u8() {
2963 assert_eq!(u8::from(BcdByte::<0, 99>(0x12)), 12u8);
2964 }
2965
2966 #[test]
2967 fn test_decode_empty() {
2968 assert_eq!(
2969 BcdByte::<0, 99>::try_from_bytes(&[]),
2970 Err(Error::OutOfRange {
2971 got: 0,
2972 expected: Range::AtLeast(1),
2973 quantity: "bytes"
2974 })
2975 );
2976 }
2977}
2978
2979#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
2980pub struct AribData {
2981 pub transport_stream_id: u16,
2982 pub service_id: u16,
2983 pub original_network_id: u16,
2984}
2985
2986#[cfg(test)]
2987mod test_arib_data {
2988 use super::*;
2989
2990 opcode_test! {
2991 ty: AribData,
2992 instance: AribData {
2993 transport_stream_id: 0x1234,
2994 service_id: 0x5678,
2995 original_network_id: 0xABCD,
2996 },
2997 bytes: [0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD],
2998 extra: [Overfull],
2999 }
3000
3001 #[test]
3002 fn test_decode_empty() {
3003 assert_eq!(
3004 AribData::try_from_bytes(&[]),
3005 Err(Error::OutOfRange {
3006 got: 0,
3007 expected: Range::AtLeast(6),
3008 quantity: "bytes"
3009 })
3010 );
3011 }
3012}
3013
3014#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3015pub struct AtscData {
3016 pub transport_stream_id: u16,
3017 pub program_number: u16,
3018}
3019
3020impl OperandEncodable for AtscData {
3021 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
3022 self.transport_stream_id.to_bytes(buf);
3023 self.program_number.to_bytes(buf);
3024 <u16 as OperandEncodable>::to_bytes(&0, buf);
3025 }
3026
3027 fn try_from_bytes(bytes: &[u8]) -> Result<AtscData> {
3028 Self::expected_len().check(bytes.len(), "bytes")?;
3029 let transport_stream_id = u16::try_from_bytes(bytes)?;
3030 let program_number = u16::try_from_bytes(&bytes[2..]).map_err(Error::add_offset(2))?;
3031 if u16::try_from_bytes(&bytes[4..]).map_err(Error::add_offset(4))? != 0 {
3032 return Err(Error::InvalidData);
3033 }
3034 Ok(AtscData {
3035 transport_stream_id,
3036 program_number,
3037 })
3038 }
3039
3040 fn len(&self) -> usize {
3041 6
3042 }
3043
3044 fn expected_len() -> Range<usize> {
3045 Range::AtLeast(6)
3046 }
3047}
3048
3049#[cfg(test)]
3050mod test_atsc_data {
3051 use super::*;
3052
3053 opcode_test! {
3054 ty: AtscData,
3055 instance: AtscData {
3056 transport_stream_id: 0x1234,
3057 program_number: 0x5678,
3058 },
3059 bytes: [0x12, 0x34, 0x56, 0x78, 0x00, 0x00],
3060 extra: [Overfull],
3061 }
3062
3063 #[test]
3064 fn test_decode_junk() {
3065 assert_eq!(
3066 AtscData::try_from_bytes(&[0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]),
3067 Err(Error::InvalidData)
3068 );
3069 }
3070
3071 #[test]
3072 fn test_decode_empty() {
3073 assert_eq!(
3074 AtscData::try_from_bytes(&[]),
3075 Err(Error::OutOfRange {
3076 got: 0,
3077 expected: Range::AtLeast(6),
3078 quantity: "bytes"
3079 })
3080 );
3081 }
3082}
3083
3084#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3085#[non_exhaustive]
3086pub enum ChannelId {
3087 OnePart(u16),
3088 TwoPart(u16, u16),
3089}
3090
3091impl OperandEncodable for ChannelId {
3092 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
3093 let number_format;
3094 let high;
3095 let low;
3096 match self {
3097 ChannelId::OnePart(part) => {
3098 number_format = ChannelNumberFormat::Fmt1Part;
3099 high = 0;
3100 low = *part;
3101 }
3102 ChannelId::TwoPart(major, minor) => {
3103 number_format = ChannelNumberFormat::Fmt2Part;
3104 high = *major;
3105 low = *minor;
3106 }
3107 }
3108 let number_format = u16::from(u8::from(number_format)) << 10;
3109 let high: u16 = number_format | high;
3110 high.to_bytes(buf);
3111 low.to_bytes(buf);
3112 }
3113
3114 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
3115 Self::expected_len().check(bytes.len(), "bytes")?;
3116 let high = u16::try_from_bytes(bytes)?;
3117 let low = u16::try_from_bytes(&bytes[2..]).map_err(Error::add_offset(2))?;
3118 let number_format = u8::try_from(high >> 10).unwrap();
3119 let number_format = ChannelNumberFormat::try_from_primitive(number_format)?;
3120 match number_format {
3121 ChannelNumberFormat::Fmt1Part => Ok(ChannelId::OnePart(low)),
3122 ChannelNumberFormat::Fmt2Part => Ok(ChannelId::TwoPart(high & 0x3FF, low)),
3123 }
3124 }
3125
3126 fn len(&self) -> usize {
3127 4
3128 }
3129
3130 fn expected_len() -> Range<usize> {
3131 Range::AtLeast(4)
3132 }
3133}
3134
3135#[cfg(test)]
3136mod test_channel_id {
3137 use super::*;
3138
3139 opcode_test! {
3140 name: _1_part,
3141 ty: ChannelId,
3142 instance: ChannelId::OnePart(0x1234),
3143 bytes: [0x04, 0x00, 0x12, 0x34],
3144 extra: [Overfull],
3145 }
3146
3147 opcode_test! {
3148 name: _2_part,
3149 ty: ChannelId,
3150 instance: ChannelId::TwoPart(0x0123, 0x4567),
3151 bytes: [0x09, 0x23, 0x45, 0x67],
3152 extra: [Overfull],
3153 }
3154
3155 #[test]
3156 fn test_decode_invalid_format() {
3157 assert_eq!(
3158 ChannelId::try_from_bytes(&[0x00, 0x00, 0x12, 0x34]),
3159 Err(Error::InvalidValueForType {
3160 ty: "ChannelNumberFormat",
3161 value: String::from("0")
3162 })
3163 )
3164 }
3165
3166 #[test]
3167 fn test_decode_ignored_bytes() {
3168 assert_eq!(
3169 ChannelId::try_from_bytes(&[
3170 (ChannelNumberFormat::Fmt1Part as u8) << 2,
3171 0x56,
3172 0x12,
3173 0x34
3174 ]),
3175 Ok(ChannelId::OnePart(0x1234))
3176 )
3177 }
3178
3179 #[test]
3180 fn test_decode_empty() {
3181 assert_eq!(
3182 ChannelId::try_from_bytes(&[]),
3183 Err(Error::OutOfRange {
3184 got: 0,
3185 expected: Range::AtLeast(4),
3186 quantity: "bytes"
3187 })
3188 );
3189 }
3190}
3191
3192#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3193pub struct DeviceFeatures {
3194 pub device_features_1: DeviceFeatures1,
3195 pub device_features_n: BoundedBufferOperand<14, u8>,
3196}
3197
3198impl DeviceFeatures {
3199 #[must_use]
3200 pub fn new(device_features_1: DeviceFeatures1) -> DeviceFeatures {
3201 DeviceFeatures {
3202 device_features_1,
3203 device_features_n: BoundedBufferOperand::default(),
3204 }
3205 }
3206}
3207
3208impl TaggedLengthBuffer for DeviceFeatures {
3209 type FixedParam = DeviceFeatures1;
3210
3211 fn try_new(first: DeviceFeatures1, extra_params: &[u8]) -> Result<DeviceFeatures> {
3212 Ok(DeviceFeatures {
3213 device_features_1: first,
3214 device_features_n: BoundedBufferOperand::<14, u8>::try_from_bytes(extra_params)?,
3215 })
3216 }
3217
3218 fn fixed_param(&self) -> DeviceFeatures1 {
3219 self.device_features_1
3220 }
3221
3222 fn extra_params(&self) -> &[u8] {
3223 &self.device_features_n.buffer[..self.device_features_n.len]
3224 }
3225}
3226
3227#[cfg(test)]
3228mod test_device_features {
3229 use super::*;
3230
3231 opcode_test! {
3232 name: _1_only,
3233 ty: DeviceFeatures,
3234 instance: DeviceFeatures {
3235 device_features_1: DeviceFeatures1::HAS_RECORD_TV_SCREEN |
3236 DeviceFeatures1::HAS_SET_OSD_STRING |
3237 DeviceFeatures1::HAS_SET_AUDIO_VOLUME_LEVEL,
3238 device_features_n: BoundedBufferOperand::default(),
3239 },
3240 bytes: [0x61],
3241 }
3242
3243 opcode_test! {
3244 name: _n,
3245 ty: DeviceFeatures,
3246 instance: DeviceFeatures {
3247 device_features_1: DeviceFeatures1::HAS_RECORD_TV_SCREEN |
3248 DeviceFeatures1::HAS_SET_OSD_STRING |
3249 DeviceFeatures1::HAS_SET_AUDIO_VOLUME_LEVEL,
3250 device_features_n: BoundedBufferOperand::try_from_bytes(&[
3251 0x40,
3252 0x20,
3253 0x10,
3254 0x08,
3255 0x04,
3256 0x02,
3257 0x01,
3258 0x00
3259 ]).unwrap(),
3260 },
3261 bytes: [0xE1, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x81, 0x00],
3262 }
3263}
3264
3265#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
3266pub struct Duration {
3267 pub hours: DurationHours,
3268 pub minutes: Minute,
3269}
3270
3271#[cfg(test)]
3272mod test_duration {
3273 use super::*;
3274
3275 opcode_test! {
3276 ty: Duration,
3277 instance: Duration {
3278 hours: DurationHours::try_from(99u8).unwrap(),
3279 minutes: Minute::try_from(20u8).unwrap(),
3280 },
3281 bytes: [0x99, 0x20],
3282 extra: [Overfull],
3283 }
3284
3285 #[test]
3286 fn test_invalid_minute() {
3287 assert_eq!(
3288 Duration::try_from_bytes(&[0x04, 0x69]),
3289 Err(Error::OutOfRange {
3290 expected: Range::from(0..=59),
3291 got: 69,
3292 quantity: "value",
3293 })
3294 );
3295 }
3296
3297 #[test]
3298 fn test_invalid_bcd_hour() {
3299 assert_eq!(
3300 Duration::try_from_bytes(&[0x0A, 0x20]),
3301 Err(Error::OutOfRange {
3302 expected: Range::from(0..=9),
3303 got: 10,
3304 quantity: "low bits",
3305 })
3306 );
3307 }
3308
3309 #[test]
3310 fn test_invalid_bcd_minute() {
3311 assert_eq!(
3312 Duration::try_from_bytes(&[0x04, 0x1A]),
3313 Err(Error::OutOfRange {
3314 expected: Range::from(0..=9),
3315 got: 10,
3316 quantity: "low bits",
3317 })
3318 );
3319 }
3320}
3321
3322#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
3323pub struct DvbData {
3324 pub transport_stream_id: u16,
3325 pub service_id: u16,
3326 pub original_network_id: u16,
3327}
3328
3329#[cfg(test)]
3330mod test_dvb_data {
3331 use super::*;
3332
3333 opcode_test! {
3334 ty: DvbData,
3335 instance: DvbData {
3336 transport_stream_id: 0x1234,
3337 service_id: 0x5678,
3338 original_network_id: 0xABCD,
3339 },
3340 bytes: [0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD],
3341 extra: [Overfull],
3342 }
3343
3344 #[test]
3345 fn test_decode_empty() {
3346 assert_eq!(
3347 DvbData::try_from_bytes(&[]),
3348 Err(Error::OutOfRange {
3349 got: 0,
3350 expected: Range::AtLeast(6),
3351 quantity: "bytes"
3352 })
3353 );
3354 }
3355}
3356
3357#[bitfield(u8)]
3358#[derive(PartialEq, Eq, Hash, Operand)]
3359pub struct LatencyFlags {
3360 #[bits(2)]
3361 pub audio_out_compensated: AudioOutputCompensated,
3362 pub low_latency_mode: bool,
3363 #[bits(5)]
3364 _reserved: usize,
3365}
3366
3367#[cfg(test)]
3368mod test_latency_flags {
3369 use super::*;
3370
3371 opcode_test! {
3372 ty: LatencyFlags,
3373 instance: LatencyFlags::new()
3374 .with_audio_out_compensated(AudioOutputCompensated::PartialDelay)
3375 .with_low_latency_mode(true),
3376 bytes: [0x07],
3377 extra: [Overfull],
3378 }
3379
3380 #[test]
3381 fn test_decode_empty() {
3382 assert_eq!(
3383 LatencyFlags::try_from_bytes(&[]),
3384 Err(Error::OutOfRange {
3385 got: 0,
3386 expected: Range::AtLeast(1),
3387 quantity: "bytes"
3388 })
3389 );
3390 }
3391}
3392
3393#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3394pub struct RcProfile {
3395 pub rc_profile_1: RcProfile1,
3396 pub rc_profile_n: BoundedBufferOperand<14, u8>,
3397}
3398
3399impl RcProfile {
3400 #[must_use]
3401 pub fn new(rc_profile_1: RcProfile1) -> RcProfile {
3402 RcProfile {
3403 rc_profile_1,
3404 rc_profile_n: BoundedBufferOperand::default(),
3405 }
3406 }
3407}
3408
3409impl TaggedLengthBuffer for RcProfile {
3410 type FixedParam = RcProfile1;
3411
3412 fn try_new(first: RcProfile1, extra_params: &[u8]) -> Result<RcProfile> {
3413 Ok(RcProfile {
3414 rc_profile_1: first,
3415 rc_profile_n: BoundedBufferOperand::<14, u8>::try_from_bytes(extra_params)?,
3416 })
3417 }
3418
3419 fn fixed_param(&self) -> RcProfile1 {
3420 self.rc_profile_1
3421 }
3422
3423 fn extra_params(&self) -> &[u8] {
3424 &self.rc_profile_n.buffer[..self.rc_profile_n.len]
3425 }
3426}
3427
3428#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3429pub enum RcProfile1 {
3430 Source(RcProfileSource),
3431 Tv { id: RcProfileId },
3432}
3433
3434impl From<RcProfile1> for u8 {
3435 #[inline]
3436 fn from(profile: RcProfile1) -> u8 {
3437 match profile {
3438 RcProfile1::Source(profile_source) => profile_source.bits(),
3439 RcProfile1::Tv { id: profile_id } => profile_id.into(),
3440 }
3441 }
3442}
3443
3444impl TryFrom<u8> for RcProfile1 {
3445 type Error = Error;
3446
3447 fn try_from(flags: u8) -> Result<RcProfile1> {
3448 let flags = flags & 0x7F;
3449 if (flags & 0x40) == 0x40 {
3450 Ok(RcProfile1::Source(RcProfileSource::from_bits_retain(flags)))
3451 } else {
3452 Ok(RcProfile1::Tv {
3453 id: RcProfileId::try_from_primitive(flags & 0xF)?,
3454 })
3455 }
3456 }
3457}
3458
3459impl From<RcProfileSource> for RcProfile1 {
3460 fn from(val: RcProfileSource) -> RcProfile1 {
3461 RcProfile1::Source(val)
3462 }
3463}
3464
3465#[cfg(test)]
3466mod test_rc_profile {
3467 use super::*;
3468
3469 #[test]
3470 fn test_rc_profile_1_tv() {
3471 assert_eq!(
3472 RcProfile1::try_from(0x02),
3473 Ok(RcProfile1::Tv {
3474 id: RcProfileId::Profile1
3475 })
3476 );
3477 }
3478
3479 #[test]
3480 fn test_rc_profile_1_source() {
3481 assert_eq!(
3482 RcProfile1::try_from(0x5F),
3483 Ok(RcProfile1::Source(RcProfileSource::all()))
3484 );
3485 }
3486
3487 opcode_test! {
3488 name: _tv,
3489 ty: RcProfile,
3490 instance: RcProfile {
3491 rc_profile_1: RcProfile1::Tv { id: RcProfileId::Profile1 },
3492 rc_profile_n: BoundedBufferOperand::default(),
3493 },
3494 bytes: [0x02],
3495 extra: [Overfull],
3496 }
3497
3498 opcode_test! {
3499 name: _source,
3500 ty: RcProfile,
3501 instance: RcProfile {
3502 rc_profile_1: RcProfile1::Source(RcProfileSource::HAS_DEV_ROOT_MENU),
3503 rc_profile_n: BoundedBufferOperand::default(),
3504 },
3505 bytes: [0x40 | RcProfileSource::HAS_DEV_ROOT_MENU.bits()],
3506 extra: [Overfull],
3507 }
3508
3509 opcode_test! {
3510 name: _extra_bytes,
3511 ty: RcProfile,
3512 instance: RcProfile {
3513 rc_profile_1: RcProfile1::Tv { id: RcProfileId::Profile1 },
3514 rc_profile_n: BoundedBufferOperand::try_from_bytes(&[0x40]).unwrap(),
3515 },
3516 bytes: [0x82, 0x40],
3517 extra: [Overfull],
3518 }
3519}
3520
3521#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Operand)]
3522pub struct Time {
3523 pub hour: Hour,
3524 pub minute: Minute,
3525}
3526
3527#[cfg(test)]
3528mod test_time {
3529 use super::*;
3530
3531 opcode_test! {
3532 ty: Time,
3533 instance: Time {
3534 hour: Hour::try_from(4u8).unwrap(),
3535 minute: Minute::try_from(20u8).unwrap(),
3536 },
3537 bytes: [0x04, 0x20],
3538 extra: [Overfull],
3539 }
3540
3541 #[test]
3542 fn test_invalid_hour() {
3543 assert_eq!(
3544 Time::try_from_bytes(&[0x24, 0x30]),
3545 Err(Error::OutOfRange {
3546 expected: Range::from(0..=23),
3547 got: 24,
3548 quantity: "value",
3549 })
3550 );
3551 }
3552
3553 #[test]
3554 fn test_invalid_minute() {
3555 assert_eq!(
3556 Time::try_from_bytes(&[0x04, 0x69]),
3557 Err(Error::OutOfRange {
3558 expected: Range::from(0..=59),
3559 got: 69,
3560 quantity: "value",
3561 })
3562 );
3563 }
3564
3565 #[test]
3566 fn test_invalid_bcd_hour() {
3567 assert_eq!(
3568 Time::try_from_bytes(&[0x0A, 0x20]),
3569 Err(Error::OutOfRange {
3570 expected: Range::from(0..=9),
3571 got: 10,
3572 quantity: "low bits",
3573 })
3574 );
3575 }
3576
3577 #[test]
3578 fn test_invalid_bcd_minute() {
3579 assert_eq!(
3580 Time::try_from_bytes(&[0x04, 0x1A]),
3581 Err(Error::OutOfRange {
3582 expected: Range::from(0..=9),
3583 got: 10,
3584 quantity: "low bits",
3585 })
3586 );
3587 }
3588}
3589
3590#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3591pub struct TimerStatusData {
3592 pub overlap_warning: bool,
3593 pub media_info: MediaInfo,
3594 pub programmed_info: TimerProgrammedInfo,
3595}
3596
3597impl OperandEncodable for TimerStatusData {
3598 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
3599 let mut byte: u8 = 0;
3600 if self.overlap_warning {
3601 byte |= 0x80;
3602 }
3603 byte |= <_ as Into<u8>>::into(self.media_info) << 5;
3604 let mut duration = None;
3605
3606 match self.programmed_info {
3607 TimerProgrammedInfo::Programmed(programmed) => {
3608 byte |= 0x10;
3609 byte |= match programmed {
3610 ProgrammedInfo::EnoughSpace => constants::CEC_OP_PROG_INFO_ENOUGH_SPACE,
3611 ProgrammedInfo::NotEnoughSpace { duration_available } => {
3612 duration = duration_available;
3613 constants::CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE
3614 }
3615 ProgrammedInfo::MayNotBeEnoughSpace { duration_available } => {
3616 duration = duration_available;
3617 constants::CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE
3618 }
3619 ProgrammedInfo::NoneAvailable => constants::CEC_OP_PROG_INFO_NONE_AVAILABLE,
3620 };
3621 }
3622 TimerProgrammedInfo::NotProgrammed(not_programmed) => {
3623 byte |= match not_programmed {
3624 NotProgrammedErrorInfo::NoFreeTimer => {
3625 constants::CEC_OP_PROG_ERROR_NO_FREE_TIMER
3626 }
3627 NotProgrammedErrorInfo::DateOutOfRange => {
3628 constants::CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE
3629 }
3630 NotProgrammedErrorInfo::RecordingSequenceError => {
3631 constants::CEC_OP_PROG_ERROR_REC_SEQ_ERROR
3632 }
3633 NotProgrammedErrorInfo::InvalidExternalPlug => {
3634 constants::CEC_OP_PROG_ERROR_INV_EXT_PLUG
3635 }
3636 NotProgrammedErrorInfo::InvalidExternalPhysicalAddress => {
3637 constants::CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR
3638 }
3639 NotProgrammedErrorInfo::CaUnsupported => constants::CEC_OP_PROG_ERROR_CA_UNSUPP,
3640 NotProgrammedErrorInfo::InsufficientCaEntitlements => {
3641 constants::CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS
3642 }
3643 NotProgrammedErrorInfo::ResolutionUnsupported => {
3644 constants::CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP
3645 }
3646 NotProgrammedErrorInfo::ParentalLock => {
3647 constants::CEC_OP_PROG_ERROR_PARENTAL_LOCK
3648 }
3649 NotProgrammedErrorInfo::ClockFailure => {
3650 constants::CEC_OP_PROG_ERROR_CLOCK_FAILURE
3651 }
3652 NotProgrammedErrorInfo::Duplicate { duration_available } => {
3653 duration = duration_available;
3654 constants::CEC_OP_PROG_ERROR_DUPLICATE
3655 }
3656 };
3657 }
3658 }
3659
3660 buf.extend([byte]);
3661 duration.to_bytes(buf);
3662 }
3663
3664 fn try_from_bytes(bytes: &[u8]) -> Result<TimerStatusData> {
3665 Self::expected_len().check(bytes.len(), "bytes")?;
3666 let byte = bytes[0];
3667 let overlap_warning = (byte & 0x80) == 0x80;
3668 let media_info = MediaInfo::try_from_primitive((byte >> 5) & 3)?;
3669 let programmed_info = if (byte & 0x10) == 0x10 {
3670 TimerProgrammedInfo::Programmed(match byte & 0xF {
3671 constants::CEC_OP_PROG_INFO_ENOUGH_SPACE => ProgrammedInfo::EnoughSpace,
3672 constants::CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE => {
3673 let duration_available = if bytes.len() >= 3 {
3674 Some(
3675 Duration::try_from_bytes(&bytes[1..])
3676 .map_err(crate::Error::add_offset(1))?,
3677 )
3678 } else {
3679 None
3680 };
3681 ProgrammedInfo::NotEnoughSpace { duration_available }
3682 }
3683 constants::CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE => {
3684 let duration_available = if bytes.len() >= 3 {
3685 Some(
3686 Duration::try_from_bytes(&bytes[1..])
3687 .map_err(crate::Error::add_offset(1))?,
3688 )
3689 } else {
3690 None
3691 };
3692 ProgrammedInfo::MayNotBeEnoughSpace { duration_available }
3693 }
3694 constants::CEC_OP_PROG_INFO_NONE_AVAILABLE => ProgrammedInfo::NoneAvailable,
3695 v => {
3696 return Err(Error::InvalidValueForType {
3697 ty: "ProgrammedInfo",
3698 value: v.to_string(),
3699 })
3700 }
3701 })
3702 } else {
3703 TimerProgrammedInfo::NotProgrammed(match byte & 0xF {
3704 constants::CEC_OP_PROG_ERROR_NO_FREE_TIMER => NotProgrammedErrorInfo::NoFreeTimer,
3705 constants::CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE => {
3706 NotProgrammedErrorInfo::DateOutOfRange
3707 }
3708 constants::CEC_OP_PROG_ERROR_REC_SEQ_ERROR => {
3709 NotProgrammedErrorInfo::RecordingSequenceError
3710 }
3711 constants::CEC_OP_PROG_ERROR_INV_EXT_PLUG => {
3712 NotProgrammedErrorInfo::InvalidExternalPlug
3713 }
3714 constants::CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR => {
3715 NotProgrammedErrorInfo::InvalidExternalPhysicalAddress
3716 }
3717 constants::CEC_OP_PROG_ERROR_CA_UNSUPP => NotProgrammedErrorInfo::CaUnsupported,
3718 constants::CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS => {
3719 NotProgrammedErrorInfo::InsufficientCaEntitlements
3720 }
3721 constants::CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP => {
3722 NotProgrammedErrorInfo::ResolutionUnsupported
3723 }
3724 constants::CEC_OP_PROG_ERROR_PARENTAL_LOCK => NotProgrammedErrorInfo::ParentalLock,
3725 constants::CEC_OP_PROG_ERROR_CLOCK_FAILURE => NotProgrammedErrorInfo::ClockFailure,
3726 constants::CEC_OP_PROG_ERROR_DUPLICATE => {
3727 let duration_available = if bytes.len() >= 3 {
3728 Some(
3729 Duration::try_from_bytes(&bytes[1..])
3730 .map_err(crate::Error::add_offset(1))?,
3731 )
3732 } else {
3733 None
3734 };
3735 NotProgrammedErrorInfo::Duplicate { duration_available }
3736 }
3737 v => {
3738 return Err(Error::InvalidValueForType {
3739 ty: "NotProgrammedErrorInfo",
3740 value: v.to_string(),
3741 })
3742 }
3743 })
3744 };
3745 Ok(TimerStatusData {
3746 overlap_warning,
3747 media_info,
3748 programmed_info,
3749 })
3750 }
3751
3752 fn len(&self) -> usize {
3753 match self.programmed_info {
3754 TimerProgrammedInfo::Programmed(programmed) => match programmed {
3755 ProgrammedInfo::NotEnoughSpace { duration_available }
3756 | ProgrammedInfo::MayNotBeEnoughSpace { duration_available } => {
3757 if duration_available.is_some() {
3758 3
3759 } else {
3760 1
3761 }
3762 }
3763 _ => 1,
3764 },
3765 TimerProgrammedInfo::NotProgrammed(not_programmed) => match not_programmed {
3766 NotProgrammedErrorInfo::Duplicate { duration_available } => {
3767 if duration_available.is_some() {
3768 3
3769 } else {
3770 1
3771 }
3772 }
3773 _ => 1,
3774 },
3775 }
3776 }
3777
3778 fn expected_len() -> Range<usize> {
3779 Range::AtLeast(1)
3780 }
3781}
3782
3783#[cfg(test)]
3784mod test_timer_status_data {
3785 use super::*;
3786
3787 opcode_test! {
3788 name: _not_programmed_no_free_timer,
3789 ty: TimerStatusData,
3790 instance: TimerStatusData {
3791 overlap_warning: false,
3792 media_info: MediaInfo::UnprotectedMedia,
3793 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::NoFreeTimer),
3794 },
3795 bytes: [0x01],
3796 extra: [Overfull],
3797 }
3798
3799 opcode_test! {
3800 name: _overlap_warning,
3801 ty: TimerStatusData,
3802 instance: TimerStatusData {
3803 overlap_warning: true,
3804 media_info: MediaInfo::UnprotectedMedia,
3805 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::NoFreeTimer),
3806 },
3807 bytes: [0x81],
3808 extra: [Overfull],
3809 }
3810
3811 opcode_test! {
3812 name: _protected_media,
3813 ty: TimerStatusData,
3814 instance: TimerStatusData {
3815 overlap_warning: false,
3816 media_info: MediaInfo::ProtectedMedia,
3817 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::NoFreeTimer),
3818 },
3819 bytes: [0x21],
3820 extra: [Overfull],
3821 }
3822
3823 opcode_test! {
3824 name: _no_media,
3825 ty: TimerStatusData,
3826 instance: TimerStatusData {
3827 overlap_warning: false,
3828 media_info: MediaInfo::NoMedia,
3829 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::NoFreeTimer),
3830 },
3831 bytes: [0x41],
3832 extra: [Overfull],
3833 }
3834
3835 opcode_test! {
3836 name: _not_programmed_date_out_of_range,
3837 ty: TimerStatusData,
3838 instance: TimerStatusData {
3839 overlap_warning: false,
3840 media_info: MediaInfo::UnprotectedMedia,
3841 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::DateOutOfRange),
3842 },
3843 bytes: [0x02],
3844 extra: [Overfull],
3845 }
3846
3847 opcode_test! {
3848 name: _not_programmed_recording_sequence_error,
3849 ty: TimerStatusData,
3850 instance: TimerStatusData {
3851 overlap_warning: false,
3852 media_info: MediaInfo::UnprotectedMedia,
3853 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::RecordingSequenceError),
3854 },
3855 bytes: [0x03],
3856 extra: [Overfull],
3857 }
3858
3859 opcode_test! {
3860 name: _not_programmed_invalid_ext_plug,
3861 ty: TimerStatusData,
3862 instance: TimerStatusData {
3863 overlap_warning: false,
3864 media_info: MediaInfo::UnprotectedMedia,
3865 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::InvalidExternalPlug),
3866 },
3867 bytes: [0x04],
3868 extra: [Overfull],
3869 }
3870
3871 opcode_test! {
3872 name: _not_programmed_invalid_ext_phys_addr,
3873 ty: TimerStatusData,
3874 instance: TimerStatusData {
3875 overlap_warning: false,
3876 media_info: MediaInfo::UnprotectedMedia,
3877 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::InvalidExternalPhysicalAddress),
3878 },
3879 bytes: [0x05],
3880 extra: [Overfull],
3881 }
3882
3883 opcode_test! {
3884 name: _not_programmed_ca_unsupported,
3885 ty: TimerStatusData,
3886 instance: TimerStatusData {
3887 overlap_warning: false,
3888 media_info: MediaInfo::UnprotectedMedia,
3889 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::CaUnsupported),
3890 },
3891 bytes: [0x06],
3892 extra: [Overfull],
3893 }
3894
3895 opcode_test! {
3896 name: _not_programmed_insufficient_ca_entitlements,
3897 ty: TimerStatusData,
3898 instance: TimerStatusData {
3899 overlap_warning: false,
3900 media_info: MediaInfo::UnprotectedMedia,
3901 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::InsufficientCaEntitlements),
3902 },
3903 bytes: [0x07],
3904 extra: [Overfull],
3905 }
3906
3907 opcode_test! {
3908 name: _not_programmed_resolution_unsupported,
3909 ty: TimerStatusData,
3910 instance: TimerStatusData {
3911 overlap_warning: false,
3912 media_info: MediaInfo::UnprotectedMedia,
3913 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::ResolutionUnsupported),
3914 },
3915 bytes: [0x08],
3916 extra: [Overfull],
3917 }
3918
3919 opcode_test! {
3920 name: _not_programmed_parental_lock,
3921 ty: TimerStatusData,
3922 instance: TimerStatusData {
3923 overlap_warning: false,
3924 media_info: MediaInfo::UnprotectedMedia,
3925 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::ParentalLock),
3926 },
3927 bytes: [0x09],
3928 extra: [Overfull],
3929 }
3930
3931 opcode_test! {
3932 name: _not_programmed_clock_failure,
3933 ty: TimerStatusData,
3934 instance: TimerStatusData {
3935 overlap_warning: false,
3936 media_info: MediaInfo::UnprotectedMedia,
3937 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::ClockFailure),
3938 },
3939 bytes: [0x0A],
3940 extra: [Overfull],
3941 }
3942
3943 opcode_test! {
3944 name: _not_programmed_duplicate,
3945 ty: TimerStatusData,
3946 instance: TimerStatusData {
3947 overlap_warning: false,
3948 media_info: MediaInfo::UnprotectedMedia,
3949 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::Duplicate {
3950 duration_available: None
3951 }),
3952 },
3953 bytes: [0x0E],
3954 }
3955
3956 opcode_test! {
3957 name: _not_programmed_duplicate_duration,
3958 ty: TimerStatusData,
3959 instance: TimerStatusData {
3960 overlap_warning: false,
3961 media_info: MediaInfo::UnprotectedMedia,
3962 programmed_info: TimerProgrammedInfo::NotProgrammed(NotProgrammedErrorInfo::Duplicate {
3963 duration_available: Some(Duration {
3964 hours: DurationHours::try_from(23).unwrap(),
3965 minutes: Minute::try_from(59).unwrap(),
3966 })
3967 }),
3968 },
3969 bytes: [0x0E, 0x23, 0x59],
3970 extra: [Overfull],
3971 }
3972
3973 opcode_test! {
3974 name: _programmed_enough_space,
3975 ty: TimerStatusData,
3976 instance: TimerStatusData {
3977 overlap_warning: false,
3978 media_info: MediaInfo::UnprotectedMedia,
3979 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::EnoughSpace),
3980 },
3981 bytes: [0x18],
3982 extra: [Overfull],
3983 }
3984
3985 opcode_test! {
3986 name: _programmed_not_enough_space,
3987 ty: TimerStatusData,
3988 instance: TimerStatusData {
3989 overlap_warning: false,
3990 media_info: MediaInfo::UnprotectedMedia,
3991 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::NotEnoughSpace {
3992 duration_available: None
3993 }),
3994 },
3995 bytes: [0x19],
3996 }
3997
3998 opcode_test! {
3999 name: _programmed_not_enough_space_duration,
4000 ty: TimerStatusData,
4001 instance: TimerStatusData {
4002 overlap_warning: false,
4003 media_info: MediaInfo::UnprotectedMedia,
4004 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::NotEnoughSpace {
4005 duration_available: Some(Duration {
4006 hours: DurationHours::try_from(23).unwrap(),
4007 minutes: Minute::try_from(59).unwrap(),
4008 })
4009 }),
4010 },
4011 bytes: [0x19, 0x23, 0x59],
4012 extra: [Overfull],
4013 }
4014
4015 opcode_test! {
4016 name: _programmed_no_media,
4017 ty: TimerStatusData,
4018 instance: TimerStatusData {
4019 overlap_warning: false,
4020 media_info: MediaInfo::UnprotectedMedia,
4021 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::NoneAvailable),
4022 },
4023 bytes: [0x1A],
4024 extra: [Overfull],
4025 }
4026
4027 opcode_test! {
4028 name: _programmed_maybe_enough_space,
4029 ty: TimerStatusData,
4030 instance: TimerStatusData {
4031 overlap_warning: false,
4032 media_info: MediaInfo::UnprotectedMedia,
4033 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::MayNotBeEnoughSpace {
4034 duration_available: None
4035 }),
4036 },
4037 bytes: [0x1B],
4038 }
4039
4040 opcode_test! {
4041 name: _programmed_maybe_enough_space_duration,
4042 ty: TimerStatusData,
4043 instance: TimerStatusData {
4044 overlap_warning: false,
4045 media_info: MediaInfo::UnprotectedMedia,
4046 programmed_info: TimerProgrammedInfo::Programmed(ProgrammedInfo::MayNotBeEnoughSpace {
4047 duration_available: Some(Duration {
4048 hours: DurationHours::try_from(23).unwrap(),
4049 minutes: Minute::try_from(59).unwrap(),
4050 })
4051 }),
4052 },
4053 bytes: [0x1B, 0x23, 0x59],
4054 extra: [Overfull],
4055 }
4056
4057 #[test]
4058 fn test_decode_empty() {
4059 assert_eq!(
4060 TimerStatusData::try_from_bytes(&[]),
4061 Err(Error::OutOfRange {
4062 got: 0,
4063 expected: Range::AtLeast(1),
4064 quantity: "bytes"
4065 })
4066 );
4067 }
4068
4069 #[test]
4070 fn test_decode_reserved_not_programmed_error_info() {
4071 assert_eq!(
4072 TimerStatusData::try_from_bytes(&[0x00]),
4073 Err(Error::InvalidValueForType {
4074 ty: "NotProgrammedErrorInfo",
4075 value: String::from("0")
4076 })
4077 );
4078 }
4079
4080 #[test]
4081 fn test_decode_reserved_programmed_info() {
4082 assert_eq!(
4083 TimerStatusData::try_from_bytes(&[0x10]),
4084 Err(Error::InvalidValueForType {
4085 ty: "ProgrammedInfo",
4086 value: String::from("0")
4087 })
4088 );
4089 }
4090
4091 #[test]
4092 fn test_decode_reserved_duration_underfull() {
4093 assert_eq!(
4094 TimerStatusData::try_from_bytes(&[0x0E, 0x01]),
4095 Ok(TimerStatusData {
4096 overlap_warning: false,
4097 media_info: MediaInfo::UnprotectedMedia,
4098 programmed_info: TimerProgrammedInfo::NotProgrammed(
4099 NotProgrammedErrorInfo::Duplicate {
4100 duration_available: None
4101 }
4102 ),
4103 })
4104 );
4105 }
4106
4107 #[test]
4108 fn test_decode_reserved_media_info() {
4109 assert_eq!(
4110 TimerStatusData::try_from_bytes(&[0x60]),
4111 Err(Error::InvalidValueForType {
4112 ty: "MediaInfo",
4113 value: String::from("3")
4114 })
4115 );
4116 }
4117}
4118
4119#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4120pub struct TunerDeviceInfo {
4121 pub recording: bool,
4122 pub tuner_display_info: TunerDisplayInfo,
4123 pub service_id: ServiceId,
4124}
4125
4126impl OperandEncodable for TunerDeviceInfo {
4127 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
4128 match self.service_id {
4129 ServiceId::Analogue(service_id) => {
4130 let recording = if self.recording { 0x80 } else { 0 };
4131 let display_info = u8::from(self.tuner_display_info);
4132 <u8 as OperandEncodable>::to_bytes(&(recording | display_info), buf);
4133 service_id.to_bytes(buf);
4134 }
4135 ServiceId::Digital(service_id) => {
4136 let recording = if self.recording { 0x80 } else { 0 };
4137 let display_info = u8::from(self.tuner_display_info);
4138 <u8 as OperandEncodable>::to_bytes(&(recording | display_info), buf);
4139 service_id.to_bytes(buf);
4140 }
4141 }
4142 }
4143
4144 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
4145 Self::expected_len().check(bytes.len(), "bytes")?;
4146 let head = bytes[0];
4147 let recording = (head & 0x80) == 0x80;
4148 let tuner_display_info = TunerDisplayInfo::try_from_primitive(head & 0x7F)?;
4149 let service_id = match bytes.len() {
4150 5 => ServiceId::Analogue(AnalogueServiceId::try_from_bytes(&bytes[1..])?),
4151 8 => ServiceId::Digital(DigitalServiceId::try_from_bytes(&bytes[1..])?),
4152 _ => unreachable!(),
4153 };
4154 Ok(TunerDeviceInfo {
4155 recording,
4156 tuner_display_info,
4157 service_id,
4158 })
4159 }
4160
4161 fn len(&self) -> usize {
4162 match self.service_id {
4163 ServiceId::Analogue(_) => 5,
4164 ServiceId::Digital(_) => 8,
4165 }
4166 }
4167
4168 fn expected_len() -> Range<usize> {
4169 Range::Only(vec![5, 8])
4170 }
4171}
4172
4173#[cfg(test)]
4174mod test_tuner_device_info {
4175 use super::*;
4176
4177 opcode_test! {
4178 name: _analogue,
4179 ty: TunerDeviceInfo,
4180 instance: TunerDeviceInfo {
4181 recording: true,
4182 tuner_display_info: TunerDisplayInfo::Analogue,
4183 service_id: ServiceId::Analogue(AnalogueServiceId {
4184 broadcast_type: AnalogueBroadcastType::Terrestrial,
4185 frequency: 0x1234,
4186 broadcast_system: BroadcastSystem::PalBG,
4187 })
4188 },
4189 bytes: [
4190 0x82,
4191 AnalogueBroadcastType::Terrestrial as u8,
4192 0x12,
4193 0x34,
4194 BroadcastSystem::PalBG as u8
4195 ],
4196 }
4197
4198 opcode_test! {
4199 name: _digital,
4200 ty: TunerDeviceInfo,
4201 instance: TunerDeviceInfo {
4202 recording: true,
4203 tuner_display_info: TunerDisplayInfo::Analogue,
4204 service_id: ServiceId::Digital(DigitalServiceId::DvbGeneric(DvbData {
4205 transport_stream_id: 0x1234,
4206 service_id: 0x5678,
4207 original_network_id: 0xABCD,
4208 }))
4209 },
4210 bytes: [
4211 0x82,
4212 DigitalServiceBroadcastSystem::DvbGeneric as u8,
4213 0x12,
4214 0x34,
4215 0x56,
4216 0x78,
4217 0xAB,
4218 0xCD
4219 ],
4220 }
4221
4222 #[test]
4223 fn test_decode_empty() {
4224 assert_eq!(
4225 TunerDeviceInfo::try_from_bytes(&[]),
4226 Err(Error::OutOfRange {
4227 got: 0,
4228 expected: Range::Only(vec![5, 8]),
4229 quantity: "bytes"
4230 })
4231 );
4232 }
4233
4234 #[test]
4235 fn test_decode_midrange_6() {
4236 assert_eq!(
4237 TunerDeviceInfo::try_from_bytes(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB]),
4238 Err(Error::OutOfRange {
4239 got: 6,
4240 expected: Range::Only(vec![5, 8]),
4241 quantity: "bytes"
4242 })
4243 );
4244 }
4245
4246 #[test]
4247 fn test_decode_midrange_7() {
4248 assert_eq!(
4249 TunerDeviceInfo::try_from_bytes(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD]),
4250 Err(Error::OutOfRange {
4251 got: 7,
4252 expected: Range::Only(vec![5, 8]),
4253 quantity: "bytes"
4254 })
4255 );
4256 }
4257
4258 #[test]
4259 fn test_decode_overfull() {
4260 assert_eq!(
4261 TunerDeviceInfo::try_from_bytes(&[
4262 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01
4263 ]),
4264 Err(Error::OutOfRange {
4265 got: 9,
4266 expected: Range::Only(vec![5, 8]),
4267 quantity: "bytes"
4268 })
4269 );
4270 }
4271
4272 #[test]
4273 fn test_decode_invalid_tuner_display_info() {
4274 assert_eq!(
4275 TunerDeviceInfo::try_from_bytes(&[0x09, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]),
4276 Err(Error::InvalidValueForType {
4277 ty: "TunerDisplayInfo",
4278 value: String::from("9"),
4279 })
4280 );
4281 }
4282}
4283
4284#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4285#[non_exhaustive]
4286pub enum ExternalSource {
4287 Plug(u8),
4288 PhysicalAddress(PhysicalAddress),
4289}
4290
4291impl From<PhysicalAddress> for ExternalSource {
4292 fn from(val: PhysicalAddress) -> ExternalSource {
4293 ExternalSource::PhysicalAddress(val)
4294 }
4295}
4296
4297impl OperandEncodable for ExternalSource {
4298 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
4299 match self {
4300 ExternalSource::Plug(plug) => buf.extend([*plug]),
4301 ExternalSource::PhysicalAddress(phys_addr) => phys_addr.to_bytes(buf),
4302 }
4303 }
4304
4305 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
4306 Self::expected_len().check(bytes.len(), "bytes")?;
4307 match bytes.len() {
4308 1 => Ok(ExternalSource::Plug(bytes[0])),
4309 2 => Ok(ExternalSource::PhysicalAddress(
4310 <PhysicalAddress as OperandEncodable>::try_from_bytes(bytes)?,
4311 )),
4312 _ => unreachable!(),
4313 }
4314 }
4315
4316 fn len(&self) -> usize {
4317 match self {
4318 ExternalSource::Plug(_) => 1,
4319 ExternalSource::PhysicalAddress(_) => 2,
4320 }
4321 }
4322
4323 fn expected_len() -> Range<usize> {
4324 Range::Only(vec![1, 2])
4325 }
4326}
4327
4328#[cfg(test)]
4329mod test_external_source {
4330 use super::*;
4331
4332 opcode_test! {
4333 name: _plug,
4334 ty: ExternalSource,
4335 instance: ExternalSource::Plug(0x56),
4336 bytes: [0x56],
4337 }
4338
4339 opcode_test! {
4340 name: _phys_addr,
4341 ty: ExternalSource,
4342 instance: ExternalSource::PhysicalAddress(PhysicalAddress(0x5678)),
4343 bytes: [0x56, 0x78],
4344 }
4345
4346 #[test]
4347 fn test_decode_overfull() {
4348 assert_eq!(
4349 ExternalSource::try_from_bytes(&[0x12, 0x34, 0x56]),
4350 Err(Error::OutOfRange {
4351 got: 3,
4352 expected: Range::Only(vec![1, 2]),
4353 quantity: "bytes"
4354 })
4355 );
4356 }
4357
4358 #[test]
4359 fn test_decode_empty() {
4360 assert_eq!(
4361 ExternalSource::try_from_bytes(&[]),
4362 Err(Error::OutOfRange {
4363 got: 0,
4364 expected: Range::Only(vec![1, 2]),
4365 quantity: "bytes"
4366 })
4367 );
4368 }
4369}
4370
4371#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4372#[non_exhaustive]
4373pub enum RecordSource {
4374 Own,
4375 DigitalService(DigitalServiceId),
4376 AnalogueService(AnalogueServiceId),
4377 External(ExternalSource),
4378}
4379
4380impl From<DigitalServiceId> for RecordSource {
4381 fn from(val: DigitalServiceId) -> RecordSource {
4382 RecordSource::DigitalService(val)
4383 }
4384}
4385
4386impl From<AnalogueServiceId> for RecordSource {
4387 fn from(val: AnalogueServiceId) -> RecordSource {
4388 RecordSource::AnalogueService(val)
4389 }
4390}
4391
4392impl From<ExternalSource> for RecordSource {
4393 fn from(val: ExternalSource) -> RecordSource {
4394 RecordSource::External(val)
4395 }
4396}
4397
4398impl OperandEncodable for RecordSource {
4399 fn to_bytes(&self, buf: &mut impl Extend<u8>) {
4400 match self {
4401 RecordSource::Own => RecordSourceType::Own.to_bytes(buf),
4402 RecordSource::DigitalService(ref service_id) => {
4403 RecordSourceType::Digital.to_bytes(buf);
4404 service_id.to_bytes(buf);
4405 }
4406 RecordSource::AnalogueService(ref service_id) => {
4407 RecordSourceType::Analogue.to_bytes(buf);
4408 service_id.to_bytes(buf);
4409 }
4410 RecordSource::External(ref source) => {
4411 match source {
4412 ExternalSource::Plug(_) => RecordSourceType::ExternalPlug.to_bytes(buf),
4413 ExternalSource::PhysicalAddress(_) => {
4414 RecordSourceType::ExternalPhysicalAddress.to_bytes(buf);
4415 }
4416 }
4417 source.to_bytes(buf);
4418 }
4419 }
4420 }
4421
4422 fn try_from_bytes(bytes: &[u8]) -> Result<Self> {
4423 let record_source_type = RecordSourceType::try_from_bytes(bytes)?;
4424 match record_source_type {
4425 RecordSourceType::Own => Ok(RecordSource::Own),
4426 RecordSourceType::Digital => Ok(RecordSource::DigitalService(
4427 DigitalServiceId::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
4428 )),
4429 RecordSourceType::Analogue => Ok(RecordSource::AnalogueService(
4430 AnalogueServiceId::try_from_bytes(&bytes[1..]).map_err(Error::add_offset(1))?,
4431 )),
4432 RecordSourceType::ExternalPlug => {
4433 Range::AtLeast(2).check(bytes.len(), "bytes")?;
4434 Ok(RecordSource::External(ExternalSource::Plug(bytes[1])))
4435 }
4436 RecordSourceType::ExternalPhysicalAddress => {
4437 Ok(RecordSource::External(ExternalSource::PhysicalAddress(
4438 <PhysicalAddress as OperandEncodable>::try_from_bytes(&bytes[1..])
4439 .map_err(Error::add_offset(1))?,
4440 )))
4441 }
4442 }
4443 }
4444
4445 fn len(&self) -> usize {
4446 let len = match self {
4447 RecordSource::Own => 0,
4448 RecordSource::DigitalService(ref service_id) => service_id.len(),
4449 RecordSource::AnalogueService(ref service_id) => service_id.len(),
4450 RecordSource::External(ref source) => source.len(),
4451 };
4452 len + 1
4453 }
4454
4455 fn expected_len() -> Range<usize> {
4456 Range::AtLeast(1)
4457 }
4458}
4459
4460#[cfg(test)]
4461mod test_record_source {
4462 use super::*;
4463
4464 opcode_test! {
4465 name: _own,
4466 ty: RecordSource,
4467 instance: RecordSource::Own,
4468 bytes: [RecordSourceType::Own as u8],
4469 extra: [Overfull],
4470 }
4471
4472 opcode_test! {
4473 name: _digital,
4474 ty: RecordSource,
4475 instance: RecordSource::DigitalService(
4476 DigitalServiceId::AribGeneric(AribData {
4477 transport_stream_id: 0x1234,
4478 service_id: 0x5678,
4479 original_network_id: 0x9ABC,
4480 })
4481 ),
4482 bytes: [
4483 RecordSourceType::Digital as u8,
4484 DigitalServiceBroadcastSystem::AribGeneric as u8,
4485 0x12,
4486 0x34,
4487 0x56,
4488 0x78,
4489 0x9A,
4490 0xBC
4491 ],
4492 extra: [Overfull],
4493 }
4494
4495 opcode_test! {
4496 name: _analogue,
4497 ty: RecordSource,
4498 instance: RecordSource::AnalogueService(AnalogueServiceId {
4499 broadcast_type: AnalogueBroadcastType::Satellite,
4500 frequency: 0x1234,
4501 broadcast_system: BroadcastSystem::SecamL,
4502 }),
4503 bytes: [
4504 RecordSourceType::Analogue as u8,
4505 AnalogueBroadcastType::Satellite as u8,
4506 0x12,
4507 0x34,
4508 BroadcastSystem::SecamL as u8
4509 ],
4510 extra: [Overfull],
4511 }
4512
4513 opcode_test! {
4514 name: _external_plug,
4515 ty: RecordSource,
4516 instance: RecordSource::External(ExternalSource::Plug(0x56)),
4517 bytes: [
4518 RecordSourceType::ExternalPlug as u8,
4519 0x56,
4520 ],
4521 extra: [Overfull],
4522 }
4523
4524 opcode_test! {
4525 name: _external_phys_addr,
4526 ty: RecordSource,
4527 instance: RecordSource::External(ExternalSource::PhysicalAddress(PhysicalAddress(0x1234))),
4528 bytes: [
4529 RecordSourceType::ExternalPhysicalAddress as u8,
4530 0x12,
4531 0x34
4532 ],
4533 extra: [Overfull],
4534 }
4535
4536 #[test]
4537 fn test_digital_decoding_missing_bytes_1() {
4538 assert_eq!(
4539 RecordSource::try_from_bytes(&[
4540 RecordSourceType::Digital as u8,
4541 DigitalServiceBroadcastSystem::AribGeneric as u8,
4542 0x12,
4543 0x34,
4544 0x56,
4545 0x78,
4546 0x9A
4547 ]),
4548 Err(Error::OutOfRange {
4549 expected: Range::AtLeast(8),
4550 got: 7,
4551 quantity: "bytes",
4552 })
4553 );
4554 }
4555
4556 #[test]
4557 fn test_digital_decoding_missing_bytes_2() {
4558 assert_eq!(
4559 RecordSource::try_from_bytes(&[
4560 RecordSourceType::Digital as u8,
4561 DigitalServiceBroadcastSystem::AribGeneric as u8,
4562 0x12,
4563 0x34,
4564 0x56,
4565 0x78
4566 ]),
4567 Err(Error::OutOfRange {
4568 expected: Range::AtLeast(8),
4569 got: 6,
4570 quantity: "bytes",
4571 })
4572 );
4573 }
4574
4575 #[test]
4576 fn test_digital_decoding_missing_bytes_3() {
4577 assert_eq!(
4578 RecordSource::try_from_bytes(&[
4579 RecordSourceType::Digital as u8,
4580 DigitalServiceBroadcastSystem::AribGeneric as u8,
4581 0x12,
4582 0x34,
4583 0x56
4584 ]),
4585 Err(Error::OutOfRange {
4586 expected: Range::AtLeast(8),
4587 got: 5,
4588 quantity: "bytes",
4589 })
4590 );
4591 }
4592
4593 #[test]
4594 fn test_digital_decoding_missing_bytes_4() {
4595 assert_eq!(
4596 RecordSource::try_from_bytes(&[
4597 RecordSourceType::Digital as u8,
4598 DigitalServiceBroadcastSystem::AribGeneric as u8,
4599 0x12,
4600 0x34
4601 ]),
4602 Err(Error::OutOfRange {
4603 expected: Range::AtLeast(8),
4604 got: 4,
4605 quantity: "bytes",
4606 })
4607 );
4608 }
4609
4610 #[test]
4611 fn test_digital_decoding_missing_bytes_5() {
4612 assert_eq!(
4613 RecordSource::try_from_bytes(&[
4614 RecordSourceType::Digital as u8,
4615 DigitalServiceBroadcastSystem::AribGeneric as u8,
4616 0x12,
4617 ]),
4618 Err(Error::OutOfRange {
4619 expected: Range::AtLeast(8),
4620 got: 3,
4621 quantity: "bytes",
4622 })
4623 );
4624 }
4625
4626 #[test]
4627 fn test_digital_decoding_missing_operand_1() {
4628 assert_eq!(
4629 RecordSource::try_from_bytes(&[
4630 RecordSourceType::Digital as u8,
4631 DigitalServiceBroadcastSystem::AribGeneric as u8,
4632 ]),
4633 Err(Error::OutOfRange {
4634 expected: Range::AtLeast(8),
4635 got: 2,
4636 quantity: "bytes",
4637 })
4638 );
4639 }
4640
4641 #[test]
4642 fn test_digital_decoding_missing_operand_2() {
4643 assert_eq!(
4644 RecordSource::try_from_bytes(&[RecordSourceType::Digital as u8]),
4645 Err(Error::OutOfRange {
4646 expected: Range::AtLeast(8),
4647 got: 1,
4648 quantity: "bytes",
4649 })
4650 );
4651 }
4652
4653 #[test]
4654 fn test_analogue_decoding_missing_operands_1() {
4655 assert_eq!(
4656 RecordSource::try_from_bytes(&[
4657 RecordSourceType::Analogue as u8,
4658 AnalogueBroadcastType::Satellite as u8,
4659 0x12,
4660 0x34
4661 ]),
4662 Err(Error::OutOfRange {
4663 expected: Range::AtLeast(5),
4664 got: 4,
4665 quantity: "bytes",
4666 })
4667 );
4668 }
4669
4670 #[test]
4671 fn test_analogue_decoding_missing_operands_1_and_byte() {
4672 assert_eq!(
4673 RecordSource::try_from_bytes(&[
4674 RecordSourceType::Analogue as u8,
4675 AnalogueBroadcastType::Satellite as u8,
4676 0x12,
4677 ]),
4678 Err(Error::OutOfRange {
4679 expected: Range::AtLeast(5),
4680 got: 3,
4681 quantity: "bytes",
4682 })
4683 );
4684 }
4685
4686 #[test]
4687 fn test_analogue_decoding_missing_operands_2() {
4688 assert_eq!(
4689 RecordSource::try_from_bytes(&[
4690 RecordSourceType::Analogue as u8,
4691 AnalogueBroadcastType::Satellite as u8,
4692 ]),
4693 Err(Error::OutOfRange {
4694 expected: Range::AtLeast(5),
4695 got: 2,
4696 quantity: "bytes",
4697 })
4698 );
4699 }
4700
4701 #[test]
4702 fn test_analogue_decoding_missing_operands_3() {
4703 assert_eq!(
4704 RecordSource::try_from_bytes(&[RecordSourceType::Analogue as u8]),
4705 Err(Error::OutOfRange {
4706 expected: Range::AtLeast(5),
4707 got: 1,
4708 quantity: "bytes",
4709 })
4710 );
4711 }
4712
4713 #[test]
4714 fn test_external_plug_decoding_missing_operand() {
4715 assert_eq!(
4716 RecordSource::try_from_bytes(&[RecordSourceType::ExternalPlug as u8]),
4717 Err(Error::OutOfRange {
4718 expected: Range::AtLeast(2),
4719 got: 1,
4720 quantity: "bytes",
4721 })
4722 );
4723 }
4724
4725 #[test]
4726 fn test_external_phys_addr_decoding_missing_byte() {
4727 assert_eq!(
4728 RecordSource::try_from_bytes(&[RecordSourceType::ExternalPhysicalAddress as u8, 0x12]),
4729 Err(Error::OutOfRange {
4730 expected: Range::AtLeast(3),
4731 got: 2,
4732 quantity: "bytes",
4733 })
4734 );
4735 }
4736
4737 #[test]
4738 fn test_external_phys_addr_decoding_missing_operand() {
4739 assert_eq!(
4740 RecordSource::try_from_bytes(&[RecordSourceType::ExternalPhysicalAddress as u8]),
4741 Err(Error::OutOfRange {
4742 expected: Range::AtLeast(3),
4743 got: 1,
4744 quantity: "bytes",
4745 })
4746 );
4747 }
4748
4749 #[test]
4750 fn test_decoding_invalid_operand() {
4751 assert_eq!(
4752 RecordSource::try_from_bytes(&[0xFE]),
4753 Err(Error::InvalidValueForType {
4754 ty: "RecordSourceType",
4755 value: String::from("254"),
4756 })
4757 );
4758 }
4759}