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