Skip to content

Commit 41bb3d8

Browse files
committed
cea608: add conversion into Code values
1 parent ea94a3d commit 41bb3d8

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed

src/lib.rs

+205
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,113 @@ impl Cea608 {
154154
Self::DeleteToEndOfRow(chan) => *chan,
155155
}
156156
}
157+
158+
/// Convert into one or two [`Code`] values.
159+
pub fn into_code(&self, field: Field) -> [Code; 2] {
160+
match self {
161+
Self::Text(text) => {
162+
let mut ret = [Code::NUL, Code::NUL];
163+
if let Some(char1) = text.char1 {
164+
ret[0] = Code::from_char(char1, text.channel).unwrap();
165+
}
166+
if let Some(char2) = text.char2 {
167+
ret[1] = Code::from_char(char2, text.channel).unwrap();
168+
}
169+
ret
170+
}
171+
Self::NewMode(chan, mode) => [
172+
Code::Control(tables::ControlCode {
173+
field: Some(field),
174+
channel: *chan,
175+
control: match mode {
176+
Mode::RollUp2 => tables::Control::RollUp2,
177+
Mode::RollUp3 => tables::Control::RollUp3,
178+
Mode::RollUp4 => tables::Control::RollUp4,
179+
Mode::PaintOn => tables::Control::ResumeDirectionCaptioning,
180+
Mode::PopOn => tables::Control::ResumeCaptionLoading,
181+
},
182+
}),
183+
Code::NUL,
184+
],
185+
Self::EraseDisplay(chan) => [
186+
Code::Control(tables::ControlCode {
187+
field: Some(field),
188+
channel: *chan,
189+
control: tables::Control::EraseDisplayedMemory,
190+
}),
191+
Code::NUL,
192+
],
193+
Self::EraseNonDisplay(chan) => [
194+
Code::Control(tables::ControlCode {
195+
field: Some(field),
196+
channel: *chan,
197+
control: tables::Control::EraseNonDisplayedMemory,
198+
}),
199+
Code::NUL,
200+
],
201+
Self::CarriageReturn(chan) => [
202+
Code::Control(tables::ControlCode {
203+
field: Some(field),
204+
channel: *chan,
205+
control: tables::Control::CarriageReturn,
206+
}),
207+
Code::NUL,
208+
],
209+
Self::Backspace(chan) => [
210+
Code::Control(tables::ControlCode {
211+
field: Some(field),
212+
channel: *chan,
213+
control: tables::Control::Backspace,
214+
}),
215+
Code::NUL,
216+
],
217+
Self::EndOfCaption(chan) => [
218+
Code::Control(tables::ControlCode {
219+
field: Some(field),
220+
channel: *chan,
221+
control: tables::Control::EndOfCaption,
222+
}),
223+
Code::NUL,
224+
],
225+
Self::TabOffset(chan, count) => [
226+
Code::Control(tables::ControlCode {
227+
field: Some(field),
228+
channel: *chan,
229+
control: match count {
230+
1 => tables::Control::TabOffset1,
231+
2 => tables::Control::TabOffset2,
232+
3 => tables::Control::TabOffset3,
233+
_ => unreachable!(),
234+
},
235+
}),
236+
Code::NUL,
237+
],
238+
Self::Preamble(chan, preamble) => [
239+
Code::Control(tables::ControlCode {
240+
field: Some(field),
241+
channel: *chan,
242+
control: tables::Control::PreambleAddress(*preamble),
243+
}),
244+
Code::NUL,
245+
],
246+
Self::MidRowChange(chan, midrow) => [
247+
Code::Control(tables::ControlCode {
248+
field: Some(field),
249+
channel: *chan,
250+
control: tables::Control::MidRow(*midrow),
251+
}),
252+
Code::NUL,
253+
],
254+
Self::DeleteToEndOfRow(chan) => [
255+
Code::Control(tables::ControlCode {
256+
field: Some(field),
257+
channel: *chan,
258+
control: tables::Control::DeleteToEndOfRow,
259+
}),
260+
Code::NUL,
261+
],
262+
}
263+
}
157264
}
158265

159266
/// Helper struct that has two purposes:
@@ -181,6 +288,7 @@ impl Cea608State {
181288
}
182289
}
183290
self.last_data = Some(data);
291+
trace!("decoded into codes {code:x?}");
184292

185293
// TODO: handle xds and text mode
186294

@@ -278,43 +386,51 @@ impl Cea608Writer {
278386
let mut prev = None::<Code>;
279387

280388
if let Some(code) = self.pending_code.take() {
389+
trace!("returning pending code {code:?}");
281390
code.write_into(&mut ret);
282391
return ret;
283392
}
284393

285394
while let Some(code) = self.pending.pop_back() {
286395
if let Some(prev) = prev {
396+
trace!("have prev {prev:?}");
287397
if code.byte_len() == 1 {
288398
let mut data = [0; 2];
289399
prev.write_into(&mut ret);
290400
code.write_into(&mut data);
291401
ret[1] = data[0];
402+
trace!("have 1 byte code {code:?}, returning {ret:x?}");
292403
return ret;
293404
} else if code.needs_backspace() {
294405
self.pending_code = Some(code);
295406
let mut data = [0; 2];
296407
prev.write_into(&mut ret);
297408
Code::Space.write_into(&mut data);
298409
ret[1] = data[0];
410+
trace!("have backspace needing code {code:?} stored as pending, pushing space with previous code {prev:?}");
299411
return ret;
300412
} else {
301413
self.pending_code = Some(code);
302414
prev.write_into(&mut ret);
415+
trace!("have two byte code {code:?} stored as pending, pushing space");
303416
return ret;
304417
}
305418
} else if code.needs_backspace() {
306419
// all back space needing codes are 2 byte commands
307420
self.pending_code = Some(code);
308421
Code::Space.write_into(&mut ret);
422+
trace!("have backspace needing code {code:?} stored as pending, pushing space");
309423
return ret;
310424
} else if code.byte_len() == 1 {
311425
prev = Some(code);
312426
} else {
427+
trace!("have standalone 2 byte code {code:?}");
313428
code.write_into(&mut ret);
314429
return ret;
315430
}
316431
}
317432
if let Some(prev) = prev {
433+
trace!("have no more pending codes, writing prev {prev:?}");
318434
prev.write_into(&mut ret);
319435
}
320436
ret
@@ -553,6 +669,95 @@ mod test {
553669
assert_eq!(writer.pop(), [0x91, 0x31]);
554670
assert_eq!(writer.pop(), [0x80, 0x80]);
555671
}
672+
673+
#[test]
674+
fn state_into_writer() {
675+
test_init_log();
676+
let stream = [[0x20, 0x80], [0x13, 0x2f], [0x80, 0x80]];
677+
let mut state = Cea608State::default();
678+
let mut writer = Cea608Writer::default();
679+
for pair in stream {
680+
let Some(cea608) = state.decode(pair).unwrap() else {
681+
continue;
682+
};
683+
for code in cea608.into_code(Field::ONE) {
684+
writer.push(code);
685+
}
686+
}
687+
assert_eq!(writer.pop(), [0x20, 0x80]);
688+
assert_eq!(writer.pop(), [0x13, 0x2f]);
689+
assert_eq!(writer.pop(), [0x80, 0x80]);
690+
}
691+
692+
#[test]
693+
fn cea608_to_from_code() {
694+
test_init_log();
695+
696+
let controls = [
697+
tables::Control::ResumeCaptionLoading,
698+
tables::Control::RollUp2,
699+
tables::Control::RollUp3,
700+
tables::Control::RollUp4,
701+
tables::Control::EndOfCaption,
702+
tables::Control::ResumeDirectionCaptioning,
703+
tables::Control::tab_offset(1).unwrap(),
704+
tables::Control::tab_offset(2).unwrap(),
705+
tables::Control::tab_offset(3).unwrap(),
706+
tables::Control::PreambleAddress(PreambleAddressCode::new(
707+
3,
708+
true,
709+
tables::PreambleType::Indent4,
710+
)),
711+
tables::Control::EraseDisplayedMemory,
712+
tables::Control::EraseNonDisplayedMemory,
713+
tables::Control::CarriageReturn,
714+
tables::Control::Backspace,
715+
tables::Control::DeleteToEndOfRow,
716+
];
717+
let controls_with_preceding_overwritten_char = [tables::Control::MidRow(
718+
MidRow::new_color(tables::Color::Green, true),
719+
)];
720+
721+
let codes = [Code::PercentSign, Code::LatinLowerA];
722+
723+
let mut writer = Cea608Writer::default();
724+
725+
let mut state = Cea608State::default();
726+
727+
for field in [Field::ONE, Field::TWO] {
728+
for channel in [Channel::ONE, Channel::TWO] {
729+
for control in controls {
730+
let code = Code::Control(ControlCode {
731+
field: Some(field),
732+
channel,
733+
control,
734+
});
735+
writer.push(code);
736+
let cea608 = state.decode(writer.pop()).unwrap().unwrap();
737+
assert_eq!(cea608.into_code(field)[0], code);
738+
}
739+
for control in controls_with_preceding_overwritten_char {
740+
let code = Code::Control(ControlCode {
741+
field: Some(field),
742+
channel,
743+
control,
744+
});
745+
writer.push(code);
746+
writer.pop(); // eat the preceding char
747+
let cea608 = state.decode(writer.pop()).unwrap().unwrap();
748+
assert_eq!(cea608.into_code(field)[0], code);
749+
}
750+
}
751+
}
752+
753+
for code in codes {
754+
debug!("pushing {code:?}");
755+
writer.push(code);
756+
let data = writer.pop();
757+
let cea608 = state.decode(data).unwrap().unwrap();
758+
assert_eq!(cea608.into_code(Field::ONE)[0], code);
759+
}
760+
}
556761
}
557762

558763
#[cfg(test)]

0 commit comments

Comments
 (0)