blob: eed69172394a44b997efe8fd897803b030ca884b [file] [log] [blame]
/* 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>);
}