cea708_types/
tables.rs

1// Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the MIT license <LICENSE-MIT> or
4// http://opensource.org/licenses/MIT>, at your option. This file may not be
5// copied, modified, or distributed except according to those terms.
6
7//! Module for the various [Code] tables available
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
10pub enum CodeError {
11    /// Length of data does not match length advertised
12    #[error("The length of the data ({actual}) does not match the advertised expected ({expected}) length")]
13    LengthMismatch {
14        /// The expected size
15        expected: usize,
16        /// The actual size
17        actual: usize,
18    },
19}
20
21/// Enum representing characters or commands accessible through the [Ext1] byte
22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
23// must be ordered the same as the byte values
24pub enum Ext1 {
25    TransparentSpace,
26    NonBreakingTransparentSpace,
27    HorizontalElipses,
28    LatinCapitalSWithCaron,
29    LatinCapitalLigatureOE,
30    FullBlock,
31    SingleOpenQuote,
32    SingleCloseQuote,
33    DoubleOpenQuote,
34    DoubleCloseQuote,
35    SolidDot,
36    TradeMarkSign,
37    LatinLowerSWithCaron,
38    LatinLowerLigatureOE,
39    LatinCapitalYWithDiaeresis,
40    Fraction18,
41    Fraction38,
42    Fraction58,
43    Fraction78,
44    VerticalBorder,
45    UpperRightBorder,
46    LowerLeftBorder,
47    HorizontalBorder,
48    LowerRightBorder,
49    UpperLeftBorder,
50    ClosedCaptionSign,
51
52    Unknown(Vec<u8>),
53}
54
55/// Enum of all possible characters or commands available within [Service](super::Service) block
56#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
57// must be ordered the same as the byte values for binary search to be successful
58pub enum Code {
59    NUL,
60    ETX,
61    BS,
62    FF,
63    CR,
64    HCR,
65    Ext1(Ext1),
66    P16(u16),
67    // G0
68    Space, // 0x20
69    ExclamationMark,
70    QuotationMark,
71    NumberSign,
72    DollarSign,
73    PercentSign,
74    Ampersand,
75    Apostrophe,
76    LeftParenthesis,
77    RightParenthesis,
78    Asterisk,
79    PlusSign,
80    Comma,
81    HyphenMinus,
82    FullStop,
83    Solidus,
84    Zero,
85    One,
86    Two,
87    Three,
88    Four,
89    Five,
90    Six,
91    Seven,
92    Eight,
93    Nine,
94    Colon,
95    SemiColon,
96    LessThan,
97    Equals,
98    GreaterThan,
99    QuestionMark,
100    CommercialAt,
101    LatinCapitalA,
102    LatinCapitalB,
103    LatinCapitalC,
104    LatinCapitalD,
105    LatinCapitalE,
106    LatinCapitalF,
107    LatinCapitalG,
108    LatinCapitalH,
109    LatinCapitalI,
110    LatinCapitalJ,
111    LatinCapitalK,
112    LatinCapitalL,
113    LatinCapitalM,
114    LatinCapitalN,
115    LatinCapitalO,
116    LatinCapitalP,
117    LatinCapitalQ,
118    LatinCapitalR,
119    LatinCapitalS,
120    LatinCapitalT,
121    LatinCapitalU,
122    LatinCapitalV,
123    LatinCapitalW,
124    LatinCapitalX,
125    LatinCapitalY,
126    LatinCapitalZ,
127    LeftSquareBracket,
128    ReverseSolidus,
129    RightSquareBracket,
130    CircumflexAccent,
131    LowLine,
132    GraveAccent,
133    LatinLowerA,
134    LatinLowerB,
135    LatinLowerC,
136    LatinLowerD,
137    LatinLowerE,
138    LatinLowerF,
139    LatinLowerG,
140    LatinLowerH,
141    LatinLowerI,
142    LatinLowerJ,
143    LatinLowerK,
144    LatinLowerL,
145    LatinLowerM,
146    LatinLowerN,
147    LatinLowerO,
148    LatinLowerP,
149    LatinLowerQ,
150    LatinLowerR,
151    LatinLowerS,
152    LatinLowerT,
153    LatinLowerU,
154    LatinLowerV,
155    LatinLowerW,
156    LatinLowerX,
157    LatinLowerY,
158    LatinLowerZ,
159    LeftCurlyBracket,
160    VerticalLine,
161    RightCurlyBracket,
162    Tilde,
163    MusicalSymbolEighthNote, // 0x7F
164
165    // C1
166    SetCurrentWindow0, // 0x80
167    SetCurrentWindow1,
168    SetCurrentWindow2,
169    SetCurrentWindow3,
170    SetCurrentWindow4,
171    SetCurrentWindow5,
172    SetCurrentWindow6,
173    SetCurrentWindow7,
174    ClearWindows(WindowBits), // 0x88
175    DisplayWindows(WindowBits),
176    HideWindows(WindowBits),
177    ToggleWindows(WindowBits),
178    DeleteWindows(WindowBits),
179    Delay(u8),
180    DelayCancel,
181    Reset,
182    SetPenAttributes(SetPenAttributesArgs),
183    SetPenColor(SetPenColorArgs),
184    SetPenLocation(SetPenLocationArgs), // 0x92
185
186    SetWindowAttributes(SetWindowAttributesArgs), // 0x97
187    DefineWindow(DefineWindowArgs),               // [0x98, 0x9F]
188
189    // G1
190    NonBreakingSpace, // 0xA0
191    InvertedExclamationMark,
192    CentSign,
193    PoundSign,
194    GeneralCurrencySign,
195    YenSign,
196    BrokenVerticalBar,
197    SectionSign,
198    Umlaut,
199    CopyrightSign,
200    FeminineOrdinalSign,
201    LeftDoubleAngleQuote,
202    LogicalNotSign,
203    SoftHyphen,
204    RegisteredTrademarkSign,
205    SpacingMacronLongAccent,
206    DegreeSign,
207    PlusOrMinusSign,
208    Superscript2,
209    Superscript3,
210    SpacingAccuteAccent,
211    MicroSign,
212    ParagraphSign,
213    MiddleDot,
214    SpacingCedilla,
215    Superscript1,
216    MasculineOrdinalSign,
217    RightDoubleAngleQuote,
218    Fraction14,
219    Fraction12,
220    Fraction34,
221    InvertedQuestionMark,
222    LatinCapitalAWithGrave,
223    LatinCapitalAWithAcute,
224    LatinCapitalAWithCircumflex,
225    LatinCapitalAWithTilde,
226    LatinCapitalAWithDiaeresis,
227    LatinCapitalAWithRingAbove,
228    LatinCapitalAe,
229    LatinCapitalCWithCedilla,
230    LatinCapitalEWithGrave,
231    LatinCapitalEWithAcute,
232    LatinCapitalEWithCircumflex,
233    LatinCapitalEWithDiaeseris,
234    LatinCapitalIWithGrave,
235    LatinCapitalIWithAcute,
236    LatinCapitalIWithCircumflex,
237    LatinCapitalIWithDiaeseris,
238    LatinCapitalEth,
239    LatinCapitalNWithTilde,
240    LatinCapitalOWithGrave,
241    LatinCapitalOWithAcute,
242    LatinCapitalOWithCircumflex,
243    LatinCapitalOWithTilde,
244    LatinCapitalOWithDiaeresis,
245    MultiplicationSign,
246    LatinCapitalOWithStroke,
247    LatinCapitalUWithGrave,
248    LatinCapitalUWithAcute,
249    LatinCapitalUWithCircumflex,
250    LatinCapitalUWithDiaeresis,
251    LatinCapitalYWithAcute,
252    LatinCapitalThorn,
253    LatinLowerSharpS,
254    LatinLowerAWithGrave,
255    LatinLowerAWithAcute,
256    LatinLowerAWithCircumflex,
257    LatinLowerAWithTilde,
258    LatinLowerAWithDiaeresis,
259    LatinLowerAWithRingAbove,
260    LatinLowerAe,
261    LatinLowerCWithCedilla,
262    LatinLowerEWithGrave,
263    LatinLowerEWithAcute,
264    LatinLowerEWithCircumflex,
265    LatinLowerEWithDiaeseris,
266    LatinLowerIWithGrave,
267    LatinLowerIWithAcute,
268    LatinLowerIWithCircumflex,
269    LatinLowerIWithDiaeseris,
270    LatinLowerEth,
271    LatinLowerNWithTilde,
272    LatinLowerOWithGrave,
273    LatinLowerOWithAcute,
274    LatinLowerOWithCircumflex,
275    LatinLowerOWithTilde,
276    LatinLowerOWithDiaeresis,
277    DivisionSign,
278    LatinLowerOWithStroke,
279    LatinLowerUWithGrave,
280    LatinLowerUWithAcute,
281    LatinLowerUWithCircumflex,
282    LatinLowerUWithDiaeresis,
283    LatinLowerYWithAcute,
284    LatinLowerThorn,
285    LatinLowerYWithDiaeresis,
286    Unknown(Vec<u8>),
287}
288
289/// A collection of 8 Windows (0-7) represented as a bitfield
290#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
291pub struct WindowBits(u8);
292
293impl From<u8> for WindowBits {
294    fn from(bits: u8) -> Self {
295        Self(bits)
296    }
297}
298
299impl From<[u8; 1]> for WindowBits {
300    fn from(bits: [u8; 1]) -> Self {
301        Self(bits[0])
302    }
303}
304
305impl From<WindowBits> for u8 {
306    fn from(bits: WindowBits) -> Self {
307        bits.0
308    }
309}
310
311impl From<WindowBits> for [u8; 1] {
312    fn from(bits: WindowBits) -> Self {
313        [bits.0]
314    }
315}
316
317impl WindowBits {
318    pub const NONE: WindowBits = WindowBits(0x0);
319    pub const ZERO: WindowBits = WindowBits(0x01);
320    pub const ONE: WindowBits = WindowBits(0x02);
321    pub const TWO: WindowBits = WindowBits(0x04);
322    pub const THREE: WindowBits = WindowBits(0x08);
323    pub const FOUR: WindowBits = WindowBits(0x10);
324    pub const FIVE: WindowBits = WindowBits(0x20);
325    pub const SIX: WindowBits = WindowBits(0x40);
326    pub const SEVEN: WindowBits = WindowBits(0x80);
327
328    pub const fn or(self, rhs: Self) -> Self {
329        Self(self.0 | rhs.0)
330    }
331
332    pub const fn and(self, rhs: Self) -> Self {
333        Self(self.0 & rhs.0)
334    }
335
336    pub const fn not(self) -> Self {
337        Self(!self.0)
338    }
339
340    /// Create a [`WindowBits`] from a window identifier.
341    ///
342    /// Panics if window_id >= 8
343    pub const fn from_window_id(window_id: u8) -> Self {
344        assert!(window_id < 8);
345        Self(1 << window_id)
346    }
347}
348
349impl std::ops::BitOr for WindowBits {
350    type Output = Self;
351
352    fn bitor(self, rhs: Self) -> Self::Output {
353        Self(self.0 | rhs.0)
354    }
355}
356
357impl std::ops::BitAnd for WindowBits {
358    type Output = Self;
359
360    fn bitand(self, rhs: Self) -> Self::Output {
361        Self(self.0 & rhs.0)
362    }
363}
364
365impl std::ops::Not for WindowBits {
366    type Output = Self;
367
368    fn not(self) -> Self::Output {
369        Self(!self.0)
370    }
371}
372
373impl std::fmt::Debug for WindowBits {
374    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
375        write!(f, "WindowBits(b{:0>8b})", self.0)
376    }
377}
378
379/// Anchor points
380#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
381pub enum Anchor {
382    TopLeft,
383    TopMiddle,
384    TopRight,
385    CenterLeft,
386    CenterMiddle,
387    CenterRight,
388    BottomLeft,
389    BottomMiddle,
390    BottomRight,
391    Undefined9,
392    Undefined10,
393    Undefined11,
394    Undefined12,
395    Undefined13,
396    Undefined14,
397    Undefined15,
398}
399
400impl From<u8> for Anchor {
401    fn from(a: u8) -> Self {
402        match a {
403            0 => Anchor::TopLeft,
404            1 => Anchor::TopMiddle,
405            2 => Anchor::TopRight,
406            3 => Anchor::CenterLeft,
407            4 => Anchor::CenterMiddle,
408            5 => Anchor::CenterRight,
409            6 => Anchor::BottomLeft,
410            7 => Anchor::BottomMiddle,
411            8 => Anchor::BottomRight,
412            9 => Anchor::Undefined9,
413            10 => Anchor::Undefined10,
414            11 => Anchor::Undefined11,
415            12 => Anchor::Undefined12,
416            13 => Anchor::Undefined13,
417            14 => Anchor::Undefined14,
418            15 => Anchor::Undefined15,
419            _ => unreachable!(),
420        }
421    }
422}
423
424impl From<Anchor> for u8 {
425    fn from(a: Anchor) -> u8 {
426        match a {
427            Anchor::TopLeft => 0,
428            Anchor::TopMiddle => 1,
429            Anchor::TopRight => 2,
430            Anchor::CenterLeft => 3,
431            Anchor::CenterMiddle => 4,
432            Anchor::CenterRight => 5,
433            Anchor::BottomLeft => 6,
434            Anchor::BottomMiddle => 7,
435            Anchor::BottomRight => 8,
436            Anchor::Undefined9 => 9,
437            Anchor::Undefined10 => 10,
438            Anchor::Undefined11 => 11,
439            Anchor::Undefined12 => 12,
440            Anchor::Undefined13 => 13,
441            Anchor::Undefined14 => 14,
442            Anchor::Undefined15 => 15,
443        }
444    }
445}
446
447/// Arguments required for the [Code::DefineWindow] command
448#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
449pub struct DefineWindowArgs {
450    pub window_id: u8, // [0, 7]
451    pub priority: u8,  // [0, 7]
452    pub anchor_point: Anchor,
453    pub relative_positioning: bool,
454    pub anchor_vertical: u8,   // [0, 255]
455    pub anchor_horizontal: u8, // [0, 255]
456    pub row_count: u8,         // [0, 11]
457    pub column_count: u8,      // [0, 31/41]
458    pub row_lock: bool,
459    pub column_lock: bool,
460    pub visible: bool,
461    pub window_style_id: u8, // [0, 7]
462    pub pen_style_id: u8,    // [0, 7]
463}
464
465impl From<[u8; 6]> for DefineWindowArgs {
466    fn from(args: [u8; 6]) -> Self {
467        Self {
468            window_id: 0, // needs to be filled in later
469            priority: args[0] & 0x7,
470            anchor_point: ((args[3] & 0xF0) >> 4).into(),
471            relative_positioning: (args[1] & 0x80) > 0,
472            anchor_vertical: args[1] & 0x7F,
473            anchor_horizontal: args[2],
474            row_count: args[3] & 0x0F,
475            column_count: args[4] & 0x3F,
476            row_lock: (args[0] & 0x10) > 0,
477            column_lock: (args[0] & 0x08) > 0,
478            visible: (args[0] & 0x20) > 0,
479            window_style_id: (args[5] & 0x38) >> 3,
480            pen_style_id: args[5] & 0x07,
481        }
482    }
483}
484
485impl From<DefineWindowArgs> for [u8; 6] {
486    fn from(args: DefineWindowArgs) -> Self {
487        [
488            args.priority & 0x7
489                | u8::from(args.column_lock) << 3
490                | u8::from(args.row_lock) << 4
491                | u8::from(args.visible) << 5,
492            args.anchor_vertical & 0x7F | u8::from(args.relative_positioning) << 7,
493            args.anchor_horizontal,
494            (args.row_count & 0x0F) | u8::from(args.anchor_point) << 4,
495            args.column_count & 0x3F,
496            args.pen_style_id & 0x07 | (args.window_style_id & 0x7) << 3,
497        ]
498    }
499}
500
501impl DefineWindowArgs {
502    #[allow(clippy::too_many_arguments)]
503    pub const fn new(
504        window_id: u8,
505        priority: u8,
506        anchor_point: Anchor,
507        relative_positioning: bool,
508        anchor_vertical: u8,
509        anchor_horizontal: u8,
510        row_count: u8,
511        column_count: u8,
512        row_lock: bool,
513        column_lock: bool,
514        visible: bool,
515        window_style_id: u8,
516        pen_style_id: u8,
517    ) -> Self {
518        Self {
519            window_id,
520            priority,
521            anchor_point,
522            relative_positioning,
523            anchor_vertical,
524            anchor_horizontal,
525            row_count,
526            column_count,
527            row_lock,
528            column_lock,
529            visible,
530            window_style_id,
531            pen_style_id,
532        }
533    }
534
535    /// Retrieve the default window attributes for this [`DefineWindowArgs`]
536    pub fn window_attributes(&self) -> SetWindowAttributesArgs {
537        PREDEFINED_WINDOW_STYLES[self.window_style_id.max(1) as usize - 1]
538    }
539
540    /// Retrieve the default pen attributes for this [`DefineWindowArgs`]
541    pub fn pen_attributes(&self) -> SetPenAttributesArgs {
542        PREDEFINED_PEN_STYLES_ATTRIBUTES[self.pen_style_id.max(1) as usize - 1]
543    }
544
545    /// Retrieve the default pen color for this [`DefineWindowArgs`]
546    pub fn pen_color(&self) -> SetPenColorArgs {
547        PREDEFINED_PEN_STYLES_COLOR[self.pen_style_id.max(1) as usize - 1]
548    }
549}
550
551static PREDEFINED_WINDOW_STYLES: [SetWindowAttributesArgs; 7] = [
552    // style 1
553    SetWindowAttributesArgs {
554        justify: Justify::Left,
555        print_direction: Direction::LeftToRight,
556        scroll_direction: Direction::BottomToTop,
557        wordwrap: false,
558        display_effect: DisplayEffect::Snap,
559        effect_direction: Direction::LeftToRight,
560        effect_speed: 1,
561        fill_color: Color::BLACK,
562        fill_opacity: Opacity::Solid,
563        border_type: BorderType::None,
564        border_color: Color::BLACK,
565    },
566    // style 2
567    SetWindowAttributesArgs {
568        justify: Justify::Left,
569        print_direction: Direction::LeftToRight,
570        scroll_direction: Direction::BottomToTop,
571        wordwrap: false,
572        display_effect: DisplayEffect::Snap,
573        effect_direction: Direction::LeftToRight,
574        effect_speed: 1,
575        fill_color: Color::BLACK,
576        fill_opacity: Opacity::Transparent,
577        border_type: BorderType::None,
578        border_color: Color::BLACK,
579    },
580    // style 3
581    SetWindowAttributesArgs {
582        justify: Justify::Center,
583        print_direction: Direction::LeftToRight,
584        scroll_direction: Direction::BottomToTop,
585        wordwrap: false,
586        display_effect: DisplayEffect::Snap,
587        effect_direction: Direction::LeftToRight,
588        effect_speed: 1,
589        fill_color: Color::BLACK,
590        fill_opacity: Opacity::Solid,
591        border_type: BorderType::None,
592        border_color: Color::BLACK,
593    },
594    // style 4
595    SetWindowAttributesArgs {
596        justify: Justify::Left,
597        print_direction: Direction::LeftToRight,
598        scroll_direction: Direction::BottomToTop,
599        wordwrap: true,
600        display_effect: DisplayEffect::Snap,
601        effect_direction: Direction::LeftToRight,
602        effect_speed: 1,
603        fill_color: Color::BLACK,
604        fill_opacity: Opacity::Solid,
605        border_type: BorderType::None,
606        border_color: Color::BLACK,
607    },
608    // style 5
609    SetWindowAttributesArgs {
610        justify: Justify::Left,
611        print_direction: Direction::LeftToRight,
612        scroll_direction: Direction::BottomToTop,
613        wordwrap: true,
614        display_effect: DisplayEffect::Snap,
615        effect_direction: Direction::LeftToRight,
616        effect_speed: 1,
617        fill_color: Color::BLACK,
618        fill_opacity: Opacity::Transparent,
619        border_type: BorderType::None,
620        border_color: Color::BLACK,
621    },
622    // style 6
623    SetWindowAttributesArgs {
624        justify: Justify::Center,
625        print_direction: Direction::LeftToRight,
626        scroll_direction: Direction::BottomToTop,
627        wordwrap: true,
628        display_effect: DisplayEffect::Snap,
629        effect_direction: Direction::LeftToRight,
630        effect_speed: 1,
631        fill_color: Color::BLACK,
632        fill_opacity: Opacity::Solid,
633        border_type: BorderType::None,
634        border_color: Color::BLACK,
635    },
636    // style 7
637    SetWindowAttributesArgs {
638        justify: Justify::Left,
639        print_direction: Direction::TopToBottom,
640        scroll_direction: Direction::RightToLeft,
641        wordwrap: false,
642        display_effect: DisplayEffect::Snap,
643        effect_direction: Direction::LeftToRight,
644        effect_speed: 1,
645        fill_color: Color::BLACK,
646        fill_opacity: Opacity::Solid,
647        border_type: BorderType::None,
648        border_color: Color::BLACK,
649    },
650];
651
652static PREDEFINED_PEN_STYLES_ATTRIBUTES: [SetPenAttributesArgs; 7] = [
653    // style 1
654    SetPenAttributesArgs {
655        pen_size: PenSize::Standard,
656        font_style: FontStyle::Default,
657        text_tag: TextTag::Dialog,
658        offset: TextOffset::Normal,
659        italics: false,
660        underline: false,
661        edge_type: EdgeType::None,
662    },
663    // style 2
664    SetPenAttributesArgs {
665        pen_size: PenSize::Standard,
666        font_style: FontStyle::MonospacedWithSerifs,
667        text_tag: TextTag::Dialog,
668        offset: TextOffset::Normal,
669        italics: false,
670        underline: false,
671        edge_type: EdgeType::None,
672    },
673    // style 3
674    SetPenAttributesArgs {
675        pen_size: PenSize::Standard,
676        font_style: FontStyle::ProportionallySpacedWithSerifs,
677        text_tag: TextTag::Dialog,
678        offset: TextOffset::Normal,
679        italics: false,
680        underline: false,
681        edge_type: EdgeType::None,
682    },
683    // style 4
684    SetPenAttributesArgs {
685        pen_size: PenSize::Standard,
686        font_style: FontStyle::MonospacedWithoutSerifs,
687        text_tag: TextTag::Dialog,
688        offset: TextOffset::Normal,
689        italics: false,
690        underline: false,
691        edge_type: EdgeType::None,
692    },
693    // style 5
694    SetPenAttributesArgs {
695        pen_size: PenSize::Standard,
696        font_style: FontStyle::ProportionallySpacedWithoutSerifs,
697        text_tag: TextTag::Dialog,
698        offset: TextOffset::Normal,
699        italics: false,
700        underline: false,
701        edge_type: EdgeType::None,
702    },
703    // style 6
704    SetPenAttributesArgs {
705        pen_size: PenSize::Standard,
706        font_style: FontStyle::MonospacedWithoutSerifs,
707        text_tag: TextTag::Dialog,
708        offset: TextOffset::Normal,
709        italics: false,
710        underline: false,
711        edge_type: EdgeType::Uniform,
712    },
713    // style 7
714    SetPenAttributesArgs {
715        pen_size: PenSize::Standard,
716        font_style: FontStyle::ProportionallySpacedWithoutSerifs,
717        text_tag: TextTag::Dialog,
718        offset: TextOffset::Normal,
719        italics: false,
720        underline: false,
721        edge_type: EdgeType::Uniform,
722    },
723];
724
725static PREDEFINED_PEN_STYLES_COLOR: [SetPenColorArgs; 7] = [
726    // style 1
727    SetPenColorArgs {
728        foreground_color: Color::WHITE,
729        foreground_opacity: Opacity::Solid,
730        background_color: Color::BLACK,
731        background_opacity: Opacity::Solid,
732        edge_color: Color::BLACK,
733    },
734    // style 2
735    SetPenColorArgs {
736        foreground_color: Color::WHITE,
737        foreground_opacity: Opacity::Solid,
738        background_color: Color::BLACK,
739        background_opacity: Opacity::Solid,
740        edge_color: Color::BLACK,
741    },
742    // style 3
743    SetPenColorArgs {
744        foreground_color: Color::WHITE,
745        foreground_opacity: Opacity::Solid,
746        background_color: Color::BLACK,
747        background_opacity: Opacity::Solid,
748        edge_color: Color::BLACK,
749    },
750    // style 4
751    SetPenColorArgs {
752        foreground_color: Color::WHITE,
753        foreground_opacity: Opacity::Solid,
754        background_color: Color::BLACK,
755        background_opacity: Opacity::Solid,
756        edge_color: Color::BLACK,
757    },
758    // style 5
759    SetPenColorArgs {
760        foreground_color: Color::WHITE,
761        foreground_opacity: Opacity::Solid,
762        background_color: Color::BLACK,
763        background_opacity: Opacity::Solid,
764        edge_color: Color::BLACK,
765    },
766    // style 6
767    SetPenColorArgs {
768        foreground_color: Color::WHITE,
769        foreground_opacity: Opacity::Solid,
770        background_color: Color::BLACK,
771        background_opacity: Opacity::Transparent,
772        edge_color: Color::BLACK,
773    },
774    // style 7
775    SetPenColorArgs {
776        foreground_color: Color::WHITE,
777        foreground_opacity: Opacity::Solid,
778        background_color: Color::BLACK,
779        background_opacity: Opacity::Transparent,
780        edge_color: Color::BLACK,
781    },
782];
783
784/// Text tustification options
785#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
786pub enum Justify {
787    Left,
788    Right,
789    Center,
790    Full,
791}
792
793impl From<u8> for Justify {
794    fn from(j: u8) -> Self {
795        match j {
796            0 => Justify::Left,
797            1 => Justify::Right,
798            2 => Justify::Center,
799            3 => Justify::Full,
800            _ => unreachable!(),
801        }
802    }
803}
804
805impl From<Justify> for u8 {
806    fn from(j: Justify) -> u8 {
807        match j {
808            Justify::Left => 0,
809            Justify::Right => 1,
810            Justify::Center => 2,
811            Justify::Full => 3,
812        }
813    }
814}
815
816/// Text/Scroll/etc direction options
817#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
818pub enum Direction {
819    LeftToRight,
820    RightToLeft,
821    TopToBottom,
822    BottomToTop,
823}
824
825impl From<u8> for Direction {
826    fn from(d: u8) -> Self {
827        match d {
828            0 => Direction::LeftToRight,
829            1 => Direction::RightToLeft,
830            2 => Direction::TopToBottom,
831            3 => Direction::BottomToTop,
832            _ => unreachable!(),
833        }
834    }
835}
836
837impl From<Direction> for u8 {
838    fn from(j: Direction) -> u8 {
839        match j {
840            Direction::LeftToRight => 0,
841            Direction::RightToLeft => 1,
842            Direction::TopToBottom => 2,
843            Direction::BottomToTop => 3,
844        }
845    }
846}
847
848/// Display effect options
849#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
850pub enum DisplayEffect {
851    Snap,
852    Fade,
853    Wipe,
854    Undefined,
855}
856
857impl From<u8> for DisplayEffect {
858    fn from(d: u8) -> Self {
859        match d {
860            0 => DisplayEffect::Snap,
861            1 => DisplayEffect::Fade,
862            2 => DisplayEffect::Wipe,
863            3 => DisplayEffect::Undefined,
864            _ => unreachable!(),
865        }
866    }
867}
868
869impl From<DisplayEffect> for u8 {
870    fn from(de: DisplayEffect) -> u8 {
871        match de {
872            DisplayEffect::Snap => 0,
873            DisplayEffect::Fade => 1,
874            DisplayEffect::Wipe => 2,
875            DisplayEffect::Undefined => 3,
876        }
877    }
878}
879
880/// Opacity options
881#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
882pub enum Opacity {
883    Solid,
884    Flash,
885    Translucent,
886    Transparent,
887}
888
889impl From<u8> for Opacity {
890    fn from(op: u8) -> Opacity {
891        match op {
892            0 => Opacity::Solid,
893            1 => Opacity::Flash,
894            2 => Opacity::Translucent,
895            3 => Opacity::Transparent,
896            _ => unreachable!(),
897        }
898    }
899}
900
901impl From<Opacity> for u8 {
902    fn from(op: Opacity) -> u8 {
903        match op {
904            Opacity::Solid => 0,
905            Opacity::Flash => 1,
906            Opacity::Translucent => 2,
907            Opacity::Transparent => 3,
908        }
909    }
910}
911
912/// Color value options
913#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
914pub enum ColorValue {
915    None,
916    OneThird,
917    TwoThirds,
918    Full,
919}
920
921impl From<u8> for ColorValue {
922    fn from(val: u8) -> ColorValue {
923        match val {
924            0 => ColorValue::None,
925            1 => ColorValue::OneThird,
926            2 => ColorValue::TwoThirds,
927            3 => ColorValue::Full,
928            _ => unreachable!(),
929        }
930    }
931}
932
933impl From<ColorValue> for u8 {
934    fn from(cv: ColorValue) -> u8 {
935        match cv {
936            ColorValue::None => 0,
937            ColorValue::OneThird => 1,
938            ColorValue::TwoThirds => 2,
939            ColorValue::Full => 3,
940        }
941    }
942}
943
944/// A RGB color
945#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
946pub struct Color {
947    pub r: ColorValue,
948    pub g: ColorValue,
949    pub b: ColorValue,
950}
951
952impl From<Color> for u8 {
953    fn from(c: Color) -> Self {
954        u8::from(c.r) << 4 | u8::from(c.g) << 2 | u8::from(c.b)
955    }
956}
957
958impl From<u8> for Color {
959    fn from(c: u8) -> Color {
960        Color {
961            r: ((c & 0x30) >> 4).into(),
962            g: ((c & 0x0C) >> 2).into(),
963            b: (c & 0x03).into(),
964        }
965    }
966}
967
968impl Color {
969    pub const BLACK: Color = Color::new(ColorValue::None, ColorValue::None, ColorValue::None);
970    pub const WHITE: Color = Color::new(ColorValue::Full, ColorValue::Full, ColorValue::Full);
971    pub const RED: Color = Color::new(ColorValue::Full, ColorValue::None, ColorValue::None);
972    pub const GREEN: Color = Color::new(ColorValue::None, ColorValue::Full, ColorValue::None);
973    pub const BLUE: Color = Color::new(ColorValue::None, ColorValue::None, ColorValue::Full);
974
975    pub const fn new(r: ColorValue, g: ColorValue, b: ColorValue) -> Self {
976        Self { r, g, b }
977    }
978}
979
980struct ColorOpacity(Color, Opacity);
981
982impl From<ColorOpacity> for u8 {
983    fn from(c_o: ColorOpacity) -> Self {
984        u8::from(c_o.1) << 6 | u8::from(c_o.0)
985    }
986}
987
988impl From<u8> for ColorOpacity {
989    fn from(c_o: u8) -> Self {
990        Self((c_o & 0x3F).into(), ((c_o & 0xC0) >> 6).into())
991    }
992}
993
994/// Border options
995#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
996pub enum BorderType {
997    None,
998    Raised,
999    Depressed,
1000    Uniform,
1001    ShadowLeft,
1002    ShadowRight,
1003    Undefined6,
1004    Undefined7,
1005}
1006
1007impl From<BorderType> for u8 {
1008    fn from(bt: BorderType) -> Self {
1009        match bt {
1010            BorderType::None => 0,
1011            BorderType::Raised => 1,
1012            BorderType::Depressed => 2,
1013            BorderType::Uniform => 3,
1014            BorderType::ShadowLeft => 4,
1015            BorderType::ShadowRight => 5,
1016            BorderType::Undefined6 => 6,
1017            BorderType::Undefined7 => 7,
1018        }
1019    }
1020}
1021
1022impl From<u8> for BorderType {
1023    fn from(bt: u8) -> Self {
1024        match bt {
1025            0 => BorderType::None,
1026            1 => BorderType::Raised,
1027            2 => BorderType::Depressed,
1028            3 => BorderType::Uniform,
1029            4 => BorderType::ShadowLeft,
1030            5 => BorderType::ShadowRight,
1031            6 => BorderType::Undefined6,
1032            7 => BorderType::Undefined7,
1033            _ => unreachable!(),
1034        }
1035    }
1036}
1037
1038/// Arguments required for the [Code::SetWindowAttributes] command
1039#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1040pub struct SetWindowAttributesArgs {
1041    pub justify: Justify,
1042    pub print_direction: Direction,
1043    pub scroll_direction: Direction,
1044    pub wordwrap: bool,
1045    pub display_effect: DisplayEffect,
1046    pub effect_direction: Direction,
1047    pub effect_speed: u8, // [1, 15] in units of 500ms
1048    pub fill_color: Color,
1049    pub fill_opacity: Opacity,
1050    pub border_type: BorderType,
1051    pub border_color: Color,
1052}
1053
1054impl From<SetWindowAttributesArgs> for [u8; 4] {
1055    fn from(args: SetWindowAttributesArgs) -> Self {
1056        let bt = u8::from(args.border_type);
1057        [
1058            ColorOpacity(args.fill_color, args.fill_opacity).into(),
1059            (bt & 0x3) << 6 | u8::from(args.border_color),
1060            (bt & 0x4) << 5
1061                | u8::from(args.wordwrap) << 6
1062                | u8::from(args.print_direction) << 4
1063                | u8::from(args.scroll_direction) << 2
1064                | u8::from(args.justify),
1065            args.effect_speed << 4
1066                | u8::from(args.effect_direction) << 2
1067                | u8::from(args.display_effect),
1068        ]
1069    }
1070}
1071
1072impl From<[u8; 4]> for SetWindowAttributesArgs {
1073    fn from(args: [u8; 4]) -> Self {
1074        let fill: ColorOpacity = args[0].into();
1075        let border_type = (args[1] & 0xC0) >> 6 | (args[2] & 0x80) >> 5;
1076        Self {
1077            justify: (args[2] & 0x03).into(),
1078            print_direction: ((args[2] & 0x30) >> 4).into(),
1079            scroll_direction: ((args[2] & 0x0C) >> 2).into(),
1080            wordwrap: (args[2] & 0x40) > 0,
1081            display_effect: (args[3] & 0x03).into(),
1082            effect_direction: ((args[3] & 0x0C) >> 2).into(),
1083            effect_speed: (args[3] & 0xF0) >> 4,
1084            fill_color: fill.0,
1085            fill_opacity: fill.1,
1086            border_type: border_type.into(),
1087            border_color: args[1].into(),
1088        }
1089    }
1090}
1091
1092impl SetWindowAttributesArgs {
1093    #[allow(clippy::too_many_arguments)]
1094    pub const fn new(
1095        justify: Justify,
1096        print_direction: Direction,
1097        scroll_direction: Direction,
1098        wordwrap: bool,
1099        display_effect: DisplayEffect,
1100        effect_direction: Direction,
1101        effect_speed: u8,
1102        fill_color: Color,
1103        fill_opacity: Opacity,
1104        border_type: BorderType,
1105        border_color: Color,
1106    ) -> Self {
1107        Self {
1108            justify,
1109            print_direction,
1110            scroll_direction,
1111            wordwrap,
1112            display_effect,
1113            effect_direction,
1114            effect_speed,
1115            fill_color,
1116            fill_opacity,
1117            border_type,
1118            border_color,
1119        }
1120    }
1121}
1122
1123/// Pen size options
1124#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1125pub enum PenSize {
1126    Small,
1127    Standard,
1128    Large,
1129    Undefined,
1130}
1131
1132impl From<PenSize> for u8 {
1133    fn from(pen_size: PenSize) -> Self {
1134        match pen_size {
1135            PenSize::Small => 0,
1136            PenSize::Standard => 1,
1137            PenSize::Large => 2,
1138            PenSize::Undefined => 3,
1139        }
1140    }
1141}
1142
1143impl From<u8> for PenSize {
1144    fn from(pen_size: u8) -> Self {
1145        match pen_size {
1146            0 => PenSize::Small,
1147            1 => PenSize::Standard,
1148            2 => PenSize::Large,
1149            3 => PenSize::Undefined,
1150            _ => unreachable!(),
1151        }
1152    }
1153}
1154
1155/// Font style options
1156#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1157pub enum FontStyle {
1158    Default,
1159    MonospacedWithSerifs,
1160    ProportionallySpacedWithSerifs,
1161    MonospacedWithoutSerifs,
1162    ProportionallySpacedWithoutSerifs,
1163    CasualFontType,
1164    CursiveFontType,
1165    SmallCapitals,
1166}
1167
1168impl From<FontStyle> for u8 {
1169    fn from(font_style: FontStyle) -> Self {
1170        match font_style {
1171            FontStyle::Default => 0,
1172            FontStyle::MonospacedWithSerifs => 1,
1173            FontStyle::ProportionallySpacedWithSerifs => 2,
1174            FontStyle::MonospacedWithoutSerifs => 3,
1175            FontStyle::ProportionallySpacedWithoutSerifs => 4,
1176            FontStyle::CasualFontType => 5,
1177            FontStyle::CursiveFontType => 6,
1178            FontStyle::SmallCapitals => 7,
1179        }
1180    }
1181}
1182
1183impl From<u8> for FontStyle {
1184    fn from(font_style: u8) -> Self {
1185        match font_style {
1186            0 => FontStyle::Default,
1187            1 => FontStyle::MonospacedWithSerifs,
1188            2 => FontStyle::ProportionallySpacedWithSerifs,
1189            3 => FontStyle::MonospacedWithoutSerifs,
1190            4 => FontStyle::ProportionallySpacedWithoutSerifs,
1191            5 => FontStyle::CasualFontType,
1192            6 => FontStyle::CursiveFontType,
1193            7 => FontStyle::SmallCapitals,
1194            _ => unreachable!(),
1195        }
1196    }
1197}
1198
1199/// Text tag options
1200#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1201pub enum TextTag {
1202    Dialog,
1203    SourceOrSpeakerId,
1204    ElectronicallyReproducedVoice,
1205    DialogInNonPrimaryLanguage,
1206    Voiceover,
1207    AudibleTranslation,
1208    SubtitleTranslation,
1209    VoiceQualityDescription,
1210    SongLyrics,
1211    SoundEffectDescription,
1212    MusicalScoreDescription,
1213    Expletive,
1214    Undefined12,
1215    Undefined13,
1216    Undefined14,
1217    TextNotToBeDisplayed,
1218}
1219
1220impl From<TextTag> for u8 {
1221    fn from(text_tag: TextTag) -> Self {
1222        match text_tag {
1223            TextTag::Dialog => 0,
1224            TextTag::SourceOrSpeakerId => 1,
1225            TextTag::ElectronicallyReproducedVoice => 2,
1226            TextTag::DialogInNonPrimaryLanguage => 3,
1227            TextTag::Voiceover => 4,
1228            TextTag::AudibleTranslation => 5,
1229            TextTag::SubtitleTranslation => 6,
1230            TextTag::VoiceQualityDescription => 7,
1231            TextTag::SongLyrics => 8,
1232            TextTag::SoundEffectDescription => 9,
1233            TextTag::MusicalScoreDescription => 10,
1234            TextTag::Expletive => 11,
1235            TextTag::Undefined12 => 12,
1236            TextTag::Undefined13 => 13,
1237            TextTag::Undefined14 => 14,
1238            TextTag::TextNotToBeDisplayed => 15,
1239        }
1240    }
1241}
1242
1243impl From<u8> for TextTag {
1244    fn from(text_tag: u8) -> Self {
1245        match text_tag {
1246            0 => TextTag::Dialog,
1247            1 => TextTag::SourceOrSpeakerId,
1248            2 => TextTag::ElectronicallyReproducedVoice,
1249            3 => TextTag::DialogInNonPrimaryLanguage,
1250            4 => TextTag::Voiceover,
1251            5 => TextTag::AudibleTranslation,
1252            6 => TextTag::SubtitleTranslation,
1253            7 => TextTag::VoiceQualityDescription,
1254            8 => TextTag::SongLyrics,
1255            9 => TextTag::SoundEffectDescription,
1256            10 => TextTag::MusicalScoreDescription,
1257            11 => TextTag::Expletive,
1258            12 => TextTag::Undefined12,
1259            13 => TextTag::Undefined13,
1260            14 => TextTag::Undefined14,
1261            15 => TextTag::TextNotToBeDisplayed,
1262            _ => unreachable!(),
1263        }
1264    }
1265}
1266
1267/// Text offset options
1268#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1269pub enum TextOffset {
1270    Subscript,
1271    Normal,
1272    Superscript,
1273    Undefined,
1274}
1275
1276impl From<TextOffset> for u8 {
1277    fn from(text_offset: TextOffset) -> Self {
1278        match text_offset {
1279            TextOffset::Subscript => 0,
1280            TextOffset::Normal => 1,
1281            TextOffset::Superscript => 2,
1282            TextOffset::Undefined => 3,
1283        }
1284    }
1285}
1286
1287impl From<u8> for TextOffset {
1288    fn from(text_offset: u8) -> Self {
1289        match text_offset {
1290            0 => TextOffset::Subscript,
1291            1 => TextOffset::Normal,
1292            2 => TextOffset::Superscript,
1293            3 => TextOffset::Undefined,
1294            _ => unreachable!(),
1295        }
1296    }
1297}
1298
1299/// Edge type options
1300#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1301pub enum EdgeType {
1302    None,
1303    Raised,
1304    Depressed,
1305    Uniform,
1306    LeftDropShadow,
1307    RightDropShadow,
1308    Undefined6,
1309    Undefined7,
1310}
1311
1312impl From<u8> for EdgeType {
1313    fn from(edge_type: u8) -> Self {
1314        match edge_type {
1315            0 => EdgeType::None,
1316            1 => EdgeType::Raised,
1317            2 => EdgeType::Depressed,
1318            3 => EdgeType::Uniform,
1319            4 => EdgeType::LeftDropShadow,
1320            5 => EdgeType::RightDropShadow,
1321            6 => EdgeType::Undefined6,
1322            7 => EdgeType::Undefined7,
1323            _ => unreachable!(),
1324        }
1325    }
1326}
1327
1328impl From<EdgeType> for u8 {
1329    fn from(edge_type: EdgeType) -> Self {
1330        match edge_type {
1331            EdgeType::None => 0,
1332            EdgeType::Raised => 1,
1333            EdgeType::Depressed => 2,
1334            EdgeType::Uniform => 3,
1335            EdgeType::LeftDropShadow => 4,
1336            EdgeType::RightDropShadow => 5,
1337            EdgeType::Undefined6 => 6,
1338            EdgeType::Undefined7 => 7,
1339        }
1340    }
1341}
1342
1343/// Arguments required for the [Code::SetPenAttributes] command
1344#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1345pub struct SetPenAttributesArgs {
1346    pub pen_size: PenSize,
1347    pub font_style: FontStyle,
1348    pub text_tag: TextTag,
1349    pub offset: TextOffset,
1350    pub italics: bool,
1351    pub underline: bool,
1352    pub edge_type: EdgeType,
1353}
1354
1355impl From<SetPenAttributesArgs> for [u8; 2] {
1356    fn from(args: SetPenAttributesArgs) -> Self {
1357        [
1358            u8::from(args.pen_size) | u8::from(args.offset) << 2 | u8::from(args.text_tag) << 4,
1359            u8::from(args.font_style)
1360                | u8::from(args.edge_type) << 3
1361                | u8::from(args.underline) << 6
1362                | u8::from(args.italics) << 7,
1363        ]
1364    }
1365}
1366
1367impl From<[u8; 2]> for SetPenAttributesArgs {
1368    fn from(args: [u8; 2]) -> Self {
1369        Self {
1370            pen_size: (args[0] & 0x3).into(),
1371            font_style: (args[1] & 0x07).into(),
1372            text_tag: ((args[0] & 0xF0) >> 4).into(),
1373            offset: ((args[0] & 0x0C) >> 2).into(),
1374            italics: (args[1] & 0x80) > 0,
1375            underline: (args[1] & 0x40) > 0,
1376            edge_type: ((args[1] & 0x38) >> 3).into(),
1377        }
1378    }
1379}
1380
1381impl SetPenAttributesArgs {
1382    pub const fn new(
1383        pen_size: PenSize,
1384        font_style: FontStyle,
1385        text_tag: TextTag,
1386        text_offset: TextOffset,
1387        italics: bool,
1388        underline: bool,
1389        edge_type: EdgeType,
1390    ) -> Self {
1391        Self {
1392            pen_size,
1393            font_style,
1394            text_tag,
1395            offset: text_offset,
1396            italics,
1397            underline,
1398            edge_type,
1399        }
1400    }
1401}
1402
1403#[derive(Debug, Clone)]
1404struct CodeMap<'a> {
1405    pub cea708_bytes: &'a [u8],
1406    pub code: Code,
1407    pub utf8: Option<char>,
1408}
1409
1410/// Arguments required for the [Code::SetPenColor] command
1411#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1412pub struct SetPenColorArgs {
1413    pub foreground_color: Color,
1414    pub foreground_opacity: Opacity,
1415    pub background_color: Color,
1416    pub background_opacity: Opacity,
1417    pub edge_color: Color,
1418}
1419
1420impl SetPenColorArgs {
1421    pub const fn new(
1422        foreground_color: Color,
1423        foreground_opacity: Opacity,
1424        background_color: Color,
1425        background_opacity: Opacity,
1426        edge_color: Color,
1427    ) -> Self {
1428        Self {
1429            foreground_color,
1430            foreground_opacity,
1431            background_color,
1432            background_opacity,
1433            edge_color,
1434        }
1435    }
1436}
1437
1438impl From<[u8; 3]> for SetPenColorArgs {
1439    fn from(data: [u8; 3]) -> Self {
1440        let foreground: ColorOpacity = data[0].into();
1441        let background: ColorOpacity = data[1].into();
1442        let edge: Color = data[2].into();
1443        Self {
1444            foreground_color: foreground.0,
1445            foreground_opacity: foreground.1,
1446            background_color: background.0,
1447            background_opacity: background.1,
1448            edge_color: edge,
1449        }
1450    }
1451}
1452
1453impl From<SetPenColorArgs> for [u8; 3] {
1454    fn from(data: SetPenColorArgs) -> Self {
1455        [
1456            ColorOpacity(data.foreground_color, data.foreground_opacity).into(),
1457            ColorOpacity(data.background_color, data.background_opacity).into(),
1458            data.edge_color.into(),
1459        ]
1460    }
1461}
1462
1463/// Arguments required for the [Code::SetPenLocation] command
1464#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
1465pub struct SetPenLocationArgs {
1466    pub row: u8,    // [0, 14]
1467    pub column: u8, // [0, 31/41]
1468}
1469
1470impl SetPenLocationArgs {
1471    pub const fn new(row: u8, column: u8) -> Self {
1472        Self { row, column }
1473    }
1474}
1475
1476impl From<[u8; 2]> for SetPenLocationArgs {
1477    fn from(data: [u8; 2]) -> Self {
1478        Self {
1479            row: data[0] & 0x0F,
1480            column: data[1] & 0x3F,
1481        }
1482    }
1483}
1484
1485impl From<SetPenLocationArgs> for [u8; 2] {
1486    fn from(data: SetPenLocationArgs) -> Self {
1487        [data.row & 0x0F, data.column & 0x3F]
1488    }
1489}
1490
1491macro_rules! code_map_bytes {
1492    ($bytes:expr, $code:expr, $utf8:expr) => {
1493        CodeMap {
1494            cea708_bytes: &$bytes,
1495            code: $code,
1496            utf8: $utf8,
1497        }
1498    };
1499}
1500
1501macro_rules! code_map_single_byte {
1502    ($byte:expr, $code:expr, $utf8:expr) => {
1503        code_map_bytes!([$byte], $code, $utf8)
1504    };
1505}
1506
1507// needs to be sorted by bytes and Code
1508static CODE_MAP_TABLE: [CodeMap; 234] = [
1509    code_map_single_byte!(0x00, Code::NUL, None),
1510    code_map_single_byte!(0x03, Code::ETX, None),
1511    code_map_single_byte!(0x08, Code::BS, None),
1512    code_map_single_byte!(0x0C, Code::FF, None),
1513    code_map_single_byte!(0x0D, Code::CR, None),
1514    code_map_single_byte!(0x0E, Code::HCR, None),
1515    code_map_bytes!([0x10, 0x20], Code::Ext1(Ext1::TransparentSpace), None),
1516    code_map_bytes!(
1517        [0x10, 0x21],
1518        Code::Ext1(Ext1::NonBreakingTransparentSpace),
1519        None
1520    ),
1521    code_map_bytes!([0x10, 0x25], Code::Ext1(Ext1::HorizontalElipses), Some('…')),
1522    code_map_bytes!(
1523        [0x10, 0x2A],
1524        Code::Ext1(Ext1::LatinCapitalSWithCaron),
1525        Some('Š')
1526    ),
1527    code_map_bytes!(
1528        [0x10, 0x2C],
1529        Code::Ext1(Ext1::LatinCapitalLigatureOE),
1530        Some('Œ')
1531    ),
1532    code_map_bytes!([0x10, 0x30], Code::Ext1(Ext1::FullBlock), Some('█')),
1533    code_map_bytes!([0x10, 0x31], Code::Ext1(Ext1::SingleOpenQuote), Some('‘')),
1534    code_map_bytes!([0x10, 0x32], Code::Ext1(Ext1::SingleCloseQuote), Some('’')),
1535    code_map_bytes!([0x10, 0x33], Code::Ext1(Ext1::DoubleOpenQuote), Some('“')),
1536    code_map_bytes!([0x10, 0x34], Code::Ext1(Ext1::DoubleCloseQuote), Some('”')),
1537    code_map_bytes!([0x10, 0x35], Code::Ext1(Ext1::SolidDot), None),
1538    code_map_bytes!([0x10, 0x39], Code::Ext1(Ext1::TradeMarkSign), Some('™')),
1539    code_map_bytes!(
1540        [0x10, 0x3A],
1541        Code::Ext1(Ext1::LatinLowerSWithCaron),
1542        Some('š')
1543    ),
1544    code_map_bytes!(
1545        [0x10, 0x3C],
1546        Code::Ext1(Ext1::LatinLowerLigatureOE),
1547        Some('œ')
1548    ),
1549    code_map_bytes!(
1550        [0x10, 0x3F],
1551        Code::Ext1(Ext1::LatinCapitalYWithDiaeresis),
1552        Some('Ÿ')
1553    ),
1554    code_map_bytes!([0x10, 0x76], Code::Ext1(Ext1::Fraction18), Some('⅛')),
1555    code_map_bytes!([0x10, 0x77], Code::Ext1(Ext1::Fraction38), Some('⅜')),
1556    code_map_bytes!([0x10, 0x78], Code::Ext1(Ext1::Fraction58), Some('⅝')),
1557    code_map_bytes!([0x10, 0x79], Code::Ext1(Ext1::Fraction78), Some('⅞')),
1558    code_map_bytes!([0x10, 0x7A], Code::Ext1(Ext1::VerticalBorder), None),
1559    code_map_bytes!([0x10, 0x7B], Code::Ext1(Ext1::UpperRightBorder), None),
1560    code_map_bytes!([0x10, 0x7C], Code::Ext1(Ext1::LowerLeftBorder), None),
1561    code_map_bytes!([0x10, 0x7D], Code::Ext1(Ext1::HorizontalBorder), None),
1562    code_map_bytes!([0x10, 0x7E], Code::Ext1(Ext1::LowerRightBorder), None),
1563    code_map_bytes!([0x10, 0x7F], Code::Ext1(Ext1::UpperLeftBorder), None),
1564    code_map_bytes!([0x10, 0xA0], Code::Ext1(Ext1::ClosedCaptionSign), None),
1565    code_map_single_byte!(0x20, Code::Space, Some(' ')),
1566    code_map_single_byte!(0x21, Code::ExclamationMark, Some('!')),
1567    code_map_single_byte!(0x22, Code::QuotationMark, Some('\"')),
1568    code_map_single_byte!(0x23, Code::NumberSign, Some('#')),
1569    code_map_single_byte!(0x24, Code::DollarSign, Some('$')),
1570    code_map_single_byte!(0x25, Code::PercentSign, Some('%')),
1571    code_map_single_byte!(0x26, Code::Ampersand, Some('&')),
1572    code_map_single_byte!(0x27, Code::Apostrophe, Some('\'')),
1573    code_map_single_byte!(0x28, Code::LeftParenthesis, Some('(')),
1574    code_map_single_byte!(0x29, Code::RightParenthesis, Some(')')),
1575    code_map_single_byte!(0x2A, Code::Asterisk, Some('*')),
1576    code_map_single_byte!(0x2B, Code::PlusSign, Some('+')),
1577    code_map_single_byte!(0x2C, Code::Comma, Some(',')),
1578    code_map_single_byte!(0x2D, Code::HyphenMinus, Some('-')),
1579    code_map_single_byte!(0x2E, Code::FullStop, Some('.')),
1580    code_map_single_byte!(0x2F, Code::Solidus, Some('/')),
1581    code_map_single_byte!(0x30, Code::Zero, Some('0')),
1582    code_map_single_byte!(0x31, Code::One, Some('1')),
1583    code_map_single_byte!(0x32, Code::Two, Some('2')),
1584    code_map_single_byte!(0x33, Code::Three, Some('3')),
1585    code_map_single_byte!(0x34, Code::Four, Some('4')),
1586    code_map_single_byte!(0x35, Code::Five, Some('5')),
1587    code_map_single_byte!(0x36, Code::Six, Some('6')),
1588    code_map_single_byte!(0x37, Code::Seven, Some('7')),
1589    code_map_single_byte!(0x38, Code::Eight, Some('8')),
1590    code_map_single_byte!(0x39, Code::Nine, Some('9')),
1591    code_map_single_byte!(0x3A, Code::Colon, Some(':')),
1592    code_map_single_byte!(0x3B, Code::SemiColon, Some(';')),
1593    code_map_single_byte!(0x3C, Code::LessThan, Some('<')),
1594    code_map_single_byte!(0x3D, Code::Equals, Some('=')),
1595    code_map_single_byte!(0x3E, Code::GreaterThan, Some('>')),
1596    code_map_single_byte!(0x3F, Code::QuestionMark, Some('?')),
1597    code_map_single_byte!(0x40, Code::CommercialAt, Some('@')),
1598    code_map_single_byte!(0x41, Code::LatinCapitalA, Some('A')),
1599    code_map_single_byte!(0x42, Code::LatinCapitalB, Some('B')),
1600    code_map_single_byte!(0x43, Code::LatinCapitalC, Some('C')),
1601    code_map_single_byte!(0x44, Code::LatinCapitalD, Some('D')),
1602    code_map_single_byte!(0x45, Code::LatinCapitalE, Some('E')),
1603    code_map_single_byte!(0x46, Code::LatinCapitalF, Some('F')),
1604    code_map_single_byte!(0x47, Code::LatinCapitalG, Some('G')),
1605    code_map_single_byte!(0x48, Code::LatinCapitalH, Some('H')),
1606    code_map_single_byte!(0x49, Code::LatinCapitalI, Some('I')),
1607    code_map_single_byte!(0x4A, Code::LatinCapitalJ, Some('J')),
1608    code_map_single_byte!(0x4B, Code::LatinCapitalK, Some('K')),
1609    code_map_single_byte!(0x4C, Code::LatinCapitalL, Some('L')),
1610    code_map_single_byte!(0x4D, Code::LatinCapitalM, Some('M')),
1611    code_map_single_byte!(0x4E, Code::LatinCapitalN, Some('N')),
1612    code_map_single_byte!(0x4F, Code::LatinCapitalO, Some('O')),
1613    code_map_single_byte!(0x50, Code::LatinCapitalP, Some('P')),
1614    code_map_single_byte!(0x51, Code::LatinCapitalQ, Some('Q')),
1615    code_map_single_byte!(0x52, Code::LatinCapitalR, Some('R')),
1616    code_map_single_byte!(0x53, Code::LatinCapitalS, Some('S')),
1617    code_map_single_byte!(0x54, Code::LatinCapitalT, Some('T')),
1618    code_map_single_byte!(0x55, Code::LatinCapitalU, Some('U')),
1619    code_map_single_byte!(0x56, Code::LatinCapitalV, Some('V')),
1620    code_map_single_byte!(0x57, Code::LatinCapitalW, Some('W')),
1621    code_map_single_byte!(0x58, Code::LatinCapitalX, Some('X')),
1622    code_map_single_byte!(0x59, Code::LatinCapitalY, Some('Y')),
1623    code_map_single_byte!(0x5A, Code::LatinCapitalZ, Some('Z')),
1624    code_map_single_byte!(0x5B, Code::LeftSquareBracket, Some('[')),
1625    code_map_single_byte!(0x5C, Code::ReverseSolidus, Some('\\')),
1626    code_map_single_byte!(0x5D, Code::RightSquareBracket, Some(']')),
1627    code_map_single_byte!(0x5E, Code::CircumflexAccent, Some('^')),
1628    code_map_single_byte!(0x5F, Code::LowLine, Some('_')),
1629    code_map_single_byte!(0x60, Code::GraveAccent, Some('`')),
1630    code_map_single_byte!(0x61, Code::LatinLowerA, Some('a')),
1631    code_map_single_byte!(0x62, Code::LatinLowerB, Some('b')),
1632    code_map_single_byte!(0x63, Code::LatinLowerC, Some('c')),
1633    code_map_single_byte!(0x64, Code::LatinLowerD, Some('d')),
1634    code_map_single_byte!(0x65, Code::LatinLowerE, Some('e')),
1635    code_map_single_byte!(0x66, Code::LatinLowerF, Some('f')),
1636    code_map_single_byte!(0x67, Code::LatinLowerG, Some('g')),
1637    code_map_single_byte!(0x68, Code::LatinLowerH, Some('h')),
1638    code_map_single_byte!(0x69, Code::LatinLowerI, Some('i')),
1639    code_map_single_byte!(0x6A, Code::LatinLowerJ, Some('j')),
1640    code_map_single_byte!(0x6B, Code::LatinLowerK, Some('k')),
1641    code_map_single_byte!(0x6C, Code::LatinLowerL, Some('l')),
1642    code_map_single_byte!(0x6D, Code::LatinLowerM, Some('m')),
1643    code_map_single_byte!(0x6E, Code::LatinLowerN, Some('n')),
1644    code_map_single_byte!(0x6F, Code::LatinLowerO, Some('o')),
1645    code_map_single_byte!(0x70, Code::LatinLowerP, Some('p')),
1646    code_map_single_byte!(0x71, Code::LatinLowerQ, Some('q')),
1647    code_map_single_byte!(0x72, Code::LatinLowerR, Some('r')),
1648    code_map_single_byte!(0x73, Code::LatinLowerS, Some('s')),
1649    code_map_single_byte!(0x74, Code::LatinLowerT, Some('t')),
1650    code_map_single_byte!(0x75, Code::LatinLowerU, Some('u')),
1651    code_map_single_byte!(0x76, Code::LatinLowerV, Some('v')),
1652    code_map_single_byte!(0x77, Code::LatinLowerW, Some('w')),
1653    code_map_single_byte!(0x78, Code::LatinLowerX, Some('x')),
1654    code_map_single_byte!(0x79, Code::LatinLowerY, Some('y')),
1655    code_map_single_byte!(0x7A, Code::LatinLowerZ, Some('z')),
1656    code_map_single_byte!(0x7B, Code::LeftCurlyBracket, Some('{')),
1657    code_map_single_byte!(0x7C, Code::VerticalLine, Some('|')),
1658    code_map_single_byte!(0x7D, Code::RightCurlyBracket, Some('}')),
1659    code_map_single_byte!(0x7E, Code::Tilde, Some('~')),
1660    code_map_single_byte!(0x7F, Code::MusicalSymbolEighthNote, Some('♪')),
1661    code_map_single_byte!(0x80, Code::SetCurrentWindow0, None),
1662    code_map_single_byte!(0x81, Code::SetCurrentWindow1, None),
1663    code_map_single_byte!(0x82, Code::SetCurrentWindow2, None),
1664    code_map_single_byte!(0x83, Code::SetCurrentWindow3, None),
1665    code_map_single_byte!(0x84, Code::SetCurrentWindow4, None),
1666    code_map_single_byte!(0x85, Code::SetCurrentWindow5, None),
1667    code_map_single_byte!(0x86, Code::SetCurrentWindow6, None),
1668    code_map_single_byte!(0x87, Code::SetCurrentWindow7, None),
1669    code_map_single_byte!(0x8E, Code::DelayCancel, None),
1670    code_map_single_byte!(0x8F, Code::Reset, None),
1671    code_map_single_byte!(0xA0, Code::NonBreakingSpace, Some('\u{A0}')),
1672    code_map_single_byte!(0xA1, Code::InvertedExclamationMark, Some('¡')),
1673    code_map_single_byte!(0xA2, Code::CentSign, Some('¢')),
1674    code_map_single_byte!(0xA3, Code::PoundSign, Some('£')),
1675    code_map_single_byte!(0xA4, Code::GeneralCurrencySign, Some('¤')),
1676    code_map_single_byte!(0xA5, Code::YenSign, Some('¥')),
1677    code_map_single_byte!(0xA6, Code::BrokenVerticalBar, Some('¦')),
1678    code_map_single_byte!(0xA7, Code::SectionSign, Some('§')),
1679    code_map_single_byte!(0xA8, Code::Umlaut, Some('¨')),
1680    code_map_single_byte!(0xA9, Code::CopyrightSign, Some('©')),
1681    code_map_single_byte!(0xAA, Code::FeminineOrdinalSign, Some('ª')),
1682    code_map_single_byte!(0xAB, Code::LeftDoubleAngleQuote, Some('«')),
1683    code_map_single_byte!(0xAC, Code::LogicalNotSign, Some('¬')),
1684    code_map_single_byte!(0xAD, Code::SoftHyphen, Some('\u{00ad}')),
1685    code_map_single_byte!(0xAE, Code::RegisteredTrademarkSign, Some('Ⓡ')),
1686    code_map_single_byte!(0xAF, Code::SpacingMacronLongAccent, Some('¯')),
1687    code_map_single_byte!(0xB0, Code::DegreeSign, Some('°')),
1688    code_map_single_byte!(0xB1, Code::PlusOrMinusSign, Some('±')),
1689    code_map_single_byte!(0xB2, Code::Superscript2, Some('²')),
1690    code_map_single_byte!(0xB3, Code::Superscript3, Some('³')),
1691    code_map_single_byte!(0xB4, Code::SpacingAccuteAccent, Some('´')),
1692    code_map_single_byte!(0xB5, Code::MicroSign, Some('µ')),
1693    code_map_single_byte!(0xB6, Code::ParagraphSign, Some('¶')),
1694    code_map_single_byte!(0xB7, Code::MiddleDot, Some('·')),
1695    code_map_single_byte!(0xB8, Code::SpacingCedilla, Some('¸')),
1696    code_map_single_byte!(0xB9, Code::Superscript1, Some('¹')),
1697    code_map_single_byte!(0xBA, Code::MasculineOrdinalSign, Some('º')),
1698    code_map_single_byte!(0xBB, Code::RightDoubleAngleQuote, Some('»')),
1699    code_map_single_byte!(0xBC, Code::Fraction14, Some('¼')),
1700    code_map_single_byte!(0xBD, Code::Fraction12, Some('½')),
1701    code_map_single_byte!(0xBE, Code::Fraction34, Some('¾')),
1702    code_map_single_byte!(0xBF, Code::InvertedQuestionMark, Some('¿')),
1703    code_map_single_byte!(0xC0, Code::LatinCapitalAWithGrave, Some('À')),
1704    code_map_single_byte!(0xC1, Code::LatinCapitalAWithAcute, Some('Á')),
1705    code_map_single_byte!(0xC2, Code::LatinCapitalAWithCircumflex, Some('Â')),
1706    code_map_single_byte!(0xC3, Code::LatinCapitalAWithTilde, Some('Ã')),
1707    code_map_single_byte!(0xC4, Code::LatinCapitalAWithDiaeresis, Some('Ä')),
1708    code_map_single_byte!(0xC5, Code::LatinCapitalAWithRingAbove, Some('Å')),
1709    code_map_single_byte!(0xC6, Code::LatinCapitalAe, Some('Æ')),
1710    code_map_single_byte!(0xC7, Code::LatinCapitalCWithCedilla, Some('Ç')),
1711    code_map_single_byte!(0xC8, Code::LatinCapitalEWithGrave, Some('È')),
1712    code_map_single_byte!(0xC9, Code::LatinCapitalEWithAcute, Some('É')),
1713    code_map_single_byte!(0xCA, Code::LatinCapitalEWithCircumflex, Some('Ê')),
1714    code_map_single_byte!(0xCB, Code::LatinCapitalEWithDiaeseris, Some('Ë')),
1715    code_map_single_byte!(0xCC, Code::LatinCapitalIWithGrave, Some('Ì')),
1716    code_map_single_byte!(0xCD, Code::LatinCapitalIWithAcute, Some('Í')),
1717    code_map_single_byte!(0xCE, Code::LatinCapitalIWithCircumflex, Some('Î')),
1718    code_map_single_byte!(0xCF, Code::LatinCapitalIWithDiaeseris, Some('Ï')),
1719    code_map_single_byte!(0xD0, Code::LatinCapitalEth, Some('Đ')),
1720    code_map_single_byte!(0xD1, Code::LatinCapitalNWithTilde, Some('Ñ')),
1721    code_map_single_byte!(0xD2, Code::LatinCapitalOWithGrave, Some('Ò')),
1722    code_map_single_byte!(0xD3, Code::LatinCapitalOWithAcute, Some('Ó')),
1723    code_map_single_byte!(0xD4, Code::LatinCapitalOWithCircumflex, Some('Ô')),
1724    code_map_single_byte!(0xD5, Code::LatinCapitalOWithTilde, Some('Õ')),
1725    code_map_single_byte!(0xD6, Code::LatinCapitalOWithDiaeresis, Some('Ö')),
1726    code_map_single_byte!(0xD7, Code::MultiplicationSign, Some('×')),
1727    code_map_single_byte!(0xD8, Code::LatinCapitalOWithStroke, Some('Ø')),
1728    code_map_single_byte!(0xD9, Code::LatinCapitalUWithGrave, Some('Ù')),
1729    code_map_single_byte!(0xDA, Code::LatinCapitalUWithAcute, Some('Ú')),
1730    code_map_single_byte!(0xDB, Code::LatinCapitalUWithCircumflex, Some('Û')),
1731    code_map_single_byte!(0xDC, Code::LatinCapitalUWithDiaeresis, Some('Ü')),
1732    code_map_single_byte!(0xDD, Code::LatinCapitalYWithAcute, Some('Ý')),
1733    code_map_single_byte!(0xDE, Code::LatinCapitalThorn, Some('Þ')),
1734    code_map_single_byte!(0xDF, Code::LatinLowerSharpS, Some('ß')),
1735    code_map_single_byte!(0xE0, Code::LatinLowerAWithGrave, Some('à')),
1736    code_map_single_byte!(0xE1, Code::LatinLowerAWithAcute, Some('á')),
1737    code_map_single_byte!(0xE2, Code::LatinLowerAWithCircumflex, Some('â')),
1738    code_map_single_byte!(0xE3, Code::LatinLowerAWithTilde, Some('ã')),
1739    code_map_single_byte!(0xE4, Code::LatinLowerAWithDiaeresis, Some('ä')),
1740    code_map_single_byte!(0xE5, Code::LatinLowerAWithRingAbove, Some('å')),
1741    code_map_single_byte!(0xE6, Code::LatinLowerAe, Some('æ')),
1742    code_map_single_byte!(0xE7, Code::LatinLowerCWithCedilla, Some('ç')),
1743    code_map_single_byte!(0xE8, Code::LatinLowerEWithGrave, Some('è')),
1744    code_map_single_byte!(0xE9, Code::LatinLowerEWithAcute, Some('é')),
1745    code_map_single_byte!(0xEA, Code::LatinLowerEWithCircumflex, Some('ê')),
1746    code_map_single_byte!(0xEB, Code::LatinLowerEWithDiaeseris, Some('ë')),
1747    code_map_single_byte!(0xEC, Code::LatinLowerIWithGrave, Some('ì')),
1748    code_map_single_byte!(0xED, Code::LatinLowerIWithAcute, Some('í')),
1749    code_map_single_byte!(0xEE, Code::LatinLowerIWithCircumflex, Some('î')),
1750    code_map_single_byte!(0xEF, Code::LatinLowerIWithDiaeseris, Some('ï')),
1751    code_map_single_byte!(0xF0, Code::LatinLowerEth, Some('ð')),
1752    code_map_single_byte!(0xF1, Code::LatinLowerNWithTilde, Some('ñ')),
1753    code_map_single_byte!(0xF2, Code::LatinLowerOWithGrave, Some('ò')),
1754    code_map_single_byte!(0xF3, Code::LatinLowerOWithAcute, Some('ó')),
1755    code_map_single_byte!(0xF4, Code::LatinLowerOWithCircumflex, Some('ô')),
1756    code_map_single_byte!(0xF5, Code::LatinLowerOWithTilde, Some('õ')),
1757    code_map_single_byte!(0xF6, Code::LatinLowerOWithDiaeresis, Some('ö')),
1758    code_map_single_byte!(0xF7, Code::DivisionSign, Some('÷')),
1759    code_map_single_byte!(0xF8, Code::LatinLowerOWithStroke, Some('ø')),
1760    code_map_single_byte!(0xF9, Code::LatinLowerUWithGrave, Some('ù')),
1761    code_map_single_byte!(0xFA, Code::LatinLowerUWithAcute, Some('ú')),
1762    code_map_single_byte!(0xFB, Code::LatinLowerUWithCircumflex, Some('û')),
1763    code_map_single_byte!(0xFC, Code::LatinLowerUWithDiaeresis, Some('ü')),
1764    code_map_single_byte!(0xFD, Code::LatinLowerYWithAcute, Some('ý')),
1765    code_map_single_byte!(0xFE, Code::LatinLowerThorn, Some('þ')),
1766    code_map_single_byte!(0xFF, Code::LatinLowerYWithDiaeresis, Some('ÿ')),
1767];
1768
1769macro_rules! parse_control_code {
1770    ($data:expr, $arg_len:expr, $enum_val:path) => {{
1771        let args: [u8; $arg_len] = $data[1..$arg_len + 1].try_into().unwrap();
1772        $enum_val(args.into())
1773    }};
1774}
1775
1776macro_rules! write_control_code {
1777    ($control_byte:expr, $w:expr, $args:expr, $arg_len:expr) => {{
1778        $w.write_all(&[$control_byte])?;
1779        let args: [u8; $arg_len] = $args.into();
1780        $w.write_all(&args)
1781    }};
1782}
1783
1784impl Code {
1785    fn expected_size(bytes: &[u8]) -> Result<usize, CodeError> {
1786        if bytes.is_empty() {
1787            return Err(CodeError::LengthMismatch {
1788                expected: 1,
1789                actual: 0,
1790            });
1791        }
1792        match bytes[0] {
1793            0x00..=0x0F => Ok(1),
1794            0x10 => Ok(Ext1::expected_size(&bytes[1..])? + 1),
1795            0x11..=0x17 => Ok(2),
1796            0x18..=0x1F => Ok(3),
1797            0x20..=0x7F => Ok(1),
1798            0x80..=0x87 => Ok(1), // CWx
1799            0x88..=0x8C => Ok(2), // CLW, DSW, HDW, TGW, DLW
1800            0x8D => Ok(2),        // DLY
1801            0x8E => Ok(1),        // DLC
1802            0x8F => Ok(1),        // RST
1803            0x90 => Ok(3),        // SPA
1804            0x91 => Ok(4),        // SPC
1805            0x92 => Ok(3),        // SPL
1806            0x93..=0x96 => Ok(1), // reserved
1807            0x97 => Ok(5),        // SWA
1808            0x98..=0x9F => Ok(7), // DFx
1809            0xA0..=0xFF => Ok(1),
1810        }
1811    }
1812
1813    /// The length in bytes of this [Code]
1814    ///
1815    /// # Examples
1816    /// ```
1817    /// # use cea708_types::tables::Code;
1818    /// assert_eq!(Code::LatinCapitalA.byte_len(), 1);
1819    /// ```
1820    pub fn byte_len(&self) -> usize {
1821        if let Ok(idx) = CODE_MAP_TABLE.binary_search_by_key(&self, |code_map| &code_map.code) {
1822            return CODE_MAP_TABLE[idx].cea708_bytes.len();
1823        }
1824        match self {
1825            Code::Ext1(ext1) => ext1.byte_len(),
1826            Code::P16(_) => 3,
1827            Code::ClearWindows(_args) => 2,
1828            Code::DisplayWindows(_args) => 2,
1829            Code::HideWindows(_args) => 2,
1830            Code::ToggleWindows(_args) => 2,
1831            Code::DeleteWindows(_args) => 2,
1832            Code::SetPenAttributes(_args) => 3,
1833            Code::SetPenColor(_args) => 4,
1834            Code::SetPenLocation(_args) => 3,
1835            Code::SetWindowAttributes(_args) => 5,
1836            Code::DefineWindow(_args) => 7,
1837            Code::Unknown(data) => data.len(),
1838            _ => unreachable!(),
1839        }
1840    }
1841
1842    fn parse_element(data: &[u8]) -> Result<Code, CodeError> {
1843        let size = Code::expected_size(data)?;
1844        if data.len() > size {
1845            return Err(CodeError::LengthMismatch {
1846                expected: size,
1847                actual: data.len(),
1848            });
1849        }
1850        if let Ok(idx) =
1851            CODE_MAP_TABLE.binary_search_by_key(&data, |code_map| code_map.cea708_bytes)
1852        {
1853            return Ok(CODE_MAP_TABLE[idx].code.clone());
1854        }
1855        Ok(match data[0] {
1856            0x10 => Code::Ext1(Ext1::parse(&data[1..])?),
1857            0x18 => Code::P16((data[1] as u16) << 8 | data[2] as u16),
1858            0x88 => parse_control_code!(data, 1, Code::ClearWindows),
1859            0x89 => parse_control_code!(data, 1, Code::DisplayWindows),
1860            0x8A => parse_control_code!(data, 1, Code::HideWindows),
1861            0x8B => parse_control_code!(data, 1, Code::ToggleWindows),
1862            0x8C => parse_control_code!(data, 1, Code::DeleteWindows),
1863            0x90 => parse_control_code!(data, 2, Code::SetPenAttributes),
1864            0x91 => parse_control_code!(data, 3, Code::SetPenColor),
1865            0x92 => parse_control_code!(data, 2, Code::SetPenLocation),
1866            0x97 => parse_control_code!(data, 4, Code::SetWindowAttributes),
1867            0x98..=0x9F => {
1868                let args: [u8; 6] = data[1..7].try_into().unwrap();
1869                let args = args.into();
1870                let args = DefineWindowArgs {
1871                    window_id: data[0] & 0x07,
1872                    ..args
1873                };
1874                Code::DefineWindow(args)
1875            }
1876            _ => Code::Unknown(data.to_vec()),
1877        })
1878    }
1879
1880    /// Parse a byte sequence into a list of [Code]s
1881    ///
1882    /// # Examples
1883    /// ```
1884    /// # use cea708_types::tables::Code;
1885    /// assert_eq!(Code::from_data(&[0x41]), Ok(vec![Code::LatinCapitalA]));
1886    /// ```
1887    pub fn from_data(data: &[u8]) -> Result<Vec<Code>, CodeError> {
1888        let mut data_iter = data;
1889        let mut ret = vec![];
1890        while !data_iter.is_empty() {
1891            let size = Code::expected_size(data_iter)?;
1892            if data_iter.len() < size {
1893                return Err(CodeError::LengthMismatch {
1894                    expected: size,
1895                    actual: data_iter.len(),
1896                });
1897            }
1898            let element = &data_iter[..size];
1899            let element = Code::parse_element(element)?;
1900            ret.push(element);
1901
1902            data_iter = &data_iter[size..];
1903        }
1904        Ok(ret)
1905    }
1906
1907    /// Write a [Code] to a byte stream
1908    ///
1909    /// # Examples
1910    /// ```
1911    /// # use cea708_types::tables::Code;
1912    /// let mut written = vec![];
1913    /// Code::LatinCapitalA.write(&mut written).unwrap();
1914    /// assert_eq!(written, [0x41]);
1915    /// ```
1916    pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
1917        if let Ok(idx) = CODE_MAP_TABLE.binary_search_by_key(&self, |code_map| &code_map.code) {
1918            return w.write_all(CODE_MAP_TABLE[idx].cea708_bytes);
1919        }
1920        match self {
1921            Code::Ext1(ext1) => {
1922                w.write_all(&[0x10])?;
1923                ext1.write(w)
1924            }
1925            Code::P16(c) => w.write_all(&[0x18, ((c & 0xFF00) >> 8) as u8, (c & 0xFF) as u8]),
1926            Code::ClearWindows(args) => write_control_code!(0x88, w, *args, 1),
1927            Code::DisplayWindows(args) => write_control_code!(0x89, w, *args, 1),
1928            Code::HideWindows(args) => write_control_code!(0x8A, w, *args, 1),
1929            Code::ToggleWindows(args) => write_control_code!(0x8B, w, *args, 1),
1930            Code::DeleteWindows(args) => write_control_code!(0x8C, w, *args, 1),
1931            Code::SetPenAttributes(args) => write_control_code!(0x90, w, *args, 2),
1932            Code::SetPenColor(args) => write_control_code!(0x91, w, *args, 3),
1933            Code::SetPenLocation(args) => write_control_code!(0x92, w, *args, 2),
1934            Code::SetWindowAttributes(args) => write_control_code!(0x97, w, *args, 4),
1935            Code::DefineWindow(args) => {
1936                write_control_code!(0x98 | (args.window_id & 0x07), w, *args, 6)
1937            }
1938            Code::Unknown(data) => w.write_all(data),
1939            _ => unreachable!(),
1940        }
1941    }
1942
1943    /// The utf8 char for this [Code]
1944    ///
1945    /// [Code]s that represent a command will return None.
1946    ///
1947    /// # Examples
1948    /// ```
1949    /// # use cea708_types::tables::Code;
1950    /// assert_eq!(Code::LatinCapitalA.char(), Some('A'));
1951    /// ```
1952    pub fn char(&self) -> Option<char> {
1953        // table is not currently sorted by utf8 value so cannot binary search through it.  May
1954        // need another lookup table if this is a performance concern
1955        CODE_MAP_TABLE.iter().find_map(|code_map| {
1956            if code_map.code == *self {
1957                code_map.utf8
1958            } else {
1959                None
1960            }
1961        })
1962    }
1963
1964    /// Retrieve a [Code] for a utf8 char
1965    ///
1966    /// If the char is not representable as a [Code], None will be returned.
1967    ///
1968    /// # Examples
1969    /// ```
1970    /// # use cea708_types::tables::Code;
1971    /// assert_eq!(Code::from_char('A'), Some(Code::LatinCapitalA));
1972    /// ```
1973    pub fn from_char(c: char) -> Option<Code> {
1974        // table is not currently sorted by utf8 value so cannot binary search through it.  May
1975        // need another lookup table if this is a performance concern
1976        CODE_MAP_TABLE.iter().find_map(|code_map| {
1977            if code_map.utf8 == Some(c) {
1978                Some(code_map.code.clone())
1979            } else {
1980                None
1981            }
1982        })
1983    }
1984}
1985
1986impl Ext1 {
1987    fn expected_size(bytes: &[u8]) -> Result<usize, CodeError> {
1988        if bytes.is_empty() {
1989            return Err(CodeError::LengthMismatch {
1990                expected: 1,
1991                actual: 0,
1992            });
1993        }
1994        match bytes[0] {
1995            0x00..=0x07 => Ok(1),
1996            0x08..=0x0F => Ok(2),
1997            0x10..=0x17 => Ok(3),
1998            0x18..=0x1F => Ok(4),
1999            0x20..=0x7F => Ok(1), // G2
2000            0x80..=0x87 => Ok(5),
2001            0x88..=0x8F => Ok(6),
2002            0x90..=0x9F => {
2003                if bytes.len() < 2 {
2004                    return Err(CodeError::LengthMismatch {
2005                        expected: 2,
2006                        actual: 0,
2007                    });
2008                }
2009                Ok(((bytes[1] & 0x3F) as usize) + 1)
2010            }
2011            0xA0..=0xFF => Ok(1), // G3
2012        }
2013    }
2014
2015    fn byte_len(&self) -> usize {
2016        // All currently known Ext1 codes are covered in the static table
2017        match self {
2018            Ext1::Unknown(data) => data.len(),
2019            _ => unreachable!(),
2020        }
2021    }
2022
2023    fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
2024        // All currently known Ext1 codes are covered in the static table
2025        match self {
2026            Ext1::Unknown(data) => w.write_all(data),
2027            _ => unreachable!(),
2028        }
2029    }
2030
2031    fn parse(data: &[u8]) -> Result<Ext1, CodeError> {
2032        // All currently known Ext1 codes are covered in the static table
2033        Ok(Ext1::Unknown(data.to_vec()))
2034    }
2035}
2036
2037#[cfg(test)]
2038mod test {
2039    use super::*;
2040    use crate::tests::*;
2041    use log::trace;
2042
2043    #[test]
2044    fn codes_table_ordered() {
2045        test_init_log();
2046        let mut iter = CODE_MAP_TABLE.iter().peekable();
2047        while let Some(code_map) = iter.next() {
2048            if let Some(peek) = iter.peek() {
2049                trace!("checking ordinality for {code_map:?} and {peek:?}");
2050                assert!(peek.code > code_map.code);
2051                assert!(peek.cea708_bytes > code_map.cea708_bytes);
2052            }
2053        }
2054    }
2055
2056    static VARIABLE_TEST_CODES: [CodeMap; 10] = [
2057        code_map_bytes!(
2058            [0x9A, 0x38, 0x4A, 0xD1, 0x8B, 0x0F, 0x11],
2059            Code::DefineWindow(DefineWindowArgs::new(
2060                2,
2061                0,
2062                Anchor::BottomRight,
2063                false,
2064                74,
2065                209,
2066                11,
2067                15,
2068                true,
2069                true,
2070                true,
2071                2,
2072                1,
2073            )),
2074            None
2075        ),
2076        code_map_bytes!(
2077            [0x97, 0x64, 0x53, 0x88, 0x22],
2078            Code::SetWindowAttributes(SetWindowAttributesArgs::new(
2079                Justify::Left,
2080                Direction::LeftToRight,
2081                Direction::TopToBottom,
2082                false,
2083                DisplayEffect::Wipe,
2084                Direction::LeftToRight,
2085                2,
2086                Color::new(
2087                    ColorValue::TwoThirds,
2088                    ColorValue::OneThird,
2089                    ColorValue::None
2090                ),
2091                Opacity::Flash,
2092                BorderType::ShadowRight,
2093                Color::new(ColorValue::OneThird, ColorValue::None, ColorValue::Full)
2094            )),
2095            None
2096        ),
2097        code_map_bytes!(
2098            [0x8B, 0xF6],
2099            Code::ToggleWindows(WindowBits::ZERO.or(WindowBits::THREE).not()),
2100            None
2101        ),
2102        code_map_bytes!(
2103            [0x8A, 0x7E],
2104            Code::HideWindows(WindowBits::ZERO.or(WindowBits::SEVEN).not()),
2105            None
2106        ),
2107        code_map_bytes!([0x89, 0x80], Code::DisplayWindows(WindowBits::SEVEN), None),
2108        code_map_bytes!(
2109            [0x8C, 0xFE],
2110            Code::DeleteWindows(WindowBits::ZERO.not()),
2111            None
2112        ),
2113        code_map_bytes!(
2114            [0x88, 0x13],
2115            Code::ClearWindows(WindowBits::ZERO.or(WindowBits::ONE).or(WindowBits::FOUR)),
2116            None
2117        ),
2118        code_map_bytes!(
2119            [0x90, 0x4A, 0xCA],
2120            Code::SetPenAttributes(SetPenAttributesArgs::new(
2121                PenSize::Large,
2122                FontStyle::ProportionallySpacedWithSerifs,
2123                TextTag::Voiceover,
2124                TextOffset::Superscript,
2125                true,
2126                true,
2127                EdgeType::Raised
2128            )),
2129            None
2130        ),
2131        code_map_bytes!(
2132            [0x91, 0x3F, 0xC0, 0x15],
2133            Code::SetPenColor(SetPenColorArgs::new(
2134                Color::new(ColorValue::Full, ColorValue::Full, ColorValue::Full),
2135                Opacity::Solid,
2136                Color::new(ColorValue::None, ColorValue::None, ColorValue::None),
2137                Opacity::Transparent,
2138                Color::new(
2139                    ColorValue::OneThird,
2140                    ColorValue::OneThird,
2141                    ColorValue::OneThird
2142                )
2143            )),
2144            None
2145        ),
2146        code_map_bytes!(
2147            [0x92, 0x05, 0x08],
2148            Code::SetPenLocation(SetPenLocationArgs::new(5, 8)),
2149            None
2150        ),
2151    ];
2152
2153    #[test]
2154    fn codes_to_from_bytes() {
2155        test_init_log();
2156        for code_map in CODE_MAP_TABLE.iter().chain(VARIABLE_TEST_CODES.iter()) {
2157            trace!("parsing {code_map:?}");
2158            let parsed_code = Code::parse_element(code_map.cea708_bytes).unwrap();
2159            assert_eq!(parsed_code, code_map.code);
2160            let mut written = vec![];
2161            parsed_code.write(&mut written).unwrap();
2162            assert_eq!(written, code_map.cea708_bytes);
2163            assert_eq!(written.len(), code_map.code.byte_len());
2164        }
2165    }
2166
2167    #[test]
2168    fn codes_to_from_char() {
2169        test_init_log();
2170        for code_map in CODE_MAP_TABLE.iter() {
2171            trace!("parsing {code_map:?}");
2172            if let Some(c) = code_map.utf8 {
2173                let parsed_code = Code::from_char(c).unwrap();
2174                assert_eq!(parsed_code.char(), code_map.utf8);
2175                assert_eq!(parsed_code, code_map.code);
2176                let mut written = vec![];
2177                parsed_code.write(&mut written).unwrap();
2178                assert_eq!(written, code_map.cea708_bytes);
2179            }
2180        }
2181    }
2182
2183    #[test]
2184    fn define_zero_style_id() {
2185        test_init_log();
2186        let define = DefineWindowArgs::new(
2187            0,
2188            0,
2189            Anchor::BottomMiddle,
2190            true,
2191            100,
2192            50,
2193            11,
2194            31,
2195            true,
2196            true,
2197            true,
2198            0,
2199            0,
2200        );
2201        let win_attrs = define.window_attributes();
2202        assert_eq!(win_attrs.fill_opacity, Opacity::Solid);
2203        assert_eq!(win_attrs.fill_color, Color::BLACK);
2204        let pen_attrs = define.pen_attributes();
2205        assert_eq!(pen_attrs.font_style, FontStyle::Default);
2206        let pen_color = define.pen_color();
2207        assert_eq!(pen_color, PREDEFINED_PEN_STYLES_COLOR[0]);
2208    }
2209}