From f96fa2a9c72821e7e881dd30da09fed9e44658e9 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Mon, 25 Nov 2024 16:56:46 +1100 Subject: [PATCH 01/13] Fixed csharp instance method generation --- src/translators/csharp.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translators/csharp.rb b/src/translators/csharp.rb index c59167a..4bd56f7 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -105,7 +105,7 @@ def docs_signatures_for(function) end result.unshift "public #{method_data[:static]}#{method_data[:return_type]} #{method_data[:class_name]}.#{property_name.to_pascal_case()} { #{text} }" - else + elsif method_data[:method_name] result.unshift "public #{method_data[:static]}#{method_data[:return_type]} #{method_data[:class_name]}.#{method_data[:method_name]}(#{method_data[:params]});" end end @@ -116,7 +116,7 @@ def docs_signatures_for(function) def get_method_data(fn) { - method_name: fn[:name].to_s.to_pascal_case, + method_name: fn[:method_name].nil? ? nil : fn[:method_name].to_s.to_pascal_case, class_name: fn[:attributes][:class].nil? ? fn[:attributes][:static].to_pascal_case() : fn[:attributes][:class].to_pascal_case(), params: method_parameter_list_for(fn), args: method_argument_list_for(fn), From 761f67d5d417b00a118dde1962ec8e3535d96841 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Thu, 28 Nov 2024 03:09:37 +1100 Subject: [PATCH 02/13] Created erb template files --- .../rust/implementation/functions.rs.erb | 42 +++++++ .../rust/implementation/mappers.rs.erb | 8 +- .../rust/implementation/mappers/bool.rs.erb | 20 ++-- .../rust/implementation/mappers/char.rs.erb | 20 +--- .../rust/implementation/mappers/direct.rs.erb | 19 ++- .../rust/implementation/mappers/enum.rs.erb | 32 +++++ .../mappers/function_pointer.rs.erb | 14 +++ .../rust/implementation/mappers/string.rs.erb | 50 ++++++++ .../rust/implementation/mappers/struct.rs.erb | 30 +++++ .../mappers/typealias_pointer.rs.erb | 28 +++++ .../rust/implementation/mappers/vector.rs.erb | 84 +++++++++++++ .../rust/implementation/types.rs.erb | 10 ++ .../rust/implementation/types/callback.rs.erb | 70 +++++++++++ .../rust/implementation/types/enum.rs.erb | 33 ++++++ .../types/function_pointer.rs.erb | 20 ++++ .../implementation/types/functions.rs.erb | 15 +++ .../rust/implementation/types/struct.rs.erb | 44 +++++++ .../types/typealias_pointer.rs.erb | 22 ++++ res/translators/rust/splashkit.rs.erb | 81 ++----------- src/translators/csharp.rb | 1 - src/translators/rust.rb | 111 +++++++++--------- translate | 1 - 22 files changed, 591 insertions(+), 164 deletions(-) create mode 100644 res/translators/rust/implementation/functions.rs.erb create mode 100644 res/translators/rust/implementation/mappers/enum.rs.erb create mode 100644 res/translators/rust/implementation/mappers/function_pointer.rs.erb create mode 100644 res/translators/rust/implementation/mappers/string.rs.erb create mode 100644 res/translators/rust/implementation/mappers/struct.rs.erb create mode 100644 res/translators/rust/implementation/mappers/typealias_pointer.rs.erb create mode 100644 res/translators/rust/implementation/mappers/vector.rs.erb create mode 100644 res/translators/rust/implementation/types.rs.erb create mode 100644 res/translators/rust/implementation/types/callback.rs.erb create mode 100644 res/translators/rust/implementation/types/enum.rs.erb create mode 100644 res/translators/rust/implementation/types/function_pointer.rs.erb create mode 100644 res/translators/rust/implementation/types/functions.rs.erb create mode 100644 res/translators/rust/implementation/types/struct.rs.erb create mode 100644 res/translators/rust/implementation/types/typealias_pointer.rs.erb delete mode 120000 translate diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb new file mode 100644 index 0000000..2ded6fd --- /dev/null +++ b/res/translators/rust/implementation/functions.rs.erb @@ -0,0 +1,42 @@ +<%# + Create the SplashKit functions that wrap the sklib FFI calls +%> +<% + @functions.each do |function| +%> +<%= sk_signature_for(function) %> { +<% + # Convert parameters to C types for FFI using existing mappers + function[:parameters].each do |param_name, param_data| + param_var = param_name.to_s.to_snake_case + # Escape 'fn' keyword if needed + param_var = "r##{param_var}" if param_var == 'fn' +%> + let __skparam__<%= param_name %> = <%= lib_mapper_fn_for param_data %>(<%= param_var %>); +<% + end +%> + unsafe { +<% + if is_func?(function) + return_type = function[:return][:type] + rust_return_type = sk_type_for(function[:return]) +%> + let __skreturn = <%= lib_function_name_for function %>(<%= + lib_argument_list_for(function) + %>); + <%= sk_mapper_fn_for function[:return] %>(__skreturn) +<% + else +%> + <%= lib_function_name_for function %>(<%= + lib_argument_list_for(function) + %>) +<% + end +%> + } +} +<% + end # functions.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers.rs.erb b/res/translators/rust/implementation/mappers.rs.erb index c26614c..c35ca69 100644 --- a/res/translators/rust/implementation/mappers.rs.erb +++ b/res/translators/rust/implementation/mappers.rs.erb @@ -1,3 +1,9 @@ <%= read_template 'implementation/mappers/bool' %> <%= read_template 'implementation/mappers/char' %> -<%= read_template 'implementation/mappers/direct' %> \ No newline at end of file +<%= read_template 'implementation/mappers/direct' %> +<%= read_template 'implementation/mappers/enum' %> +<%= read_template 'implementation/mappers/struct' %> +<%= read_template 'implementation/mappers/vector' %> +<%= read_template 'implementation/mappers/string' %> +<%= read_template 'implementation/mappers/function_pointer' %> +<%= read_template 'implementation/mappers/typealias_pointer' %> diff --git a/res/translators/rust/implementation/mappers/bool.rs.erb b/res/translators/rust/implementation/mappers/bool.rs.erb index b1abf9f..bd51c11 100644 --- a/res/translators/rust/implementation/mappers/bool.rs.erb +++ b/res/translators/rust/implementation/mappers/bool.rs.erb @@ -1,16 +1,12 @@ <%# Mappers from `bool` <-> `i32` + Based on bindgen's actual implementation %> -fn __skadapter__to_sklib_bool(v: bool) -> i32 -{ - if v { - return -1; - } else { - return 0; - } -} - -fn __skadapter__to_bool(v: i32) -> bool -{ - return v != 0; +#[inline] +pub(crate) fn __skadapter__to_sklib_bool(v: bool) -> i32 { + if v { 1 } else { 0 } } +#[inline] +pub(crate) fn __skadapter__to_bool(v: i32) -> bool { + v != 0 +} \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/char.rs.erb b/res/translators/rust/implementation/mappers/char.rs.erb index 89efa8e..29fc421 100644 --- a/res/translators/rust/implementation/mappers/char.rs.erb +++ b/res/translators/rust/implementation/mappers/char.rs.erb @@ -1,17 +1,9 @@ -fn __skadapter__to_sklib_char(v: char) -> u8 { - return v as u8; +#[inline] +pub(crate) fn __skadapter__to_sklib_char(v: char) -> i8 { + return v as i8; } - -fn __skadapter__to_char(v: u8) -> char +#[inline] +pub(crate) fn __skadapter__to_char(v: i8) -> char { - return v as char; -} -fn __skadapter__to_sklib_unsigned_char(v: char) -> u8 -{ - return v as u8; -} - -fn __skadapter__to_unsigned_char(v: u8) -> char -{ - return v as char; + return v as u8 as char; } diff --git a/res/translators/rust/implementation/mappers/direct.rs.erb b/res/translators/rust/implementation/mappers/direct.rs.erb index a6c9693..b4b786f 100644 --- a/res/translators/rust/implementation/mappers/direct.rs.erb +++ b/res/translators/rust/implementation/mappers/direct.rs.erb @@ -1,21 +1,20 @@ <%# - Direct type mappers have no change between the C- and rust-types and + Direct type mappers have no change between the C and Rust types and can therefore directly translate one to the other by just returning the value %> <% - direct_types.each do |c_type, rs_type| + direct_types.each do |c_type, rust_type| c_type = c_type.tr("\s", "_") %> -fn __skadapter__to_sklib_<%= c_type %>(v: <%= rs_type %>) -> <%= rs_type %> -{ - return v; +#[inline] +pub(crate) fn __skadapter__to_sklib_<%= c_type %>(v: <%= rust_type %>) -> <%= rust_type %> { + v } - -fn __skadapter__to_<%= c_type %>(v: <%= rs_type %>) -> <%= rs_type %> -{ - return v; +#[inline] +pub(crate) fn __skadapter__to_<%= c_type %>(v: <%= rust_type %>) -> <%= rust_type %> { + v } <% end -%> +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/enum.rs.erb b/res/translators/rust/implementation/mappers/enum.rs.erb new file mode 100644 index 0000000..1f4ca07 --- /dev/null +++ b/res/translators/rust/implementation/mappers/enum.rs.erb @@ -0,0 +1,32 @@ +<%# + Enum mapper from Rust enum <-> C int +%> +<% @enums.each do |enum| %> +<% + # Track seen numbers to only use first instance + seen_numbers = {} + # Counter for non-numbered enums + counter = 0 +%> +pub(crate) fn __skadapter__to_<%= enum[:name] %>(v: i32) -> <%= enum[:name].to_pascal_case %> { + match v { +<% enum[:constants].each do |name, data| %> +<% if data[:number] %> +<% if !seen_numbers[data[:number]] %> +<% seen_numbers[data[:number]] = true %> + <%= data[:number] %> => <%= enum[:name].to_pascal_case %>::<%= name.to_s.to_pascal_case %>, +<% end %> +<% else %> + <%= counter %> => <%= enum[:name].to_pascal_case %>::<%= name.to_s.to_pascal_case %>, +<% counter += 1 %> +<% end %> +<% end %> + _ => panic!("Invalid <%= enum[:name].to_pascal_case %> value: {}", v) + } +} +#[inline] +pub(crate) fn __skadapter__to_sklib_<%= enum[:name] %>(v: <%= enum[:name].to_pascal_case %>) -> i32 { + v as i32 +} + +<% end # enums.each %> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/function_pointer.rs.erb b/res/translators/rust/implementation/mappers/function_pointer.rs.erb new file mode 100644 index 0000000..279a7ba --- /dev/null +++ b/res/translators/rust/implementation/mappers/function_pointer.rs.erb @@ -0,0 +1,14 @@ +<%# + Function pointer translations +%> +<% + @function_pointers.each do |fp| + sk_fp_type = fp[:name].type_case +%> +#[inline] +pub(crate) fn __skadapter__to_sklib_<%= fp[:name] %>(v: <%= sk_fp_type %>) -> <%= sk_fp_type %> { + v +} +<% + end # end function_pointers.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/string.rs.erb b/res/translators/rust/implementation/mappers/string.rs.erb new file mode 100644 index 0000000..011bbc3 --- /dev/null +++ b/res/translators/rust/implementation/mappers/string.rs.erb @@ -0,0 +1,50 @@ +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __sklib_string { + pub str: *mut c_char, + pub size: c_int, + pub ptr: __sklib_ptr, +} +impl __sklib_string { + pub fn new() -> Self { + Self { + str: null_mut(), + size: 0, + ptr: null_mut(), + } + } +} +pub(crate) fn __skadapter__to_sklib_string>(s: S) -> __sklib_string { + let s = s.as_ref(); + let c_string = CString::new(s).expect("Failed to create CString"); + let ptr = c_string.into_raw(); + let size = s.len() as c_int; + + __sklib_string { + str: ptr, + size, + ptr: null_mut(), + } +} + +pub(crate) fn __skadapter__to_string(v: __sklib_string) -> String { + unsafe { + let result = if v.str.is_null() { + String::new() + } else { + CStr::from_ptr(v.str) + .to_string_lossy() + .into_owned() + }; + + if !v.str.is_null() { + let _ = CString::from_raw(v.str); + } + + result + } +} + +pub(crate) fn __skadapter__free__sklib_string(_s: __sklib_string) { + // The actual freeing is handled by the C library +} \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/struct.rs.erb b/res/translators/rust/implementation/mappers/struct.rs.erb new file mode 100644 index 0000000..f485bd5 --- /dev/null +++ b/res/translators/rust/implementation/mappers/struct.rs.erb @@ -0,0 +1,30 @@ +<% @structs.each do |struct| %> +pub(crate) fn __skadapter__to_sklib_<%= struct[:name] %>(v: <%= struct[:name].type_case %>) -> __sklib_<%= struct[:name] %> { + let mut result = __sklib_<%= struct[:name] %>::new(); +<% struct[:fields].each do |field_name, field_data| + field_name = field_name + if field_data[:is_array] + array_size_as_one_dimensional(field_data).times do |i| %> + result.<%= field_name %>[<%= i %>] = <%= lib_mapper_fn_for field_data %>(v.<%= field_name %><%= array_mapper_index_for(field_data, i) %>); +<% end + else %> + result.<%= field_name %> = <%= lib_mapper_fn_for field_data %>(v.<%= field_name %>); +<% end +end %> + result +} +pub(crate) fn __skadapter__to_<%= struct[:name] %>(v: __sklib_<%= struct[:name] %>) -> <%= struct[:name].type_case %> { + let mut result = <%= struct[:name].type_case %>::new(); +<% struct[:fields].each do |field_name, field_data| + field_name = field_name.to_s.to_snake_case + if field_data[:is_array] + array_size_as_one_dimensional(field_data).times do |i| %> + result.<%= field_name %><%= array_mapper_index_for(field_data, i) %> = <%= sk_mapper_fn_for field_data %>(v.<%= field_name %>[<%= i %>]); +<% end + else %> + result.<%= field_name %> = <%= sk_mapper_fn_for field_data %>(v.<%= field_name %>); +<% end +end %> + result +} +<% end %> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/typealias_pointer.rs.erb b/res/translators/rust/implementation/mappers/typealias_pointer.rs.erb new file mode 100644 index 0000000..07c771f --- /dev/null +++ b/res/translators/rust/implementation/mappers/typealias_pointer.rs.erb @@ -0,0 +1,28 @@ +<%# + Typealias pointer mappers map: + __sklib_ptr to RustType + RustType to __sklib_ptr +%> +#[inline] +pub(crate) fn __skadapter__to_sklib_ptr(v: __sklib_ptr) -> __sklib_ptr { + v +} + +#[inline] +pub(crate) fn __skadapter__to_ptr(v: __sklib_ptr) -> __sklib_ptr { + v +} +<% + @typealiases.pluck(:name).each do |typealias| +%> +#[inline] +pub(crate) fn __skadapter__to_<%= typealias %>(v: __sklib_ptr) -> <%= typealias.type_case %> { + <%= typealias.type_case %> { ptr: v } +} +#[inline] +pub(crate) fn __skadapter__to_sklib_<%= typealias %>(v: <%= typealias.type_case %>) -> __sklib_ptr { + v.ptr +} +<% + end # typealiases.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/mappers/vector.rs.erb b/res/translators/rust/implementation/mappers/vector.rs.erb new file mode 100644 index 0000000..c46a67b --- /dev/null +++ b/res/translators/rust/implementation/mappers/vector.rs.erb @@ -0,0 +1,84 @@ +<%# + Vector adapter functions mapping all fields to/from their __sklib type +%> +<% + @vector_types.each do |type| + sk_type = sk_type_for(type) + lib_type = lib_type_for(type) +%> +#[repr(C)] +pub(crate) struct __sklib_vector_<%= type %> { + data_from_app: *mut <%= lib_type %>, + size_from_app: usize, + data_from_lib: *mut <%= lib_type %>, + size_from_lib: usize, +} + +impl __sklib_vector_<%= type %> { + pub fn new(size: usize) -> Self { + let mut vec = Vec::with_capacity(size); + vec.resize(size, unsafe { zeroed() }); + let ptr = vec.as_mut_ptr(); + forget(vec); + Self { + data_from_app: ptr, + size_from_app: size, + data_from_lib: null_mut(), + size_from_lib: 0, + } + } +} + +extern "C" { + fn __sklib__free__sklib_vector_<%= type %>(v: __sklib_vector_<%= type %>); +} + +pub(crate) fn __skadapter__free__sklib_vector_<%= type %>(v: &mut __sklib_vector_<%= type %>) { + if !v.data_from_app.is_null() { + unsafe { +<% if type == "string" %> + for i in 0..v.size_from_app { + __skadapter__free__sklib_string(*v.data_from_app.add(i)); + } +<% end %> + Vec::from_raw_parts(v.data_from_app, v.size_from_app, v.size_from_app); + } + v.data_from_app = null_mut(); + } +} + +pub(crate) fn __skadapter__to_sklib_vector_<%= type %>(v: Vec<<%= sk_type %>>) -> __sklib_vector_<%= type %> { + let result = __sklib_vector_<%= type %>::new(v.len()); + for (i, item) in v.iter().enumerate() { + unsafe { + *result.data_from_app.add(i) = __skadapter__to_sklib_<%= type %>(item.clone()); + } + } + result +} + +pub(crate) fn __skadapter__to_vector_<%= type %>(v: __sklib_vector_<%= type %>) -> Vec<<%= sk_type %>> { + let mut result = Vec::with_capacity(v.size_from_lib); + unsafe { + for i in 0..v.size_from_lib { + let item = (*v.data_from_lib.add(i)).clone(); + result.push(__skadapter__to_<%= type %>(item)); + } + __sklib__free__sklib_vector_<%= type %>(v); + } + result +} + +pub(crate) fn __skadapter__update_from_vector_<%= type %>(v: __sklib_vector_<%= type %>, result: &mut Vec<<%= sk_type %>>) { + result.clear(); + unsafe { + for i in 0..v.size_from_lib { + let item = (*v.data_from_lib.add(i)).clone(); + result.push(__skadapter__to_<%= type %>(item)); + } + __sklib__free__sklib_vector_<%= type %>(v); + } +} +<% + end # vector_types.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types.rs.erb b/res/translators/rust/implementation/types.rs.erb new file mode 100644 index 0000000..7e70d76 --- /dev/null +++ b/res/translators/rust/implementation/types.rs.erb @@ -0,0 +1,10 @@ +<%# + Include all type definitions +%> +<%= read_template 'implementation/types/enum' %> +<%= read_template 'implementation/types/struct' %> +<%= read_template 'implementation/types/function_pointer' %> +<%= read_template 'implementation/types/callback' %> +<%= read_template 'implementation/types/typealias_pointer' %> +<%= read_template 'implementation/types/functions' %> + diff --git a/res/translators/rust/implementation/types/callback.rs.erb b/res/translators/rust/implementation/types/callback.rs.erb new file mode 100644 index 0000000..417bbaf --- /dev/null +++ b/res/translators/rust/implementation/types/callback.rs.erb @@ -0,0 +1,70 @@ +<%# + Callback wrapper implementations +%> +<% + @function_pointers.each do |fp| + if is_func?(fp) + return_type = sk_type_for(fp[:return]) + else + return_type = "()" + end + + # Only include types, not parameter names in the Fn signature + param_types = fp[:parameters].map do |_, param_data| + sk_type_for(param_data) + end.join(", ") +%> + +static <%= fp[:name].type_case.upcase %>_WRAPPER: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); + +pub struct <%= fp[:name].type_case %>Wrapper { + callback: Box) -> <%= return_type %> + Send + Sync> +} + +impl <%= fp[:name].type_case %>Wrapper { + pub fn new(callback: F) -> <%= fp[:name].type_case %> + where + F: Fn(<%= param_types %>) -> <%= return_type %> + Send + Sync + 'static + { + let boxed_callback = Box::new(callback); + <%= fp[:name].type_case.upcase %>_WRAPPER.get_or_init(|| boxed_callback); + + extern "C" fn wrapper_fn( +<% + fp[:parameters].each do |param_name, param_data| +%> + <%= param_name.to_s.to_snake_case %>: <%= lib_type_for(param_data) %>, +<% + end +%> + ) -> <%= return_type %> { + if let Some(callback) = <%= fp[:name].type_case.upcase %>_WRAPPER.get() { + callback( +<% + fp[:parameters].each_with_index do |(param_name, param_data), index| + comma = index < fp[:parameters].length - 1 ? "," : "" + if void_pointer?(param_data) +%> + <%= param_name.to_s.to_snake_case %><%= comma %> +<% + else +%> + __skadapter__to_<%= param_data[:type] %>(<%= param_name.to_s.to_snake_case %>)<%= comma %> +<% + end +%> +<% + end +%> + ) + } else { + Default::default() + } + } + wrapper_fn + } +} + +<% + end # end function_pointers.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/enum.rs.erb b/res/translators/rust/implementation/types/enum.rs.erb new file mode 100644 index 0000000..5ac531d --- /dev/null +++ b/res/translators/rust/implementation/types/enum.rs.erb @@ -0,0 +1,33 @@ +<%# + Enum declarations +%> +<% + @enums.each do |enum| +%> +#[repr(i32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum <%= enum[:name].to_pascal_case %> { +<% + seen_numbers = {} + enum[:constants].each do |constant_key, constant_data| + has_numb = constant_data[:number].is_a? Integer + + next if has_numb && seen_numbers[constant_data[:number]] + seen_numbers[constant_data[:number]] = true if has_numb + + if has_numb + value = " = #{constant_data[:number]}" + else + value = "" + end + + constant_decl = " #{constant_key.to_s.to_pascal_case}#{value}," +%> +<%= constant_decl %> +<% + end # end constants.each +%> +} +<% + end # end enums.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/function_pointer.rs.erb b/res/translators/rust/implementation/types/function_pointer.rs.erb new file mode 100644 index 0000000..20b48c9 --- /dev/null +++ b/res/translators/rust/implementation/types/function_pointer.rs.erb @@ -0,0 +1,20 @@ +<%# + Function pointer type definitions +%> +<% + @function_pointers.each do |fp| + return_type = if is_func?(fp) + sk_type_for(fp[:return]) + else + "()" + end + + params = fp[:parameters].map do |param_name, param_data| + type = sk_type_for(param_data) + type + end.join(", ") +%> +pub type <%= fp[:name].type_case %> = extern "C" fn(<%= params %>) -> <%= return_type %>; +<% + end # end function_pointers.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/functions.rs.erb b/res/translators/rust/implementation/types/functions.rs.erb new file mode 100644 index 0000000..9ab5924 --- /dev/null +++ b/res/translators/rust/implementation/types/functions.rs.erb @@ -0,0 +1,15 @@ +<%# + Function declarations +%> +<% + @functions.each do |function| + params = lib_parameter_list_for(function) +%> +extern "C" { + #[link_name = "<%= lib_function_name_for(function) %>"] + fn <%= lib_function_name_for(function) %>(<%= params %><% if is_func?(function) %>) + -> <%= lib_type_for(function[:return]) %><% else %>)<% end %>; +} +<% + end # functions.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/struct.rs.erb b/res/translators/rust/implementation/types/struct.rs.erb new file mode 100644 index 0000000..d0bd97f --- /dev/null +++ b/res/translators/rust/implementation/types/struct.rs.erb @@ -0,0 +1,44 @@ +<%# + Struct declarations +%> +<% + @structs.each do |struct| +%> +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct __sklib_<%= struct[:name] %> { +<% + struct[:fields].each do |field_name, field_data| + field_name = field_name.to_s.to_snake_case +%> + <%= lib_struct_field_for(field_name, field_data) %>, +<% + end +%> +} +impl __sklib_<%= struct[:name] %> { + pub fn new() -> Self { + let uninit = MaybeUninit::zeroed(); + unsafe { uninit.assume_init() } + } +} +#[derive(Debug, Clone, Copy)] +pub struct <%= struct[:name].to_pascal_case %> { +<% + struct[:fields].each do |field_name, field_data| + field_name = field_name.to_s.to_snake_case +%> + <%= sk_struct_field_for(field_name, field_data) %>, +<% + end +%> +} +impl <%= struct[:name].to_pascal_case %> { + pub fn new() -> Self { + let uninit = MaybeUninit::zeroed(); + unsafe { uninit.assume_init() } + } +} +<% + end # structs.each +%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/typealias_pointer.rs.erb b/res/translators/rust/implementation/types/typealias_pointer.rs.erb new file mode 100644 index 0000000..63e43ea --- /dev/null +++ b/res/translators/rust/implementation/types/typealias_pointer.rs.erb @@ -0,0 +1,22 @@ +<%# + Typealias struct pointer declarations +%> +<% + @typealiases.pluck(:name).each do |typealias| +%> +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct <%= typealias.type_case %> { + ptr: __sklib_ptr, +} +impl <%= typealias.type_case %> { + fn new(ptr: __sklib_ptr) -> Self { + Self { ptr } + } + + fn as_ptr(&self) -> __sklib_ptr { + self.ptr + } +} +<% + end # typealiases.each +%> \ No newline at end of file diff --git a/res/translators/rust/splashkit.rs.erb b/res/translators/rust/splashkit.rs.erb index 0bd7f16..d76b627 100644 --- a/res/translators/rust/splashkit.rs.erb +++ b/res/translators/rust/splashkit.rs.erb @@ -1,73 +1,16 @@ -<%# - Define Rust - SplashKit interface -%> #![allow(non_snake_case)] -#[link(name = "SplashKit")] -extern "C" { -<% - @functions.each do |function| -%> - <%= lib_signature_for(function) %>; +#![allow(dead_code)] +#![allow(non_camel_case_types)] +use std::{ + ffi::{CStr, CString, c_void}, + mem::{forget, zeroed, MaybeUninit}, + os::raw::{c_char, c_int}, + ptr::{null_mut}, + sync::{OnceLock}, +}; -<% - end # functions.each -%> -} - -// --------- +type __sklib_ptr = *mut c_void; +<%= read_template 'implementation/types' %> <%= read_template 'implementation/mappers' %> - -// --------- -<%# - Implement Rust functions -- Rust --> this code --> Interface --> C/C++ -%> -<% - @functions.each do |function| - lib_fn_name = lib_function_name_for(function) - param_list = lib_argument_list_for(function) - return_val = "__skreturn = " if is_func?(function) - func_call = "#{return_val}#{lib_fn_name}(#{param_list})" -%> -pub <%= sk_signature_for(function) %> { -<% - # Declare each parameter prefixed with __skparam - function[:parameters].each do |param_name, param_data| - ptr = '*' if param_data[:is_reference] - mut = 'mut ' if param_data[:is_reference] -%> - let <%= mut%>__skparam__<%= param_name %>: <%= lib_type_for(param_data) %> = <%= lib_mapper_fn_for param_data %>(<%= ptr%> <%= param_name.variable_case %>); -<% - end # end parameters.each - - # if it is a function... then add a mutable return variable - if is_func?(function) -%> - let mut __skreturn: <%= sk_type_for(function[:return], is_lib: true) %>; -<% - end # end if func -%> - unsafe { - <%= func_call %>; - } - -<% - # Declare each parameter prefixed with __skparam - function[:parameters].each do |param_name, param_data| - next unless param_data[:is_reference] -%> - *<%= param_name.variable_case %> = <%= sk_mapper_fn_for param_data %>(__skparam__<%= param_name %>); -<% - end # end parameters.each - - # if it is a function... then return value... - if is_func?(function) -%> - return <%= sk_mapper_fn_for function[:return] %>(__skreturn); -<% - end # end if function -%> -} -<% - end # functions.each -%> \ No newline at end of file +<%= read_template 'implementation/functions' %> \ No newline at end of file diff --git a/src/translators/csharp.rb b/src/translators/csharp.rb index 4bd56f7..1896950 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -112,7 +112,6 @@ def docs_signatures_for(function) result end - def get_method_data(fn) { diff --git a/src/translators/rust.rb b/src/translators/rust.rb index 02794f9..1bc29c7 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -2,9 +2,6 @@ require_relative 'translator_helper' module Translators - # - # SplashKit Rust Library code generator - # class Rust < AbstractTranslator include TranslatorHelper @@ -12,28 +9,22 @@ def initialize(data, logging = false) super(data, logging) end - # - # Generate the splashkit module - # def render_templates { - 'splashkit.rs' => read_template('splashkit.rs') + 'splashkit.rs' => read_template('splashkit.rs'), } end #=== internal === RUST_IDENTIFIER_CASES = { - types: :snake_case, + types: :pascal_case, functions: :snake_case, variables: :snake_case, fields: :snake_case, constants: :upper_case } - # - # Direct type map primitive data types between the adapter and library code. - # DIRECT_TYPES = { 'int8_t' => 'i8', 'int' => 'i32', @@ -42,74 +33,82 @@ def render_templates 'float' => 'f32', 'double' => 'f64', 'byte' => 'u8', + 'unsigned char' => 'u8', 'unsigned int' => 'u32', 'unsigned short' => 'u16' } + SK_TYPES_TO_RUST_TYPES = { + 'bool' => 'bool', + 'string' => 'String', + 'void' => '()', + 'char' => 'char' + } + SK_TYPES_TO_LIB_TYPES = { 'bool' => 'i32', + 'string' => '__sklib_string', + 'void' => 'c_void', 'enum' => 'i32', - 'char' => 'u8', - 'unsigned char' => 'u8', + 'char' => 'c_char' } - SK_TYPES_TO_RUST_TYPES = { - 'bool' => 'bool', - 'char' => 'char', - 'unsigned char' => 'char', - } + def type_exceptions(type_data, type_conversion_fn, opts = {}) + return '__sklib_ptr' if void_pointer?(type_data) + return type_data[:type].type_case if function_pointer?(type_data) + + if vector_type?(type_data) + return "__sklib_vector_#{type_data[:type_parameter]}" if opts[:is_lib] + + return "Vec<#{send(type_conversion_fn, type_data[:type_parameter])}>" + end + + nil + end - # - # Generate a Rust type signature from a SK function - # def signature_syntax(function, function_name, parameter_list, return_type, opts = {}) - func_suffix = " -> #{return_type}" if is_func?(function) - "fn #{function_name}(#{parameter_list})#{func_suffix}" + pub = opts[:is_lib] ? '' : 'pub ' + unsafe = opts[:is_lib] ? 'unsafe ' : '' + extern = opts[:is_lib] ? 'extern "C" ' : '' + return_type = return_type.nil? ? '' : " -> #{return_type}" + + "#{pub}#{unsafe}#{extern}fn #{function_name}(#{parameter_list})#{return_type}" end def sk_function_name_for(function) - "#{function[:name].function_case}#{function[:attributes][:suffix].nil? ? '':'_'}#{function[:attributes][:suffix]}" + "#{function[:name].function_case}#{function[:attributes][:suffix].nil? ? '' : '_'}#{function[:attributes][:suffix]}" end - # - # Convert a list of parameters to a Rust parameter list - # Use the type conversion function to get which type to use - # as this function is used to for both Library and Front-End code - # - def parameter_list_syntax(parameters, type_conversion_fn, opts = {}) + def parameter_list_syntax(parameters, type_conversion_fn = :sk_type_for, opts = {}) + type_fn = opts[:is_lib] ? :lib_type_for : type_conversion_fn parameters.map do |param_name, param_data| - type = send(type_conversion_fn, param_data) - var = if param_data[:is_reference] - '&mut ' # param_data[:is_const] ? '* const ' : '* mut ' - else - '' - end - "#{param_name.variable_case}: #{var}#{type}" - end.join('; ') + parameter_syntax(param_name, param_data, type_fn) + end.join(', ') end - # - # Joins the argument list using a comma - # - def argument_list_syntax(arguments) - args = arguments.map do | arg_data | - if arg_data[:param_data][:is_reference] # && ! arg_data[:param_data][:is_const] - "&mut #{arg_data[:name]}" - else - arg_data[:name] - end - end + def parameter_syntax(param_name, param_data, type_conversion_fn = :sk_type_for) + var = param_name.variable_case + "#{var == 'fn' ? 'r#fn' : var}: #{send(type_conversion_fn, param_data)}" + end - args.join(', ') + def struct_field_syntax(field_name, field_type, field_data) + "pub #{field_name}: #{field_type}" end - # - # Handle any explicitly mapped data types - # - def type_exceptions(type_data, type_conversion_fn, opts = {}) - # No exception for this type - return nil + def array_declaration_syntax(array_type, dim1_size, dim2_size = nil) + if dim2_size.nil? + "[#{array_type}; #{dim1_size}]" + else + "[[#{array_type}; #{dim2_size}]; #{dim1_size}]" + end end + def array_at_index_syntax(idx1, idx2 = nil) + if idx2.nil? + "[#{idx1}]" + else + "[#{idx1}][#{idx2}]" + end + end end end diff --git a/translate b/translate deleted file mode 120000 index 95a690c..0000000 --- a/translate +++ /dev/null @@ -1 +0,0 @@ -src/main.rb \ No newline at end of file From 77ae8d3b338e3e67cae12804365e6dbc3305565e Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Sun, 8 Dec 2024 03:46:39 +1100 Subject: [PATCH 03/13] Fixed rust refererncing --- .../rust/implementation/functions.rs.erb | 20 +++++++++++++++++-- src/main.rb | 2 +- src/translators/rust.rb | 17 +++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb index 2ded6fd..ef3cf0c 100644 --- a/res/translators/rust/implementation/functions.rs.erb +++ b/res/translators/rust/implementation/functions.rs.erb @@ -25,13 +25,29 @@ let __skreturn = <%= lib_function_name_for function %>(<%= lib_argument_list_for(function) %>); - <%= sk_mapper_fn_for function[:return] %>(__skreturn) <% else %> <%= lib_function_name_for function %>(<%= lib_argument_list_for(function) - %>) + %>); +<% + end +%> +<% + # Write back any non-const reference parameters (for both cases) + function[:parameters].each do |param_name, param_data| + if param_data[:is_reference] && !param_data[:is_const] +%> + *<%= param_name.variable_case %> = <%= sk_mapper_fn_for param_data %>(__skparam__<%= param_name %>); +<% + end + end +%> +<% + if is_func?(function) +%> + <%= sk_mapper_fn_for function[:return] %>(__skreturn) <% end %> diff --git a/src/main.rb b/src/main.rb index 2327ea5..c82b1d0 100755 --- a/src/main.rb +++ b/src/main.rb @@ -11,7 +11,7 @@ require_relative 'translators/python' require_relative 'translators/csharp' require_relative 'translators/cpp' -# require_relative 'translators/rust' +require_relative 'translators/rust' require_relative 'translators/docs' # Access to config vars diff --git a/src/translators/rust.rb b/src/translators/rust.rb index 1bc29c7..a35dfd4 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -75,6 +75,19 @@ def signature_syntax(function, function_name, parameter_list, return_type, opts "#{pub}#{unsafe}#{extern}fn #{function_name}(#{parameter_list})#{return_type}" end + def argument_list_syntax(arguments) + args = arguments.map do |arg_data| + var = arg_data[:name].variable_case + var = "r##{var}" if var == 'fn' + if arg_data[:param_data][:is_reference] && !arg_data[:param_data][:is_const] + "&mut #{var}" + else + var + end + end + args.join(', ') + end + def sk_function_name_for(function) "#{function[:name].function_case}#{function[:attributes][:suffix].nil? ? '' : '_'}#{function[:attributes][:suffix]}" end @@ -88,7 +101,9 @@ def parameter_list_syntax(parameters, type_conversion_fn = :sk_type_for, opts = def parameter_syntax(param_name, param_data, type_conversion_fn = :sk_type_for) var = param_name.variable_case - "#{var == 'fn' ? 'r#fn' : var}: #{send(type_conversion_fn, param_data)}" + type = send(type_conversion_fn, param_data) + param_data[:is_reference] && !param_data[:is_const] && type = "&mut #{type}" + "#{var == 'fn' ? 'r#fn' : var}: #{type}" end def struct_field_syntax(field_name, field_type, field_data) From 9e8b4a785474f3863ab023740790a8a6049db310 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Tue, 17 Dec 2024 19:47:17 +1100 Subject: [PATCH 04/13] Add is_null and partialeq to structs and typealiases --- res/translators/rust/implementation/functions.rs.erb | 1 - .../rust/implementation/types/struct.rs.erb | 10 +++++++++- .../rust/implementation/types/typealias_pointer.rs.erb | 4 ++++ src/translators/csharp.rb | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb index ef3cf0c..dcf932e 100644 --- a/res/translators/rust/implementation/functions.rs.erb +++ b/res/translators/rust/implementation/functions.rs.erb @@ -35,7 +35,6 @@ end %> <% - # Write back any non-const reference parameters (for both cases) function[:parameters].each do |param_name, param_data| if param_data[:is_reference] && !param_data[:is_const] %> diff --git a/res/translators/rust/implementation/types/struct.rs.erb b/res/translators/rust/implementation/types/struct.rs.erb index d0bd97f..da1bace 100644 --- a/res/translators/rust/implementation/types/struct.rs.erb +++ b/res/translators/rust/implementation/types/struct.rs.erb @@ -22,7 +22,7 @@ impl __sklib_<%= struct[:name] %> { unsafe { uninit.assume_init() } } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct <%= struct[:name].to_pascal_case %> { <% struct[:fields].each do |field_name, field_data| @@ -33,11 +33,19 @@ pub struct <%= struct[:name].to_pascal_case %> { end %> } +impl Default for <%= struct[:name].to_pascal_case %> { + fn default() -> Self { + Self::new() + } +} impl <%= struct[:name].to_pascal_case %> { pub fn new() -> Self { let uninit = MaybeUninit::zeroed(); unsafe { uninit.assume_init() } } + pub fn is_null(&self) -> bool { + *self == Self::default() + } } <% end # structs.each diff --git a/res/translators/rust/implementation/types/typealias_pointer.rs.erb b/res/translators/rust/implementation/types/typealias_pointer.rs.erb index 63e43ea..ce634ba 100644 --- a/res/translators/rust/implementation/types/typealias_pointer.rs.erb +++ b/res/translators/rust/implementation/types/typealias_pointer.rs.erb @@ -16,6 +16,10 @@ impl <%= typealias.type_case %> { fn as_ptr(&self) -> __sklib_ptr { self.ptr } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } } <% end # typealiases.each diff --git a/src/translators/csharp.rb b/src/translators/csharp.rb index 1896950..e3797ee 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -115,7 +115,7 @@ def docs_signatures_for(function) def get_method_data(fn) { - method_name: fn[:method_name].nil? ? nil : fn[:method_name].to_s.to_pascal_case, + method_name: fn[:name].nil? ? nil : fn[:name].to_s.to_pascal_case, class_name: fn[:attributes][:class].nil? ? fn[:attributes][:static].to_pascal_case() : fn[:attributes][:class].to_pascal_case(), params: method_parameter_list_for(fn), args: method_argument_list_for(fn), From 60a2ebd9086c5a80a8aa2be16d35d06206549a26 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 18 Dec 2024 06:36:56 +1100 Subject: [PATCH 05/13] Updated rust callbacks --- .../rust/implementation/types/callback.rs.erb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/res/translators/rust/implementation/types/callback.rs.erb b/res/translators/rust/implementation/types/callback.rs.erb index 417bbaf..71cac16 100644 --- a/res/translators/rust/implementation/types/callback.rs.erb +++ b/res/translators/rust/implementation/types/callback.rs.erb @@ -15,19 +15,15 @@ end.join(", ") %> -static <%= fp[:name].type_case.upcase %>_WRAPPER: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); +static <%= fp[:name].type_case.upcase %>: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); -pub struct <%= fp[:name].type_case %>Wrapper { - callback: Box) -> <%= return_type %> + Send + Sync> -} - -impl <%= fp[:name].type_case %>Wrapper { +impl <%= fp[:name].type_case %> { pub fn new(callback: F) -> <%= fp[:name].type_case %> where F: Fn(<%= param_types %>) -> <%= return_type %> + Send + Sync + 'static { let boxed_callback = Box::new(callback); - <%= fp[:name].type_case.upcase %>_WRAPPER.get_or_init(|| boxed_callback); + <%= fp[:name].type_case.upcase %>.get_or_init(|| boxed_callback); extern "C" fn wrapper_fn( <% @@ -38,7 +34,7 @@ impl <%= fp[:name].type_case %>Wrapper { end %> ) -> <%= return_type %> { - if let Some(callback) = <%= fp[:name].type_case.upcase %>_WRAPPER.get() { + if let Some(callback) = <%= fp[:name].type_case.upcase %>.get() { callback( <% fp[:parameters].each_with_index do |(param_name, param_data), index| @@ -56,7 +52,7 @@ impl <%= fp[:name].type_case %>Wrapper { <% end %> - ) +) } else { Default::default() } From 99f5f69c4fe8173a3bf7133fe1166d2dd27cc2d0 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 18 Dec 2024 07:31:43 +1100 Subject: [PATCH 06/13] Callback updates reverted and functions updated --- res/translators/rust/implementation/functions.rs.erb | 10 +++++++++- .../rust/implementation/types/callback.rs.erb | 12 ++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb index dcf932e..3430e08 100644 --- a/res/translators/rust/implementation/functions.rs.erb +++ b/res/translators/rust/implementation/functions.rs.erb @@ -11,8 +11,16 @@ param_var = param_name.to_s.to_snake_case # Escape 'fn' keyword if needed param_var = "r##{param_var}" if param_var == 'fn' + if param_data[:is_reference] && !param_data[:is_const] +%> + let mut __skparam__<%= param_name.to_s.to_snake_case %> = <%= lib_mapper_fn_for param_data %>((*<%= param_var %>).clone()); +<% + else +%> + let __skparam__<%= param_name.to_s.to_snake_case %> = <%= lib_mapper_fn_for param_data %>(<%= param_var %>); +<% + end %> - let __skparam__<%= param_name %> = <%= lib_mapper_fn_for param_data %>(<%= param_var %>); <% end %> diff --git a/res/translators/rust/implementation/types/callback.rs.erb b/res/translators/rust/implementation/types/callback.rs.erb index 71cac16..ea446e3 100644 --- a/res/translators/rust/implementation/types/callback.rs.erb +++ b/res/translators/rust/implementation/types/callback.rs.erb @@ -15,15 +15,19 @@ end.join(", ") %> -static <%= fp[:name].type_case.upcase %>: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); +static <%= fp[:name].type_case.upcase %>_WRAPPER: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); -impl <%= fp[:name].type_case %> { +pub struct <%= fp[:name].type_case %>Wrapper { + callback: Box) -> <%= return_type %> + Send + Sync> +} + +impl <%= fp[:name].type_case %>Wrapper { pub fn new(callback: F) -> <%= fp[:name].type_case %> where F: Fn(<%= param_types %>) -> <%= return_type %> + Send + Sync + 'static { let boxed_callback = Box::new(callback); - <%= fp[:name].type_case.upcase %>.get_or_init(|| boxed_callback); + <%= fp[:name].type_case.upcase %>_WRAPPER.get_or_init(|| boxed_callback); extern "C" fn wrapper_fn( <% @@ -34,7 +38,7 @@ impl <%= fp[:name].type_case %> { end %> ) -> <%= return_type %> { - if let Some(callback) = <%= fp[:name].type_case.upcase %>.get() { + if let Some(callback) = <%= fp[:name].type_case.upcase %>_WRAPPER.get() { callback( <% fp[:parameters].each_with_index do |(param_name, param_data), index| From e8acf72970696b9611233f1b578d32193a8f3e5a Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 18 Dec 2024 22:48:35 +1100 Subject: [PATCH 07/13] Removed changes for a different branch --- src/translators/csharp.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translators/csharp.rb b/src/translators/csharp.rb index e3797ee..a9b9217 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -105,7 +105,7 @@ def docs_signatures_for(function) end result.unshift "public #{method_data[:static]}#{method_data[:return_type]} #{method_data[:class_name]}.#{property_name.to_pascal_case()} { #{text} }" - elsif method_data[:method_name] + else result.unshift "public #{method_data[:static]}#{method_data[:return_type]} #{method_data[:class_name]}.#{method_data[:method_name]}(#{method_data[:params]});" end end @@ -115,7 +115,7 @@ def docs_signatures_for(function) def get_method_data(fn) { - method_name: fn[:name].nil? ? nil : fn[:name].to_s.to_pascal_case, + method_name: fn[:name].to_s.to_pascal_case, class_name: fn[:attributes][:class].nil? ? fn[:attributes][:static].to_pascal_case() : fn[:attributes][:class].to_pascal_case(), params: method_parameter_list_for(fn), args: method_argument_list_for(fn), From bb44e01df68f3057757acbfdeaa92dfa44d17f9d Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 18 Dec 2024 23:08:32 +1100 Subject: [PATCH 08/13] Added comments --- .../rust/implementation/mappers/bool.rs.erb | 1 - .../rust/implementation/types.rs.erb | 3 -- src/translators/rust.rb | 29 +++++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/res/translators/rust/implementation/mappers/bool.rs.erb b/res/translators/rust/implementation/mappers/bool.rs.erb index bd51c11..fda6953 100644 --- a/res/translators/rust/implementation/mappers/bool.rs.erb +++ b/res/translators/rust/implementation/mappers/bool.rs.erb @@ -1,6 +1,5 @@ <%# Mappers from `bool` <-> `i32` - Based on bindgen's actual implementation %> #[inline] pub(crate) fn __skadapter__to_sklib_bool(v: bool) -> i32 { diff --git a/res/translators/rust/implementation/types.rs.erb b/res/translators/rust/implementation/types.rs.erb index 7e70d76..e0a6e39 100644 --- a/res/translators/rust/implementation/types.rs.erb +++ b/res/translators/rust/implementation/types.rs.erb @@ -1,6 +1,3 @@ -<%# - Include all type definitions -%> <%= read_template 'implementation/types/enum' %> <%= read_template 'implementation/types/struct' %> <%= read_template 'implementation/types/function_pointer' %> diff --git a/src/translators/rust.rb b/src/translators/rust.rb index a35dfd4..c58b4fc 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -2,6 +2,7 @@ require_relative 'translator_helper' module Translators + # Rust translator class that converts SplashKit code to Rust class Rust < AbstractTranslator include TranslatorHelper @@ -66,6 +67,9 @@ def type_exceptions(type_data, type_conversion_fn, opts = {}) nil end + ## + # Generate Rust function signatures with appropriate modifiers + ## def signature_syntax(function, function_name, parameter_list, return_type, opts = {}) pub = opts[:is_lib] ? '' : 'pub ' unsafe = opts[:is_lib] ? 'unsafe ' : '' @@ -75,10 +79,13 @@ def signature_syntax(function, function_name, parameter_list, return_type, opts "#{pub}#{unsafe}#{extern}fn #{function_name}(#{parameter_list})#{return_type}" end + ## + # Format function argument lists for calls + ## def argument_list_syntax(arguments) args = arguments.map do |arg_data| var = arg_data[:name].variable_case - var = "r##{var}" if var == 'fn' + var = "r##{var}" if var == 'fn' # Handle 'fn' keyword conflict if arg_data[:param_data][:is_reference] && !arg_data[:param_data][:is_const] "&mut #{var}" else @@ -88,10 +95,16 @@ def argument_list_syntax(arguments) args.join(', ') end + ## + # Generate SplashKit function names with optional suffixes + ## def sk_function_name_for(function) "#{function[:name].function_case}#{function[:attributes][:suffix].nil? ? '' : '_'}#{function[:attributes][:suffix]}" end + ## + # Format parameter lists for function declarations + ## def parameter_list_syntax(parameters, type_conversion_fn = :sk_type_for, opts = {}) type_fn = opts[:is_lib] ? :lib_type_for : type_conversion_fn parameters.map do |param_name, param_data| @@ -99,17 +112,26 @@ def parameter_list_syntax(parameters, type_conversion_fn = :sk_type_for, opts = end.join(', ') end + ## + # Format individual parameter declarations + ## def parameter_syntax(param_name, param_data, type_conversion_fn = :sk_type_for) var = param_name.variable_case type = send(type_conversion_fn, param_data) param_data[:is_reference] && !param_data[:is_const] && type = "&mut #{type}" - "#{var == 'fn' ? 'r#fn' : var}: #{type}" + "#{var == 'fn' ? 'r#fn' : var}: #{type}" # Handle 'fn' keyword conflict end + ## + # Format struct field declarations + ## def struct_field_syntax(field_name, field_type, field_data) "pub #{field_name}: #{field_type}" end + ## + # Generate array type declarations for 1D and 2D arrays + ## def array_declaration_syntax(array_type, dim1_size, dim2_size = nil) if dim2_size.nil? "[#{array_type}; #{dim1_size}]" @@ -118,6 +140,9 @@ def array_declaration_syntax(array_type, dim1_size, dim2_size = nil) end end + ## + # Generate array access syntax for 1D and 2D arrays + ## def array_at_index_syntax(idx1, idx2 = nil) if idx2.nil? "[#{idx1}]" From c80ecc1df24d1178be40b226a83d8f14c75205c8 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 18 Dec 2024 23:12:15 +1100 Subject: [PATCH 09/13] Added more comments --- src/translators/csharp.rb | 1 + src/translators/rust.rb | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/translators/csharp.rb b/src/translators/csharp.rb index a9b9217..8d3bf6a 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -113,6 +113,7 @@ def docs_signatures_for(function) result end + def get_method_data(fn) { method_name: fn[:name].to_s.to_pascal_case, diff --git a/src/translators/rust.rb b/src/translators/rust.rb index c58b4fc..13f87c6 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -2,7 +2,7 @@ require_relative 'translator_helper' module Translators - # Rust translator class that converts SplashKit code to Rust + # SplashKit Rust Library code generator class Rust < AbstractTranslator include TranslatorHelper @@ -10,6 +10,9 @@ def initialize(data, logging = false) super(data, logging) end + # + # Generate the splashkit module + # def render_templates { 'splashkit.rs' => read_template('splashkit.rs'), @@ -26,6 +29,9 @@ def render_templates constants: :upper_case } + # + # Direct type map primitive data types between the adapter and library code. + # DIRECT_TYPES = { 'int8_t' => 'i8', 'int' => 'i32', From cdcc41ace90dbe3a91617cc2a04bdd0f4517c63e Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Thu, 2 Jan 2025 03:13:07 +1100 Subject: [PATCH 10/13] updated vector size type --- .../rust/implementation/functions.rs.erb | 2 +- .../rust/implementation/mappers/vector.rs.erb | 22 +++++++++---------- .../rust/implementation/types/callback.rs.erb | 4 +--- .../rust/implementation/types/enum.rs.erb | 6 +++++ 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb index 3430e08..35cfdd3 100644 --- a/res/translators/rust/implementation/functions.rs.erb +++ b/res/translators/rust/implementation/functions.rs.erb @@ -1,5 +1,5 @@ <%# - Create the SplashKit functions that wrap the sklib FFI calls + Creates the SplashKit functions that wrap the sklib FFI calls %> <% @functions.each do |function| diff --git a/res/translators/rust/implementation/mappers/vector.rs.erb b/res/translators/rust/implementation/mappers/vector.rs.erb index c46a67b..e11b5ef 100644 --- a/res/translators/rust/implementation/mappers/vector.rs.erb +++ b/res/translators/rust/implementation/mappers/vector.rs.erb @@ -9,15 +9,15 @@ #[repr(C)] pub(crate) struct __sklib_vector_<%= type %> { data_from_app: *mut <%= lib_type %>, - size_from_app: usize, + size_from_app: u32, data_from_lib: *mut <%= lib_type %>, - size_from_lib: usize, + size_from_lib: u32, } impl __sklib_vector_<%= type %> { - pub fn new(size: usize) -> Self { - let mut vec = Vec::with_capacity(size); - vec.resize(size, unsafe { zeroed() }); + pub fn new(size: u32) -> Self { + let mut vec = Vec::with_capacity(size as usize); + vec.resize(size as usize, unsafe { zeroed() }); let ptr = vec.as_mut_ptr(); forget(vec); Self { @@ -38,17 +38,17 @@ pub(crate) fn __skadapter__free__sklib_vector_<%= type %>(v: &mut __sklib_vector unsafe { <% if type == "string" %> for i in 0..v.size_from_app { - __skadapter__free__sklib_string(*v.data_from_app.add(i)); + __skadapter__free__sklib_string(*v.data_from_app.add(i as usize)); } <% end %> - Vec::from_raw_parts(v.data_from_app, v.size_from_app, v.size_from_app); + Vec::from_raw_parts(v.data_from_app, v.size_from_app as usize, v.size_from_app as usize); } v.data_from_app = null_mut(); } } pub(crate) fn __skadapter__to_sklib_vector_<%= type %>(v: Vec<<%= sk_type %>>) -> __sklib_vector_<%= type %> { - let result = __sklib_vector_<%= type %>::new(v.len()); + let result = __sklib_vector_<%= type %>::new(v.len() as u32); for (i, item) in v.iter().enumerate() { unsafe { *result.data_from_app.add(i) = __skadapter__to_sklib_<%= type %>(item.clone()); @@ -58,10 +58,10 @@ pub(crate) fn __skadapter__to_sklib_vector_<%= type %>(v: Vec<<%= sk_type %>>) - } pub(crate) fn __skadapter__to_vector_<%= type %>(v: __sklib_vector_<%= type %>) -> Vec<<%= sk_type %>> { - let mut result = Vec::with_capacity(v.size_from_lib); + let mut result = Vec::with_capacity(v.size_from_lib as usize); unsafe { for i in 0..v.size_from_lib { - let item = (*v.data_from_lib.add(i)).clone(); + let item = (*v.data_from_lib.add(i as usize)).clone(); result.push(__skadapter__to_<%= type %>(item)); } __sklib__free__sklib_vector_<%= type %>(v); @@ -73,7 +73,7 @@ pub(crate) fn __skadapter__update_from_vector_<%= type %>(v: __sklib_vector_<%= result.clear(); unsafe { for i in 0..v.size_from_lib { - let item = (*v.data_from_lib.add(i)).clone(); + let item = (*v.data_from_lib.add(i as usize)).clone(); result.push(__skadapter__to_<%= type %>(item)); } __sklib__free__sklib_vector_<%= type %>(v); diff --git a/res/translators/rust/implementation/types/callback.rs.erb b/res/translators/rust/implementation/types/callback.rs.erb index ea446e3..e5a6272 100644 --- a/res/translators/rust/implementation/types/callback.rs.erb +++ b/res/translators/rust/implementation/types/callback.rs.erb @@ -17,9 +17,7 @@ static <%= fp[:name].type_case.upcase %>_WRAPPER: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); -pub struct <%= fp[:name].type_case %>Wrapper { - callback: Box) -> <%= return_type %> + Send + Sync> -} +pub struct <%= fp[:name].type_case %>Wrapper; impl <%= fp[:name].type_case %>Wrapper { pub fn new(callback: F) -> <%= fp[:name].type_case %> diff --git a/res/translators/rust/implementation/types/enum.rs.erb b/res/translators/rust/implementation/types/enum.rs.erb index 5ac531d..98b275f 100644 --- a/res/translators/rust/implementation/types/enum.rs.erb +++ b/res/translators/rust/implementation/types/enum.rs.erb @@ -28,6 +28,12 @@ pub enum <%= enum[:name].to_pascal_case %> { end # end constants.each %> } + +impl From for <%= enum[:name].to_pascal_case %> { + fn from(v: i32) -> Self { + __skadapter__to_<%= enum[:name].to_snake_case %>(v) + } +} <% end # end enums.each %> \ No newline at end of file From ef5ac23e9d5cca98f00fc7874ebba9f9c4768193 Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Mon, 6 Jan 2025 22:59:41 +1100 Subject: [PATCH 11/13] updated callbacks --- .../mappers/function_pointer.rs.erb | 51 +++++++++++++- .../rust/implementation/types.rs.erb | 1 - .../rust/implementation/types/callback.rs.erb | 68 ------------------- .../types/function_pointer.rs.erb | 5 +- res/translators/rust/splashkit.rs.erb | 2 +- 5 files changed, 52 insertions(+), 75 deletions(-) delete mode 100644 res/translators/rust/implementation/types/callback.rs.erb diff --git a/res/translators/rust/implementation/mappers/function_pointer.rs.erb b/res/translators/rust/implementation/mappers/function_pointer.rs.erb index 279a7ba..a01e86d 100644 --- a/res/translators/rust/implementation/mappers/function_pointer.rs.erb +++ b/res/translators/rust/implementation/mappers/function_pointer.rs.erb @@ -4,11 +4,56 @@ <% @function_pointers.each do |fp| sk_fp_type = fp[:name].type_case + return_type = if is_func?(fp) + lib_type_for(fp[:return]) + else + "()" + end %> #[inline] -pub(crate) fn __skadapter__to_sklib_<%= fp[:name] %>(v: <%= sk_fp_type %>) -> <%= sk_fp_type %> { - v +pub(crate) fn __skadapter__to_sklib_<%= fp[:name] %>(callback: <%= sk_fp_type %>) -> __sklib_<%= fp[:name].to_snake_case %> { + thread_local! { + static CALLBACK: RefCell>> = RefCell::new(None); + } + + CALLBACK.with(|cell| { + *cell.borrow_mut() = Some(callback); + }); + + extern "C" fn wrapper( +<% + fp[:parameters].each do |param_name, param_data| +%> + <%= param_name.to_s.to_snake_case %>: <%= lib_type_for(param_data) %>, +<% + end +%> + ) -> <%= return_type %> { + CALLBACK.with(|cell| { + if let Some(cb) = &*cell.borrow() { + cb( +<% + fp[:parameters].each_with_index do |(param_name, param_data), index| + comma = index < fp[:parameters].length - 1 ? "," : "" + if void_pointer?(param_data) +%> + <%= param_name.to_s.to_snake_case %><%= comma %> +<% + else +%> + __skadapter__to_<%= param_data[:type] %>(<%= param_name.to_s.to_snake_case %>)<%= comma %> +<% + end +%> +<% + end +%> + ) + } + }) + } + wrapper } <% - end # end function_pointers.each + end %> \ No newline at end of file diff --git a/res/translators/rust/implementation/types.rs.erb b/res/translators/rust/implementation/types.rs.erb index e0a6e39..a857d59 100644 --- a/res/translators/rust/implementation/types.rs.erb +++ b/res/translators/rust/implementation/types.rs.erb @@ -1,7 +1,6 @@ <%= read_template 'implementation/types/enum' %> <%= read_template 'implementation/types/struct' %> <%= read_template 'implementation/types/function_pointer' %> -<%= read_template 'implementation/types/callback' %> <%= read_template 'implementation/types/typealias_pointer' %> <%= read_template 'implementation/types/functions' %> diff --git a/res/translators/rust/implementation/types/callback.rs.erb b/res/translators/rust/implementation/types/callback.rs.erb deleted file mode 100644 index e5a6272..0000000 --- a/res/translators/rust/implementation/types/callback.rs.erb +++ /dev/null @@ -1,68 +0,0 @@ -<%# - Callback wrapper implementations -%> -<% - @function_pointers.each do |fp| - if is_func?(fp) - return_type = sk_type_for(fp[:return]) - else - return_type = "()" - end - - # Only include types, not parameter names in the Fn signature - param_types = fp[:parameters].map do |_, param_data| - sk_type_for(param_data) - end.join(", ") -%> - -static <%= fp[:name].type_case.upcase %>_WRAPPER: OnceLock) -> <%= return_type %> + Send + Sync>> = OnceLock::new(); - -pub struct <%= fp[:name].type_case %>Wrapper; - -impl <%= fp[:name].type_case %>Wrapper { - pub fn new(callback: F) -> <%= fp[:name].type_case %> - where - F: Fn(<%= param_types %>) -> <%= return_type %> + Send + Sync + 'static - { - let boxed_callback = Box::new(callback); - <%= fp[:name].type_case.upcase %>_WRAPPER.get_or_init(|| boxed_callback); - - extern "C" fn wrapper_fn( -<% - fp[:parameters].each do |param_name, param_data| -%> - <%= param_name.to_s.to_snake_case %>: <%= lib_type_for(param_data) %>, -<% - end -%> - ) -> <%= return_type %> { - if let Some(callback) = <%= fp[:name].type_case.upcase %>_WRAPPER.get() { - callback( -<% - fp[:parameters].each_with_index do |(param_name, param_data), index| - comma = index < fp[:parameters].length - 1 ? "," : "" - if void_pointer?(param_data) -%> - <%= param_name.to_s.to_snake_case %><%= comma %> -<% - else -%> - __skadapter__to_<%= param_data[:type] %>(<%= param_name.to_s.to_snake_case %>)<%= comma %> -<% - end -%> -<% - end -%> -) - } else { - Default::default() - } - } - wrapper_fn - } -} - -<% - end # end function_pointers.each -%> \ No newline at end of file diff --git a/res/translators/rust/implementation/types/function_pointer.rs.erb b/res/translators/rust/implementation/types/function_pointer.rs.erb index 20b48c9..d8e9bf4 100644 --- a/res/translators/rust/implementation/types/function_pointer.rs.erb +++ b/res/translators/rust/implementation/types/function_pointer.rs.erb @@ -14,7 +14,8 @@ type end.join(", ") %> -pub type <%= fp[:name].type_case %> = extern "C" fn(<%= params %>) -> <%= return_type %>; +pub type <%= fp[:name].type_case %> = Box) -> <%= return_type %> + Send + Sync>; +pub type __sklib_<%= fp[:name].to_snake_case %> = extern "C" fn(<%= params %>) -> <%= return_type %>; <% - end # end function_pointers.each + end %> \ No newline at end of file diff --git a/res/translators/rust/splashkit.rs.erb b/res/translators/rust/splashkit.rs.erb index d76b627..1dc046f 100644 --- a/res/translators/rust/splashkit.rs.erb +++ b/res/translators/rust/splashkit.rs.erb @@ -6,7 +6,7 @@ use std::{ mem::{forget, zeroed, MaybeUninit}, os::raw::{c_char, c_int}, ptr::{null_mut}, - sync::{OnceLock}, + cell::{RefCell}, }; type __sklib_ptr = *mut c_void; From 606d25b7b6cfd37fc41c04f572b30d0ae5b18ada Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Sun, 12 Jan 2025 19:33:58 +1100 Subject: [PATCH 12/13] added color equality --- .../rust/implementation/types/struct.rs.erb | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/res/translators/rust/implementation/types/struct.rs.erb b/res/translators/rust/implementation/types/struct.rs.erb index da1bace..99f7d24 100644 --- a/res/translators/rust/implementation/types/struct.rs.erb +++ b/res/translators/rust/implementation/types/struct.rs.erb @@ -22,7 +22,11 @@ impl __sklib_<%= struct[:name] %> { unsafe { uninit.assume_init() } } } -#[derive(Debug, Clone, Copy, PartialEq)] +<% + # Only derive PartialEq for non-color structs + derives = struct[:name] == "color" ? "Debug, Clone, Copy" : "Debug, Clone, Copy, PartialEq" +%> +#[derive(<%= derives %>)] pub struct <%= struct[:name].to_pascal_case %> { <% struct[:fields].each do |field_name, field_data| @@ -47,6 +51,20 @@ impl <%= struct[:name].to_pascal_case %> { *self == Self::default() } } +<% + if struct[:name] == "color" +%> +impl PartialEq for <%= struct[:name].to_pascal_case %> { + fn eq(&self, other: &Self) -> bool { + (self.r - other.r).abs() < 0.004 && + (self.g - other.g).abs() < 0.004 && + (self.b - other.b).abs() < 0.004 && + (self.a - other.a).abs() < 0.004 + } +} +<% + end +%> <% end # structs.each %> \ No newline at end of file From 8ac179781ae699a41c3eb570e8b9aac85b279c6e Mon Sep 17 00:00:00 2001 From: NoahJCross Date: Wed, 22 Jan 2025 18:16:28 +1100 Subject: [PATCH 13/13] Added sprite delagate handling in type exceptions --- src/translators/rust.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/translators/rust.rb b/src/translators/rust.rb index 13f87c6..70f4b1f 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -62,6 +62,7 @@ def render_templates def type_exceptions(type_data, type_conversion_fn, opts = {}) return '__sklib_ptr' if void_pointer?(type_data) + return "__sklib_#{type_data[:type].to_snake_case}" if function_pointer?(type_data) && opts[:is_lib] return type_data[:type].type_case if function_pointer?(type_data) if vector_type?(type_data)