js_component_bindgen/
source.rs1use 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 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 prepend_str(&mut self, src: &str) {
43 let indent = self
47 .s
48 .lines()
49 .next()
50 .map_or(0, |line| (line.len() - line.trim_start().len()) / 2);
51 let mut new_start = Source {
52 s: String::new(),
53 indent,
54 };
55 new_start.push_str(src);
56 self.s = new_start.s + &self.s;
57 }
58
59 pub fn indent(&mut self, amt: usize) {
60 self.indent += amt;
61 }
62
63 pub fn deindent(&mut self, amt: usize) {
64 self.indent -= amt;
65 }
66
67 fn newline(&mut self) {
68 self.s.push('\n');
69 for _ in 0..self.indent {
70 self.s.push_str(" ");
71 }
72 }
73
74 pub fn as_mut_string(&mut self) -> &mut String {
75 &mut self.s
76 }
77}
78
79impl Write for Source {
80 fn write_str(&mut self, s: &str) -> fmt::Result {
81 self.push_str(s);
82 Ok(())
83 }
84}
85
86impl Deref for Source {
87 type Target = str;
88 fn deref(&self) -> &str {
89 &self.s
90 }
91}
92
93impl From<Source> for String {
94 fn from(s: Source) -> String {
95 s.s
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::Source;
102
103 #[test]
104 fn simple_append() {
105 let mut s = Source::default();
106 s.push_str("x");
107 assert_eq!(s.s, "x");
108 s.push_str("y");
109 assert_eq!(s.s, "xy");
110 s.push_str("z ");
111 assert_eq!(s.s, "xyz ");
112 s.push_str(" a ");
113 assert_eq!(s.s, "xyz a ");
114 s.push_str("\na");
115 assert_eq!(s.s, "xyz a \na");
116 }
117
118 #[test]
119 fn newline_remap() {
120 let mut s = Source::default();
121 s.push_str("function() {\n");
122 s.push_str("y\n");
123 s.push_str("}\n");
124 assert_eq!(s.s, "function() {\n y\n}\n");
125 }
126
127 #[test]
128 fn if_else() {
129 let mut s = Source::default();
130 s.push_str("if() {\n");
131 s.push_str("y\n");
132 s.push_str("} else if () {\n");
133 s.push_str("z\n");
134 s.push_str("}\n");
135 assert_eq!(s.s, "if() {\n y\n} else if () {\n z\n}\n");
136 }
137
138 #[test]
139 fn trim_ws() {
140 let mut s = Source::default();
141 s.push_str(
142 "function() {
143 x
144 }",
145 );
146 assert_eq!(s.s, "function() {\n x\n}");
147 }
148}