use crate::common::{CRC_SZ, Error, HEADER, Id, Result, checksum};
use bincode::Options;
use serde::Serialize;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;
pub struct Writer {
file: File,
}
impl Writer {
pub fn new<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
{
let mut file = OpenOptions::new()
.create_new(true)
.append(true)
.open(path)?;
let len = file.metadata()?.len();
if len > 0 && len < 8 {
return Err(Error::InvalidHeader);
}
if len == 0 {
file.write_all(HEADER)?;
}
Ok(Self { file })
}
pub fn push<D>(&mut self, data: &D) -> Result<Id>
where
D: Serialize,
{
let len = self.file.metadata()?.len();
let id = Id::new(len);
let options = bincode::DefaultOptions::new()
.with_little_endian()
.with_varint_encoding();
let len = options.serialized_size(data)? as usize;
let lenlen = options.serialized_size(&len)? as usize;
let mut buf = Vec::with_capacity(lenlen + len + CRC_SZ);
options.serialize_into(&mut buf, &len)?;
options.serialize_into(&mut buf, &data)?;
if let Some(checksum) = checksum(&buf) {
buf.extend(checksum.to_le_bytes());
}
self.file.write_all(&buf)?;
Ok(id)
}
pub fn checkpoint(&mut self, id: Id) -> Result<()> {
self.push(&id.to_le_bytes())?;
self.file.sync_data()?;
Ok(())
}
}