use std::collections::HashMap;
use std::default::Default;
use std::str::FromStr;
use std::{fmt, fs, path::Path as StdPath};
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;
use bindgen::ir::repr::ReprAlign;
pub use bindgen::rename::RenameRule;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Language {
Cxx,
C,
}
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)),
}
}
}
deserialize_enum_str!(Language);
#[derive(Debug, Clone, PartialEq)]
pub enum Braces {
SameLine,
NextLine,
}
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)),
}
}
}
deserialize_enum_str!(Braces);
#[derive(Debug, Clone, PartialEq)]
pub enum Layout {
Horizontal,
Vertical,
Auto,
}
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!(Layout);
#[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<DocumentationStyle, Self::Err> {
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);
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Style {
Both,
Tag,
Type,
}
impl Style {
pub fn generate_tag(self) -> bool {
match self {
Style::Both | Style::Tag => true,
Style::Type => false,
}
}
pub fn generate_typedef(self) -> bool {
match self {
Style::Both | Style::Type => true,
Style::Tag => false,
}
}
}
impl FromStr for Style {
type Err = String;
fn from_str(s: &str) -> Result<Style, Self::Err> {
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);
#[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<Self, Self::Err> {
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);
#[derive(Debug, Clone, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct ExportConfig {
pub include: Vec<String>,
pub exclude: Vec<String>,
pub rename: HashMap<String, String>,
pub body: HashMap<String, String>,
pub prefix: Option<String>,
pub item_types: Vec<ItemType>,
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);
}
}
}
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct LayoutConfig {
pub packed: Option<String>,
pub aligned_n: Option<String>,
}
impl LayoutConfig {
pub(crate) fn ensure_safe_to_represent(&self, align: &ReprAlign) -> Result<(), String> {
match (align, &self.packed, &self.aligned_n) {
(ReprAlign::Packed, None, _) => Err("Cannot safely represent #[repr(packed)] type without configured 'packed' annotation.".to_string()),
(ReprAlign::Align(_), _, None) => Err("Cannot safely represent #[repr(aligned(...))] type without configured 'aligned_n' annotation.".to_string()),
_ => Ok(()),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct FunctionConfig {
pub prefix: Option<String>,
pub postfix: Option<String>,
pub must_use: Option<String>,
pub args: Layout,
pub rename_args: Option<RenameRule>,
}
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<String> {
if let Some(x) = annotations.atom("prefix") {
return x;
}
self.prefix.clone()
}
pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option<String> {
if let Some(x) = annotations.atom("postfix") {
return x;
}
self.postfix.clone()
}
}
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct StructConfig {
pub rename_fields: Option<RenameRule>,
pub derive_constructor: bool,
pub derive_eq: bool,
pub derive_neq: bool,
pub derive_lt: bool,
pub derive_lte: bool,
pub derive_gt: bool,
pub derive_gte: bool,
pub associated_constants_in_body: bool,
pub must_use: Option<String>,
}
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
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct EnumConfig {
pub rename_variants: Option<RenameRule>,
pub add_sentinel: bool,
pub prefix_with_name: bool,
pub derive_helper_methods: bool,
pub derive_const_casts: bool,
pub derive_mut_casts: bool,
pub cast_assert_name: Option<String>,
pub must_use: Option<String>,
pub derive_tagged_enum_destructor: bool,
pub derive_tagged_enum_copy_constructor: bool,
pub derive_tagged_enum_copy_assignment: bool,
pub private_default_tagged_enum_constructor: bool,
}
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
}
pub(crate) fn derive_tagged_enum_destructor(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-tagged-enum-destructor") {
return x;
}
self.derive_tagged_enum_destructor
}
pub(crate) fn derive_tagged_enum_copy_constructor(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-tagged-enum-copy-constructor") {
return x;
}
self.derive_tagged_enum_copy_constructor
}
pub(crate) fn derive_tagged_enum_copy_assignment(&self, annotations: &AnnotationSet) -> bool {
if let Some(x) = annotations.bool("derive-tagged-enum-copy-assignment") {
return x;
}
self.derive_tagged_enum_copy_assignment
}
pub(crate) fn private_default_tagged_enum_constructor(
&self,
annotations: &AnnotationSet,
) -> bool {
if let Some(x) = annotations.bool("private-default-tagged-enum-constructor") {
return x;
}
self.private_default_tagged_enum_constructor
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct ConstantConfig {
pub allow_static_const: bool,
}
impl Default for ConstantConfig {
fn default() -> ConstantConfig {
ConstantConfig {
allow_static_const: true,
}
}
}
#[derive(Debug, Clone, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct MacroExpansionConfig {
pub bitflags: bool,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct ParseExpandConfig {
pub crates: Vec<String>,
pub all_features: bool,
pub default_features: bool,
pub features: Option<Vec<String>>,
}
impl Default for ParseExpandConfig {
fn default() -> ParseExpandConfig {
ParseExpandConfig {
crates: Vec::new(),
all_features: false,
default_features: true,
features: None,
}
}
}
fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<ParseExpandConfig, D::Error> {
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<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
let crates =
<Vec<String> as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?;
Ok(ParseExpandConfig {
crates,
all_features: true,
default_features: true,
features: None,
})
}
fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
<ParseExpandConfig as Deserialize>::deserialize(MapAccessDeserializer::new(map))
}
}
deserializer.deserialize_any(ParseExpandVisitor)
}
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct ParseConfig {
pub parse_deps: bool,
pub include: Option<Vec<String>>,
pub exclude: Vec<String>,
#[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")]
pub expand: ParseExpandConfig,
pub clean: bool,
pub extra_bindings: Vec<String>,
}
impl ParseConfig {
pub(crate) fn should_generate_top_level_item(
&self,
crate_name: &str,
binding_crate_name: &str,
) -> bool {
if crate_name == binding_crate_name {
return true;
}
self.extra_bindings.iter().any(|dep| dep == crate_name)
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub struct Config {
pub header: Option<String>,
pub includes: Vec<String>,
pub sys_includes: Vec<String>,
pub trailer: Option<String>,
pub include_guard: Option<String>,
pub no_includes: bool,
pub autogen_warning: Option<String>,
pub include_version: bool,
pub namespace: Option<String>,
pub namespaces: Option<Vec<String>>,
pub using_namespaces: Option<Vec<String>>,
pub braces: Braces,
pub line_length: usize,
pub tab_width: usize,
pub language: Language,
pub cpp_compat: bool,
pub style: Style,
pub parse: ParseConfig,
pub export: ExportConfig,
pub macro_expansion: MacroExpansionConfig,
pub layout: LayoutConfig,
#[serde(rename = "fn")]
pub function: FunctionConfig,
#[serde(rename = "struct")]
pub structure: StructConfig,
#[serde(rename = "enum")]
pub enumeration: EnumConfig,
#[serde(rename = "const")]
pub constant: ConstantConfig,
pub defines: HashMap<String, String>,
pub documentation: bool,
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,
using_namespaces: None,
braces: Braces::SameLine,
line_length: 100,
tab_width: 2,
language: Language::Cxx,
cpp_compat: false,
style: Style::Type,
macro_expansion: Default::default(),
parse: ParseConfig::default(),
export: ExportConfig::default(),
layout: LayoutConfig::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<P: AsRef<StdPath>>(file_name: P) -> Result<Config, String> {
let config_text = fs::read_to_string(file_name.as_ref()).or_else(|_| {
Err(format!(
"Couldn't open config file: {}.",
file_name.as_ref().display()
))
})?;
match toml::from_str::<Config>(&config_text) {
Ok(x) => Ok(x),
Err(e) => Err(format!("Couldn't parse config file: {}.", e)),
}
}
pub fn from_root_or_default<P: AsRef<StdPath>>(root: P) -> Config {
let c = root.as_ref().join("cbindgen.toml");
if c.exists() {
Config::from_file(c).unwrap()
} else {
Config::default()
}
}
}