1*2329ecc3SAlex Crichton use std::fmt::{self, Write};
2*2329ecc3SAlex Crichton use std::ops::Deref;
3*2329ecc3SAlex Crichton 
4*2329ecc3SAlex Crichton /// Helper structure to maintain indentation automatically when printing.
5*2329ecc3SAlex Crichton #[derive(Default)]
6*2329ecc3SAlex Crichton pub struct Source {
7*2329ecc3SAlex Crichton     s: String,
8*2329ecc3SAlex Crichton     indent: usize,
9*2329ecc3SAlex Crichton }
10*2329ecc3SAlex Crichton 
11*2329ecc3SAlex Crichton impl Source {
push_str(&mut self, src: &str)12*2329ecc3SAlex Crichton     pub fn push_str(&mut self, src: &str) {
13*2329ecc3SAlex Crichton         let lines = src.lines().collect::<Vec<_>>();
14*2329ecc3SAlex Crichton         for (i, line) in lines.iter().enumerate() {
15*2329ecc3SAlex Crichton             let trimmed = line.trim();
16*2329ecc3SAlex Crichton             if trimmed.starts_with('}') && self.s.ends_with("  ") {
17*2329ecc3SAlex Crichton                 self.s.pop();
18*2329ecc3SAlex Crichton                 self.s.pop();
19*2329ecc3SAlex Crichton             }
20*2329ecc3SAlex Crichton             self.s.push_str(if lines.len() == 1 {
21*2329ecc3SAlex Crichton                 line
22*2329ecc3SAlex Crichton             } else {
23*2329ecc3SAlex Crichton                 line.trim_start()
24*2329ecc3SAlex Crichton             });
25*2329ecc3SAlex Crichton             if trimmed.ends_with('{') {
26*2329ecc3SAlex Crichton                 self.indent += 1;
27*2329ecc3SAlex Crichton             }
28*2329ecc3SAlex Crichton             if trimmed.starts_with('}') {
29*2329ecc3SAlex Crichton                 // Note that a `saturating_sub` is used here to prevent a panic
30*2329ecc3SAlex Crichton                 // here in the case of invalid code being generated in debug
31*2329ecc3SAlex Crichton                 // mode. It's typically easier to debug those issues through
32*2329ecc3SAlex Crichton                 // looking at the source code rather than getting a panic.
33*2329ecc3SAlex Crichton                 self.indent = self.indent.saturating_sub(1);
34*2329ecc3SAlex Crichton             }
35*2329ecc3SAlex Crichton             if i != lines.len() - 1 || src.ends_with('\n') {
36*2329ecc3SAlex Crichton                 self.newline();
37*2329ecc3SAlex Crichton             }
38*2329ecc3SAlex Crichton         }
39*2329ecc3SAlex Crichton     }
40*2329ecc3SAlex Crichton 
indent(&mut self, amt: usize)41*2329ecc3SAlex Crichton     pub fn indent(&mut self, amt: usize) {
42*2329ecc3SAlex Crichton         self.indent += amt;
43*2329ecc3SAlex Crichton     }
44*2329ecc3SAlex Crichton 
deindent(&mut self, amt: usize)45*2329ecc3SAlex Crichton     pub fn deindent(&mut self, amt: usize) {
46*2329ecc3SAlex Crichton         self.indent -= amt;
47*2329ecc3SAlex Crichton     }
48*2329ecc3SAlex Crichton 
newline(&mut self)49*2329ecc3SAlex Crichton     fn newline(&mut self) {
50*2329ecc3SAlex Crichton         self.s.push('\n');
51*2329ecc3SAlex Crichton         for _ in 0..self.indent {
52*2329ecc3SAlex Crichton             self.s.push_str("  ");
53*2329ecc3SAlex Crichton         }
54*2329ecc3SAlex Crichton     }
55*2329ecc3SAlex Crichton 
as_mut_string(&mut self) -> &mut String56*2329ecc3SAlex Crichton     pub fn as_mut_string(&mut self) -> &mut String {
57*2329ecc3SAlex Crichton         &mut self.s
58*2329ecc3SAlex Crichton     }
59*2329ecc3SAlex Crichton }
60*2329ecc3SAlex Crichton 
61*2329ecc3SAlex Crichton impl Write for Source {
write_str(&mut self, s: &str) -> fmt::Result62*2329ecc3SAlex Crichton     fn write_str(&mut self, s: &str) -> fmt::Result {
63*2329ecc3SAlex Crichton         self.push_str(s);
64*2329ecc3SAlex Crichton         Ok(())
65*2329ecc3SAlex Crichton     }
66*2329ecc3SAlex Crichton }
67*2329ecc3SAlex Crichton 
68*2329ecc3SAlex Crichton impl Deref for Source {
69*2329ecc3SAlex Crichton     type Target = str;
deref(&self) -> &str70*2329ecc3SAlex Crichton     fn deref(&self) -> &str {
71*2329ecc3SAlex Crichton         &self.s
72*2329ecc3SAlex Crichton     }
73*2329ecc3SAlex Crichton }
74*2329ecc3SAlex Crichton 
75*2329ecc3SAlex Crichton impl From<Source> for String {
from(s: Source) -> String76*2329ecc3SAlex Crichton     fn from(s: Source) -> String {
77*2329ecc3SAlex Crichton         s.s
78*2329ecc3SAlex Crichton     }
79*2329ecc3SAlex Crichton }
80*2329ecc3SAlex Crichton 
81*2329ecc3SAlex Crichton #[cfg(test)]
82*2329ecc3SAlex Crichton mod tests {
83*2329ecc3SAlex Crichton     use super::Source;
84*2329ecc3SAlex Crichton 
85*2329ecc3SAlex Crichton     #[test]
simple_append()86*2329ecc3SAlex Crichton     fn simple_append() {
87*2329ecc3SAlex Crichton         let mut s = Source::default();
88*2329ecc3SAlex Crichton         s.push_str("x");
89*2329ecc3SAlex Crichton         assert_eq!(s.s, "x");
90*2329ecc3SAlex Crichton         s.push_str("y");
91*2329ecc3SAlex Crichton         assert_eq!(s.s, "xy");
92*2329ecc3SAlex Crichton         s.push_str("z ");
93*2329ecc3SAlex Crichton         assert_eq!(s.s, "xyz ");
94*2329ecc3SAlex Crichton         s.push_str(" a ");
95*2329ecc3SAlex Crichton         assert_eq!(s.s, "xyz  a ");
96*2329ecc3SAlex Crichton         s.push_str("\na");
97*2329ecc3SAlex Crichton         assert_eq!(s.s, "xyz  a \na");
98*2329ecc3SAlex Crichton     }
99*2329ecc3SAlex Crichton 
100*2329ecc3SAlex Crichton     #[test]
newline_remap()101*2329ecc3SAlex Crichton     fn newline_remap() {
102*2329ecc3SAlex Crichton         let mut s = Source::default();
103*2329ecc3SAlex Crichton         s.push_str("function() {\n");
104*2329ecc3SAlex Crichton         s.push_str("y\n");
105*2329ecc3SAlex Crichton         s.push_str("}\n");
106*2329ecc3SAlex Crichton         assert_eq!(s.s, "function() {\n  y\n}\n");
107*2329ecc3SAlex Crichton     }
108*2329ecc3SAlex Crichton 
109*2329ecc3SAlex Crichton     #[test]
if_else()110*2329ecc3SAlex Crichton     fn if_else() {
111*2329ecc3SAlex Crichton         let mut s = Source::default();
112*2329ecc3SAlex Crichton         s.push_str("if() {\n");
113*2329ecc3SAlex Crichton         s.push_str("y\n");
114*2329ecc3SAlex Crichton         s.push_str("} else if () {\n");
115*2329ecc3SAlex Crichton         s.push_str("z\n");
116*2329ecc3SAlex Crichton         s.push_str("}\n");
117*2329ecc3SAlex Crichton         assert_eq!(s.s, "if() {\n  y\n} else if () {\n  z\n}\n");
118*2329ecc3SAlex Crichton     }
119*2329ecc3SAlex Crichton 
120*2329ecc3SAlex Crichton     #[test]
trim_ws()121*2329ecc3SAlex Crichton     fn trim_ws() {
122*2329ecc3SAlex Crichton         let mut s = Source::default();
123*2329ecc3SAlex Crichton         s.push_str(
124*2329ecc3SAlex Crichton             "function() {
125*2329ecc3SAlex Crichton                 x
126*2329ecc3SAlex Crichton         }",
127*2329ecc3SAlex Crichton         );
128*2329ecc3SAlex Crichton         assert_eq!(s.s, "function() {\n  x\n}");
129*2329ecc3SAlex Crichton     }
130*2329ecc3SAlex Crichton }
131