use std::fs::File;
use std::io::{BufWriter, Write};
use crate::Location;
#[must_use = "call `finish()` to flush the backing file"]
pub struct FileArenaWriter {
file_index: u16,
file: File,
writer: BufWriter<File>,
cursor: usize,
}
impl FileArenaWriter {
pub fn new(file_index: u16) -> std::io::Result<Self> {
let file = tempfile::tempfile()?;
let writer = BufWriter::new(file.try_clone()?);
Ok(Self {
file_index,
file,
writer,
cursor: 0,
})
}
pub fn push(&mut self, data: impl AsRef<[u8]>) -> std::io::Result<Location> {
let data = data.as_ref();
if data.is_empty() {
return Ok(Location::new(self.file_index, self.cursor, 0));
}
let offset = self.cursor;
self.writer.write_all(data)?;
self.cursor += data.len();
Ok(Location::new(self.file_index, offset, data.len()))
}
pub fn len(&self) -> usize {
self.cursor
}
pub fn is_empty(&self) -> bool {
self.cursor == 0
}
pub fn finish(mut self) -> std::io::Result<File> {
self.writer.flush()?;
Ok(self.file)
}
pub fn into_arena(self) -> std::io::Result<crate::FileArena> {
let file = self.finish()?;
crate::FileArena::new(vec![file])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_returns_location_with_correct_file_index() {
let mut writer = FileArenaWriter::new(3).unwrap();
let loc = writer.push("hello").unwrap();
assert_eq!(loc.file_index, 3);
assert_eq!(loc.offset, 0);
assert_eq!(loc.len, 5);
}
#[test]
fn push_advances_offset() {
let mut writer = FileArenaWriter::new(0).unwrap();
let loc1 = writer.push("abc").unwrap();
let loc2 = writer.push("de").unwrap();
assert_eq!(loc1.offset, 0);
assert_eq!(loc1.len, 3);
assert_eq!(loc2.offset, 3);
assert_eq!(loc2.len, 2);
}
#[test]
fn push_empty_returns_zero_location() {
let mut writer = FileArenaWriter::new(1).unwrap();
let loc = writer.push("").unwrap();
assert_eq!(loc.len, 0);
assert_eq!(loc.offset, 0);
assert!(writer.is_empty());
}
#[test]
fn push_empty_after_data_uses_current_cursor() {
let mut writer = FileArenaWriter::new(0).unwrap();
let loc1 = writer.push("hello").unwrap();
assert_eq!(loc1.offset, 0);
assert_eq!(loc1.len, 5);
assert_eq!(writer.len(), 5);
let loc2 = writer.push("").unwrap();
assert_eq!(loc2.len, 0);
assert_eq!(loc2.offset, 5);
let loc3 = writer.push("world").unwrap();
assert_eq!(loc3.offset, 5);
assert_eq!(loc3.len, 5);
}
#[test]
fn finish_empty_writer_returns_file() {
let writer = FileArenaWriter::new(0).unwrap();
let file = writer.finish().unwrap();
assert_eq!(file.metadata().unwrap().len(), 0);
}
#[test]
fn finish_returns_file_after_push() {
let mut writer = FileArenaWriter::new(0).unwrap();
writer.push("data").unwrap();
let file = writer.finish().unwrap();
assert_eq!(file.metadata().unwrap().len(), 4);
}
}