af_core/fmt/
indent.rs

1// Copyright © 2021 Alexandra Frydl
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use super::*;
8
9/// A wrapper returned from [`indent()`] that displays its inner value with
10/// custom indentation.
11pub struct Indented<'a, T> {
12  initial: &'a str,
13  hanging: &'a str,
14  value: T,
15}
16
17/// A formatter that automatically indents lines.
18pub struct IndentedFormatter<'a, F> {
19  f: F,
20  initial: &'a str,
21  hanging: &'a str,
22  line: usize,
23  start_of_line: bool,
24}
25
26/// Wraps the value so that it is displayed with the given initial and hanging
27/// indentation.
28pub fn indent<'a, T>(initial: &'a str, hanging: &'a str, value: T) -> Indented<'a, T> {
29  Indented { initial, hanging, value }
30}
31
32impl<'a, F: Write + 'a> IndentedFormatter<'a, F> {
33  /// Creates a new indented formatter with the given initial and hanging
34  /// indentation.
35  pub fn new(f: F, initial: &'a str, hanging: &'a str) -> Self {
36    Self { f, initial, hanging, line: 1, start_of_line: true }
37  }
38}
39
40impl<'a, T: Debug> Debug for Indented<'a, T> {
41  fn fmt(&self, f: &mut Formatter) -> Result {
42    let alt = f.alternate();
43    let mut f = IndentedFormatter::new(f, self.initial, self.hanging);
44
45    if alt {
46      write!(f, "{:#?}", self.value)
47    } else {
48      write!(f, "{:?}", self.value)
49    }
50  }
51}
52
53impl<'a, T: Display> Display for Indented<'a, T> {
54  fn fmt(&self, f: &mut Formatter) -> Result {
55    let alt = f.alternate();
56    let mut f = IndentedFormatter::new(f, self.initial, self.hanging);
57
58    if alt {
59      write!(f, "{:#}", self.value)
60    } else {
61      write!(f, "{}", self.value)
62    }
63  }
64}
65
66impl<'a, F: 'a + Write> Write for IndentedFormatter<'a, F> {
67  fn write_str(&mut self, s: &str) -> Result {
68    for c in s.chars() {
69      // Mark new lines.
70
71      if c == '\n' {
72        self.f.write_char(c)?;
73        self.start_of_line = true;
74        self.line += 1;
75
76        continue;
77      }
78
79      // Output indentation before each line.
80
81      if self.start_of_line {
82        if self.line == 1 {
83          self.f.write_str(self.initial)?;
84        } else {
85          self.f.write_str(self.hanging)?;
86        }
87
88        self.start_of_line = false;
89      }
90
91      // Output the original character.
92
93      self.f.write_char(c)?;
94    }
95
96    Ok(())
97  }
98}