diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 28466ca58bd3396b6e1262ec1887f7c4fa02d5bb..41adcb7f3f9c58862419957372f70e50f2aa73ef 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -24,7 +24,7 @@ pub enum Braces { NextLine, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Layout { Horizontal, @@ -48,9 +48,9 @@ pub struct Config { /// The style to use for braces pub braces: Braces, /// The preferred length of a line, used for auto breaking function arguments - pub line_length: u32, + pub line_length: usize, /// The amount of spaces in a tab - pub tab_width: u32, + pub tab_width: usize, /// The language to output bindings for pub language: Language, /// The configuration options for functions @@ -131,7 +131,7 @@ impl Config { function: FunctionConfig { prefix: Some(String::from("WR_INLINE")), postfix: Some(String::from("WR_FUNC")), - args: Layout::Horizontal, + args: Layout::Vertical, }, structure: StructConfig { derive_eq: true, diff --git a/src/bindgen/items.rs b/src/bindgen/items.rs index 3c52aab917da132f5b2bc6fa537c97087ac56077..e4d41cc011df9763e277fb1b29e9e83ff7e29b7d 100644 --- a/src/bindgen/items.rs +++ b/src/bindgen/items.rs @@ -3,7 +3,7 @@ use std::fmt; use syn::*; -use bindgen::config::Config; +use bindgen::config::{Config, Layout}; use bindgen::directive::*; use bindgen::library::*; use bindgen::syn_helpers::*; @@ -223,83 +223,89 @@ impl Type { } } - fn write<F: Write>(&self, out: &mut Writer<F>) { + fn to_string(&self) -> String { match self { &Type::ConstPtr(ref t) => { - out.write("const "); - t.write(out); - out.write("*"); + format!("const {}*", t.to_string()) } &Type::Ptr(ref t) => { - t.write(out); - out.write("*"); + format!("{}*", t.to_string()) } &Type::Path(ref p) => { - out.write(p); + p.clone() } &Type::Primitive(ref p) => { - out.write(&format!("{}", p)); + format!("{}", p) } &Type::Array(ref t, ref sz) => { - t.write(out); - out.write(&format!("[{}]", sz)); + format!("{}[{}]", t.to_string(), sz) } &Type::FuncPtr(ref ret, ref args) => { + let mut out = String::new(); + if let &Some(ref ret) = ret { - ret.write(out); + out.push_str(&ret.to_string()); } else { - out.write("void"); + out.push_str("void"); } - out.write(" (*)("); + out.push_str(" (*)("); for (i, arg) in args.iter().enumerate() { if i != 0 { - out.write(", "); + out.push_str(", "); } - arg.write(out); + out.push_str(&arg.to_string()); } - out.write(")"); + out.push_str(")"); + + out } } } - fn write_with_ident<F: Write>(&self, ident: &str, out: &mut Writer<F>) { + fn to_string_with_ident(&self, ident: &str) -> String { match self { &Type::ConstPtr(ref t) => { - out.write("const "); - t.write(out); - out.write(&format!("* {}", ident)); + format!("const {}* {}", t.to_string(), ident) } &Type::Ptr(ref t) => { - t.write(out); - out.write(&format!("* {}", ident)); + format!("{}* {}", t.to_string(), ident) } &Type::Path(ref p) => { - out.write(&format!("{} {}", p, ident)); + format!("{} {}", p, ident) } &Type::Primitive(ref p) => { - out.write(&format!("{} {}", p, ident)); + format!("{} {}", p, ident) } &Type::Array(ref t, ref sz) => { - t.write(out); - out.write(&format!(" {}[{}]", ident, sz)); + format!("{} {}[{}]", t.to_string(), ident, sz) } &Type::FuncPtr(ref ret, ref args) => { + let mut out = String::new(); + if let &Some(ref ret) = ret { - ret.write(out); + out.push_str(&ret.to_string()); } else { - out.write("void"); + out.push_str("void"); } - out.write(&format!(" (*{})(", ident)); + out.push_str(" (*"); + out.push_str(ident); + out.push_str(")("); for (i, arg) in args.iter().enumerate() { if i != 0 { - out.write(", "); + out.push_str(", "); } - arg.write(out); + out.push_str(&arg.to_string()); } - out.write(")"); + out.push_str(")"); + + out } } } + + fn write_with_ident<F: Write>(&self, ident: &str, out: &mut Writer<F>) { + out.write(&self.to_string_with_ident(ident)); + } } #[derive(Debug, Clone)] @@ -340,30 +346,103 @@ impl Function { } pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { - if let Some(ref f) = config.function.prefix(&self.directives) { - out.write(&format!("{} ", f)); - } - - match self.ret.as_ref() { - Some(ret) => ret.write(out), - None => out.write("void"), - } - - out.new_line(); - out.write(&format!("{}(", self.name)); - for (i, arg) in self.args.iter().enumerate() { - if i != 0 { - out.write(",\n "); + // Try three different ways of formatting, depending on the line length + // 1. PREFIX RET NAME ( ARGS ... ) POSTFIX ; + // 2. PREFIX + // RET NAME ( ARGS ... ) + // POSTFIX ; + // 3. PREFIX + // RET NAME ( ARGS + // ... ) + // POSTFIX ; + + let prefix = config.function.prefix(&self.directives); + let ret = match self.ret.as_ref() { + Some(ret) => ret.to_string(), + None => format!("void"), + }; + let name = &self.name; + let args = self.args.iter().map(|x| x.1.to_string_with_ident(&x.0)).collect::<Vec<_>>(); + let postfix = config.function.postfix(&self.directives); + + let option_1: usize = prefix.as_ref().map_or(0, |x| x.len()) + + ret.len() + + name.len() + + args.iter().map(|x| x.len()).sum::<usize>() + + postfix.as_ref().map_or(0, |x| x.len()) + 7; + + let option_2: usize = ret.len() + + name.len() + + args.iter().map(|x| x.len()).sum::<usize>(); + + if (config.function.args == Layout::Auto && option_1 <= config.line_length) || + config.function.args == Layout::Horizontal { + // 1. PREFIX RET NAME ( ARGS ... ) POSTFIX ; + + if let Some(ref prefix) = prefix { + out.write(prefix); + out.write(" "); + } + out.write(&format!("{} {}({}) ", + &ret, + name, + args.join(", "))); + if let Some(ref postfix) = postfix { + out.write(postfix); } - arg.1.write_with_ident(&arg.0, out); - } - out.write(")"); + out.write(";"); + } else if config.function.args == Layout::Auto && option_2 <= config.line_length { + // 2. PREFIX + // RET NAME ( ARGS ... ) + // POSTFIX ; - if let Some(ref f) = config.function.postfix(&self.directives) { - out.new_line(); - out.write(f) + if let Some(ref prefix) = prefix { + out.write(prefix); + out.new_line(); + } + out.write(&format!("{} {}({}) ", + &ret, + name, + args.join(", "))); + if let Some(ref postfix) = postfix { + out.new_line(); + out.write(postfix); + } + out.write(";"); + } else { + // 3. PREFIX + // RET NAME ( ARGS + // ... ) + // POSTFIX ; + + if let Some(ref prefix) = prefix { + out.write(prefix); + out.new_line(); + } + out.write(&format!("{} {}(", + &ret, + name)); + for (i, arg) in args.iter().enumerate() { + out.write(arg); + if i != args.len() - 1 { + out.write(","); + if i == 0 { + let align_length = ret.len() + name.len() + 2; + out.push_set_spaces(align_length); + } + out.new_line(); + } + } + if args.len() > 1 { + out.pop_tab(); + } + out.write(")"); + if let Some(ref postfix) = postfix { + out.new_line(); + out.write(postfix); + } + out.write(";"); } - out.write(";"); } } diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs index f13dd64e7b0519156df65e14de86d6e402c2c747..0947a086ab5ab15163991b1ed510b8412f247dc9 100644 --- a/src/bindgen/writer.rs +++ b/src/bindgen/writer.rs @@ -4,8 +4,8 @@ use std::io::Write; pub struct Writer<'a, 'f, F: 'f + Write> { out: &'f mut F, config: &'a Config, - tabs: Vec<u32>, - pending_spaces: u32, + spaces: Vec<usize>, + pending_spaces: usize, has_written: bool, } @@ -14,37 +14,46 @@ impl<'a, 'f, F: Write> Writer<'a, 'f, F> { Writer { out: out, config: config, - tabs: vec![0], + spaces: vec![0], pending_spaces: 0, has_written: false, } } - fn tabs(&mut self) -> u32 { - *self.tabs.last().unwrap() + fn spaces(&mut self) -> usize { + *self.spaces.last().unwrap() } pub fn push_tab(&mut self) { - let tabs = self.tabs() + 1; - self.tabs.push(tabs); + let spaces = self.spaces() - + (self.spaces() % self.config.tab_width) + + self.config.tab_width; + self.spaces.push(spaces); if !self.has_written { - self.pending_spaces = self.tabs() * self.config.tab_width; + self.pending_spaces = self.spaces(); + } + } + pub fn push_set_spaces(&mut self, spaces: usize) { + self.spaces.push(spaces); + + if !self.has_written { + self.pending_spaces = self.spaces(); } } pub fn pop_tab(&mut self) { - assert!(!self.tabs.is_empty()); - self.tabs.pop(); + assert!(!self.spaces.is_empty()); + self.spaces.pop(); if !self.has_written { - self.pending_spaces = self.tabs() * self.config.tab_width; + self.pending_spaces = self.spaces(); } } pub fn new_line(&mut self) { write!(self.out, "\n").unwrap(); - self.pending_spaces = self.tabs() * self.config.tab_width; + self.pending_spaces = self.spaces(); self.has_written = false; }