1use crate::error::Result;
2use airbender_codec::{AirbenderCodec, AirbenderCodecV0};
3use airbender_core::wire::frame_words_from_bytes;
4use std::fmt::Write as _;
5use std::path::Path;
6
7#[derive(Clone, Debug, Default)]
9pub struct Inputs {
10 words: Vec<u32>,
11}
12
13impl Inputs {
14 pub fn new() -> Self {
15 Self { words: Vec::new() }
16 }
17
18 pub fn push<T: serde::Serialize>(&mut self, value: &T) -> Result<()> {
20 let bytes = AirbenderCodecV0::encode(value)?;
21 self.push_bytes(&bytes)?;
22 Ok(())
23 }
24
25 pub fn push_bytes(&mut self, bytes: &[u8]) -> Result<()> {
27 let words = frame_words_from_bytes(bytes)?;
28 self.words.extend(words);
29 Ok(())
30 }
31
32 pub fn words(&self) -> &[u32] {
34 &self.words
35 }
36
37 pub fn write_hex_file(&self, path: impl AsRef<Path>) -> Result<()> {
39 let mut hex = String::new();
40 for word in &self.words {
41 writeln!(&mut hex, "{word:08x}").expect("writing to string cannot fail");
42 }
43 std::fs::write(path, hex)?;
44 Ok(())
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::Inputs;
51 use std::fs;
52 use std::path::PathBuf;
53 use std::time::{SystemTime, UNIX_EPOCH};
54
55 #[test]
56 fn writes_hex_file_with_one_word_per_line() {
57 let mut inputs = Inputs::new();
58 inputs.push_bytes(&[0x29]).expect("frame input bytes");
59
60 let file_path = test_file_path("inputs-hex");
61 if let Some(parent) = file_path.parent() {
62 fs::create_dir_all(parent).expect("create test parent directory");
63 }
64
65 inputs
66 .write_hex_file(&file_path)
67 .expect("write input hex file");
68
69 let written = fs::read_to_string(&file_path).expect("read written input hex file");
70 assert_eq!(written, "00000001\n29000000\n");
71
72 fs::remove_file(&file_path).expect("remove input hex file");
73 }
74
75 #[test]
76 fn serializes_small_u32_as_varint_payload() {
77 let mut inputs = Inputs::new();
78 inputs.push(&10u32).expect("frame input value");
79
80 assert_eq!(inputs.words(), &[1, 0x0a000000]);
84 }
85
86 fn test_file_path(prefix: &str) -> PathBuf {
87 let timestamp = SystemTime::now()
88 .duration_since(UNIX_EPOCH)
89 .expect("system clock should be after unix epoch")
90 .as_nanos();
91 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
92 .join("..")
93 .join("..")
94 .join("tmp")
95 .join(format!("{prefix}-{timestamp}-{}", std::process::id()))
96 .join("input.hex")
97 }
98}