use std::fs::File;
use std::io::Write;
use std::path::Path;
use crate::error::Result;
pub struct Recorder {
file: File,
failed: Option<std::io::Error>,
}
impl Recorder {
pub fn create(path: &Path, device: Option<&str>) -> Result<Self> {
let mut file = File::create(path)?;
writeln!(
file,
"# nord-usb replay script, recorded from hardware.\n\
# Format: '<O|I> <hex>' -- O = host->device, I = device->host.\n\
# source: nord"
)?;
if let Some(device) = device {
writeln!(file, "# device: {device}")?;
}
Ok(Self { file, failed: None })
}
pub fn intent(&mut self, intent: &str) {
if self.failed.is_some() {
return;
}
if let Err(e) = writeln!(self.file, "\n# intent: {intent}") {
self.failed = Some(e);
}
}
pub fn out(&mut self, bytes: &[u8]) {
self.line('O', bytes);
}
pub fn r#in(&mut self, bytes: &[u8]) {
self.line('I', bytes);
}
pub fn expect(&mut self, e: &crate::error::Error) {
if self.failed.is_some() {
return;
}
if let Err(io) = writeln!(self.file, "# expect: err {}", e.expect_kind()) {
self.failed = Some(io);
}
}
pub fn comment(&mut self, text: &str) {
if self.failed.is_some() {
return;
}
if let Err(e) = writeln!(self.file, "\n# {text}") {
self.failed = Some(e);
}
}
pub fn check(&mut self) -> Result<()> {
match self.failed.take() {
Some(e) => Err(e.into()),
None => Ok(()),
}
}
fn line(&mut self, tag: char, bytes: &[u8]) {
if self.failed.is_some() {
return;
}
let mut hex = String::with_capacity(bytes.len() * 2);
for b in bytes {
hex.push_str(&format!("{b:02x}"));
}
if let Err(e) = writeln!(self.file, "{tag} {hex}") {
self.failed = Some(e);
}
}
}