use std::io::{self, Write};
use super::{Sink, with_formatted};
use crate::error::{Error, Result};
use crate::format::Format;
use crate::record::Record;
#[cfg_attr(all(feature = "logfmt"), doc = "```")]
#[cfg_attr(not(all(feature = "logfmt")), doc = "```ignore")]
pub struct StdoutSink<F: Format> {
format: F,
}
impl<F: Format> StdoutSink<F> {
pub const fn new(format: F) -> Self {
Self { format }
}
}
impl<F: Format> Sink for StdoutSink<F> {
fn write_record(&self, record: &Record<'_>) -> Result<()> {
with_formatted(&self.format, record, |text| {
io::stdout().lock().write_all(text.as_bytes())
})
}
fn flush(&self) -> Result<()> {
io::stdout().lock().flush().map_err(Error::from)
}
}
impl<F: Format + core::fmt::Debug> core::fmt::Debug for StdoutSink<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StdoutSink")
.field("format", &self.format)
.finish()
}
}
#[cfg_attr(all(feature = "human"), doc = "```")]
#[cfg_attr(not(all(feature = "human")), doc = "```ignore")]
pub struct StderrSink<F: Format> {
format: F,
}
impl<F: Format> StderrSink<F> {
pub const fn new(format: F) -> Self {
Self { format }
}
}
impl<F: Format> Sink for StderrSink<F> {
fn write_record(&self, record: &Record<'_>) -> Result<()> {
with_formatted(&self.format, record, |text| {
io::stderr().lock().write_all(text.as_bytes())
})
}
fn flush(&self) -> Result<()> {
io::stderr().lock().flush().map_err(Error::from)
}
}
impl<F: Format + core::fmt::Debug> core::fmt::Debug for StderrSink<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("StderrSink")
.field("format", &self.format)
.finish()
}
}
#[cfg(all(test, feature = "json"))]
mod tests {
use super::*;
use crate::format::JsonFormat;
use crate::{Level, Metadata};
#[test]
fn test_stdio_sinks_write_and_flush() {
let record = Record::new(Metadata::new(Level::Info, "stdio-test"), "smoke", &[]);
let out = StdoutSink::new(JsonFormat::new());
out.write_record(&record).unwrap();
out.flush().unwrap();
out.shutdown().unwrap();
let err = StderrSink::new(JsonFormat::new());
err.write_record(&record).unwrap();
err.flush().unwrap();
assert!(format!("{out:?}").starts_with("StdoutSink"));
assert!(format!("{err:?}").starts_with("StderrSink"));
}
}