diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 3a3117ee93e6abea5e3c14239a5462ad39b4d9b5..d134b7b18eb9b075028c3b9c16954f7a70da3880 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -12,7 +12,14 @@ pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] -pub enum Curly { +pub enum Language { + Cxx, + C, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Braces { SameLine, NextLine, } @@ -25,20 +32,56 @@ pub enum Layout { Auto, } -#[derive(Default, Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct Config { - pub file: FileConfig, + /// Optional text to output at the beginning of the file + pub header: Option<String>, + /// Optional text to output at the end of the file + pub trailer: Option<String>, + /// Optional text to output at major sections to deter manual editing + pub autogen_warning: Option<String>, + /// Include a comment with the version of cbindgen used to generate the file + pub include_version: bool, + /// 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, + /// The amount of spaces in a tab + pub tab_width: u32, + /// The language to output bindings for + pub language: Language, + /// The configuration options for functions #[serde(rename = "fn")] pub function: FunctionConfig, + /// The configuration options for structs #[serde(rename = "struct")] pub structure: StructConfig, + /// The configuration options for enums #[serde(rename = "enum")] pub enumeration: EnumConfig, } +impl Default for Config { + fn default() -> Config { + Config { + header: None, + trailer: None, + autogen_warning: None, + include_version: true, + braces: Braces::SameLine, + line_length: 100, + tab_width: 2, + language: Language::C, + function: FunctionConfig::default(), + structure: StructConfig::default(), + enumeration: EnumConfig::default(), + } + } +} + impl Config { pub fn from_file(file_name: &str) -> Result<Config, String> { fn read(file_name: &str) -> io::Result<String> { @@ -77,29 +120,29 @@ impl Config { * then run `cbindgen -c wr gfx/webrender_bindings/ gfx/webrender_bindings/webrender_ffi_generated.h` */"###; Config { - file: FileConfig { - header: Some(String::from(license)), - trailer: None, - autogen_warning: Some(String::from(autogen)), - include_version: Some(true), - }, + header: Some(String::from(license)), + trailer: None, + autogen_warning: Some(String::from(autogen)), + include_version: true, + braces: Braces::SameLine, + line_length: 100, + tab_width: 2, + language: Language::Cxx, function: FunctionConfig { prefix: Some(String::from("WR_INLINE")), postfix: Some(String::from("WR_FUNC")), - args: Some(Layout::Horizontal), + args: Layout::Horizontal, }, structure: StructConfig { - derive_eq: Some(true), - derive_neq: Some(false), - derive_lt: Some(false), - derive_lte: Some(false), - derive_gt: Some(false), - derive_gte: Some(false), - braces: Some(Curly::SameLine), + derive_eq: true, + derive_neq: false, + derive_lt: false, + derive_lte: false, + derive_gt: false, + derive_gte: false, }, enumeration: EnumConfig { - add_sentinel: Some(true), - braces: Some(Curly::SameLine), + add_sentinel: true, }, } } @@ -113,22 +156,7 @@ impl Config { } } -#[derive(Default, Debug, Clone, Deserialize)] -#[serde(rename_all = "kebab-case")] -#[serde(deny_unknown_fields)] -#[serde(default)] -pub struct FileConfig { - /// Optional text to output at the beginning of the file - pub header: Option<String>, - /// Optional text to output at the end of the file - pub trailer: Option<String>, - /// Optional text to output at major sections to deter manual editing - pub autogen_warning: Option<String>, - /// Include a comment with the version of cbindgen used to generate the file - pub include_version: Option<bool>, -} - -#[derive(Default, Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(deny_unknown_fields)] #[serde(default)] @@ -138,7 +166,17 @@ pub struct FunctionConfig { /// Optional text to output after each function declaration pub postfix: Option<String>, /// The style to layout the args - pub args: Option<Layout>, + pub args: Layout, +} + +impl Default for FunctionConfig { + fn default() -> FunctionConfig { + FunctionConfig { + prefix: None, + postfix: None, + args: Layout::Auto, + } + } } impl FunctionConfig { @@ -163,25 +201,36 @@ impl FunctionConfig { } } -#[derive(Default, Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct StructConfig { /// Whether to generate a piecewise equality operator - pub derive_eq: Option<bool>, + pub derive_eq: bool, /// Whether to generate a piecewise inequality operator - pub derive_neq: Option<bool>, + pub derive_neq: bool, /// Whether to generate a less than operator on structs with one field - pub derive_lt: Option<bool>, + pub derive_lt: bool, /// Whether to generate a less than or equal to operator on structs with one field - pub derive_lte: Option<bool>, + pub derive_lte: bool, /// Whether to generate a greater than operator on structs with one field - pub derive_gt: Option<bool>, + pub derive_gt: bool, /// Whether to generate a greater than or equal to operator on structs with one field - pub derive_gte: Option<bool>, - /// The style to use for braces - pub braces: Option<Curly>, + pub derive_gte: bool, +} + +impl Default for StructConfig { + fn default() -> StructConfig { + StructConfig { + derive_eq: false, + derive_neq: false, + derive_lt: false, + derive_lte: false, + derive_gt: false, + derive_gte: false, + } + } } impl StructConfig { @@ -192,7 +241,7 @@ impl StructConfig { if let Some(x) = directives.bool("derive-eq") { return x; } - self.derive_eq.unwrap_or(false) + self.derive_eq } pub fn derive_neq(&self, directives: &DirectiveSet) -> bool { if let Some(x) = directives.bool("struct-gen-op-neq") { @@ -201,7 +250,7 @@ impl StructConfig { if let Some(x) = directives.bool("derive-neq") { return x; } - self.derive_neq.unwrap_or(false) + self.derive_neq } pub fn derive_lt(&self, directives: &DirectiveSet) -> bool { if let Some(x) = directives.bool("struct-gen-op-lt") { @@ -210,7 +259,7 @@ impl StructConfig { if let Some(x) = directives.bool("derive-lt") { return x; } - self.derive_lt.unwrap_or(false) + self.derive_lt } pub fn derive_lte(&self, directives: &DirectiveSet) -> bool { if let Some(x) = directives.bool("struct-gen-op-lte") { @@ -219,7 +268,7 @@ impl StructConfig { if let Some(x) = directives.bool("derive-lte") { return x; } - self.derive_lte.unwrap_or(false) + self.derive_lte } pub fn derive_gt(&self, directives: &DirectiveSet) -> bool { if let Some(x) = directives.bool("struct-gen-op-gt") { @@ -228,7 +277,7 @@ impl StructConfig { if let Some(x) = directives.bool("derive-gt") { return x; } - self.derive_gt.unwrap_or(false) + self.derive_gt } pub fn derive_gte(&self, directives: &DirectiveSet) -> bool { if let Some(x) = directives.bool("struct-gen-op-gte") { @@ -237,20 +286,26 @@ impl StructConfig { if let Some(x) = directives.bool("derive-gte") { return x; } - self.derive_gte.unwrap_or(false) + self.derive_gte } } -#[derive(Default, Debug, Clone, Deserialize)] +#[derive( Debug, Clone, Deserialize)] #[serde(rename_all = "kebab-case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct EnumConfig { /// Whether to add a `Sentinel` value at the end of every enum /// This is useful in Gecko for IPC serialization - pub add_sentinel: Option<bool>, - /// The style to use for braces - pub braces: Option<Curly>, + pub add_sentinel: bool, +} + +impl Default for EnumConfig { + fn default() -> EnumConfig { + EnumConfig { + add_sentinel: false, + } + } } impl EnumConfig { @@ -261,6 +316,6 @@ impl EnumConfig { if let Some(x) = directives.bool("add-sentinel") { return x; } - self.add_sentinel.unwrap_or(false) + self.add_sentinel } } diff --git a/src/bindgen/items.rs b/src/bindgen/items.rs index 3b2162b4bf95aee3890d79e52ee3958226ab7555..3173a8944899b64113f51da350c4ca8e0970be24 100644 --- a/src/bindgen/items.rs +++ b/src/bindgen/items.rs @@ -7,6 +7,7 @@ use bindgen::config::Config; use bindgen::directive::*; use bindgen::library::*; use bindgen::syn_helpers::*; +use bindgen::writer::*; #[derive(Debug, Clone)] pub enum PrimitiveType { @@ -188,81 +189,80 @@ impl Type { } } - fn write<F: Write>(&self, out: &mut F) { + fn write<F: Write>(&self, out: &mut Writer<F>) { match self { &Type::ConstPtr(ref t) => { - write!(out, "const ").unwrap(); + out.write("const "); t.write(out); - write!(out, "*").unwrap(); + out.write("*"); } &Type::Ptr(ref t) => { t.write(out); - write!(out, "*").unwrap(); + out.write("*"); } &Type::Path(ref p) => { - write!(out, "{}", p).unwrap(); + out.write(p); } &Type::Primitive(ref p) => { - write!(out, "{}", p).unwrap(); + out.write(&format!("{}", p)); } &Type::Array(ref t, ref sz) => { t.write(out); - write!(out, "[{}]", sz).unwrap(); + out.write(&format!("[{}]", sz)); } &Type::FuncPtr(ref ret, ref args) => { if let &Some(ref ret) = ret { ret.write(out); } else { - write!(out, "void").unwrap(); + out.write("void"); } - write!(out, " (*)(").unwrap(); + out.write(" (*)("); for (i, arg) in args.iter().enumerate() { if i != 0 { - write!(out, ", ").unwrap(); + out.write(", "); } arg.write(out); } - write!(out, ")").unwrap(); + out.write(")"); } } } - fn write_with_ident<F: Write>(&self, ident: &str, out: &mut F) { + fn write_with_ident<F: Write>(&self, ident: &str, out: &mut Writer<F>) { match self { &Type::ConstPtr(ref t) => { - write!(out, "const ").unwrap(); + out.write("const "); t.write(out); - write!(out, "* {}", ident).unwrap(); - + out.write(&format!("* {}", ident)); } &Type::Ptr(ref t) => { t.write(out); - write!(out, "* {}", ident).unwrap(); + out.write(&format!("* {}", ident)); } &Type::Path(ref p) => { - write!(out, "{} {}", p, ident).unwrap(); + out.write(&format!("{} {}", p, ident)); } &Type::Primitive(ref p) => { - write!(out, "{} {}", p, ident).unwrap(); + out.write(&format!("{} {}", p, ident)); } &Type::Array(ref t, ref sz) => { t.write(out); - write!(out, " {}[{}]", ident, sz).unwrap(); + out.write(&format!(" {}[{}]", ident, sz)); } &Type::FuncPtr(ref ret, ref args) => { if let &Some(ref ret) = ret { ret.write(out); } else { - write!(out, "void").unwrap(); + out.write("void"); } - write!(out, " (*{})(", ident).unwrap(); + out.write(&format!(" (*{})(", ident)); for (i, arg) in args.iter().enumerate() { if i != 0 { - write!(out, ", ").unwrap(); + out.write(", "); } arg.write(out); } - write!(out, ")").unwrap(); + out.write(")"); } } } @@ -305,29 +305,31 @@ impl Function { } } - pub fn write<F: Write>(&self, config: &Config, out: &mut F) { + pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { if let Some(ref f) = config.function.prefix(&self.directives) { - write!(out, "{} ", f).unwrap(); + out.write(&format!("{} ", f)); } match self.ret.as_ref() { Some(ret) => ret.write(out), - None => write!(out, "void").unwrap(), + None => out.write("void"), } - write!(out, "\n{}(", self.name).unwrap(); + out.new_line(); + out.write(&format!("{}(", self.name)); for (i, arg) in self.args.iter().enumerate() { if i != 0 { - write!(out, ",\n ").unwrap(); + out.write(",\n "); } arg.1.write_with_ident(&arg.0, out); } - write!(out, ")").unwrap(); + out.write(")"); if let Some(ref f) = config.function.postfix(&self.directives) { - write!(out, "\n{}", f).unwrap(); + out.new_line(); + out.write(f) } - write!(out, ";").unwrap() + out.write(";"); } } @@ -383,11 +385,10 @@ impl Struct { } } - pub fn write<F: Write>(&self, config: &Config, out: &mut F) { + pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { assert!(self.generic_params.is_empty()); - writeln!(out, "struct {} {{", self.name).unwrap(); - + // Calculate overriden fields, should be done in convert let fields = match self.directives.list("field-names") { Some(overrides) => { let mut fields = Vec::new(); @@ -405,102 +406,64 @@ impl Struct { _ => self.fields.clone(), }; + out.write(&format!("struct {}", self.name)); + out.open_brace(); + for (i, &(ref name, ref ty)) in fields.iter().enumerate() { if i != 0 { - write!(out, "\n").unwrap(); + out.new_line() } - write!(out, " ").unwrap(); ty.write_with_ident(name, out); - write!(out, ";").unwrap(); + out.write(";"); } - write!(out, "\n").unwrap(); + out.new_line(); - if config.structure.derive_eq(&self.directives) && !self.fields.is_empty() { - write!(out, "\n").unwrap(); - write!(out, " bool operator==(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " &&\n ").unwrap(); - } - write!(out, "{} == aOther.{}", field.0, field.0).unwrap(); - } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); - } + { + let mut emit_op = |op, conjuc| { + out.new_line(); - if config.structure.derive_neq(&self.directives) && !self.fields.is_empty() { - write!(out, "\n").unwrap(); - write!(out, " bool operator!=(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " ||\n ").unwrap(); + out.write(&format!("bool operator{}(const {}& aOther) const", op, self.name)); + out.open_brace(); + out.write("return "); + for (i, field) in fields.iter().enumerate() { + if i == 1 { + out.push_tab(); + } + if i != 0 { + out.write(&format!(" {}", conjuc)); + out.new_line(); + } + out.write(&format!("{} {} aOther.{}", field.0, op, field.0)); } - write!(out, "{} != aOther.{}", field.0, field.0).unwrap(); - } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); - } - - if config.structure.derive_lt(&self.directives) && self.fields.len() == 1 { - write!(out, "\n").unwrap(); - write!(out, " bool operator<(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " &&\n ").unwrap(); + out.write(";"); + if fields.len() > 1 { + out.pop_tab(); } - write!(out, "{} < aOther.{}", field.0, field.0).unwrap(); - } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); - } + out.close_brace(false); + }; - if config.structure.derive_lte(&self.directives) && self.fields.len() == 1 { - write!(out, "\n").unwrap(); - write!(out, " bool operator<=(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " &&\n ").unwrap(); - } - write!(out, "{} <= aOther.{}", field.0, field.0).unwrap(); + if config.structure.derive_eq(&self.directives) && !self.fields.is_empty() { + emit_op("==", "&&"); } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); - } - - if config.structure.derive_gt(&self.directives) && self.fields.len() == 1 { - write!(out, "\n").unwrap(); - write!(out, " bool operator>(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " &&\n ").unwrap(); - } - write!(out, "{} > aOther.{}", field.0, field.0).unwrap(); + if config.structure.derive_neq(&self.directives) && !self.fields.is_empty() { + emit_op("!=", "||"); } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); - } - - if config.structure.derive_gte(&self.directives) && self.fields.len() == 1 { - write!(out, "\n").unwrap(); - write!(out, " bool operator>=(const {}& aOther) const {{\n", self.name).unwrap(); - write!(out, " return ").unwrap(); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - write!(out, " &&\n ").unwrap(); - } - write!(out, "{} > aOther.{}", field.0, field.0).unwrap(); + if config.structure.derive_lt(&self.directives) && self.fields.len() == 1 { + emit_op("<", "&&"); + } + if config.structure.derive_lte(&self.directives) && self.fields.len() == 1 { + emit_op("<=", "&&"); + } + if config.structure.derive_gt(&self.directives) && self.fields.len() == 1 { + emit_op(">", "&&"); + } + if config.structure.derive_gte(&self.directives) && self.fields.len() == 1 { + emit_op(">=", "&&"); } - write!(out, ";\n }}").unwrap(); - write!(out, "\n").unwrap(); } - write!(out, "}};").unwrap(); + out.close_brace(true); } } @@ -519,8 +482,8 @@ impl OpaqueStruct { } } - pub fn write<F: Write>(&self, out: &mut F) { - write!(out, "struct {};", self.name).unwrap(); + pub fn write<F: Write>(&self, out: &mut Writer<F>) { + out.write(&format!("struct {};", self.name)); } } @@ -588,7 +551,7 @@ impl Enum { }) } - pub fn write<F: Write>(&self, config: &Config, out: &mut F) { + pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { let size = match self.repr { Repr::U32 => "uint32_t", Repr::U16 => "uint16_t", @@ -596,17 +559,19 @@ impl Enum { _ => unreachable!(), }; - writeln!(out, "enum class {} : {} {{", self.name, size).unwrap(); + out.write(&format!("enum class {} : {} {{", self.name, size)); + out.new_line(); for (i, value) in self.values.iter().enumerate() { if i != 0 { - write!(out, "\n").unwrap(); + out.new_line() } - write!(out, " {} = {},", value.0, value.1).unwrap(); + out.write(&format!(" {} = {},", value.0, value.1)); } if config.enumeration.add_sentinel(&self.directives) { - write!(out, "\n\n Sentinel /* this must be last for serialization purposes. */").unwrap(); + out.write("\n\n Sentinel /* this must be last for serialization purposes. */"); } - write!(out, "\n}};").unwrap(); + out.new_line(); + out.write("};"); } } @@ -733,9 +698,9 @@ impl Typedef { self.aliased.add_deps(library, out); } - pub fn write<F: Write>(&self, out: &mut F) { - write!(out, "typedef ").unwrap(); + pub fn write<F: Write>(&self, out: &mut Writer<F>) { + out.write("typedef "); self.aliased.write_with_ident(&self.name, out); - write!(out, ";").unwrap(); + out.write(";"); } } diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 748f962ce2bcbf30a277a1ee6c26292cb535aa6a..d40a1291b02d372100d81ff73886929537a7a1f1 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -11,6 +11,7 @@ use bindgen::directive::*; use bindgen::items::*; use bindgen::rust_lib; use bindgen::syn_helpers::*; +use bindgen::writer::Writer; pub type ConvertResult<T> = Result<T, String>; pub type GenerateResult<T> = Result<T, String>; @@ -371,33 +372,42 @@ impl<'a> GeneratedLibrary<'a> { self.write(&mut File::create(path).unwrap()); } - pub fn write<F: Write>(&self, out: &mut F) { - if let Some(ref f) = self.config.file.header { - write!(out, "{}\n", f).unwrap(); + pub fn write<F: Write>(&self, file: &mut F) { + let mut out = Writer::new(file, self.config); + + if let Some(ref f) = self.config.header { + out.write(&f); + out.new_line(); } - if self.config.file.include_version.unwrap_or(false) { - write!(out, "\n/* Generated with cbindgen:{} */\n", config::VERSION).unwrap(); + if self.config.include_version { + out.new_line(); + out.write(&format!("/* Generated with cbindgen:{} */", config::VERSION)); + out.new_line(); } - if let Some(ref f) = self.config.file.autogen_warning { - write!(out, "\n{}\n", f).unwrap(); + if let Some(ref f) = self.config.autogen_warning { + out.new_line(); + out.write(&f); + out.new_line(); } for item in &self.items { - write!(out, "\n").unwrap(); + out.new_line(); match item { - &PathValue::Enum(ref x) => x.write(self.config, out), - &PathValue::Struct(ref x) => x.write(self.config, out), - &PathValue::OpaqueStruct(ref x) => x.write(out), - &PathValue::Typedef(ref x) => x.write(out), + &PathValue::Enum(ref x) => x.write(self.config, &mut out), + &PathValue::Struct(ref x) => x.write(self.config, &mut out), + &PathValue::OpaqueStruct(ref x) => x.write(&mut out), + &PathValue::Typedef(ref x) => x.write(&mut out), &PathValue::Specialization(_) => { panic!("should not encounter a specialization in a built library") } } - write!(out, "\n").unwrap(); + out.new_line(); } - if let Some(ref f) = self.config.file.autogen_warning { - write!(out, "\n{}\n", f).unwrap(); + if let Some(ref f) = self.config.autogen_warning { + out.new_line(); + out.write(&f); + out.new_line(); } for function in &self.functions { @@ -405,16 +415,20 @@ impl<'a> GeneratedLibrary<'a> { continue; } - write!(out, "\n").unwrap(); - function.write(self.config, out); - write!(out, "\n").unwrap(); + out.new_line(); + function.write(self.config, &mut out); + out.new_line(); } - if let Some(ref f) = self.config.file.autogen_warning { - write!(out, "\n{}\n", f).unwrap(); + if let Some(ref f) = self.config.autogen_warning { + out.new_line(); + out.write(&f); + out.new_line(); } - if let Some(ref f) = self.config.file.trailer { - write!(out, "\n{}\n", f).unwrap(); + if let Some(ref f) = self.config.trailer { + out.new_line(); + out.write(&f); + out.new_line(); } } } diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index 88c43e7aaab41b7ae773eacb393ba1c87c9e79cb..c944378536eec164e02ede55e3179a9c2e467ddd 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -4,6 +4,7 @@ mod items; mod library; mod rust_lib; mod syn_helpers; +mod writer; pub use self::config::*; pub use self::library::Library; diff --git a/src/bindgen/writer.rs b/src/bindgen/writer.rs new file mode 100644 index 0000000000000000000000000000000000000000..f13dd64e7b0519156df65e14de86d6e402c2c747 --- /dev/null +++ b/src/bindgen/writer.rs @@ -0,0 +1,86 @@ +use bindgen::config::{Config, Braces}; +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, + has_written: bool, +} + +impl<'a, 'f, F: Write> Writer<'a, 'f, F> { + pub fn new(out: &'f mut F, config: &'a Config) -> Writer<'a, 'f, F> { + Writer { + out: out, + config: config, + tabs: vec![0], + pending_spaces: 0, + has_written: false, + } + } + + fn tabs(&mut self) -> u32 { + *self.tabs.last().unwrap() + } + + pub fn push_tab(&mut self) { + let tabs = self.tabs() + 1; + self.tabs.push(tabs); + + if !self.has_written { + self.pending_spaces = self.tabs() * self.config.tab_width; + } + } + + pub fn pop_tab(&mut self) { + assert!(!self.tabs.is_empty()); + self.tabs.pop(); + + if !self.has_written { + self.pending_spaces = self.tabs() * self.config.tab_width; + } + } + + pub fn new_line(&mut self) { + write!(self.out, "\n").unwrap(); + self.pending_spaces = self.tabs() * self.config.tab_width; + self.has_written = false; + } + + pub fn open_brace(&mut self) { + match self.config.braces { + Braces::SameLine => { + write!(self.out, " {{").unwrap(); + self.push_tab(); + self.new_line(); + } + Braces::NextLine => { + self.new_line(); + write!(self.out, "{{").unwrap(); + self.push_tab(); + self.new_line(); + } + } + } + + pub fn close_brace(&mut self, semicolon: bool) { + self.pop_tab(); + self.new_line(); + if semicolon { + self.write("};"); + } else { + self.write("}"); + } + } + + pub fn write(&mut self, text: &str) { + for _ in 0..self.pending_spaces { + write!(self.out, " ").unwrap(); + } + self.pending_spaces = 0; + self.has_written = true; + + write!(self.out, "{}", text).unwrap() + } +}