diff --git a/res/translators/rust/implementation/functions.rs.erb b/res/translators/rust/implementation/functions.rs.erb new file mode 100644 index 0000000..35cfdd3 --- /dev/null +++ b/res/translators/rust/implementation/functions.rs.erb @@ -0,0 +1,65 @@ +<%# + Creates 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' + 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 +%> +<% + 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) + %>); +<% + else +%> + <%= lib_function_name_for function %>(<%= + lib_argument_list_for(function) + %>); +<% + end +%> +<% + 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 +%> + } +} +<% + 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..fda6953 100644 --- a/res/translators/rust/implementation/mappers/bool.rs.erb +++ b/res/translators/rust/implementation/mappers/bool.rs.erb @@ -1,16 +1,11 @@ <%# Mappers from `bool` <-> `i32` %> -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..a01e86d --- /dev/null +++ b/res/translators/rust/implementation/mappers/function_pointer.rs.erb @@ -0,0 +1,59 @@ +<%# + Function pointer translations +%> +<% + @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] %>(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 +%> \ 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..e11b5ef --- /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: u32, + data_from_lib: *mut <%= lib_type %>, + size_from_lib: u32, +} + +impl __sklib_vector_<%= type %> { + 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 { + 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 as usize)); + } +<% end %> + 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() as u32); + 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 as usize); + unsafe { + for i in 0..v.size_from_lib { + let item = (*v.data_from_lib.add(i as usize)).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 as usize)).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..a857d59 --- /dev/null +++ b/res/translators/rust/implementation/types.rs.erb @@ -0,0 +1,6 @@ +<%= read_template 'implementation/types/enum' %> +<%= read_template 'implementation/types/struct' %> +<%= read_template 'implementation/types/function_pointer' %> +<%= read_template 'implementation/types/typealias_pointer' %> +<%= read_template 'implementation/types/functions' %> + 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..98b275f --- /dev/null +++ b/res/translators/rust/implementation/types/enum.rs.erb @@ -0,0 +1,39 @@ +<%# + 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 +%> +} + +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 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..d8e9bf4 --- /dev/null +++ b/res/translators/rust/implementation/types/function_pointer.rs.erb @@ -0,0 +1,21 @@ +<%# + 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 %> = Box) -> <%= return_type %> + Send + Sync>; +pub type __sklib_<%= fp[:name].to_snake_case %> = extern "C" fn(<%= params %>) -> <%= return_type %>; +<% + end +%> \ 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..99f7d24 --- /dev/null +++ b/res/translators/rust/implementation/types/struct.rs.erb @@ -0,0 +1,70 @@ +<%# + 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() } + } +} +<% + # 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| + field_name = field_name.to_s.to_snake_case +%> + <%= sk_struct_field_for(field_name, field_data) %>, +<% + 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() + } +} +<% + 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 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..ce634ba --- /dev/null +++ b/res/translators/rust/implementation/types/typealias_pointer.rs.erb @@ -0,0 +1,26 @@ +<%# + 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 + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } +} +<% + 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..1dc046f 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}, + cell::{RefCell}, +}; -<% - 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/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/csharp.rb b/src/translators/csharp.rb index c59167a..8d3bf6a 100644 --- a/src/translators/csharp.rb +++ b/src/translators/csharp.rb @@ -112,7 +112,7 @@ 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..70f4b1f 100644 --- a/src/translators/rust.rb +++ b/src/translators/rust.rb @@ -2,9 +2,7 @@ require_relative 'translator_helper' module Translators - # # SplashKit Rust Library code generator - # class Rust < AbstractTranslator include TranslatorHelper @@ -17,14 +15,14 @@ def initialize(data, logging = false) # 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, @@ -42,74 +40,122 @@ 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 "__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) - # - # 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}" - end + if vector_type?(type_data) + return "__sklib_vector_#{type_data[:type_parameter]}" if opts[:is_lib] - def sk_function_name_for(function) - "#{function[:name].function_case}#{function[:attributes][:suffix].nil? ? '':'_'}#{function[:attributes][:suffix]}" + return "Vec<#{send(type_conversion_fn, type_data[:type_parameter])}>" + end + + nil 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 = {}) - 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('; ') + ## + # 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 ' : '' + extern = opts[:is_lib] ? 'extern "C" ' : '' + return_type = return_type.nil? ? '' : " -> #{return_type}" + + "#{pub}#{unsafe}#{extern}fn #{function_name}(#{parameter_list})#{return_type}" end - # - # Joins the argument list using a comma - # + ## + # Format function argument lists for calls + ## 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]}" + args = arguments.map do |arg_data| + var = arg_data[:name].variable_case + 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 - arg_data[:name] + var end end - args.join(', ') end - # - # Handle any explicitly mapped data types - # - def type_exceptions(type_data, type_conversion_fn, opts = {}) - # No exception for this type - return nil + ## + # 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| + parameter_syntax(param_name, param_data, type_fn) + 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}" # 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}]" + else + "[[#{array_type}; #{dim2_size}]; #{dim1_size}]" + end + end + + ## + # Generate array access syntax for 1D and 2D arrays + ## + 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