use alloc::vec::Vec;
use std::fs::{File, OpenOptions};
use std::io::{self, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::Path;
use crate::codec;
use crate::error::SinkError;
use crate::record::Record;
use crate::sink::Sink;
pub struct FileSink<W: Write> {
writer: W,
scratch: Vec<u8>,
}
impl FileSink<BufWriter<File>> {
pub fn open_or_create(path: impl AsRef<Path>) -> io::Result<Self> {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(path)?;
let len = file.seek(SeekFrom::End(0))?;
if len == 0 {
let _ = file.seek(SeekFrom::Start(0))?;
let mut header = Vec::with_capacity(codec::FILE_HEADER_LEN);
codec::write_file_header(&mut header);
file.write_all(&header)?;
let _ = file.seek(SeekFrom::End(0))?;
} else {
let _ = file.seek(SeekFrom::Start(0))?;
let mut header = [0u8; codec::FILE_HEADER_LEN];
file.read_exact(&mut header)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "missing audit header"))?;
codec::verify_file_header(&header)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid audit header"))?;
let _ = file.seek(SeekFrom::End(0))?;
}
Ok(Self::new(BufWriter::new(file)))
}
}
impl<W: Write> FileSink<W> {
#[inline]
pub fn new(writer: W) -> Self {
Self {
writer,
scratch: Vec::with_capacity(256),
}
}
#[inline]
pub fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
#[inline]
pub fn into_writer(self) -> W {
self.writer
}
}
impl<W: Write> Sink for FileSink<W> {
fn write(&mut self, record: &Record<'_>) -> core::result::Result<(), SinkError> {
self.scratch.clear();
codec::encode_record(record, &mut self.scratch).map_err(|_| SinkError::Other)?;
self.writer
.write_all(&self.scratch)
.map_err(|_| SinkError::Io)?;
Ok(())
}
}