use std::io::Write;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::sync::Mutex;
use std::sync::MutexGuard;
use logforth_core::Diagnostic;
use logforth_core::Error;
use logforth_core::Layout;
use logforth_core::Trap;
use logforth_core::append::Append;
use logforth_core::layout::PlainTextLayout;
use logforth_core::record::Record;
use crate::rolling::RollingFileWriter;
use crate::rolling::RollingFileWriterBuilder;
use crate::rotation::Rotation;
#[derive(Debug)]
pub struct FileBuilder {
builder: RollingFileWriterBuilder,
layout: Box<dyn Layout>,
}
impl FileBuilder {
pub fn new(basedir: impl Into<PathBuf>, filename: impl Into<String>) -> Self {
Self {
builder: RollingFileWriterBuilder::new(basedir, filename),
layout: Box::new(PlainTextLayout::default()),
}
}
pub fn build(self) -> Result<File, Error> {
let FileBuilder { builder, layout } = self;
let writer = builder.build()?;
Ok(File::new(writer, layout))
}
pub fn layout(mut self, layout: impl Into<Box<dyn Layout>>) -> Self {
self.layout = layout.into();
self
}
pub fn trap(mut self, trap: impl Into<Box<dyn Trap>>) -> Self {
self.builder = self.builder.trap(trap);
self
}
pub fn rollover_minutely(mut self) -> Self {
self.builder = self.builder.rotation(Rotation::Minutely);
self
}
pub fn rollover_hourly(mut self) -> Self {
self.builder = self.builder.rotation(Rotation::Hourly);
self
}
pub fn rollover_daily(mut self) -> Self {
self.builder = self.builder.rotation(Rotation::Daily);
self
}
pub fn rollover_size(mut self, n: NonZeroUsize) -> Self {
self.builder = self.builder.max_file_size(n);
self
}
pub fn filename_suffix(mut self, suffix: impl Into<String>) -> Self {
self.builder = self.builder.filename_suffix(suffix);
self
}
pub fn max_log_files(mut self, n: NonZeroUsize) -> Self {
self.builder = self.builder.max_log_files(n);
self
}
}
#[derive(Debug)]
pub struct File {
writer: Mutex<RollingFileWriter>,
layout: Box<dyn Layout>,
}
impl File {
fn new(writer: RollingFileWriter, layout: Box<dyn Layout>) -> Self {
let writer = Mutex::new(writer);
Self { writer, layout }
}
fn writer(&self) -> MutexGuard<'_, RollingFileWriter> {
self.writer.lock().unwrap_or_else(|e| e.into_inner())
}
}
impl Append for File {
fn append(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<(), Error> {
let mut bytes = self.layout.format(record, diags)?;
bytes.push(b'\n');
let mut writer = self.writer();
writer.write_all(&bytes).map_err(Error::from_io_error)?;
Ok(())
}
fn flush(&self) -> Result<(), Error> {
let mut writer = self.writer();
writer.flush().map_err(Error::from_io_error)?;
Ok(())
}
}
impl Drop for File {
fn drop(&mut self) {
let writer = self.writer.get_mut().unwrap_or_else(|e| e.into_inner());
let _ = writer.flush();
}
}