Skip to main content

biolib/binary_format/
module_output.rs

1use 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; // 36 bytes
23
24#[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}