diff --git a/samples/build-script/cbindgen.toml b/samples/build-script/cbindgen.toml index af66efb7d48c142b5a953481ad21f9bfb056ecab..904e4b5146cbf2fc03c117da53da85a03d8ebd07 100644 --- a/samples/build-script/cbindgen.toml +++ b/samples/build-script/cbindgen.toml @@ -1,7 +1,17 @@ include_guard = "build_script_h" include_version = true +braces = "NextLine" +tab_width = 4 + +[enum] +rename_variants = "SnakeCase" + +[fn] +rename_args = "UpperCase" +args = "Vertical" [struct] +rename_fields = "GeckoCase" derive_eq = true derive_lt = true derive_lte = true diff --git a/samples/build-script/src/lib.rs b/samples/build-script/src/lib.rs index df7450f32f24e59e2fcaa2788bd9c27ff13fc3a6..22201a09addaecfd9381a60875f396233d2a6b85 100644 --- a/samples/build-script/src/lib.rs +++ b/samples/build-script/src/lib.rs @@ -1,6 +1,13 @@ #![allow(dead_code)] #![allow(unused_variables)] +#[repr(u32)] +pub enum Options { + First, + Second, + LastOne, +} + pub struct Opaque { x: i32, y: f32, @@ -18,7 +25,10 @@ pub struct Comparable { } #[no_mangle] -pub extern "C" fn root(x: *mut Opaque, y: Normal, z: Comparable) +pub extern "C" fn root(x: *mut Opaque, + y: Normal, + z: Comparable, + w: Options) { } diff --git a/src/bindgen/config.rs b/src/bindgen/config.rs index 4afed2070ee6f113c80f962ddac00ef16dc889f0..73c04276bcb199dcaaed0fa0b0b808f750170dd3 100644 --- a/src/bindgen/config.rs +++ b/src/bindgen/config.rs @@ -3,35 +3,84 @@ use std::fs::File; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::PathBuf; +use std::str::FromStr; use toml; pub use bindgen::directive::*; +pub use bindgen::rename::*; pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone)] pub enum Language { Cxx, C, } - -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone)] pub enum Braces { SameLine, NextLine, } - -#[derive(Debug, Clone, PartialEq, Deserialize)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone, PartialEq)] pub enum Layout { Horizontal, Vertical, Auto, } +impl FromStr for Language { + type Err = String; + + fn from_str(s: &str) -> Result<Language, Self::Err> { + match s { + "cxx" => Ok(Language::Cxx), + "Cxx" => Ok(Language::Cxx), + "CXX" => Ok(Language::Cxx), + "cpp" => Ok(Language::Cxx), + "Cpp" => Ok(Language::Cxx), + "CPP" => Ok(Language::Cxx), + "c++" => Ok(Language::Cxx), + "C++" => Ok(Language::Cxx), + "c" => Ok(Language::C), + "C" => Ok(Language::C), + _ => Err(format!("unrecognized Language: '{}'", s)), + } + } +} +impl FromStr for Braces { + type Err = String; + + fn from_str(s: &str) -> Result<Braces, Self::Err> { + match s { + "SameLine" => Ok(Braces::SameLine), + "same_line" => Ok(Braces::SameLine), + "NextLine" => Ok(Braces::NextLine), + "next_line" => Ok(Braces::NextLine), + _ => Err(format!("unrecognized Braces: '{}'", s)), + } + } +} +impl FromStr for Layout { + type Err = String; + + fn from_str(s: &str) -> Result<Layout, Self::Err> { + match s { + "Horizontal" => Ok(Layout::Horizontal), + "horizontal" => Ok(Layout::Horizontal), + "Vertical" => Ok(Layout::Vertical), + "vertical" => Ok(Layout::Vertical), + "Auto" => Ok(Layout::Auto), + "auto" => Ok(Layout::Auto), + _ => Err(format!("unrecognized Layout: '{}'", s)), + } + } +} + +deserialize_enum_str!(Language); +deserialize_enum_str!(Braces); +deserialize_enum_str!(Layout); + #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] @@ -96,6 +145,8 @@ pub struct FunctionConfig { pub postfix: Option<String>, /// The style to layout the args pub args: Layout, + /// The rename rule to apply to function args + pub rename_args: Option<RenameRule>, } impl Default for FunctionConfig { @@ -104,6 +155,7 @@ impl Default for FunctionConfig { prefix: None, postfix: None, args: Layout::Auto, + rename_args: None, } } } @@ -113,6 +165,8 @@ impl Default for FunctionConfig { #[serde(deny_unknown_fields)] #[serde(default)] pub struct StructConfig { + /// The rename rule to apply to the name of struct fields + pub rename_fields: Option<RenameRule>, /// Whether to generate a piecewise equality operator pub derive_eq: bool, /// Whether to generate a piecewise inequality operator @@ -130,6 +184,7 @@ pub struct StructConfig { impl Default for StructConfig { fn default() -> StructConfig { StructConfig { + rename_fields: None, derive_eq: false, derive_neq: false, derive_lt: false, @@ -145,6 +200,8 @@ impl Default for StructConfig { #[serde(deny_unknown_fields)] #[serde(default)] pub struct EnumConfig { + /// The rename rule to apply to the name of enum variants + pub rename_variants: Option<RenameRule>, /// Whether to add a `Sentinel` value at the end of every enum /// This is useful in Gecko for IPC serialization pub add_sentinel: bool, @@ -153,6 +210,7 @@ pub struct EnumConfig { impl Default for EnumConfig { fn default() -> EnumConfig { EnumConfig { + rename_variants: None, add_sentinel: false, } } @@ -209,8 +267,10 @@ impl Config { prefix: Some(String::from("WR_INLINE")), postfix: Some(String::from("WR_FUNC")), args: Layout::Vertical, + rename_args: Some(RenameRule::GeckoCase), }, structure: StructConfig { + rename_fields: Some(RenameRule::GeckoCase), derive_eq: true, derive_neq: false, derive_lt: false, @@ -219,6 +279,7 @@ impl Config { derive_gte: false, }, enumeration: EnumConfig { + rename_variants: None, add_sentinel: true, }, } diff --git a/src/bindgen/items.rs b/src/bindgen/items.rs index dc518c8deebe77a18cb01a18f1547acffc65d881..c1db5eefad29c18521ebae43332cb31ef6e8c378 100644 --- a/src/bindgen/items.rs +++ b/src/bindgen/items.rs @@ -6,6 +6,7 @@ use syn::*; use bindgen::config::{Config, Layout}; use bindgen::directive::*; use bindgen::library::*; +use bindgen::rename::*; use bindgen::syn_helpers::*; use bindgen::writer::*; @@ -368,6 +369,16 @@ impl Function { }) } + pub fn resolve(&mut self, config: &Config) { + if let Some(r) = config.function.rename_args { + self.args = self.args.iter() + .map(|x| (r.apply_to_snake_case(&x.0, + RenameContext::FunctionArg), + x.1.clone())) + .collect() + } + } + pub fn add_deps(&self, library: &Library, out: &mut Vec<PathValue>) { if let &Some(ref ret) = &self.ret { ret.add_deps(library, out); @@ -512,6 +523,28 @@ impl Struct { }) } + pub fn resolve(&mut self, config: &Config) { + if let Some(o) = self.directives.list("field-names") { + let mut overriden_fields = Vec::new(); + + for (i, &(ref name, ref ty)) in self.fields.iter().enumerate() { + if i >= o.len() { + overriden_fields.push((name.clone(), ty.clone())); + } else { + overriden_fields.push((o[i].clone(), ty.clone())); + } + } + + self.fields = overriden_fields; + } else if let Some(r) = config.structure.rename_fields { + self.fields = self.fields.iter() + .map(|x| (r.apply_to_snake_case(&x.0, + RenameContext::StructMember), + x.1.clone())) + .collect(); + } + } + pub fn add_deps(&self, library: &Library, out: &mut Vec<PathValue>) { for &(_, ref ty) in &self.fields { ty.add_deps_with_generics(&self.generic_params, library, out); @@ -521,29 +554,10 @@ impl Struct { pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { assert!(self.generic_params.is_empty()); - // This needs to be done here because `type` specialization may - // provide an alternative list of directives to use for renaming - let fields = match self.directives.list("field-names") { - Some(overrides) => { - let mut overriden_fields = Vec::new(); - - for (i, &(ref name, ref ty)) in self.fields.iter().enumerate() { - if i >= overrides.len() { - overriden_fields.push((name.clone(), ty.clone())); - } else { - overriden_fields.push((overrides[i].clone(), ty.clone())); - } - } - - overriden_fields - } - _ => self.fields.clone(), - }; - out.write(&format!("struct {}", self.name)); out.open_brace(); - for (i, &(ref name, ref ty)) in fields.iter().enumerate() { + for (i, &(ref name, ref ty)) in self.fields.iter().enumerate() { if i != 0 { out.new_line() } @@ -560,36 +574,36 @@ impl Struct { out.write(&format!("bool operator{}(const {}& aOther) const", op, self.name)); out.open_brace(); out.write("return "); - out.write_aligned_list(fields.iter() - .map(|x| format!("{} {} aOther.{}", x.0, op, x.0)) - .collect(), + out.write_aligned_list(self.fields.iter() + .map(|x| format!("{} {} aOther.{}", x.0, op, x.0)) + .collect(), format!(" {}", conjuc)); out.write(";"); out.close_brace(false); }; if config.structure.derive_eq(&self.directives) && - !fields.is_empty() && fields.iter().all(|x| x.1.can_cmp_eq()) { + !self.fields.is_empty() && self.fields.iter().all(|x| x.1.can_cmp_eq()) { emit_op("==", "&&"); } if config.structure.derive_neq(&self.directives) && - !fields.is_empty() && fields.iter().all(|x| x.1.can_cmp_eq()) { + !self.fields.is_empty() && self.fields.iter().all(|x| x.1.can_cmp_eq()) { emit_op("!=", "||"); } if config.structure.derive_lt(&self.directives) && - fields.len() == 1 && fields[0].1.can_cmp_order() { + self.fields.len() == 1 && self.fields[0].1.can_cmp_order() { emit_op("<", "&&"); } if config.structure.derive_lte(&self.directives) && - fields.len() == 1 && fields[0].1.can_cmp_order() { + self.fields.len() == 1 && self.fields[0].1.can_cmp_order() { emit_op("<=", "&&"); } if config.structure.derive_gt(&self.directives) && - fields.len() == 1 && fields[0].1.can_cmp_order() { + self.fields.len() == 1 && self.fields[0].1.can_cmp_order() { emit_op(">", "&&"); } if config.structure.derive_gte(&self.directives) && - fields.len() == 1 && fields[0].1.can_cmp_order() { + self.fields.len() == 1 && self.fields[0].1.can_cmp_order() { emit_op(">=", "&&"); } } @@ -682,6 +696,16 @@ impl Enum { }) } + pub fn resolve(&mut self, config: &Config) { + if let Some(r) = config.enumeration.rename_variants { + self.values = self.values.iter() + .map(|x| (r.apply_to_pascal_case(&x.0, + RenameContext::EnumVariant), + x.1.clone())) + .collect(); + } + } + pub fn write<F: Write>(&self, config: &Config, out: &mut Writer<F>) { let size = match self.repr { Repr::U32 => "uint32_t", diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 3b60934559557b3d255f569f510540e04501f698..758fbe7927aef54185b70a65e66ac2f8973e0f24 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -251,6 +251,16 @@ impl<'a> Library<'a> { } }); + for (_, ref mut s) in &mut library.structs { + s.resolve(config); + } + for (_, ref mut f) in &mut library.functions { + f.resolve(config); + } + for (_, ref mut e) in &mut library.enums { + e.resolve(config); + } + library } diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index c944378536eec164e02ede55e3179a9c2e467ddd..572cc66890777f1d249d3993fb72fda4ccc89389 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -1,7 +1,37 @@ +macro_rules! deserialize_enum_str { + ($name:ident) => { + impl ::serde::Deserialize for $name { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: ::serde::Deserializer + { + struct Visitor; + impl ::serde::de::Visitor for Visitor { + type Value = $name; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("$name") + } + + fn visit_str<E>(self, v: &str) -> Result<$name, E> + where E: ::serde::de::Error + { + match v.parse::<$name>() { + Ok(v) => Ok(v), + Err(m) => Err(E::custom(m)), + } + } + } + deserializer.deserialize_str(Visitor) + } + } + } +} + mod config; mod directive; mod items; mod library; +mod rename; mod rust_lib; mod syn_helpers; mod writer; diff --git a/src/bindgen/rename.rs b/src/bindgen/rename.rs new file mode 100644 index 0000000000000000000000000000000000000000..7fb11ba7f513ce5c2a372941a376acef36debbb5 --- /dev/null +++ b/src/bindgen/rename.rs @@ -0,0 +1,166 @@ +use std::str::FromStr; + +#[derive(Debug, Clone, Copy)] +pub enum RenameContext { + StructMember, + EnumVariant, + FunctionArg, +} +impl RenameContext { + fn to_str(&self) -> &'static str { + match *self { + RenameContext::StructMember => "m", + RenameContext::EnumVariant => "", + RenameContext::FunctionArg => "a", + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum RenameRule { + GeckoCase, + LowerCase, + UpperCase, + PascalCase, + CamelCase, + SnakeCase, + ScreamingSnakeCase, +} + +impl RenameRule { + pub fn apply_to_pascal_case(&self, text: &str, context: RenameContext) -> String { + if text.len() == 0 { + return String::new(); + } + + match *self { + RenameRule::GeckoCase => context.to_str().to_owned() + text, + RenameRule::LowerCase => text.to_lowercase(), + RenameRule::UpperCase => text.to_uppercase(), + RenameRule::PascalCase => text.to_owned(), + RenameRule::CamelCase => { + text[..1].to_lowercase() + &text[1..] + } + RenameRule::SnakeCase => { + let mut result = String::new(); + for (i, c) in text.char_indices() { + if c.is_uppercase() && i != 0 { + result.push_str("_"); + } + for x in c.to_lowercase() { + result.push(x); + } + } + result + } + RenameRule::ScreamingSnakeCase => { + // Same as SnakeCase code above, but uses to_uppercase + let mut result = String::new(); + for (i, c) in text.char_indices() { + if c.is_uppercase() && i != 0 { + result.push_str("_"); + } + for x in c.to_uppercase() { + result.push(x); + } + } + result + } + } + } + + pub fn apply_to_snake_case(&self, text: &str, context: RenameContext) -> String { + if text.len() == 0 { + return String::new(); + } + + match *self { + RenameRule::GeckoCase => { + context.to_str().to_owned() + + &RenameRule::PascalCase.apply_to_snake_case(text, context) + } + RenameRule::LowerCase => text.to_lowercase(), + RenameRule::UpperCase => text.to_uppercase(), + RenameRule::PascalCase => { + let mut result = String::new(); + let mut is_uppercase = true; + for c in text.chars() { + if c == '_' { + is_uppercase = true; + continue; + } + + if is_uppercase { + for x in c.to_uppercase() { + result.push(x); + } + is_uppercase = false; + } else { + result.push(c); + } + } + result + } + RenameRule::CamelCase => { + // Same as PascalCase code above, but is_uppercase = false to start + let mut result = String::new(); + let mut is_uppercase = false; + for c in text.chars() { + if c == '_' { + is_uppercase = true; + continue; + } + + if is_uppercase { + for x in c.to_uppercase() { + result.push(x); + } + is_uppercase = false; + } else { + result.push(c); + } + } + result + } + RenameRule::SnakeCase => text.to_owned(), + RenameRule::ScreamingSnakeCase => text.to_owned().to_uppercase(), + } + } +} + +impl FromStr for RenameRule { + type Err = String; + + fn from_str(s: &str) -> Result<RenameRule, Self::Err> { + match s { + "mGeckoCase" => Ok(RenameRule::GeckoCase), + "GeckoCase" => Ok(RenameRule::GeckoCase), + "gecko_case" => Ok(RenameRule::GeckoCase), + + "lowercase" => Ok(RenameRule::LowerCase), + "LowerCase" => Ok(RenameRule::LowerCase), + "lower_case" => Ok(RenameRule::LowerCase), + + "UPPERCASE" => Ok(RenameRule::UpperCase), + "UpperCase" => Ok(RenameRule::UpperCase), + "upper_case" => Ok(RenameRule::UpperCase), + + "PascalCase" => Ok(RenameRule::PascalCase), + "pascal_case" => Ok(RenameRule::PascalCase), + + "camelCase" => Ok(RenameRule::CamelCase), + "CamelCase" => Ok(RenameRule::CamelCase), + "camel_case" => Ok(RenameRule::CamelCase), + + "snake_case" => Ok(RenameRule::SnakeCase), + "SnakeCase" => Ok(RenameRule::SnakeCase), + + "SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase), + "ScreamingSnakeCase" => Ok(RenameRule::ScreamingSnakeCase), + "screaming_snake_case" => Ok(RenameRule::ScreamingSnakeCase), + + _ => Err(format!("unrecognized RenameRule: '{}'", s)), + } + } +} +deserialize_enum_str!(RenameRule);