use std::fmt;
pub fn indented<D: fmt::Display>(data: D) -> Indented<D, Space4> {
Indented { data, indent: Space4 }
}
pub fn indented_with<D, I>(data: D, indent: I) -> Indented<D, I>
where
D: fmt::Display,
I: Indent,
{
Indented { data, indent }
}
pub struct Indented<D, I>
where
D: fmt::Display,
I: Indent,
{
data: D,
indent: I,
}
impl<D, I> fmt::Display for Indented<D, I>
where
D: fmt::Display,
I: Indent,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = IndentedFormatter::new(f, &self.indent);
fmt::write(&mut f, format_args!("{}", self.data))?;
Ok(())
}
}
pub trait Indent {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result;
}
impl Indent for str {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_str(self)
}
}
impl<'a, T: Indent + ?Sized> Indent for &'a T {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
(*self).indent(output)
}
}
impl Indent for char {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_char(*self)
}
}
pub struct Space2;
impl Indent for Space2 {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_str(" ")
}
}
pub struct Space4;
impl Indent for Space4 {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_str(" ")
}
}
pub struct Space8;
impl Indent for Space8 {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_str(" ")
}
}
pub struct Tab;
impl Indent for Tab {
fn indent<W: fmt::Write>(&self, output: &mut W) -> fmt::Result {
output.write_char('\t')
}
}
struct IndentedFormatter<'a, W, I>
where
W: 'a + fmt::Write + ?Sized,
I: 'a + Indent + ?Sized,
{
newline: bool,
output: &'a mut W,
indent: &'a I,
}
impl<'a, W, I> IndentedFormatter<'a, W, I>
where
W: 'a + fmt::Write + ?Sized,
I: 'a + Indent + ?Sized,
{
fn new(output: &'a mut W, indent: &'a I) -> Self {
IndentedFormatter {
newline: true,
output,
indent,
}
}
}
impl<'a, W, I> fmt::Write for IndentedFormatter<'a, W, I>
where
W: 'a + fmt::Write,
I: 'a + Indent + ?Sized,
{
fn write_str(&mut self, s: &str) -> fmt::Result {
for c in s.chars() {
if c == '\n' {
self.output.write_char(c)?;
self.newline = true;
continue
}
if self.newline {
self.indent.indent(self.output)?;
}
self.output.write_char(c)?;
self.newline = false;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::indented;
#[test]
fn single_line() {
assert_eq!(format!("{}", indented("hello")), " hello");
}
}