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)
    }
}