how.rs/src/report.rs

88 lines
2.0 KiB
Rust

#[derive(Debug, Clone, Copy)]
pub struct ReportFmt {
indentation: usize,
is_first: bool,
indent_first: bool,
}
impl ReportFmt {
#[inline]
pub fn indent(mut self) -> Self {
self.indentation += 1;
self
}
#[inline]
pub fn next(mut self) -> Self {
self.is_first = false;
self
}
#[inline]
pub fn indent_first(mut self, indent_first: bool) -> Self {
self.indent_first = indent_first;
self
}
/// Returns the amount of indentation.
#[inline]
pub fn indentation(&self) -> usize {
self.indentation
}
#[inline]
pub fn should_indent(&self) -> bool {
self.indent_first || !self.is_first
}
}
impl Default for ReportFmt {
#[inline]
fn default() -> Self {
Self {
indentation: 0,
indent_first: true,
is_first: true,
}
}
}
macro_rules! report_write {
($f:expr, $opts:expr, $msg:literal$(, $($tt:tt)+)?) => {
<std::fmt::Arguments<'_> as $crate::Report>::fmt(&format_args!($msg$(, $($tt)+)?), $f, $opts)
};
}
pub(crate) use report_write;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Indentation(pub usize);
impl std::fmt::Display for Indentation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
for _ in 0..self.0 {
f.write_char(' ')?;
}
Ok(())
}
}
/// A more flexible formatting type that is a cross between [`std::fmt::Debug`] and
/// [`std::fmt::Display`].
pub trait Report {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, opts: &ReportFmt) -> std::fmt::Result;
}
impl<T> Report for T
where
T: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, opts: &ReportFmt) -> std::fmt::Result {
if opts.should_indent() {
write!(f, "{}", Indentation(opts.indentation()))?;
}
<T as std::fmt::Display>::fmt(self, f)?;
Ok(())
}
}