Skip to main content

nbindgen/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::Config;
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/// An empty file used for creating a null source writer and measuring line
21/// metrics for various code layouts.
22pub struct NullFile;
23impl Write for NullFile {
24    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
25        Ok(buf.len())
26    }
27    fn flush(&mut self) -> io::Result<()> {
28        Ok(())
29    }
30}
31
32/// A utility wrapper to write unbuffered data and correctly adjust positions.
33struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
34
35impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
36    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
37        let writer = &mut self.0;
38
39        if !writer.line_started {
40            for _ in 0..writer.spaces() {
41                write!(writer.out, " ").unwrap();
42            }
43            writer.line_started = true;
44            writer.line_length += writer.spaces();
45        }
46
47        let written = writer.out.write(buf)?;
48        writer.line_length += written;
49        writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length);
50        Ok(written)
51    }
52
53    fn flush(&mut self) -> io::Result<()> {
54        self.0.out.flush()
55    }
56}
57
58/// A utility writer for generating code easier.
59pub struct SourceWriter<'a, F: Write> {
60    out: F,
61    bindings: &'a Bindings,
62    spaces: Vec<usize>,
63    line_started: bool,
64    line_length: usize,
65    line_number: usize,
66    max_line_length: usize,
67}
68pub type MeasureWriter<'a> = SourceWriter<'a, NullFile>;
69
70impl<'a, F: Write> SourceWriter<'a, F> {
71    pub fn new(out: F, bindings: &'a Bindings) -> Self {
72        SourceWriter {
73            out,
74            bindings,
75            spaces: vec![0],
76            line_started: false,
77            line_length: 0,
78            line_number: 1,
79            max_line_length: 0,
80        }
81    }
82
83    pub fn bindings(&self) -> &Bindings {
84        &self.bindings
85    }
86
87    /// Takes a function that writes source and returns the maximum line length
88    /// written.
89    pub fn measure<T>(&self, func: T) -> usize
90    where
91        T: Fn(&mut MeasureWriter),
92    {
93        let mut measurer = SourceWriter {
94            out: NullFile,
95            bindings: self.bindings,
96            spaces: self.spaces.clone(),
97            line_started: self.line_started,
98            line_length: self.line_length,
99            line_number: self.line_number,
100            max_line_length: self.line_length,
101        };
102
103        func(&mut measurer);
104
105        measurer.max_line_length
106    }
107
108    fn spaces(&self) -> usize {
109        *self.spaces.last().unwrap()
110    }
111
112    pub fn push_set_spaces(&mut self, spaces: usize) {
113        self.spaces.push(spaces);
114    }
115
116    pub fn line_length_for_align(&self) -> usize {
117        if self.line_started {
118            self.line_length
119        } else {
120            self.line_length + self.spaces()
121        }
122    }
123
124    pub fn pop_tab(&mut self) {
125        assert!(!self.spaces.is_empty());
126        self.spaces.pop();
127    }
128
129    pub fn new_line(&mut self) {
130        writeln!(self.out).unwrap();
131        self.line_started = false;
132        self.line_length = 0;
133        self.line_number += 1;
134    }
135
136    pub fn new_line_if_not_start(&mut self) {
137        if self.line_number != 1 {
138            self.new_line();
139        }
140    }
141
142    pub fn indent(&mut self) {
143        self.push_set_spaces(self.spaces() + 2);
144    }
145
146    pub fn dedent(&mut self) {
147        self.push_set_spaces(self.spaces() - 2);
148    }
149
150    pub fn write(&mut self, text: &'static str) {
151        write!(self, "{}", text);
152    }
153
154    pub fn write_raw_block(&mut self, block: &str) {
155        self.new_line();
156        self.line_started = true;
157        write!(self, "{}", block);
158    }
159
160    pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
161        InnerWriter(self).write_fmt(fmt).unwrap();
162    }
163
164    pub fn write_horizontal_source_list<'b, S: Source>(
165        &mut self,
166        items: &[S],
167        list_type: ListType<'b>,
168    ) {
169        for (i, ref item) in items.iter().enumerate() {
170            item.write(&self.bindings.config, self);
171
172            match list_type {
173                ListType::Join(text) => {
174                    if i != items.len() - 1 {
175                        write!(self, "{}", text);
176                    }
177                }
178                ListType::Cap(text) => {
179                    write!(self, "{}", text);
180                }
181            }
182        }
183    }
184
185    pub fn write_vertical_source_list<'b, S: Source>(
186        &mut self,
187        items: &[S],
188        list_type: ListType<'b>,
189    ) {
190        let align_length = self.line_length_for_align();
191        self.push_set_spaces(align_length);
192        for (i, ref item) in items.iter().enumerate() {
193            item.write(&self.bindings.config, self);
194
195            match list_type {
196                ListType::Join(text) => {
197                    if i != items.len() - 1 {
198                        write!(self, "{}", text);
199                    }
200                }
201                ListType::Cap(text) => {
202                    write!(self, "{}", text);
203                }
204            }
205
206            if i != items.len() - 1 {
207                self.new_line();
208            }
209        }
210        self.pop_tab();
211    }
212}
213
214pub trait Source {
215    fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
216}