codegen/
formatter.rs

1use std::fmt::{self, Write};
2
3use crate::bound::Bound;
4
5use crate::r#type::Type;
6
7const DEFAULT_INDENT: usize = 4;
8
9/// Configures how a scope is formatted.
10#[derive(Debug)]
11pub struct Formatter<'a> {
12    /// Write destination
13    dst: &'a mut String,
14
15    /// Number of spaces to start a new line with.
16    spaces: usize,
17
18    /// Number of spaces per indentiation
19    indent: usize,
20}
21
22impl<'a> Formatter<'a> {
23    /// Return a new formatter that writes to the given string.
24    pub fn new(dst: &'a mut String) -> Self {
25        Formatter {
26            dst,
27            spaces: 0,
28            indent: DEFAULT_INDENT,
29        }
30    }
31
32    /// Wrap the given function inside a block.
33    pub fn block<F>(&mut self, f: F) -> fmt::Result
34    where
35        F: FnOnce(&mut Self) -> fmt::Result,
36    {
37        if !self.is_start_of_line() {
38            write!(self, " ")?;
39        }
40
41        write!(self, "{{\n")?;
42        self.indent(f)?;
43        write!(self, "}}\n")?;
44        Ok(())
45    }
46
47    /// Call the given function with the indentation level incremented by one.
48    pub fn indent<F, R>(&mut self, f: F) -> R
49    where
50        F: FnOnce(&mut Self) -> R,
51    {
52        self.spaces += self.indent;
53        let ret = f(self);
54        self.spaces -= self.indent;
55        ret
56    }
57
58    /// Check if current destination is the start of a new line.
59    pub fn is_start_of_line(&self) -> bool {
60        self.dst.is_empty() || self.dst.as_bytes().last() == Some(&b'\n')
61    }
62
63    fn push_spaces(&mut self) {
64        for _ in 0..self.spaces {
65            self.dst.push_str(" ");
66        }
67    }
68}
69
70impl<'a> fmt::Write for Formatter<'a> {
71    fn write_str(&mut self, s: &str) -> fmt::Result {
72        let mut first = true;
73        let mut should_indent = self.is_start_of_line();
74
75        for line in s.lines() {
76            if !first {
77                self.dst.push_str("\n");
78            }
79
80            first = false;
81
82            let do_indent = should_indent && !line.is_empty() && line.as_bytes()[0] != b'\n';
83
84            if do_indent {
85                self.push_spaces();
86            }
87
88            // If this loops again, then we just wrote a new line
89            should_indent = true;
90
91            self.dst.push_str(line);
92        }
93
94        if s.as_bytes().last() == Some(&b'\n') {
95            self.dst.push_str("\n");
96        }
97
98        Ok(())
99    }
100}
101
102/// Format generics.
103pub fn fmt_generics(generics: &[String], fmt: &mut Formatter<'_>) -> fmt::Result {
104    if !generics.is_empty() {
105        write!(fmt, "<")?;
106
107        for (i, ty) in generics.iter().enumerate() {
108            if i != 0 {
109                write!(fmt, ", ")?
110            }
111            write!(fmt, "{}", ty)?;
112        }
113
114        write!(fmt, ">")?;
115    }
116
117    Ok(())
118}
119
120/// Format generic bounds.
121pub fn fmt_bounds(bounds: &[Bound], fmt: &mut Formatter<'_>) -> fmt::Result {
122    if !bounds.is_empty() {
123        write!(fmt, "\n")?;
124
125        // Write first bound
126        write!(fmt, "where {}: ", bounds[0].name)?;
127        fmt_bound_rhs(&bounds[0].bound, fmt)?;
128        write!(fmt, ",\n")?;
129
130        for bound in &bounds[1..] {
131            write!(fmt, "      {}: ", bound.name)?;
132            fmt_bound_rhs(&bound.bound, fmt)?;
133            write!(fmt, ",\n")?;
134        }
135    }
136
137    Ok(())
138}
139
140/// Format multiple generic bounds.
141pub fn fmt_bound_rhs(tys: &[Type], fmt: &mut Formatter<'_>) -> fmt::Result {
142    for (i, ty) in tys.iter().enumerate() {
143        if i != 0 {
144            write!(fmt, " + ")?
145        }
146        ty.fmt(fmt)?;
147    }
148
149    Ok(())
150}