use crate::error::BioLibError;
use super::utils::IndexableBuffer;
pub struct ModuleOutputV2 {
buffer: Box<dyn IndexableBuffer>,
metadata: Option<Metadata>,
stdout: Option<Vec<u8>>,
stderr: Option<Vec<u8>>,
files: Option<Vec<OutputFile>>,
}
struct Metadata {
stdout_length: u64,
stderr_length: u64,
files_info_length: u64,
#[allow(dead_code)]
files_data_length: u64,
exit_code: u16,
}
const METADATA_LENGTH: usize = 1 + 1 + 8 + 8 + 8 + 8 + 2;
#[derive(Debug, Clone)]
pub struct OutputFile {
pub path: String,
pub data_start: u64,
pub data_length: u64,
}
impl ModuleOutputV2 {
pub fn new(buffer: Box<dyn IndexableBuffer>) -> Self {
Self {
buffer,
metadata: None,
stdout: None,
stderr: None,
files: None,
}
}
fn get_metadata(&mut self) -> crate::Result<&Metadata> {
if self.metadata.is_none() {
let meta_bytes = self.buffer.get_data(0, METADATA_LENGTH as u64)?;
let mut pointer = 0usize;
let version = meta_bytes[pointer];
pointer += 1;
let pkg_type = meta_bytes[pointer];
pointer += 1;
if version != 1 {
return Err(BioLibError::BinaryFormat(format!(
"Version mismatch: expected 1, got {version}"
)));
}
if pkg_type != 11 {
return Err(BioLibError::BinaryFormat(format!(
"Type mismatch: expected 11, got {pkg_type}"
)));
}
let stdout_length =
u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
pointer += 8;
let stderr_length =
u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
pointer += 8;
let files_info_length =
u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
pointer += 8;
let files_data_length =
u64::from_be_bytes(meta_bytes[pointer..pointer + 8].try_into().unwrap());
pointer += 8;
let exit_code =
u16::from_be_bytes(meta_bytes[pointer..pointer + 2].try_into().unwrap());
self.metadata = Some(Metadata {
stdout_length,
stderr_length,
files_info_length,
files_data_length,
exit_code,
});
}
Ok(self.metadata.as_ref().unwrap())
}
pub fn get_exit_code(&mut self) -> crate::Result<u16> {
let metadata = self.get_metadata()?;
Ok(metadata.exit_code)
}
pub fn get_stdout(&mut self) -> crate::Result<Vec<u8>> {
if self.stdout.is_none() {
let metadata = self.get_metadata()?;
let start = METADATA_LENGTH as u64;
let length = metadata.stdout_length;
self.stdout = Some(self.buffer.get_data(start, length)?);
}
Ok(self.stdout.clone().unwrap())
}
pub fn get_stderr(&mut self) -> crate::Result<Vec<u8>> {
if self.stderr.is_none() {
let metadata = self.get_metadata()?;
let start = METADATA_LENGTH as u64 + metadata.stdout_length;
let length = metadata.stderr_length;
self.stderr = Some(self.buffer.get_data(start, length)?);
}
Ok(self.stderr.clone().unwrap())
}
pub fn get_files(&mut self) -> crate::Result<Vec<OutputFile>> {
if self.files.is_none() {
let metadata = self.get_metadata()?;
let files_info_length = metadata.files_info_length;
let stdout_length = metadata.stdout_length;
let stderr_length = metadata.stderr_length;
if files_info_length == 0 {
self.files = Some(Vec::new());
return Ok(self.files.clone().unwrap());
}
let files_info_start = METADATA_LENGTH as u64 + stdout_length + stderr_length;
let files_info_data = self.buffer.get_data(files_info_start, files_info_length)?;
let files_data_start = files_info_start + files_info_length;
let mut files = Vec::new();
let mut pointer = 0usize;
let mut data_pointer = files_data_start;
while pointer < files_info_data.len() {
let path_length =
u32::from_be_bytes(files_info_data[pointer..pointer + 4].try_into().unwrap())
as usize;
pointer += 4;
let path =
String::from_utf8_lossy(&files_info_data[pointer..pointer + path_length])
.to_string();
pointer += path_length;
let data_length =
u64::from_be_bytes(files_info_data[pointer..pointer + 8].try_into().unwrap());
pointer += 8;
files.push(OutputFile {
path,
data_start: data_pointer,
data_length,
});
data_pointer += data_length;
}
self.files = Some(files);
}
Ok(self.files.clone().unwrap())
}
pub fn get_file_data(&self, file: &OutputFile) -> crate::Result<Vec<u8>> {
self.buffer.get_data(file.data_start, file.data_length)
}
pub fn buffer(&self) -> &dyn IndexableBuffer {
&*self.buffer
}
}