use std::{
convert::Infallible,
fs::File,
io::{BufWriter, Write as _},
path::{Path, PathBuf},
};
use crate::{
formatter::{Formatter, FormatterContext},
sink::{GetSinkProp, Sink, SinkProp},
sync::*,
utils, Error, ErrorHandler, LevelFilter, Record, Result, StringBuf,
};
pub struct FileSink {
prop: SinkProp,
file: Mutex<BufWriter<File>>,
}
impl FileSink {
#[must_use]
pub fn builder() -> FileSinkBuilder<()> {
FileSinkBuilder {
prop: SinkProp::default(),
path: (),
truncate: false,
capacity: None,
}
}
#[deprecated(
since = "0.3.0",
note = "it may be removed in the future, use `FileSink::builder()` instead"
)]
pub fn new<P>(path: P, truncate: bool) -> Result<FileSink>
where
P: AsRef<Path>,
{
Self::builder()
.path(path.as_ref())
.truncate(truncate)
.build()
}
}
impl GetSinkProp for FileSink {
fn prop(&self) -> &SinkProp {
&self.prop
}
}
impl Sink for FileSink {
fn log(&self, record: &Record) -> Result<()> {
let mut string_buf = StringBuf::new();
let mut ctx = FormatterContext::new();
self.prop
.formatter()
.format(record, &mut string_buf, &mut ctx)?;
self.file
.lock_expect()
.write_all(string_buf.as_bytes())
.map_err(Error::WriteRecord)?;
Ok(())
}
fn flush(&self) -> Result<()> {
self.file.lock_expect().flush().map_err(Error::FlushBuffer)
}
}
impl Drop for FileSink {
fn drop(&mut self) {
if let Err(err) = self.file.lock_expect().flush() {
self.prop
.call_error_handler_internal("FileSink", Error::FlushBuffer(err))
}
}
}
#[doc = include_str!("../include/doc/generic-builder-note.md")]
pub struct FileSinkBuilder<ArgPath> {
prop: SinkProp,
path: ArgPath,
truncate: bool,
capacity: Option<usize>,
}
impl<ArgPath> FileSinkBuilder<ArgPath> {
#[must_use]
pub fn path<P>(self, path: P) -> FileSinkBuilder<PathBuf>
where
P: Into<PathBuf>,
{
FileSinkBuilder {
prop: self.prop,
path: path.into(),
truncate: self.truncate,
capacity: self.capacity,
}
}
#[must_use]
pub fn truncate(mut self, truncate: bool) -> Self {
self.truncate = truncate;
self
}
#[must_use]
pub fn capacity(mut self, capacity: usize) -> Self {
self.capacity = Some(capacity);
self
}
#[must_use]
pub fn level_filter(self, level_filter: LevelFilter) -> Self {
self.prop.set_level_filter(level_filter);
self
}
#[must_use]
pub fn formatter<F>(self, formatter: F) -> Self
where
F: Formatter + 'static,
{
self.prop.set_formatter(formatter);
self
}
#[must_use]
pub fn error_handler<F: Into<ErrorHandler>>(self, handler: F) -> Self {
self.prop.set_error_handler(handler);
self
}
}
impl FileSinkBuilder<()> {
#[doc(hidden)]
#[deprecated(note = "\n\n\
builder compile-time error:\n\
- missing required parameter `path`\n\n\
")]
pub fn build(self, _: Infallible) {}
#[doc(hidden)]
#[deprecated(note = "\n\n\
builder compile-time error:\n\
- missing required parameter `path`\n\n\
")]
pub fn build_arc(self, _: Infallible) {}
}
impl FileSinkBuilder<PathBuf> {
pub fn build(self) -> Result<FileSink> {
let file = utils::open_file_bufw(self.path, self.truncate, self.capacity)?;
let sink = FileSink {
prop: self.prop,
file: Mutex::new(file),
};
Ok(sink)
}
pub fn build_arc(self) -> Result<Arc<FileSink>> {
self.build().map(Arc::new)
}
}