use std::{convert::Infallible, io::Write, marker::PhantomData};
use crate::{
sink::{helper, Sink},
sync::*,
Error, Record, Result, StringBuf,
};
pub struct WriteSink<W>
where
W: Write + Send,
{
common_impl: helper::CommonImpl,
target: Mutex<W>,
}
impl<W> WriteSink<W>
where
W: Write + Send,
{
#[must_use]
pub fn builder() -> WriteSinkBuilder<W, ()> {
WriteSinkBuilder {
common_builder_impl: helper::CommonBuilderImpl::new(),
target: None,
_phantom: PhantomData,
}
}
#[must_use]
pub fn with_target<F, R>(&self, callback: F) -> R
where
F: FnOnce(&mut W) -> R,
{
callback(&mut *self.lock_target())
}
fn lock_target(&self) -> MutexGuard<W> {
self.target.lock_expect()
}
}
impl<W> WriteSink<W>
where
W: Write + Send + Clone,
{
#[must_use]
pub fn clone_target(&self) -> W {
self.lock_target().clone()
}
}
impl<W> Sink for WriteSink<W>
where
W: Write + Send,
{
fn log(&self, record: &Record) -> Result<()> {
if !self.should_log(record.level()) {
return Ok(());
}
let mut string_buf = StringBuf::new();
self.common_impl
.formatter
.read()
.format(record, &mut string_buf)?;
self.lock_target()
.write_all(string_buf.as_bytes())
.map_err(Error::WriteRecord)?;
Ok(())
}
fn flush(&self) -> Result<()> {
self.lock_target().flush().map_err(Error::FlushBuffer)
}
helper::common_impl!(@Sink: common_impl);
}
impl<W> Drop for WriteSink<W>
where
W: Write + Send,
{
fn drop(&mut self) {
let flush_result = self.lock_target().flush().map_err(Error::FlushBuffer);
if let Err(err) = flush_result {
self.common_impl.non_returnable_error("WriteSink", err)
}
}
}
#[doc = include_str!("../include/doc/generic-builder-note.md")]
pub struct WriteSinkBuilder<W, ArgW> {
common_builder_impl: helper::CommonBuilderImpl,
target: Option<W>,
_phantom: PhantomData<ArgW>,
}
impl<W, ArgW> WriteSinkBuilder<W, ArgW>
where
W: Write + Send,
{
#[must_use]
pub fn target(self, target: W) -> WriteSinkBuilder<W, PhantomData<W>> {
WriteSinkBuilder {
common_builder_impl: self.common_builder_impl,
target: Some(target),
_phantom: PhantomData,
}
}
helper::common_impl!(@SinkBuilder: common_builder_impl);
}
impl<W> WriteSinkBuilder<W, ()>
where
W: Write + Send,
{
#[doc(hidden)]
#[deprecated(note = "\n\n\
builder compile-time error:\n\
- missing required field `target`\n\n\
")]
pub fn build(self, _: Infallible) {}
}
impl<W> WriteSinkBuilder<W, PhantomData<W>>
where
W: Write + Send,
{
pub fn build(self) -> Result<WriteSink<W>> {
let sink = WriteSink {
common_impl: helper::CommonImpl::from_builder(self.common_builder_impl),
target: Mutex::new(self.target.unwrap()),
};
Ok(sink)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{prelude::*, test_utils::*};
#[test]
fn validation() {
let sink = Arc::new(WriteSink::builder().target(Vec::new()).build().unwrap());
sink.set_formatter(Box::new(NoModFormatter::new()));
let logger = build_test_logger(|b| b.sink(sink.clone()).level_filter(LevelFilter::All));
info!(logger: logger, "hello WriteSink");
let data = sink.clone_target();
assert_eq!(data.as_slice(), b"hello WriteSink");
}
}