indent_write/fmt.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
use core::fmt;
/// Adapter for writers to indent each line
///
/// An `IndentWriter` adapts a [`fmt::Write`] object to insert an indent before
/// each non-empty line. Specifically, this means it will insert an indent
/// between each newline when followed by a non-newline.
///
/// These writers can be nested to provide increasing levels of indentation.
///
/// # Example
///
/// ```
/// # use std::fmt::Write;
/// use indent_write::fmt::IndentWriter;
///
/// let output = String::new();
///
/// let mut indented = IndentWriter::new("\t", output);
///
/// // Lines will be indented
/// write!(indented, "Line 1\nLine 2\n");
///
/// // Empty lines will not be indented
/// write!(indented, "\n\nLine 3\n\n");
///
/// assert_eq!(indented.get_ref(), "\tLine 1\n\tLine 2\n\n\n\tLine 3\n\n");
/// ```
#[derive(Debug, Clone)]
pub struct IndentWriter<'i, W> {
writer: W,
indent: &'i str,
need_indent: bool,
}
impl<'i, W: fmt::Write> IndentWriter<'i, W> {
/// Create a new [`IndentWriter`].
#[inline]
pub fn new(indent: &'i str, writer: W) -> Self {
Self {
writer,
indent,
need_indent: true,
}
}
/// Create a new [`IndentWriter`] which will not add an indent to the first
/// written line.
///
/// # Example
///
/// ```
/// # use std::fmt::Write;
/// use indent_write::fmt::IndentWriter;
///
/// let mut buffer = String::new();
/// let mut writer = IndentWriter::new_skip_initial(" ", &mut buffer);
///
/// writeln!(writer, "Line 1").unwrap();
/// writeln!(writer, "Line 2").unwrap();
/// writeln!(writer, "Line 3").unwrap();
///
/// assert_eq!(buffer, "Line 1\n Line 2\n Line 3\n")
/// ```
#[inline]
pub fn new_skip_initial(indent: &'i str, writer: W) -> Self {
Self {
writer,
indent,
need_indent: false,
}
}
/// Extract the writer from the `IndentWriter`, discarding any in-progress
/// indent state.
#[inline]
pub fn into_inner(self) -> W {
self.writer
}
/// Get a reference to the wrapped writer
#[inline]
pub fn get_ref(&self) -> &W {
&self.writer
}
/// Get the string being used as an indent for each line
#[inline]
pub fn indent(&self) -> &'i str {
self.indent
}
}
impl<'i, W: fmt::Write> fmt::Write for IndentWriter<'i, W> {
fn write_str(&mut self, mut s: &str) -> fmt::Result {
loop {
match self.need_indent {
// We don't need an indent. Scan for the end of the line
false => match s.as_bytes().iter().position(|&b| b == b'\n') {
// No end of line in the input; write the entire string
None => break self.writer.write_str(s),
// We can see the end of the line. Write up to and including
// that newline, then request an indent
Some(len) => {
let (head, tail) = s.split_at(len + 1);
self.writer.write_str(head)?;
self.need_indent = true;
s = tail;
}
},
// We need an indent. Scan for the beginning of the next
// non-empty line.
true => match s.as_bytes().iter().position(|&b| b != b'\n') {
// No non-empty lines in input, write the entire string
None => break self.writer.write_str(s),
// We can see the next non-empty line. Write up to the
// beginning of that line, then insert an indent, then
// continue.
Some(len) => {
let (head, tail) = s.split_at(len);
self.writer.write_str(head)?;
self.writer.write_str(self.indent)?;
self.need_indent = false;
s = tail;
}
},
}
}
}
fn write_char(&mut self, c: char) -> fmt::Result {
// We need an indent, and this is the start of a non-empty line.
// Insert the indent.
if self.need_indent && c != '\n' {
self.writer.write_str(self.indent)?;
self.need_indent = false;
}
// This is the end of a non-empty line. Request an indent.
if !self.need_indent && c == '\n' {
self.need_indent = true;
}
self.writer.write_char(c)
}
}