Skip to main content

linux_cec/
operand.rs

1/*
2 * Copyright © 2024 Valve Software
3 * SPDX-License-Identifier: LGPL-2.1-or-later
4 */
5
6//! Message operand encoding, decoding and handling
7
8#![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; // TODO: Limit range
25pub 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    /// Unrecognized opcode
1107    UnrecognizedOp = constants::CEC_OP_ABORT_UNRECOGNIZED_OP,
1108    /// Not in correct mode to respond
1109    IncorrectMode = constants::CEC_OP_ABORT_INCORRECT_MODE,
1110    /// Cannot provide source
1111    NoSource = constants::CEC_OP_ABORT_NO_SOURCE,
1112    /// Invalid operand
1113    InvalidOp = constants::CEC_OP_ABORT_INVALID_OP,
1114    /// Refused
1115    Refused = constants::CEC_OP_ABORT_REFUSED,
1116    /// Unable to determine
1117    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    /// Rate control off
1134    Off = constants::CEC_OP_AUD_RATE_OFF,
1135    /// Standard rate, wide range control (IEEE 1394 compatible)
1136    WideStandard = constants::CEC_OP_AUD_RATE_WIDE_STD,
1137    /// Fast rate, wide range control (IEEE 1394 compatible)
1138    WideFast = constants::CEC_OP_AUD_RATE_WIDE_FAST,
1139    /// Slow rate, wide range control (IEEE 1394 compatible)
1140    WideSlow = constants::CEC_OP_AUD_RATE_WIDE_SLOW,
1141    /// Standard rate, narrow range control (HDMI transparent)
1142    NarrowStandard = constants::CEC_OP_AUD_RATE_NARROW_STD,
1143    /// Fast rate, narrow range control (HDMI transparent)
1144    NarrowFast = constants::CEC_OP_AUD_RATE_NARROW_FAST,
1145    /// Slow rate, narrow range control (HDMI transparent)
1146    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    /// As defined in CEA-861-D
1154    CEA861 = constants::CEC_OP_AUD_FMT_ID_CEA861,
1155    /// CXT (Coding Extenstion Type) field as defined in CTA-861-G
1156    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    /// PAL-B/G
1176    PalBG = constants::CEC_OP_BCAST_SYSTEM_PAL_BG,
1177    /// SECAM-L'
1178    SecamLq = constants::CEC_OP_BCAST_SYSTEM_SECAM_LQ,
1179    /// PAL-M
1180    PalM = constants::CEC_OP_BCAST_SYSTEM_PAL_M,
1181    /// NTSC-M
1182    NtscM = constants::CEC_OP_BCAST_SYSTEM_NTSC_M,
1183    /// PAL-I
1184    PalI = constants::CEC_OP_BCAST_SYSTEM_PAL_I,
1185    /// SECAM-D/K
1186    SecamDK = constants::CEC_OP_BCAST_SYSTEM_SECAM_DK,
1187    /// SECAM-B/G
1188    SecamBG = constants::CEC_OP_BCAST_SYSTEM_SECAM_BG,
1189    /// SECAM-L
1190    SecamL = constants::CEC_OP_BCAST_SYSTEM_SECAM_L,
1191    /// PAL-D/K
1192    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
1400    On = constants::CEC_OP_POWER_STATUS_ON,
1401    /// Standby
1402    Standby = constants::CEC_OP_POWER_STATUS_STANDBY,
1403    /// In transition from Standby to On
1404    ToOn = constants::CEC_OP_POWER_STATUS_TO_ON,
1405    /// In transition from On to Standby
1406    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    // These first few versions predate CEC specification and are
1990    // theoretically invalid, but we should probably recognize anyway
1991    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}