/* 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::collections::HashMap; use std::default::Default; use std::fmt; use std::fs::File; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::Path as StdPath; use std::str::FromStr; use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer}; use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; use toml; use bindgen::ir::annotation::AnnotationSet; use bindgen::ir::path::Path; pub use bindgen::rename::RenameRule; pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); /// A language type to generate bindings for. #[derive(Debug, Copy, Clone, PartialEq)] pub enum Language { Cxx, C, } impl FromStr for Language { type Err = String; fn from_str(s: &str) -> Result { 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)), } } } deserialize_enum_str!(Language); /// A style of braces to use for generating code. #[derive(Debug, Clone, PartialEq)] pub enum Braces { SameLine, NextLine, } impl FromStr for Braces { type Err = String; fn from_str(s: &str) -> Result { 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)), } } } deserialize_enum_str!(Braces); /// A type of layout to use when generating long lines of code. #[derive(Debug, Clone, PartialEq)] pub enum Layout { Horizontal, Vertical, Auto, } impl FromStr for Layout { type Err = String; fn from_str(s: &str) -> Result { 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!(Layout); /// How the comments containing documentation should be styled. #[derive(Debug, Clone, PartialEq, Copy)] pub enum DocumentationStyle { C, C99, Doxy, Cxx, Auto, } impl FromStr for DocumentationStyle { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "c" => Ok(DocumentationStyle::C), "c99" => Ok(DocumentationStyle::C99), "cxx" => Ok(DocumentationStyle::Cxx), "c++" => Ok(DocumentationStyle::Cxx), "doxy" => Ok(DocumentationStyle::Doxy), "auto" => Ok(DocumentationStyle::Auto), _ => Err(format!("Unrecognized documentation style: '{}'.", s)), } } } deserialize_enum_str!(DocumentationStyle); /// A style of Style to use when generating structs and enums. #[derive(Debug, Copy, Clone, PartialEq)] pub enum Style { Both, Tag, Type, } impl Style { pub fn generate_tag(&self) -> bool { match self { &Style::Both => true, &Style::Tag => true, &Style::Type => false, } } pub fn generate_typedef(&self) -> bool { match self { &Style::Both => true, &Style::Tag => false, &Style::Type => true, } } } impl FromStr for Style { type Err = String; fn from_str(s: &str) -> Result { match s { "Both" => Ok(Style::Both), "both" => Ok(Style::Both), "Tag" => Ok(Style::Tag), "tag" => Ok(Style::Tag), "Type" => Ok(Style::Type), "type" => Ok(Style::Type), _ => Err(format!("Unrecognized Style: '{}'.", s)), } } } deserialize_enum_str!(Style); /// Different item types that we can generate and filter. #[derive(Debug, Clone, PartialEq)] pub enum ItemType { Constants, Globals, Enums, Structs, Unions, Typedefs, OpaqueItems, Functions, } impl FromStr for ItemType { type Err = String; fn from_str(s: &str) -> Result { use self::ItemType::*; Ok(match &*s.to_lowercase() { "constants" => Constants, "globals" => Globals, "enums" => Enums, "structs" => Structs, "unions" => Unions, "typedefs" => Typedefs, "opaque" => OpaqueItems, "functions" => Functions, _ => return Err(format!("Unrecognized Style: '{}'.", s)), }) } } deserialize_enum_str!(ItemType); /// Settings to apply when exporting items. #[derive(Debug, Clone, Deserialize, Default)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ExportConfig { /// A list of additional items not used by exported functions to include in /// the generated bindings pub include: Vec, /// A list of items to not include in the generated bindings pub exclude: Vec, /// Table of name conversions to apply to item names pub rename: HashMap, /// Table of raw strings to append to the body of items. pub body: HashMap, /// A prefix to add before the name of every item pub prefix: Option, /// Types of items to generate. pub item_types: Vec, /// Whether renaming overrides or extends prefixing. pub renaming_overrides_prefixing: bool, } impl ExportConfig { pub(crate) fn should_generate(&self, item_type: ItemType) -> bool { self.item_types.is_empty() || self.item_types.contains(&item_type) } pub(crate) fn extra_body(&self, path: &Path) -> Option<&str> { self.body.get(path.name()).map(|s| s.trim_matches('\n')) } pub(crate) fn rename(&self, item_name: &mut String) { if let Some(name) = self.rename.get(item_name) { *item_name = name.clone(); if self.renaming_overrides_prefixing { return; } } if let Some(ref prefix) = self.prefix { item_name.insert_str(0, &prefix); } } } /// Settings to apply to generated functions. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct FunctionConfig { /// Optional text to output before each function declaration pub prefix: Option, /// Optional text to output after each function declaration pub postfix: Option, /// The way to annotation this function as #[must_use]. pub must_use: Option, /// The style to layout the args pub args: Layout, /// The rename rule to apply to function args pub rename_args: Option, } impl Default for FunctionConfig { fn default() -> FunctionConfig { FunctionConfig { prefix: None, postfix: None, must_use: None, args: Layout::Auto, rename_args: None, } } } impl FunctionConfig { pub(crate) fn prefix(&self, annotations: &AnnotationSet) -> Option { if let Some(x) = annotations.atom("prefix") { return x; } self.prefix.clone() } pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option { if let Some(x) = annotations.atom("postfix") { return x; } self.postfix.clone() } } /// Settings to apply to generated structs. #[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct StructConfig { /// The rename rule to apply to the name of struct fields pub rename_fields: Option, /// Whether to generate a constructor for the struct (which takes /// arguments to initialize all the members) pub derive_constructor: bool, /// Whether to generate a piecewise equality operator pub derive_eq: bool, /// Whether to generate a piecewise inequality operator pub derive_neq: bool, /// Whether to generate a less than operator on structs with one field pub derive_lt: bool, /// Whether to generate a less than or equal to operator on structs with one field pub derive_lte: bool, /// Whether to generate a greater than operator on structs with one field pub derive_gt: bool, /// Whether to generate a greater than or equal to operator on structs with one field pub derive_gte: bool, /// Whether associated constants should be in the body. Only applicable to /// non-transparent structs, and in C++-only. pub associated_constants_in_body: bool, /// The way to annotation this struct as #[must_use]. pub must_use: Option, } impl StructConfig { pub(crate) fn derive_constructor(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-constructor") { return x; } self.derive_constructor } pub(crate) fn derive_eq(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-eq") { return x; } self.derive_eq } pub(crate) fn derive_neq(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-neq") { return x; } self.derive_neq } pub(crate) fn derive_lt(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-lt") { return x; } self.derive_lt } pub(crate) fn derive_lte(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-lte") { return x; } self.derive_lte } pub(crate) fn derive_gt(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-gt") { return x; } self.derive_gt } pub(crate) fn derive_gte(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-gte") { return x; } self.derive_gte } } /// Settings to apply to generated enums. #[derive(Debug, Clone, Default, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct EnumConfig { /// The rename rule to apply to the name of enum variants pub rename_variants: Option, /// Whether to add a `Sentinel` value at the end of every enum /// This is useful in Gecko for IPC serialization pub add_sentinel: bool, /// Whether the enum variants should be prefixed with the enum name pub prefix_with_name: bool, /// Whether to generate static `::X(..)` constructors and `IsX()` /// methods for tagged enums. pub derive_helper_methods: bool, /// Whether to generate `AsX() const` methods for tagged enums. pub derive_const_casts: bool, /// Whether to generate `AsX()` methods for tagged enums. pub derive_mut_casts: bool, /// The name of the macro to use for `derive_{const,mut}casts`. If custom, you're /// responsible to provide the necessary header, otherwise `assert` will be /// used, and `` will be included. pub cast_assert_name: Option, /// The way to annotation this enum as #[must_use]. pub must_use: Option, } impl EnumConfig { pub(crate) fn add_sentinel(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("add-sentinel") { return x; } self.add_sentinel } pub(crate) fn derive_helper_methods(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-helper-methods") { return x; } self.derive_helper_methods } pub(crate) fn derive_const_casts(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-const-casts") { return x; } self.derive_const_casts } pub(crate) fn derive_mut_casts(&self, annotations: &AnnotationSet) -> bool { if let Some(x) = annotations.bool("derive-mut-casts") { return x; } self.derive_mut_casts } } /// Settings to apply to generated constants. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ConstantConfig { /// Whether a generated constant can be a static const in C++ mode. pub allow_static_const: bool, } impl Default for ConstantConfig { fn default() -> ConstantConfig { ConstantConfig { allow_static_const: true, } } } /// Settings for custom macro expansion. #[derive(Debug, Clone, Deserialize, Default)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct MacroExpansionConfig { /// Whether the `bitflags` macro should be expanded. pub bitflags: bool, } /// Settings to apply when running `rustc --pretty=expanded` #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ParseExpandConfig { /// The names of crates to parse with `rustc --pretty=expanded` pub crates: Vec, /// Whether to enable all the features when expanding. pub all_features: bool, /// Whether to use the default feature set when expanding. pub default_features: bool, /// List of features to use when expanding. Combines with `default_features` like in /// `Cargo.toml`. pub features: Option>, } impl Default for ParseExpandConfig { fn default() -> ParseExpandConfig { ParseExpandConfig { crates: Vec::new(), all_features: false, default_features: true, features: None, } } } // Backwards-compatibility deserializer for ParseExpandConfig. This allows accepting both the // simple `expand = ["crate"]` and the more complex `expand = {"crates": ["crate"], // "default_features": false}` format for the `expand` key. // // Note that one (major) difference between the two forms is that, for backwards-compatibility // reasons, the `expand = ["crate"]` form will enable the `--all-features` flag by default while // the `expand = {"crates": ["crate"]}` form will use the default feature set by default. fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result { struct ParseExpandVisitor; impl<'de> Visitor<'de> for ParseExpandVisitor { type Value = ParseExpandConfig; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map or sequence of string") } fn visit_seq>(self, seq: A) -> Result { let crates = as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?; Ok(ParseExpandConfig { crates, all_features: true, default_features: true, features: None, }) } fn visit_map>(self, map: A) -> Result { ::deserialize(MapAccessDeserializer::new(map)) } } deserializer.deserialize_any(ParseExpandVisitor) } /// Settings to apply when parsing. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct ParseConfig { /// Whether to parse dependencies when generating bindings. When this is true, /// each dependent crate is found using a combination of `cargo metadata` and /// `Cargo.lock`. To further control this behavior, crates can be whitelisted or /// blacklisted using `include` and `exclude` respectively. Additionally in cases /// where crates have types to expose in bindings hidden in macros, a crate can /// be marked in `expand` and `cargo expand` will be used to expand the macros /// before parsing. A crate marked in `expand` doesn't need to be added to any /// whitelist. pub parse_deps: bool, /// An optional whitelist of names of crates to parse pub include: Option>, /// The names of crates to not parse pub exclude: Vec, /// The configuration options for `rustc --pretty=expanded` #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")] pub expand: ParseExpandConfig, /// Whether to use a new temporary target directory when running `rustc --pretty=expanded`. /// This may be required for some build processes. pub clean: bool, } impl Default for ParseConfig { fn default() -> ParseConfig { ParseConfig { parse_deps: false, include: None, exclude: Vec::new(), expand: ParseExpandConfig::default(), clean: false, } } } /// A collection of settings to customize the generated bindings. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(deny_unknown_fields)] #[serde(default)] pub struct Config { /// Optional text to output at the beginning of the file pub header: Option, /// A list of additional includes to put at the beginning of the generated header pub includes: Vec, /// A list of additional system includes to put at the beginning of the generated header pub sys_includes: Vec, /// Optional text to output at the end of the file pub trailer: Option, /// Optional name to use for an include guard pub include_guard: Option, /// Generates no includes at all. Overrides all other include options /// /// This option is useful when using cbindgen with tools such as python's cffi which /// doesn't understand include directives pub no_includes: bool, /// Optional text to output at major sections to deter manual editing pub autogen_warning: Option, /// Include a comment with the version of cbindgen used to generate the file pub include_version: bool, /// An optional name for the root namespace. Only applicable when language="C++" pub namespace: Option, /// An optional list of namespaces. Only applicable when language="C++" pub namespaces: Option>, /// The style to use for braces pub braces: Braces, /// The preferred length of a line, used for auto breaking function arguments pub line_length: usize, /// The amount of spaces in a tab pub tab_width: usize, /// The language to output bindings for pub language: Language, /// The style to declare structs, enums and unions in for C pub style: Style, /// The configuration options for parsing pub parse: ParseConfig, /// The configuration options for exporting pub export: ExportConfig, /// The configuration options for macros. pub macro_expansion: MacroExpansionConfig, /// 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, /// The configuration options for constants #[serde(rename = "const")] pub constant: ConstantConfig, /// Preprocessor defines to use when generating #ifdef's for #[cfg] pub defines: HashMap, /// Include doc comments from rust as documentation pub documentation: bool, /// How documentation comments should be styled. pub documentation_style: DocumentationStyle, } impl Default for Config { fn default() -> Config { Config { header: None, includes: Vec::new(), sys_includes: Vec::new(), trailer: None, include_guard: None, autogen_warning: None, include_version: false, no_includes: false, namespace: None, namespaces: None, braces: Braces::SameLine, line_length: 100, tab_width: 2, language: Language::Cxx, style: Style::Type, macro_expansion: Default::default(), parse: ParseConfig::default(), export: ExportConfig::default(), function: FunctionConfig::default(), structure: StructConfig::default(), enumeration: EnumConfig::default(), constant: ConstantConfig::default(), defines: HashMap::new(), documentation: true, documentation_style: DocumentationStyle::Auto, } } } impl Config { pub fn from_file>(file_name: P) -> Result { fn read(file_name: &StdPath) -> io::Result { let file = File::open(file_name)?; let mut reader = BufReader::new(&file); let mut contents = String::new(); reader.read_to_string(&mut contents)?; Ok(contents) } let config_text = read(file_name.as_ref()).unwrap(); match toml::from_str::(&config_text) { Ok(x) => Ok(x), Err(e) => Err(format!("Couldn't parse config file: {}.", e)), } } pub fn from_root_or_default>(root: P) -> Config { let c = root.as_ref().join("cbindgen.toml"); if c.exists() { Config::from_file(c).unwrap() } else { Config::default() } } }