Skip to content

Commit

Permalink
wip struct wrapping
Browse files Browse the repository at this point in the history
  • Loading branch information
ewpratten committed Apr 13, 2023
1 parent e93f846 commit e5396b1
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Expand Up @@ -18,6 +18,10 @@ exclude = [
]
build = "build/main.rs"

[dependencies]
profiling = "^1"
libc = "^0.2"

[build-dependencies]
bindgen = "^0.65.0"
cmake = "^0.1.49"
Expand Down
1 change: 1 addition & 0 deletions build/bind.rs
Expand Up @@ -77,6 +77,7 @@ pub fn generate_bindings(header_file: &str) {
let mut builder = bindgen::Builder::default()
.header(header_file)
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.wrap_unsafe_ops(true)
.blocklist_item("DEG2RAD")
.blocklist_item("PI")
.blocklist_item("RAD2DEG")
Expand Down
14 changes: 11 additions & 3 deletions build/main.rs
@@ -1,4 +1,7 @@
use crate::wrap::{raylib_api::RayLibApiDefinition, enums::wrap_exposed_enums, colors::wrap_default_colors};
use crate::wrap::{
colors::wrap_default_colors, enums::wrap_exposed_enums, profiling::auto_profile_exported_fns,

Check warning on line 2 in build/main.rs

View workflow job for this annotation

GitHub Actions / Rust project (ubuntu-latest)

unused import: `profiling::auto_profile_exported_fns`

Check warning on line 2 in build/main.rs

View workflow job for this annotation

GitHub Actions / Rust project (windows-latest)

unused import: `profiling::auto_profile_exported_fns`
raylib_api::RayLibApiDefinition, structs::wrap_exposed_structs,
};

mod bind;
mod wrap;
Expand All @@ -17,9 +20,14 @@ pub fn main() {
bind::generate_bindings("src/wrapper.h");

// Load the API definitions
let api_defs = RayLibApiDefinition::load("third_party/raylib/parser/output/raylib_api.json").unwrap();
let api_defs =
RayLibApiDefinition::load("third_party/raylib/parser/output/raylib_api.json").unwrap();

// Generate safe wrappers
wrap_exposed_enums(api_defs.clone());
wrap_default_colors(api_defs);
wrap_default_colors(api_defs.clone());
wrap_exposed_structs(api_defs);

// Make everything profile-able
// auto_profile_exported_fns(&format!("{}/bindings.rs", std::env::var("OUT_DIR").unwrap()));
}
5 changes: 4 additions & 1 deletion build/wrap/mod.rs
@@ -1,3 +1,6 @@
pub mod raylib_api;
pub mod type_xlat;
pub mod enums;
pub mod colors;
pub mod colors;
pub mod structs;
pub mod profiling;
26 changes: 26 additions & 0 deletions build/wrap/profiling.rs
@@ -0,0 +1,26 @@
use std::{fs::File, io::{Read, Write}};

use regex::Regex;

/// Finds all `pub fn` in a file and wraps them with `#[profiling::function]`
pub fn auto_profile_exported_fns(rust_file_path: &str) {

Check warning on line 6 in build/wrap/profiling.rs

View workflow job for this annotation

GitHub Actions / Rust project (ubuntu-latest)

function `auto_profile_exported_fns` is never used

Check warning on line 6 in build/wrap/profiling.rs

View workflow job for this annotation

GitHub Actions / Rust project (windows-latest)

function `auto_profile_exported_fns` is never used
// Open the file
let mut file = File::open(rust_file_path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();

// Find all `pub fn` and wrap them with `#[profiling::function]`
let exported_fn_re = Regex::new(r"\s{4}pub fn").unwrap();
let mut new_contents = String::new();
for line in contents.lines() {
if exported_fn_re.is_match(line) {
new_contents.push_str(&format!(" #[profiling::function]\n{}\n", line));
} else {
new_contents.push_str(&format!("{}\n", line));
}
}

// Re-write the file
let mut file = File::create(rust_file_path).unwrap();
file.write_all(new_contents.as_bytes()).unwrap();
}
16 changes: 16 additions & 0 deletions build/wrap/raylib_api.rs
Expand Up @@ -18,6 +18,21 @@ pub struct EnumVariant {
pub value: u32,
}

#[derive(Debug, Clone, serde::Deserialize)]
pub struct StructureMember {
pub name: String,
#[serde(rename = "type")]
pub kind: String,
pub description: String,
}

#[derive(Debug, Clone, serde::Deserialize)]
pub struct Structure {
pub name: String,
pub description: String,
pub fields: Vec<StructureMember>,
}

#[derive(Debug, Clone, serde::Deserialize)]
pub struct Enum {
pub name: String,
Expand All @@ -29,6 +44,7 @@ pub struct Enum {
pub struct RayLibApiDefinition {
pub defines: Vec<Definition>,
pub enums: Vec<Enum>,
pub structs: Vec<Structure>,
}

impl RayLibApiDefinition {
Expand Down
35 changes: 35 additions & 0 deletions build/wrap/structs.rs
@@ -0,0 +1,35 @@
use super::{raylib_api::RayLibApiDefinition, type_xlat::translate_c_type_to_rust};


pub fn wrap_exposed_structs(api_defs: RayLibApiDefinition) {
// Allocate an output buffer for lines
let mut lines = Vec::new();

// Handle each struct
for st in api_defs.structs {
// Write a doc comment with raylib's provided struct description
lines.push("".to_string());
lines.push(format!("/// {}", st.description));

// Write the struct definition
lines.push(format!("#[repr(C)]"));
lines.push(format!("pub struct {} {{", st.name));

// Write each field
for field in st.fields {
// Write a doc comment with raylib's provided field description
lines.push(format!(" /// {}", field.description));

// Write the field definition
lines.push(format!(" pub {}: {},", field.name, translate_c_type_to_rust(&field.kind)));
}

// Close the struct definition
lines.push(format!("}}"));
}

// Write the output file
let out_dir = std::env::var("OUT_DIR").unwrap();
let out_path = format!("{}/structs.rs", out_dir);
std::fs::write(out_path, lines.join("\n")).unwrap();
}
73 changes: 73 additions & 0 deletions build/wrap/type_xlat.rs
@@ -0,0 +1,73 @@
pub fn translate_c_type_to_rust(c_type: &str) -> String {
// match c_type {
// "float" => "f32".to_string(),
// "float *" => "std::ptr::null_mut()".to_string(),
// "int" => "i32".to_string(),
// "unsigned int" => "u32".to_string(),
// "unsigned char *" => "std::ptr::null_mut()".to_string(),
// "unsigned short *" => "std::ptr::null_mut()".to_string(),
// "bool" => "bool".to_string(),
// "void *" => "std::ptr::null_mut()".to_string(),
// "Texture" => "crate::structs::Texture".to_string(),
// "Rectangle" => "crate::structs::Rectangle".to_string(),
// "Rectangle *" => "std::ptr::null_mut()".to_string(),
// "Image" => "crate::structs::Image".to_string(),
// "Texture2D" => "crate::structs::Texture2D".to_string(),
// "GlyphInfo *" => "std::ptr::null_mut()".to_string(),
// "Vector3" => "crate::structs::Vector3".to_string(),
// "Vector2" => "crate::structs::Vector2".to_string(),
// "Color" => "crate::structs::Color".to_string(),
// "float[4]" => "[f32; 4]".to_string(),
// "MaterialMap *" => "std::ptr::null_mut()".to_string(),
// "Quaternion" => "crate::structs::Quaternion".to_string(),
// "char[32]" => "[u8; 32]".to_string(),
// _ => {panic!("Unknown C type: {}", c_type)}
// }

// Translate basic C types to their Rust equivalents
if let Some(ty) = match c_type {
"char" => Some("i8"),
"unsigned char" => Some("u8"),
"short" => Some("i16"),
"unsigned short" => Some("u16"),
"int" => Some("i32"),
"unsigned int" => Some("u32"),
"long" => Some("i64"),
"unsigned long" => Some("u64"),
"float" => Some("f32"),
"double" => Some("f64"),
"bool" => Some("bool"),
"char *" => Some("String"),
"void *" => Some("*mut libc::c_void"),
_ => None,
} {
return ty.to_string();
}

// For some reason, an internal data type is exposed.
if c_type == "rAudioBuffer *" || c_type == "rAudioProcessor *" {
return "std::ptr::null_mut()".to_string();
}


// If the type ends with a *, it's a pointer to an array of the type without the *
if c_type.ends_with("*") {
let ty = &c_type[..c_type.len() - 1].trim();
return format!("Vec<{}>", translate_c_type_to_rust(ty));
}

// If the type ends with `[N]`, it's an array of N elements of the type without the `[N]`
let arr_len_re = regex::Regex::new(r"\[(\d+)\]$").unwrap();
if let Some(caps) = arr_len_re.captures(c_type) {
let ty = &c_type[..c_type.len() - caps[0].len()].trim();
let len = &caps[1];
return format!("[{}; {}]", translate_c_type_to_rust(ty), len);
}

// Uppercase types are assumed to be structs
if c_type.chars().next().unwrap().is_uppercase() {
return format!("crate::structs::{}", c_type);
}

panic!("Unknown C type: {}", c_type)
}

0 comments on commit e5396b1

Please sign in to comment.