use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Error, Result};
use crate::{FileArena, Location};
pub struct FileArenaBuilder {
files: BTreeMap<u16, File>,
}
impl FileArenaBuilder {
#[must_use]
pub fn new() -> Self {
Self {
files: BTreeMap::new(),
}
}
pub fn add(&mut self, file: File, location: Location) {
self.files.insert(location.file_index(), file);
}
pub fn build(self) -> Result<FileArena> {
if self.files.is_empty() {
return Err(Error::other("FileArenaBuilder::build: no files were added"));
}
let n = self.files.len();
let mut files = Vec::with_capacity(n);
for (i, (index, file)) in self.files.into_iter().enumerate() {
let expected = u16::try_from(i).expect("file index exceeds u16 range");
if index != expected {
return Err(Error::other(format!(
"FileArenaBuilder::build: gap in file indices at \
position {expected}, found index {index} — \
indices must be contiguous from 0",
)));
}
files.push(file);
}
FileArena::new(files)
}
}
impl Default for FileArenaBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FileArenaWriter;
#[test]
fn builder_single_file() {
let mut writer = FileArenaWriter::new(0).unwrap();
let loc = writer.push("hello").unwrap();
let file = writer.finish().unwrap();
let mut builder = FileArenaBuilder::new();
builder.add(file, loc);
let arena = builder.build().unwrap();
assert_eq!(arena.get(loc).unwrap(), b"hello");
}
#[test]
fn builder_multiple_writers_reverse_order() {
let mut w0 = FileArenaWriter::new(0).unwrap();
let loc0 = w0.push("first").unwrap();
let f0 = w0.finish().unwrap();
let mut w1 = FileArenaWriter::new(1).unwrap();
let loc1 = w1.push("second").unwrap();
let f1 = w1.finish().unwrap();
let mut builder = FileArenaBuilder::new();
builder.add(f1, loc1);
builder.add(f0, loc0);
let arena = builder.build().unwrap();
assert_eq!(arena.get(loc0).unwrap(), b"first");
assert_eq!(arena.get(loc1).unwrap(), b"second");
}
#[test]
fn builder_rejects_gap_in_indices() {
let mut w0 = FileArenaWriter::new(0).unwrap();
let loc0 = w0.push("a").unwrap();
let f0 = w0.finish().unwrap();
let mut w2 = FileArenaWriter::new(2).unwrap();
let loc2 = w2.push("c").unwrap();
let f2 = w2.finish().unwrap();
let mut builder = FileArenaBuilder::new();
builder.add(f0, loc0);
builder.add(f2, loc2);
let err_msg = match builder.build() {
Ok(_) => panic!("expected error"),
Err(e) => e.to_string(),
};
assert!(err_msg.contains("gap"));
}
#[test]
fn builder_rejects_empty() {
let builder = FileArenaBuilder::new();
let err_msg = match builder.build() {
Ok(_) => panic!("expected error"),
Err(e) => e.to_string(),
};
assert!(err_msg.contains("no files"));
}
#[test]
fn builder_default_impl() {
let builder = FileArenaBuilder::default();
let err_msg = match builder.build() {
Ok(_) => panic!("expected error"),
Err(e) => e.to_string(),
};
assert!(err_msg.contains("no files"));
}
}