code_builder/
lib.rs

1/// This crate contains a utility struct for generating indented code strings.
2///
3/// The `CodeBuilder` struct accepts lines of code as input, and writes them to a string buffer.
4/// The code can then be finalized with `result`, which returns an indented code string.
5
6use std::cmp::max;
7
8#[derive(Debug)]
9pub struct CodeBuilder {
10    code: String,
11    indent_level: i32,
12    indent_size: i32,
13}
14
15impl CodeBuilder {
16    /// Adds a single line of code to this code builder, formatting it based on previous code.
17    pub fn add_line<S>(&mut self, line: S)
18        where S: AsRef<str>
19    {
20        let line = line.as_ref().trim();
21        let indent_change = (line.matches("{").count() as i32) - (line.matches("}").count() as i32);
22        let new_indent_level = max(0, self.indent_level + indent_change);
23
24        // Lines starting with '}' should be de-indented even if they contain '{' after; in
25        // addition, lines ending with ':' are typically labels, e.g., in LLVM.
26        let this_line_indent = if line.starts_with("}") || line.ends_with(":") {
27            self.indent_level - 1
28        } else {
29            self.indent_level
30        };
31
32        // self.code.push_str(this_line_indent.as_ref());
33        for _ in 0..this_line_indent * self.indent_size {
34            self.code.push(' ');
35        }
36        self.code.push_str(line);
37        self.code.push_str("\n");
38
39        self.indent_level = new_indent_level;
40    }
41
42    /// Adds one or more lines (split by "\n") to this code builder.
43    pub fn add<S>(&mut self, code: S)
44        where S: AsRef<str>
45    {
46        for l in code.as_ref().lines() {
47            self.add_line(l);
48        }
49    }
50
51    /// Adds the result of another CodeBuilder.
52    pub fn add_code(&mut self, builder: &CodeBuilder) {
53        self.code.push_str(builder.code.as_ref());
54    }
55
56    /// Returns the code in this code builder so far.
57    pub fn result(&self) -> &str {
58        self.code.as_str()
59    }
60
61    /// Returns a new CodeBuilder.
62    pub fn new() -> CodeBuilder {
63        CodeBuilder::with_indent_size(2)
64    }
65
66    /// Returns a new CodeBuilder with the given indent size.
67    pub fn with_indent_size(indent_size: i32) -> CodeBuilder {
68        CodeBuilder {
69            code: String::new(),
70            indent_level: 0,
71            indent_size: indent_size,
72        }
73    }
74
75    /// Returns a formatted string using the CodeBuilder.
76    pub fn format<S>(indent_size: i32, code: S) -> String
77        where S: AsRef<str>
78    {
79        let mut c = CodeBuilder::with_indent_size(indent_size);
80        c.add(code);
81        c.code
82    }
83}
84
85#[test]
86fn code_builder_basic() {
87    let input = "
88class A {
89blahblah;
90}";
91    let expected = "
92class A {
93  blahblah;
94}
95";
96    assert_eq!(CodeBuilder::format(2, input), expected);
97
98    let input = "
99class A {
100if (c) {
101duh
102     }
103}";
104    let expected = "
105class A {
106  if (c) {
107    duh
108  }
109}
110";
111    assert_eq!(CodeBuilder::format(2, input), expected);
112
113    let input = "
114class A {
115if (c) { duh }
116}";
117    let expected = "
118class A {
119  if (c) { duh }
120}
121";
122    assert_eq!(CodeBuilder::format(2, input), expected);
123
124    let input = "
125class A {
126if (c) { duh }
127myLabel:
128blah
129}";
130    let expected = "
131class A {
132  if (c) { duh }
133myLabel:
134  blah
135}
136";
137    assert_eq!(CodeBuilder::format(2, input), expected);
138}