summaryrefslogtreecommitdiffstats
path: root/src/bindgen/writer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/writer.rs')
-rw-r--r--src/bindgen/writer.rs258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs
new file mode 100644
index 0000000..eed6917
--- /dev/null
+++ b/src/bindgen/writer.rs
@@ -0,0 +1,258 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cmp;
+use std::io;
+use std::io::Write;
+
+use crate::bindgen::config::{Braces, Config, Language};
+use crate::bindgen::Bindings;
+
+/// A type of way to format a list.
+pub enum ListType<'a> {
+ /// Join each adjacent item with a str.
+ Join(&'a str),
+ /// End each item with a str.
+ Cap(&'a str),
+}
+
+/// A utility wrapper to write unbuffered data and correctly adjust positions.
+struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
+
+impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let writer = &mut self.0;
+
+ if !writer.line_started {
+ for _ in 0..writer.spaces() {
+ write!(writer.out, " ").unwrap();
+ }
+ writer.line_started = true;
+ writer.line_length += writer.spaces();
+ }
+
+ let written = writer.out.write(buf)?;
+ writer.line_length += written;
+ writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length);
+ Ok(written)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.0.out.flush()
+ }
+}
+
+/// A utility writer for generating code easier.
+pub struct SourceWriter<'a, F: Write> {
+ out: F,
+ bindings: &'a Bindings,
+ spaces: Vec<usize>,
+ line_started: bool,
+ line_length: usize,
+ line_number: usize,
+ max_line_length: usize,
+}
+
+pub type MeasureWriter<'a> = SourceWriter<'a, &'a mut Vec<u8>>;
+
+impl<'a, F: Write> SourceWriter<'a, F> {
+ pub fn new(out: F, bindings: &'a Bindings) -> Self {
+ SourceWriter {
+ out,
+ bindings,
+ spaces: vec![0],
+ line_started: false,
+ line_length: 0,
+ line_number: 1,
+ max_line_length: 0,
+ }
+ }
+
+ pub fn bindings(&self) -> &Bindings {
+ self.bindings
+ }
+
+ /// Takes a function that writes source and returns the maximum line length
+ /// written.
+ pub fn try_write<T>(&mut self, func: T, max_line_length: usize) -> bool
+ where
+ T: Fn(&mut MeasureWriter),
+ {
+ if self.line_length > max_line_length {
+ return false;
+ }
+
+ let mut buffer = Vec::new();
+ let line_length = {
+ let mut measurer = SourceWriter {
+ out: &mut buffer,
+ bindings: self.bindings,
+ spaces: self.spaces.clone(),
+ line_started: self.line_started,
+ line_length: self.line_length,
+ line_number: self.line_number,
+ max_line_length: self.line_length,
+ };
+
+ func(&mut measurer);
+
+ measurer.max_line_length
+ };
+
+ if line_length > max_line_length {
+ return false;
+ }
+ // We don't want the extra alignment, it's already accounted for by the
+ // measurer.
+ self.line_started = true;
+ InnerWriter(self).write_all(&buffer).unwrap();
+ true
+ }
+
+ fn spaces(&self) -> usize {
+ *self.spaces.last().unwrap()
+ }
+
+ pub fn push_set_spaces(&mut self, spaces: usize) {
+ self.spaces.push(spaces);
+ }
+
+ pub fn pop_set_spaces(&mut self) {
+ self.pop_tab()
+ }
+
+ pub fn line_length_for_align(&self) -> usize {
+ if self.line_started {
+ self.line_length
+ } else {
+ self.line_length + self.spaces()
+ }
+ }
+
+ pub fn push_tab(&mut self) {
+ let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width)
+ + self.bindings.config.tab_width;
+ self.spaces.push(spaces);
+ }
+
+ pub fn pop_tab(&mut self) {
+ assert!(!self.spaces.is_empty());
+ self.spaces.pop();
+ }
+
+ pub fn new_line(&mut self) {
+ self.out
+ .write_all(self.bindings.config.line_endings.as_str().as_bytes())
+ .unwrap();
+ self.line_started = false;
+ self.line_length = 0;
+ self.line_number += 1;
+ }
+
+ pub fn new_line_if_not_start(&mut self) {
+ if self.line_number != 1 {
+ self.new_line();
+ }
+ }
+
+ pub fn open_brace(&mut self) {
+ match self.bindings.config.language {
+ Language::Cxx | Language::C => match self.bindings.config.braces {
+ Braces::SameLine => {
+ self.write(" {");
+ self.push_tab();
+ self.new_line();
+ }
+ Braces::NextLine => {
+ self.new_line();
+ self.write("{");
+ self.push_tab();
+ self.new_line();
+ }
+ },
+ Language::Cython => {
+ self.write(":");
+ self.new_line();
+ self.push_tab();
+ }
+ }
+ }
+
+ pub fn close_brace(&mut self, semicolon: bool) {
+ self.pop_tab();
+ match self.bindings.config.language {
+ Language::Cxx | Language::C => {
+ self.new_line();
+ if semicolon {
+ self.write("};");
+ } else {
+ self.write("}");
+ }
+ }
+ Language::Cython => {}
+ }
+ }
+
+ pub fn write(&mut self, text: &'static str) {
+ write!(self, "{}", text);
+ }
+
+ pub fn write_raw_block(&mut self, block: &str) {
+ self.line_started = true;
+ write!(self, "{}", block);
+ }
+
+ pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
+ InnerWriter(self).write_fmt(fmt).unwrap();
+ }
+
+ pub fn write_horizontal_source_list<S: Source>(
+ &mut self,
+ items: &[S],
+ list_type: ListType<'_>,
+ ) {
+ for (i, item) in items.iter().enumerate() {
+ item.write(&self.bindings.config, self);
+
+ match list_type {
+ ListType::Join(text) => {
+ if i != items.len() - 1 {
+ write!(self, "{}", text);
+ }
+ }
+ ListType::Cap(text) => {
+ write!(self, "{}", text);
+ }
+ }
+ }
+ }
+
+ pub fn write_vertical_source_list<S: Source>(&mut self, items: &[S], list_type: ListType<'_>) {
+ let align_length = self.line_length_for_align();
+ self.push_set_spaces(align_length);
+ for (i, item) in items.iter().enumerate() {
+ item.write(&self.bindings.config, self);
+
+ match list_type {
+ ListType::Join(text) => {
+ if i != items.len() - 1 {
+ write!(self, "{}", text);
+ }
+ }
+ ListType::Cap(text) => {
+ write!(self, "{}", text);
+ }
+ }
+
+ if i != items.len() - 1 {
+ self.new_line();
+ }
+ }
+ self.pop_tab();
+ }
+}
+
+pub trait Source {
+ fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
+}