use crate::flexi_error::FlexiLoggerError;
use crate::formats::default_format;
use crate::{Cleanup, Criterion, FileSpec, FormatFunction, Naming, WriteMode};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use super::{Config, FileLogWriter, LogWriter, RotationConfig, State};
#[allow(clippy::module_name_repetitions)]
pub struct FileLogWriterBuilder {
cfg_print_message: bool,
cfg_append: bool,
cfg_write_mode: WriteMode,
file_spec: FileSpec,
cfg_o_create_symlink: Option<PathBuf>,
cfg_line_ending: &'static [u8],
format: FormatFunction,
o_rotation_config: Option<RotationConfig>,
max_log_level: log::LevelFilter,
cleanup_in_background_thread: bool,
}
impl FileLogWriterBuilder {
pub(crate) fn new(file_spec: FileSpec) -> FileLogWriterBuilder {
FileLogWriterBuilder {
o_rotation_config: None,
cfg_print_message: false,
file_spec,
cfg_append: false,
cfg_write_mode: WriteMode::Direct,
cfg_o_create_symlink: None,
cfg_line_ending: super::UNIX_LINE_ENDING,
format: default_format,
max_log_level: log::LevelFilter::Trace,
cleanup_in_background_thread: true,
}
}
#[must_use]
pub fn print_message(mut self) -> Self {
self.cfg_print_message = true;
self
}
pub fn format(mut self, format: FormatFunction) -> Self {
self.format = format;
self
}
#[must_use]
pub fn cleanup_in_background_thread(mut self, use_background_thread: bool) -> Self {
self.cleanup_in_background_thread = use_background_thread;
self
}
#[must_use]
pub fn rotate(mut self, criterion: Criterion, naming: Naming, cleanup: Cleanup) -> Self {
self.o_rotation_config = Some(RotationConfig {
criterion,
naming,
cleanup,
});
self.file_spec.if_default_use_timestamp(false);
self
}
#[must_use]
pub(crate) fn file_spec(mut self, mut file_spec: FileSpec) -> Self {
if self.o_rotation_config.is_some() {
file_spec.if_default_use_timestamp(false);
}
self.file_spec = file_spec;
self
}
#[must_use]
pub fn append(mut self) -> Self {
self.cfg_append = true;
self
}
pub fn create_symlink<P: Into<PathBuf>>(mut self, symlink: P) -> Self {
self.cfg_o_create_symlink = Some(symlink.into());
self
}
#[must_use]
pub fn use_windows_line_ending(mut self) -> Self {
self.cfg_line_ending = super::WINDOWS_LINE_ENDING;
self
}
#[must_use]
pub fn write_mode(mut self, write_mode: WriteMode) -> Self {
self.cfg_write_mode = write_mode;
self
}
pub(crate) fn assert_write_mode(&self, write_mode: WriteMode) -> Result<(), FlexiLoggerError> {
if self.cfg_write_mode == write_mode {
Ok(())
} else {
Err(FlexiLoggerError::Reset)
}
}
#[must_use]
pub(crate) fn get_write_mode(&self) -> &WriteMode {
&self.cfg_write_mode
}
pub fn try_build(self) -> Result<FileLogWriter, FlexiLoggerError> {
Ok(FileLogWriter::new(
self.try_build_state()?,
self.max_log_level,
self.format,
))
}
pub fn try_build_with_handle(
self,
) -> Result<(ArcFileLogWriter, FileLogWriterHandle), FlexiLoggerError> {
Ok(ArcFileLogWriter::new(FileLogWriter::new(
self.try_build_state()?,
self.max_log_level,
self.format,
)))
}
pub(crate) fn try_build_state(&self) -> Result<State, FlexiLoggerError> {
let dir = self.file_spec.get_directory();
let p_directory = Path::new(&dir);
std::fs::create_dir_all(&p_directory)?;
if !std::fs::metadata(&p_directory)?.is_dir() {
return Err(FlexiLoggerError::OutputBadDirectory);
};
#[cfg(feature = "async")]
let cleanup_in_background_thread = if let WriteMode::AsyncWith {
bufsize: _,
pool_capa: _,
message_capa: _,
flush_interval: _,
} = self.cfg_write_mode
{
false
} else {
self.cleanup_in_background_thread
};
#[cfg(not(feature = "async"))]
let cleanup_in_background_thread = self.cleanup_in_background_thread;
Ok(State::try_new(
Config {
print_message: self.cfg_print_message,
append: self.cfg_append,
line_ending: self.cfg_line_ending,
write_mode: self.cfg_write_mode,
file_spec: self.file_spec.clone(),
o_create_symlink: self.cfg_o_create_symlink.as_ref().map(Clone::clone),
},
self.o_rotation_config.as_ref().map(Clone::clone),
cleanup_in_background_thread,
))
}
}
impl FileLogWriterBuilder {
#[must_use]
pub fn o_print_message(mut self, print_message: bool) -> Self {
self.cfg_print_message = print_message;
self
}
#[must_use]
pub fn o_rotate(mut self, rotate_config: Option<(Criterion, Naming, Cleanup)>) -> Self {
if let Some((criterion, naming, cleanup)) = rotate_config {
self.o_rotation_config = Some(RotationConfig {
criterion,
naming,
cleanup,
});
self.file_spec.if_default_use_timestamp(false);
} else {
self.o_rotation_config = None;
self.file_spec.if_default_use_timestamp(true);
}
self
}
#[must_use]
pub fn o_append(mut self, append: bool) -> Self {
self.cfg_append = append;
self
}
pub fn o_create_symlink<S: Into<PathBuf>>(mut self, symlink: Option<S>) -> Self {
self.cfg_o_create_symlink = symlink.map(Into::into);
self
}
}
pub struct ArcFileLogWriter(Arc<FileLogWriter>);
impl ArcFileLogWriter {
pub(crate) fn new(flw: FileLogWriter) -> (Self, FileLogWriterHandle) {
let a_flw = Arc::new(flw);
let handle = FileLogWriterHandle(a_flw.clone());
(Self(a_flw), handle)
}
}
impl Clone for ArcFileLogWriter {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl std::io::Write for ArcFileLogWriter {
fn write(&mut self, buffer: &[u8]) -> std::result::Result<usize, std::io::Error> {
(*self.0).plain_write(buffer)
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
LogWriter::flush(&*self.0)
}
}
pub struct FileLogWriterHandle(Arc<FileLogWriter>);
impl Drop for FileLogWriterHandle {
fn drop(&mut self) {
self.0.shutdown();
}
}