cbindgen/bindgen/
writer.rs

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