1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::cmp;
6use std::io;
7use std::io::Write;
8
9use crate::bindgen::config::{Braces, Config, Language};
10use crate::bindgen::Bindings;
11
12/// A type of way to format a list.
13pub enum ListType<'a> {
14 /// Join each adjacent item with a str.
15 Join(&'a str),
16 /// End each item with a str.
17 Cap(&'a str),
18}
19
20/// A utility wrapper to write unbuffered data and correctly adjust positions.
21struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
22
23impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
24 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
25 let writer: &mut &mut SourceWriter<'_, …> = &mut self.0;
26
27 if !writer.line_started {
28 for _ in 0..writer.spaces() {
29 write!(writer.out, " ").unwrap();
30 }
31 writer.line_started = true;
32 writer.line_length += writer.spaces();
33 }
34
35 let written: usize = writer.out.write(buf)?;
36 writer.line_length += written;
37 writer.max_line_length = cmp::max(v1:writer.max_line_length, v2:writer.line_length);
38 Ok(written)
39 }
40
41 fn flush(&mut self) -> io::Result<()> {
42 self.0.out.flush()
43 }
44}
45
46/// A utility writer for generating code easier.
47pub struct SourceWriter<'a, F: Write> {
48 out: F,
49 bindings: &'a Bindings,
50 spaces: Vec<usize>,
51 line_started: bool,
52 line_length: usize,
53 line_number: usize,
54 max_line_length: usize,
55}
56
57pub type MeasureWriter<'a> = SourceWriter<'a, &'a mut Vec<u8>>;
58
59impl<'a, F: Write> SourceWriter<'a, F> {
60 pub fn new(out: F, bindings: &'a Bindings) -> Self {
61 SourceWriter {
62 out,
63 bindings,
64 spaces: vec![0],
65 line_started: false,
66 line_length: 0,
67 line_number: 1,
68 max_line_length: 0,
69 }
70 }
71
72 pub fn bindings(&self) -> &Bindings {
73 self.bindings
74 }
75
76 /// Takes a function that writes source and returns the maximum line length
77 /// written.
78 pub fn try_write<T>(&mut self, func: T, max_line_length: usize) -> bool
79 where
80 T: Fn(&mut MeasureWriter),
81 {
82 if self.line_length > max_line_length {
83 return false;
84 }
85
86 let mut buffer = Vec::new();
87 let line_length = {
88 let mut measurer = SourceWriter {
89 out: &mut buffer,
90 bindings: self.bindings,
91 spaces: self.spaces.clone(),
92 line_started: self.line_started,
93 line_length: self.line_length,
94 line_number: self.line_number,
95 max_line_length: self.line_length,
96 };
97
98 func(&mut measurer);
99
100 measurer.max_line_length
101 };
102
103 if line_length > max_line_length {
104 return false;
105 }
106 // We don't want the extra alignment, it's already accounted for by the
107 // measurer.
108 self.line_started = true;
109 InnerWriter(self).write_all(&buffer).unwrap();
110 true
111 }
112
113 fn spaces(&self) -> usize {
114 *self.spaces.last().unwrap()
115 }
116
117 pub fn push_set_spaces(&mut self, spaces: usize) {
118 self.spaces.push(spaces);
119 }
120
121 pub fn pop_set_spaces(&mut self) {
122 self.pop_tab()
123 }
124
125 pub fn line_length_for_align(&self) -> usize {
126 if self.line_started {
127 self.line_length
128 } else {
129 self.line_length + self.spaces()
130 }
131 }
132
133 pub fn push_tab(&mut self) {
134 let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width)
135 + self.bindings.config.tab_width;
136 self.spaces.push(spaces);
137 }
138
139 pub fn pop_tab(&mut self) {
140 assert!(!self.spaces.is_empty());
141 self.spaces.pop();
142 }
143
144 pub fn new_line(&mut self) {
145 self.out
146 .write_all(self.bindings.config.line_endings.as_str().as_bytes())
147 .unwrap();
148 self.line_started = false;
149 self.line_length = 0;
150 self.line_number += 1;
151 }
152
153 pub fn new_line_if_not_start(&mut self) {
154 if self.line_number != 1 {
155 self.new_line();
156 }
157 }
158
159 pub fn open_brace(&mut self) {
160 match self.bindings.config.language {
161 Language::Cxx | Language::C => match self.bindings.config.braces {
162 Braces::SameLine => {
163 self.write(" {");
164 self.push_tab();
165 self.new_line();
166 }
167 Braces::NextLine => {
168 self.new_line();
169 self.write("{");
170 self.push_tab();
171 self.new_line();
172 }
173 },
174 Language::Cython => {
175 self.write(":");
176 self.new_line();
177 self.push_tab();
178 }
179 }
180 }
181
182 pub fn close_brace(&mut self, semicolon: bool) {
183 self.pop_tab();
184 match self.bindings.config.language {
185 Language::Cxx | Language::C => {
186 self.new_line();
187 if semicolon {
188 self.write("};");
189 } else {
190 self.write("}");
191 }
192 }
193 Language::Cython => {}
194 }
195 }
196
197 pub fn write(&mut self, text: &'static str) {
198 write!(self, "{}", text);
199 }
200
201 pub fn write_raw_block(&mut self, block: &str) {
202 self.line_started = true;
203 write!(self, "{}", block);
204 }
205
206 pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
207 InnerWriter(self).write_fmt(fmt).unwrap();
208 }
209
210 pub fn write_horizontal_source_list<S: Source>(
211 &mut self,
212 items: &[S],
213 list_type: ListType<'_>,
214 ) {
215 for (i, item) in items.iter().enumerate() {
216 item.write(&self.bindings.config, self);
217
218 match list_type {
219 ListType::Join(text) => {
220 if i != items.len() - 1 {
221 write!(self, "{}", text);
222 }
223 }
224 ListType::Cap(text) => {
225 write!(self, "{}", text);
226 }
227 }
228 }
229 }
230
231 pub fn write_vertical_source_list<S: Source>(&mut self, items: &[S], list_type: ListType<'_>) {
232 let align_length = self.line_length_for_align();
233 self.push_set_spaces(align_length);
234 for (i, item) in items.iter().enumerate() {
235 item.write(&self.bindings.config, self);
236
237 match list_type {
238 ListType::Join(text) => {
239 if i != items.len() - 1 {
240 write!(self, "{}", text);
241 }
242 }
243 ListType::Cap(text) => {
244 write!(self, "{}", text);
245 }
246 }
247
248 if i != items.len() - 1 {
249 self.new_line();
250 }
251 }
252 self.pop_tab();
253 }
254}
255
256pub trait Source {
257 fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
258}
259