use std::io::{Read, Write};
use crate::error::Result;
use crate::index::lucene::codec::data_output::CodecOutput;
use crate::index::lucene::compound::strip_segment_name;
const DATA_CODEC: &str = "CompoundFileWriterData";
const ENTRY_CODEC: &str = "CompoundFileWriterEntries";
const VERSION_CURRENT: i32 = 0;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompoundEntry {
pub name: String,
pub offset: u64,
pub length: u64,
}
pub struct CompoundFileWriter<Sink: Write> {
data: CodecOutput<Sink>,
entries: Vec<CompoundEntry>,
}
impl<Sink: Write> CompoundFileWriter<Sink> {
pub fn new(sink: Sink) -> Result<Self> {
let mut data = CodecOutput::new(sink);
data.write_header(DATA_CODEC, VERSION_CURRENT)?;
Ok(Self {
data,
entries: Vec::new(),
})
}
pub fn add(&mut self, name: &str, source: &mut impl Read) -> Result<()> {
let offset = self.data.position();
let mut buffer = vec![0u8; 64 * 1024];
loop {
let read = source.read(&mut buffer)?;
if read == 0 {
break;
}
self.data.write_bytes(&buffer[..read])?;
}
self.entries.push(CompoundEntry {
name: strip_segment_name(name).to_owned(),
offset,
length: self.data.position() - offset,
});
Ok(())
}
pub fn finish(self) -> (Sink, Vec<CompoundEntry>) {
(self.data.into_inner(), self.entries)
}
}
pub fn write_entry_table<Sink: Write>(sink: Sink, entries: &[CompoundEntry]) -> Result<Sink> {
let mut output = CodecOutput::new(sink);
output.write_header(ENTRY_CODEC, VERSION_CURRENT)?;
output.write_vint(entries.len() as i32)?;
for entry in entries {
output.write_string(&entry.name)?;
output.write_long(entry.offset as i64)?;
output.write_long(entry.length as i64)?;
}
Ok(output.into_inner())
}