rust_codegen/
formatter.rs

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