Skip to main content

mpeg_syntax_dump/
write.rs

1use crate::types::{
2    BitPatternField, FieldTable, FixedWidthField, TermAnnotation, Value, VariableLengthField,
3};
4
5/// Trait for rendering MPEG specification syntax structures.
6///
7/// Renderers implement this trait to produce output in a specific format
8/// (plain text, ANSI-colored text, HTML, etc.). Producers call these
9/// methods to describe the syntax structure and field values.
10///
11/// Methods use `begin_`/`end_` pairs rather than closures to avoid
12/// borrow-checker issues when the producer needs `&mut W` inside a
13/// closure while also borrowing `&self` for data access.
14pub trait SyntaxWrite {
15    type Error: std::error::Error;
16
17    // ── Element blocks ──────────────────────────────────────
18
19    /// Begin a named syntax element (a "syntax table" in MPEG specs).
20    ///
21    /// `params` is `None` for unparameterized elements like `transport_packet()`,
22    /// or `Some("payloadType, payloadSize")` for parameterized ones like
23    /// `sei_payload(payloadType, payloadSize)`.
24    fn begin_element(&mut self, name: &str, params: Option<&str>)
25        -> Result<(), Self::Error>;
26
27    /// End the current syntax element.
28    fn end_element(&mut self) -> Result<(), Self::Error>;
29
30    // ── Fields ──────────────────────────────────────────────
31
32    /// Render a fixed-width field.
33    fn fixed_width_field(&mut self, field: &FixedWidthField<'_>)
34        -> Result<(), Self::Error>;
35
36    /// Render a variable-length coded field.
37    fn variable_length_field(&mut self, field: &VariableLengthField<'_>)
38        -> Result<(), Self::Error>;
39
40    /// Render a fixed bit pattern or marker bit.
41    fn bit_pattern(&mut self, field: &BitPatternField<'_>)
42        -> Result<(), Self::Error>;
43
44    // ── Raw byte hex dump ───────────────────────────────────
45
46    /// Render raw bytes as a hex dump. Called after a field template line;
47    /// the renderer formats as hex lines (16 bytes per line).
48    fn raw_bytes(&mut self, data: &[u8]) -> Result<(), Self::Error>;
49
50    // ── Conditionals ────────────────────────────────────────
51
52    /// Begin an `if` block. The renderer adds `if (condition) {` and any
53    /// term annotations. `taken` is a hint for dimming not-taken branches.
54    fn begin_if(&mut self, condition: &str,
55        terms: &[TermAnnotation<'_>], taken: bool)
56        -> Result<(), Self::Error>;
57
58    /// Close the previous branch and open an `else if` branch.
59    fn begin_else_if(&mut self, condition: &str,
60        terms: &[TermAnnotation<'_>], taken: bool)
61        -> Result<(), Self::Error>;
62
63    /// Close the previous branch and open an `else` branch.
64    fn begin_else(&mut self, taken: bool)
65        -> Result<(), Self::Error>;
66
67    /// Close the final branch of an if/else-if/else chain.
68    fn end_if(&mut self) -> Result<(), Self::Error>;
69
70    // ── For loops ───────────────────────────────────────────
71
72    /// Begin a `for` loop. `header` is the loop clause, e.g.
73    /// `"i = 0; i < N; i++"`. The renderer adds `for (header) {`.
74    fn begin_for(&mut self, header: &str,
75        terms: &[TermAnnotation<'_>])
76        -> Result<(), Self::Error>;
77
78    /// Mark a for-loop iteration with the variable name and index.
79    fn for_iteration(&mut self, variable: &str, index: u64)
80        -> Result<(), Self::Error>;
81
82    /// End a `for` loop.
83    fn end_for(&mut self) -> Result<(), Self::Error>;
84
85    // ── While loops ─────────────────────────────────────────
86
87    /// Begin a `while` loop.
88    fn begin_while(&mut self, condition: &str)
89        -> Result<(), Self::Error>;
90
91    /// Mark a while-loop iteration.
92    fn while_iteration(&mut self, index: u64)
93        -> Result<(), Self::Error>;
94
95    /// End a `while` loop.
96    fn end_while(&mut self) -> Result<(), Self::Error>;
97
98    // ── Do-while loops ──────────────────────────────────────
99
100    /// Begin a `do-while` loop.
101    fn begin_do_while(&mut self) -> Result<(), Self::Error>;
102
103    /// Mark a do-while iteration.
104    fn do_while_iteration(&mut self, index: u64)
105        -> Result<(), Self::Error>;
106
107    /// End a `do-while` loop with the given condition.
108    fn end_do_while(&mut self, condition: &str)
109        -> Result<(), Self::Error>;
110
111    // ── Switch/case ────────────────────────────────────────
112
113    /// Begin a `switch` statement. `expression` is the switch discriminator,
114    /// e.g. `"id"`. `terms` provides term annotations for the discriminator.
115    fn begin_switch(&mut self, expression: &str,
116        terms: &[TermAnnotation<'_>]) -> Result<(), Self::Error>;
117
118    /// Begin a `case` within a switch. `label` is the case label,
119    /// e.g. `"ID_CPE"`. `taken` indicates whether this is the active case.
120    fn begin_case(&mut self, label: &str, taken: bool)
121        -> Result<(), Self::Error>;
122
123    /// End the current case.
124    fn end_case(&mut self) -> Result<(), Self::Error>;
125
126    /// End the switch statement.
127    fn end_switch(&mut self) -> Result<(), Self::Error>;
128
129    // ── Field tables ──────────────────────────────────────────
130
131    /// Render a table of homogeneous field values from a loop.
132    ///
133    /// The default implementation expands the table into a `for` loop with
134    /// one iteration per row. Compact renderers override this to produce
135    /// inline lists or aligned tables.
136    fn field_table(&mut self, table: &FieldTable<'_>) -> Result<(), Self::Error> {
137        let n = table.rows.len();
138        let header = if table.columns.len() == 1 {
139            format!("i = 0; i < {n}; i++")
140        } else {
141            format!("i = 0; i < {n}; i++")
142        };
143        self.begin_for(&header, &[])?;
144        for (i, row) in table.rows.iter().enumerate() {
145            self.for_iteration("i", i as u64)?;
146            for (col_idx, col) in table.columns.iter().enumerate() {
147                if let Some(val) = row.get(col_idx) {
148                    let name = format!("{}[{i}]", col.name);
149                    match col.bits {
150                        Some(bits) => self.fixed_width_field(&FixedWidthField {
151                            name: &name,
152                            bits,
153                            descriptor: col.descriptor,
154                            value: Some(val.clone()),
155                            comment: None,
156                        })?,
157                        None => self.variable_length_field(&VariableLengthField {
158                            name: &name,
159                            descriptor: col.descriptor,
160                            value: Some(val.clone()),
161                            comment: None,
162                        })?,
163                    }
164                }
165            }
166        }
167        self.end_for()
168    }
169
170    // ── Assignments ─────────────────────────────────────────
171
172    /// Render an inline variable assignment. `computed_value` of `Some(value)`
173    /// renders a trailing `/* = value */` annotation.
174    fn assignment(&mut self, expression: &str,
175        computed_value: Option<&Value>) -> Result<(), Self::Error>;
176
177    // ── Comments and ellipsis ───────────────────────────────
178
179    /// Render a standalone comment line.
180    fn comment(&mut self, text: &str) -> Result<(), Self::Error>;
181
182    /// Render an ellipsis (`...`) indicating omitted content.
183    fn ellipsis(&mut self) -> Result<(), Self::Error>;
184}