use std::{
error::Error,
fs::File,
io::{Read, Seek, SeekFrom, Write},
};
use flatbuffers::WIPOffset;
use uuid::Uuid;
use crate::{
root_as_footer, ContentType, EmbeddedFile, EmbeddedFileArgs, Footer, FooterArgs, POD5_VERSION,
SIGNATURE, SOFTWARE,
};
const FOOTER_MAGIC: &[u8; 8] = b"FOOTER\0\0";
pub struct FileInfo {
pub offset: u64,
pub length: u64,
}
pub fn read_pod5_footer(filename: &str, table: ContentType) -> FileInfo {
let mut file = File::open(filename).unwrap();
let _end = file.seek(SeekFrom::End(0)).unwrap();
file.seek(SeekFrom::Current(-32)).unwrap(); let mut buffer = [0; 8];
file.read_exact(&mut buffer).unwrap();
let value = i64::from_le_bytes(buffer);
file.seek(SeekFrom::Current(-(8 + value))).unwrap();
let mut buf = vec![0; value as usize];
file.read_exact(&mut buf).unwrap();
let footer = root_as_footer(&buf).unwrap();
let x: Vec<EmbeddedFile> = footer
.contents()
.unwrap()
.iter()
.filter(|x| x.content_type() == table)
.collect();
FileInfo {
offset: x[0].offset() as u64,
length: x[0].length() as u64,
}
}
pub fn write_flatbuffer_footer(
file_handle: &mut File,
embedded_args: Vec<&EmbeddedFileArgs>,
file_identifer: Uuid,
section_marker: &[u8],
) -> Result<(), Box<dyn Error>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let file_identifier = builder.create_string(&file_identifer.to_string());
let software = builder.create_string(SOFTWARE);
let pod5_version = builder.create_string(POD5_VERSION);
let mut embedded_files: Vec<WIPOffset<EmbeddedFile>> = Vec::new();
for embedded in embedded_args {
let embedded_file = EmbeddedFile::create(&mut builder, embedded);
embedded_files.push(embedded_file);
}
let contents = builder.create_vector(&embedded_files);
let footer = Footer::create(
&mut builder,
&FooterArgs {
file_identifier: Some(file_identifier),
software: Some(software),
pod5_version: Some(pod5_version),
contents: Some(contents),
},
);
file_handle.write_all(FOOTER_MAGIC).unwrap();
builder.finish(footer, None);
let current_pos = file_handle.stream_position().unwrap();
let offset = current_pos;
let buf = builder.finished_data();
file_handle.write_all(buf)?;
let current_pos = file_handle.stream_position()?;
let padding_needed = (8 - (current_pos % 8)) % 8;
for _ in 0..padding_needed {
file_handle.write_all(&[0])?;
}
let footer_length: i64 = file_handle.stream_position().unwrap() as i64 - offset as i64;
file_handle.write_all(&footer_length.to_le_bytes())?;
file_handle.write_all(section_marker)?;
file_handle.write_all(&SIGNATURE)?;
file_handle.flush()?;
Ok(())
}