Skip to main content

wasm_bridge_jco/
source.rs

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