use core::fmt;
use std::io::Write;
use std::sync::{Mutex, MutexGuard};
use crate::error::{Error, Result};
use crate::format::Format;
use crate::record::Record;
pub trait Sink: Send + Sync {
fn write_record(&self, record: &Record<'_>) -> Result<()>;
fn flush(&self) -> Result<()>;
}
impl<S: Sink + ?Sized> Sink for std::sync::Arc<S> {
fn write_record(&self, record: &Record<'_>) -> Result<()> {
(**self).write_record(record)
}
fn flush(&self) -> Result<()> {
(**self).flush()
}
}
impl<S: Sink + ?Sized> Sink for Box<S> {
fn write_record(&self, record: &Record<'_>) -> Result<()> {
(**self).write_record(record)
}
fn flush(&self) -> Result<()> {
(**self).flush()
}
}
mod stdio;
mod writer;
pub use self::stdio::{StderrSink, StdoutSink};
pub use self::writer::{FileSink, NullSink, WriterSink};
thread_local! {
static FORMAT_BUF: core::cell::RefCell<String> = const {
core::cell::RefCell::new(String::new())
};
}
fn lock_recover<T>(m: &Mutex<T>) -> MutexGuard<'_, T> {
match m.lock() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
}
}
pub(crate) fn write_locked<W: Write, F: Format>(
target: &Mutex<W>,
format: &F,
record: &Record<'_>,
) -> Result<()> {
FORMAT_BUF.with(|cell| {
if let Ok(mut buf) = cell.try_borrow_mut() {
buf.clear();
format
.write_record(record, &mut *buf)
.map_err(|e: fmt::Error| Error::Format(e))?;
let mut guard = lock_recover(target);
guard.write_all(buf.as_bytes()).map_err(Error::from)
} else {
let mut buf = String::with_capacity(128);
format
.write_record(record, &mut buf)
.map_err(|e: fmt::Error| Error::Format(e))?;
let mut guard = lock_recover(target);
guard.write_all(buf.as_bytes()).map_err(Error::from)
}
})
}
pub(crate) fn flush_locked<W: Write>(target: &Mutex<W>) -> Result<()> {
let mut guard = lock_recover(target);
guard.flush().map_err(Error::from)
}