Utilities for shaping Rust code generated from C headers with bindgen.
This crate re-exports bindgen and adds a small wrapper for common cleanup tasks: renaming generated items, rustifying and renaming C enums, applying regex removals and case conversion to identifiers, and collecting matching integer #define constants into additional Rust enums.
The lower-level Renamer type implements bindgen parse callbacks directly for existing bindgen-style build scripts.
For new code, BindingsBuilder wraps an already configured bindgen Builder, installs the callbacks, runs generation, and writes the final bindings with any helper-generated enums appended.
# Cargo.toml
[build-dependencies]
# Bindgen_helpers re-exports all of bindgen's public API at the root level
# Do not use bindgen directly to avoid some version conflicts
bindgen_helpers = "0.3"// build.rs
use bindgen_helpers::{BindingsBuilder, Builder, define_enum, rename_enum};
fn main() {
let builder = Builder::default()
// in real code, use .header("path/to/header.h")
.header_contents("test.h", r#"
struct my_struct {
int a;
};
enum my_enum {
I_SAID_YES_ENUM,
I_SAID_NO_ENUM,
I_SAID_MV_IT_ENUM,
I_SAID_MV_IT2_ENUM,
};
#define ERR_FOO 1
#define ERR_BAR 2
"#);
// Wrap the builder with the helper to handle all additional functionality
// true to print debug info about the renames to stderr, false to be silent
let mut helpers = BindingsBuilder::new(builder, true);
// rename a single item, e.g. a struct, enum, or a typedef
helpers.rename_item("my_struct", "MyStruct");
// rename an enum and its values
rename_enum!(
helpers,
"my_enum" => "MyEnum", // rename the enum itself
remove: "^I_SAID_", // optionally any number of "remove" regexes
remove: "_ENUM$",
case: Pascal, // optionally set case convert, defaults to "PascalCase"
"MV_IT" => "Value1", // rename a specific value after pattern removal
"MV_IT2" => "Value2", // more specific value renames
);
// Collect matching #define constants into an additional Rust enum.
// Make sure parameters are given in this order. Skipping is ok.
define_enum!(
helpers,
ErrorCode, // enum name
r"^ERR_", // include matching integer defines
repr = u32, // optionally override auto-computed repr type
min: 0, // optionally include only values >= min
max: 999, // optionally include only values <= max
exclude: "_PRIVATE$", // optionally exclude matching defines
sort: Value, // optionally sort by Name, Value, or ValueDesc
derive: [Debug, Copy, Clone, PartialEq, Eq], // optionally override derives
remove: "^ERR_", // optionally any number of "remove" regexes
case: Pascal, // optionally set case convert, defaults to "PascalCase"
"FOOBAR" => "FooBar", // rename specific values after pattern removal, e.g. to fix some special cases that don't follow the general pattern
);
let bindings = helpers.into_string().unwrap();
}
//
// This is the approximate code that would be generated by the above:
//
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MyStruct {
pub a: ::std::os::raw::c_int,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum MyEnum {
Yes = 0,
No = 1,
Value1 = 2,
Value2 = 3,
}
pub const ERR_FOO: u32 = 1;
pub const ERR_BAR: u32 = 2;
#[repr(u32)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum ErrorCode {
Foo = (ERR_FOO as u32),
Bar = (ERR_BAR as u32),
}See the list of all case variants supported by the convert_case crate.
- This project is easier to develop with just, a modern alternative to
make. Install it withcargo install just. - To get a list of available commands, run
just. - To run tests, use
just test.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT) at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.