use core::fmt;
use crate::record::Record;
pub trait Format: Send + Sync {
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result;
#[inline]
fn write_record_generic<W: fmt::Write>(
&self,
record: &Record<'_>,
writer: &mut W,
) -> fmt::Result
where
Self: Sized,
{
self.write_record(record, writer)
}
}
impl<T: Format> Format for &T {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
#[inline]
fn write_record_generic<W: fmt::Write>(
&self,
record: &Record<'_>,
writer: &mut W,
) -> fmt::Result {
(**self).write_record_generic(record, writer)
}
}
impl Format for &dyn Format {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
}
#[cfg(feature = "std")]
impl<T: Format> Format for std::sync::Arc<T> {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
#[inline]
fn write_record_generic<W: fmt::Write>(
&self,
record: &Record<'_>,
writer: &mut W,
) -> fmt::Result {
(**self).write_record_generic(record, writer)
}
}
#[cfg(feature = "std")]
impl Format for std::sync::Arc<dyn Format> {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
}
#[cfg(feature = "std")]
impl<T: Format> Format for Box<T> {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
#[inline]
fn write_record_generic<W: fmt::Write>(
&self,
record: &Record<'_>,
writer: &mut W,
) -> fmt::Result {
(**self).write_record_generic(record, writer)
}
}
#[cfg(feature = "std")]
impl Format for Box<dyn Format> {
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(**self).write_record(record, writer)
}
}
#[derive(Clone, Copy)]
pub struct FnFormat<F> {
f: F,
}
impl<F> fmt::Debug for FnFormat<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnFormat").finish_non_exhaustive()
}
}
impl<F> Format for FnFormat<F>
where
F: Fn(&Record<'_>, &mut dyn fmt::Write) -> fmt::Result + Send + Sync,
{
#[inline]
fn write_record(&self, record: &Record<'_>, writer: &mut dyn fmt::Write) -> fmt::Result {
(self.f)(record, writer)
}
}
pub const fn from_fn<F>(f: F) -> FnFormat<F>
where
F: Fn(&Record<'_>, &mut dyn fmt::Write) -> fmt::Result + Send + Sync,
{
FnFormat { f }
}
#[cfg(any(feature = "json", feature = "logfmt", feature = "human"))]
pub(crate) mod escape;
#[cfg(any(feature = "json", feature = "human", feature = "logfmt"))]
pub(crate) mod rfc3339;
#[cfg(feature = "json")]
mod json;
#[cfg(feature = "json")]
pub use self::json::JsonFormat;
#[cfg(feature = "logfmt")]
mod logfmt;
#[cfg(feature = "logfmt")]
pub use self::logfmt::LogfmtFormat;
#[cfg(feature = "human")]
mod human;
#[cfg(all(feature = "human", feature = "std"))]
pub use self::human::ColorChoice;
#[cfg(feature = "human")]
pub use self::human::HumanFormat;
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::{Level, Metadata};
struct Upper;
impl Format for Upper {
fn write_record(&self, record: &Record<'_>, w: &mut dyn fmt::Write) -> fmt::Result {
writeln!(w, "{}", record.metadata.level.as_str_upper())
}
}
fn record() -> Record<'static> {
Record::new(Metadata::new(Level::Info, "t"), "m", &[])
}
fn render<F: Format>(f: &F) -> String {
let mut out = String::new();
f.write_record_generic(&record(), &mut out).unwrap();
let mut dyn_out = String::new();
f.write_record(&record(), &mut dyn_out).unwrap();
assert_eq!(out, dyn_out, "static and dynamic paths must agree");
out
}
#[test]
fn test_format_adapters_forward_to_inner() {
assert_eq!(render(&Upper), "INFO\n");
assert_eq!(render(&&Upper), "INFO\n");
assert_eq!(render(&Box::new(Upper)), "INFO\n");
assert_eq!(render(&std::sync::Arc::new(Upper)), "INFO\n");
let boxed: Box<dyn Format> = Box::new(Upper);
assert_eq!(render(&boxed), "INFO\n");
let arced: std::sync::Arc<dyn Format> = std::sync::Arc::new(Upper);
assert_eq!(render(&arced), "INFO\n");
let borrowed: &dyn Format = &Upper;
assert_eq!(render(&borrowed), "INFO\n");
}
#[test]
fn test_from_fn_closure_is_a_format() {
let f = from_fn(|r: &Record<'_>, w: &mut dyn fmt::Write| writeln!(w, "<{}>", r.message));
assert_eq!(render(&f), "<m>\n");
assert!(format!("{f:?}").starts_with("FnFormat"));
}
#[test]
fn test_format_errors_propagate() {
struct Failing;
impl fmt::Write for Failing {
fn write_str(&mut self, _: &str) -> fmt::Result {
Err(fmt::Error)
}
}
assert!(Upper.write_record_generic(&record(), &mut Failing).is_err());
}
}