biolib/binary_format/
module_output.rs1use crate::error::BioLibError;
2
3use super::utils::IndexableBuffer;
4
5pub struct ModuleOutputV2 {
6 buffer: Box<dyn IndexableBuffer>,
7 metadata: Option<Metadata>,
8 stdout: Option<Vec<u8>>,
9 stderr: Option<Vec<u8>>,
10 files: Option<Vec<OutputFile>>,
11}
12
13struct Metadata {
14 stdout_length: u64,
15 stderr_length: u64,
16 files_info_length: u64,
17 #[allow(dead_code)]
18 files_data_length: u64,
19 exit_code: u16,
20}
21
22const METADATA_LENGTH: usize = 1 + 1 + 8 + 8 + 8 + 8 + 2; #[derive(Debug, Clone)]
25pub struct OutputFile {
26 pub path: String,
27 pub data_start: u64,
28 pub data_length: u64,
29}
30
31impl ModuleOutputV2 {
32 pub fn new(buffer: Box<dyn IndexableBuffer>) -> Self {
33 Self {
34 buffer,
35 metadata: None,
36 stdout: None,
37 stderr: None,
38 files: None,
39 }
40 }
41
42 fn get_metadata(&mut self) -> crate::Result<&Metadata> {
43 if self.metadata.is_none() {
44 let meta_bytes = self.buffer.get_data(0, METADATA_LENGTH as u64)?;
45 let mut pointer = 0usize;
46
47 let version = meta_bytes[pointer];
48 pointer += 1;
49 let pkg_type = meta_bytes[pointer];
50 pointer += 1;
51
52 if version != 1 {
53 return Err(BioLibError::BinaryFormat(format!(
54 "Version mismatch: expected 1, got {version}"
55 )));
56 }
57 if pkg_type != 11 {
58 return Err(BioLibError::BinaryFormat(format!(
59 "Type mismatch: expected 11, got {pkg_type}"
60 )));
61 }
62
63 let stdout_length =
64 u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
65 pointer += 8;
66 let stderr_length =
67 u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
68 pointer += 8;
69 let files_info_length =
70 u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
71 pointer += 8;
72 let files_data_length =
73 u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
74 pointer += 8;
75 let exit_code =
76 u16::from_be_bytes(meta_bytes[pointer..pointer + 2].try_into().unwrap());
77
78 self.metadata = Some(Metadata {
79 stdout_length,
80 stderr_length,
81 files_info_length,
82 files_data_length,
83 exit_code,
84 });
85 }
86 Ok(self.metadata.as_ref().unwrap())
87 }
88
89 pub fn get_exit_code(&mut self) -> crate::Result<u16> {
90 let metadata = self.get_metadata()?;
91 Ok(metadata.exit_code)
92 }
93
94 pub fn get_stdout(&mut self) -> crate::Result<Vec<u8>> {
95 if self.stdout.is_none() {
96 let metadata = self.get_metadata()?;
97 let start = METADATA_LENGTH as u64;
98 let length = metadata.stdout_length;
99 self.stdout = Some(self.buffer.get_data(start, length)?);
100 }
101 Ok(self.stdout.clone().unwrap())
102 }
103
104 pub fn get_stderr(&mut self) -> crate::Result<Vec<u8>> {
105 if self.stderr.is_none() {
106 let metadata = self.get_metadata()?;
107 let start = METADATA_LENGTH as u64 + metadata.stdout_length;
108 let length = metadata.stderr_length;
109 self.stderr = Some(self.buffer.get_data(start, length)?);
110 }
111 Ok(self.stderr.clone().unwrap())
112 }
113
114 pub fn get_files(&mut self) -> crate::Result<Vec<OutputFile>> {
115 if self.files.is_none() {
116 let metadata = self.get_metadata()?;
117 let files_info_length = metadata.files_info_length;
118 let stdout_length = metadata.stdout_length;
119 let stderr_length = metadata.stderr_length;
120
121 if files_info_length == 0 {
122 self.files = Some(Vec::new());
123 return Ok(self.files.clone().unwrap());
124 }
125
126 let files_info_start = METADATA_LENGTH as u64 + stdout_length + stderr_length;
127 let files_info_data = self.buffer.get_data(files_info_start, files_info_length)?;
128
129 let files_data_start = files_info_start + files_info_length;
130 let mut files = Vec::new();
131 let mut pointer = 0usize;
132 let mut data_pointer = files_data_start;
133
134 while pointer < files_info_data.len() {
135 let path_length =
136 u32::from_be_bytes(files_info_data[pointer..pointer + 4].try_into().unwrap())
137 as usize;
138 pointer += 4;
139 let path =
140 String::from_utf8_lossy(&files_info_data[pointer..pointer + path_length])
141 .to_string();
142 pointer += path_length;
143 let data_length =
144 u64::from_be_bytes(files_info_data[pointer..pointer + 8].try_into().unwrap());
145 pointer += 8;
146
147 files.push(OutputFile {
148 path,
149 data_start: data_pointer,
150 data_length,
151 });
152 data_pointer += data_length;
153 }
154
155 self.files = Some(files);
156 }
157 Ok(self.files.clone().unwrap())
158 }
159
160 pub fn get_file_data(&self, file: &OutputFile) -> crate::Result<Vec<u8>> {
161 self.buffer.get_data(file.data_start, file.data_length)
162 }
163
164 pub fn buffer(&self) -> &dyn IndexableBuffer {
165 &*self.buffer
166 }
167}