diff --git a/.gitignore b/.gitignore index f9b9a402..13671da7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,4 @@ Examples/**/obj # Visual Studio Code debug -.vscode - -.DS_Store \ No newline at end of file +.vscode \ No newline at end of file diff --git a/Build/build.bat b/Build/build.bat index 9298216f..4ee2cdb6 100644 --- a/Build/build.bat +++ b/Build/build.bat @@ -3,9 +3,7 @@ set startingDir="%CD%" set basepath="%~dp0" cd %basepath%\..\Source -set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildbindingjava.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go - -set GOOS=windows +set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingjava.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go set GOARCH=amd64 echo "Build act.win64.exe" go build -o ..\act.win64.exe %Sources% diff --git a/Build/build.sh b/Build/build.sh index 5d4b9e49..8e8a31ce 100755 --- a/Build/build.sh +++ b/Build/build.sh @@ -6,7 +6,7 @@ startingpath="$(pwd)" basepath="$(cd "$(dirname "$0")" && pwd)" cd "$basepath/../Source" -Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" +Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go" echo "Build act.win64.exe" export GOARCH="amd64" diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml b/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml new file mode 100644 index 00000000..914acd5e --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml @@ -0,0 +1,16 @@ +# Copyright (C) 2019 PrimeDevelopers +# +# All rights reserved. +# +# This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. +# +# Abstract: This is an autogenerated Cargo file for the development of Prime Numbers Library. +# +# Interface version: 1.2.0 + +[package] + name = "primes" + version = "0.1.0" +[lib] + path = "lib.rs" + crate-type = ["cdylib"] diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs new file mode 100644 index 00000000..836fe590 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs @@ -0,0 +1,93 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. The functions in this file need to be implemented. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +// Handle passed through interface define the casting maps needed to extract + +use libprimes_interfaces::*; + +#[allow(dead_code)] +impl HandleImpl { + pub fn as_base(&self) -> Option<&dyn Base> { + match self { + HandleImpl::TCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_ref()), + _ => None + } + } + pub fn as_mut_base(&mut self) -> Option<&mut dyn Base> { + match self { + HandleImpl::TCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_mut()), + _ => None + } + } + + pub fn as_calculator(&self) -> Option<&dyn Calculator> { + match self { + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_ref()), + _ => None + } + } + pub fn as_mut_calculator(&mut self) -> Option<&mut dyn Calculator> { + match self { + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_mut()), + _ => None + } + } + + pub fn as_factorization_calculator(&self) -> Option<&dyn FactorizationCalculator> { + match self { + _ => None + } + } + pub fn as_mut_factorization_calculator(&mut self) -> Option<&mut dyn FactorizationCalculator> { + match self { + _ => None + } + } + + pub fn as_sieve_calculator(&self) -> Option<&dyn SieveCalculator> { + match self { + _ => None + } + } + pub fn as_mut_sieve_calculator(&mut self) -> Option<&mut dyn SieveCalculator> { + match self { + _ => None + } + } + + pub fn inc_ref_count(&mut self) { + match self { + HandleImpl::TBase(count, _) => *count += 1, + HandleImpl::TCalculator(count, _) => *count += 1, + HandleImpl::TFactorizationCalculator(count, _) => *count += 1, + HandleImpl::TSieveCalculator(count, _) => *count += 1, + } + } + pub fn dec_ref_count(&mut self) -> bool { + match self { + HandleImpl::TBase(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TCalculator(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TFactorizationCalculator(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TSieveCalculator(count, _) => {*count -= 1; *count == 0}, + } + } +} diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs new file mode 100644 index 00000000..12baa7af --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs @@ -0,0 +1,298 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. The functions in this file need to be implemented. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +// Calls from the C-Interface to the Rust traits via the CWrapper +// These are the symbols exposed in the shared object interface + +use libprimes_interfaces::*; +use libprimes::CWrapper; +use std::ffi::{c_char, CStr}; + +#[no_mangle] +pub fn libprimes_getversion(major : *mut u32, minor : *mut u32, micro : *mut u32) -> i32 { + // Convert parameter major to be used as an argument + if major.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _major = unsafe {&mut *major}; + + // Convert parameter minor to be used as an argument + if minor.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _minor = unsafe {&mut *minor}; + + // Convert parameter micro to be used as an argument + if micro.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _micro = unsafe {&mut *micro}; + + // Call into wrapper for global + CWrapper::get_version(_major, _minor, _micro); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_getlasterror(instance : BaseHandle, error_message_buffer_size : usize, error_message_needed_chars : *mut usize, error_message_buffer : *mut u8, has_error : *mut u8) -> i32 { + // Convert parameter instance to be used as an argument + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&mut *instance}; + let _optional_instance = _handle_instance.as_mut_base(); + if _optional_instance.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _instance = _optional_instance.unwrap(); + + // Convert parameter error_message to be used as an argument + let mut _string_error_message = String::new(); + let _error_message = &mut _string_error_message; + + // Call into wrapper for global + let _return_has_error = CWrapper::get_last_error(_instance, _error_message); + + // Pass the string error_message via output parameters + let _slice_error_message = _string_error_message.as_bytes(); + if error_message_buffer_size > _slice_error_message.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if error_message_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if error_message_needed_chars.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_error_message = unsafe { std::slice::from_raw_parts_mut(error_message_buffer, _slice_error_message.len()) }; + _buffer_slice_error_message.clone_from_slice(_slice_error_message); + let mut _error_message_needed_chars = unsafe { &mut *error_message_needed_chars }; + *_error_message_needed_chars = _slice_error_message.len(); + + // Pass the return value has_error via output parameters + if has_error.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_has_error = unsafe{&mut *has_error}; + *_ref_has_error = _return_has_error as u8; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +fn libprimes_acquireinstance(instance : BaseHandle) -> i32 { + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&mut *instance}; + _handle_instance.inc_ref_count(); + LIBPRIMES_SUCCESS +} +#[no_mangle] +fn libprimes_releaseinstance(instance : BaseHandle) -> i32 { + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&mut *instance}; + let free = _handle_instance.dec_ref_count(); + if free { + unsafe { let _ = Box::from_raw(instance); } + } + LIBPRIMES_SUCCESS +} +#[no_mangle] +pub fn libprimes_createfactorizationcalculator(instance : *mut FactorizationCalculatorHandle) -> i32 { + // Call into wrapper for global + let _return_instance = CWrapper::create_factorization_calculator(); + + // Pass the return value instance via output parameters + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = Box::new(HandleImpl::TFactorizationCalculator(1, _return_instance)); + let mut _ref_instance = unsafe{&mut *instance}; + *_ref_instance = Box::into_raw(_handle_instance); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_createsievecalculator(instance : *mut SieveCalculatorHandle) -> i32 { + // Call into wrapper for global + let _return_instance = CWrapper::create_sieve_calculator(); + + // Pass the return value instance via output parameters + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = Box::new(HandleImpl::TSieveCalculator(1, _return_instance)); + let mut _ref_instance = unsafe{&mut *instance}; + *_ref_instance = Box::into_raw(_handle_instance); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_setjournal(file_name : *const c_char) -> i32 { + // Convert parameter file_name to be used as an argument + if file_name.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _str_file_name = unsafe{ CStr::from_ptr(file_name) }; + let _optional_file_name = _str_file_name.to_str(); + if _optional_file_name.is_err() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _file_name = _optional_file_name.unwrap(); + + // Call into wrapper for global + CWrapper::set_journal(_file_name); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_base_classtypeid(self_ : BaseHandle, class_type_id : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_base(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + let _return_class_type_id = _self_.class_type_id(); + + // Pass the return value class_type_id via output parameters + if class_type_id.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_class_type_id = unsafe{&mut *class_type_id}; + *_ref_class_type_id = _return_class_type_id; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_getvalue(self_ : CalculatorHandle, value : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + let _return_value = _self_.get_value(); + + // Pass the return value value via output parameters + if value.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_value = unsafe{&mut *value}; + *_ref_value = _return_value; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_setvalue(self_ : CalculatorHandle, value : u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter value to be used as an argument + let _value = value; + + // Call into trait class + _self_.set_value(_value); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_calculate(self_ : CalculatorHandle) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + _self_.calculate(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_setprogresscallback(self_ : CalculatorHandle, progress_callback : ProgressCallback) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter progress_callback to be used as an argument + let _progress_callback = progress_callback; + + // Call into trait class + _self_.set_progress_callback(_progress_callback); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_factorizationcalculator_getprimefactors(self_ : FactorizationCalculatorHandle, prime_factors_buffer_size : usize, prime_factors_count : *mut usize, prime_factors_buffer : *mut PrimeFactor) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_factorization_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter prime_factors to be used as an argument + let mut _array_prime_factors : Vec = Vec::new(); + let _prime_factors = &mut _array_prime_factors; + + // Call into trait class + _self_.get_prime_factors(_prime_factors); + + // Pass the array prime_factors via output parameters + let _slice_prime_factors = _array_prime_factors.as_slice(); + if prime_factors_buffer_size > _slice_prime_factors.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if prime_factors_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if prime_factors_count.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_prime_factors = unsafe { std::slice::from_raw_parts_mut(prime_factors_buffer, _slice_prime_factors.len()) }; + _buffer_slice_prime_factors.clone_from_slice(_slice_prime_factors); + let mut _prime_factors_count = unsafe { &mut *prime_factors_count }; + *_prime_factors_count = _slice_prime_factors.len(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_sievecalculator_getprimes(self_ : SieveCalculatorHandle, primes_buffer_size : usize, primes_count : *mut usize, primes_buffer : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_sieve_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter primes to be used as an argument + let mut _array_primes : Vec = Vec::new(); + let _primes = &mut _array_primes; + + // Call into trait class + _self_.get_primes(_primes); + + // Pass the array primes via output parameters + let _slice_primes = _array_primes.as_slice(); + if primes_buffer_size > _slice_primes.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if primes_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if primes_count.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_primes = unsafe { std::slice::from_raw_parts_mut(primes_buffer, _slice_primes.len()) }; + _buffer_slice_primes.clone_from_slice(_slice_primes); + let mut _primes_count = unsafe { &mut *primes_count }; + *_primes_count = _slice_primes.len(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs new file mode 100644 index 00000000..fcdfa51d --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs @@ -0,0 +1,247 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated rust file in order to allow easy +development of Prime Numbers Library. The implementer of Prime Numbers Library needs to +derive concrete classes from the abstract classes in this header. + +Interface version: 1.2.0 + +*/ + +#[allow(unused_imports)] +use std::ffi::c_void; + +/************************************************************************************************************************* + Version definition for LibPrimes +**************************************************************************************************************************/ + +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MAJOR : usize = 1; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MINOR : usize = 2; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MICRO : usize= 0; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_PRERELEASEINFO : &str = ""; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_BUILDINFO : &str = ""; + + +/************************************************************************************************************************* + Error constants for LibPrimes +**************************************************************************************************************************/ + +#[allow(dead_code)] +pub const LIBPRIMES_SUCCESS : i32 = 0; +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_NOTIMPLEMENTED : i32 = 1; /** functionality not implemented */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INVALIDPARAM : i32 = 2; /** an invalid parameter was passed */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INVALIDCAST : i32 = 3; /** a type cast failed */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_BUFFERTOOSMALL : i32 = 4; /** a provided buffer is too small */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_GENERICEXCEPTION : i32 = 5; /** a generic exception occurred */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_COULDNOTLOADLIBRARY : i32 = 6; /** the library could not be loaded */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_COULDNOTFINDLIBRARYEXPORT : i32 = 7; /** a required exported symbol could not be found in the library */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INCOMPATIBLEBINARYVERSION : i32 = 8; /** the version of the binary interface does not match the bindings interface */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_NORESULTAVAILABLE : i32 = 9; /** no result is available */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_CALCULATIONABORTED : i32 = 10; /** a calculation has been aborted */ + + +/************************************************************************************************************************* + Handle definiton for LibPrimes +**************************************************************************************************************************/ + +// Enum of all traits - this acts as a handle as we pass trait pointers through the interface + +#[allow(dead_code)] +pub enum HandleImpl { + TBase(u64, Box), + TCalculator(u64, Box), + TFactorizationCalculator(u64, Box), + TSieveCalculator(u64, Box) +} + +pub type Handle = *mut HandleImpl; +pub type BaseHandle =Handle; +pub type CalculatorHandle =Handle; +pub type FactorizationCalculatorHandle =Handle; +pub type SieveCalculatorHandle =Handle; +/************************************************************************************************************************* + Interface Struct definitions for LibPrimes +**************************************************************************************************************************/ + +#[repr(C)] +#[derive(Clone)] +pub struct PrimeFactor { + pub prime: u64, + pub multiplicity: u32 +} + +/************************************************************************************************************************* + Function type definitions for LibPrimes +**************************************************************************************************************************/ + +// Callback to report calculation progress and query whether it should be aborted +// +// * @param[in] progress_percentage - How far has the calculation progressed? +// * @param[out] should_abort - Should the calculation be aborted? +// +pub type ProgressCallback = unsafe extern "C" fn(progress_percentage : f32, should_abort : *mut u8); +/************************************************************************************************************************* + Traits defined for LibPrimes +**************************************************************************************************************************/ + +// Trait for interface Base +// +pub trait Base { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64; + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool; + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self); + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str); +} + + +// Trait for interface Calculator +// +pub trait Calculator : Base { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64; + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64); + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self); + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback); +} + + +// Trait for interface FactorizationCalculator +// +pub trait FactorizationCalculator : Calculator { + + // get_prime_factors + // + // Returns the prime factors of this number (without multiplicity) + // * @param[out] prime_factors - The prime factors of this number + // + fn get_prime_factors(&mut self, prime_factors : &mut Vec); +} + + +// Trait for interface SieveCalculator +// +pub trait SieveCalculator : Calculator { + + // get_primes + // + // Returns all prime numbers lower or equal to the sieve's value + // * @param[out] primes - The primes lower or equal to the sieve's value + // + fn get_primes(&mut self, primes : &mut Vec); +} + + +/************************************************************************************************************************* + Trait defined for global methods of LibPrimes +**************************************************************************************************************************/ + +// Wrapper trait for global methods +// +pub trait Wrapper { + + // get_version + // + // retrieves the binary version of this library. + // * @param[out] major - returns the major version of this library + // * @param[out] minor - returns the minor version of this library + // * @param[out] micro - returns the micro version of this library + // + fn get_version(major : &mut u32, minor : &mut u32, micro : &mut u32); + + // get_last_error + // + // Returns the last error recorded on this object + // * @param[in] instance - Instance Handle + // * @param[out] error_message - Message of the last error + // * @param[return] has_error - Is there a last error to query + // + fn get_last_error(instance : &mut dyn Base, error_message : &mut String) -> bool; + + // create_factorization_calculator + // + // Creates a new FactorizationCalculator instance + // * @param[return] instance - New FactorizationCalculator instance + // + fn create_factorization_calculator() -> Box; + + // create_sieve_calculator + // + // Creates a new SieveCalculator instance + // * @param[return] instance - New SieveCalculator instance + // + fn create_sieve_calculator() -> Box; + + // set_journal + // + // Handles Library Journaling + // * @param[in] file_name - Journal FileName + // + fn set_journal(file_name : &str); +} diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs new file mode 100644 index 00000000..ed68033e --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs @@ -0,0 +1,78 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_sieve_calculator::CSieveCalculator; +use libprimes_factorization_calculator::CFactorizationCalculator; + +// Wrapper struct to implement the wrapper trait for global methods +pub struct CWrapper; + +impl Wrapper for CWrapper { + + + // get_version + // + // retrieves the binary version of this library. + // * @param[out] major - returns the major version of this library + // * @param[out] minor - returns the minor version of this library + // * @param[out] micro - returns the micro version of this library + // + fn get_version(_major : &mut u32, _minor : &mut u32, _micro : &mut u32) { + *_major = 1; + *_minor = 1; + *_micro = 1; + } + + // get_last_error + // + // Returns the last error recorded on this object + // * @param[in] instance - Instance Handle + // * @param[out] error_message - Message of the last error + // * @param[return] has_error - Is there a last error to query + // + fn get_last_error(_instance : &mut dyn Base, _error_message : &mut String) -> bool { + _instance.get_last_error_message(_error_message) + } + + // create_factorization_calculator + // + // Creates a new FactorizationCalculator instance + // * @param[return] instance - New FactorizationCalculator instance + // + fn create_factorization_calculator() -> Box { + Box::new(CFactorizationCalculator::new()) + } + + // create_sieve_calculator + // + // Creates a new SieveCalculator instance + // * @param[return] instance - New SieveCalculator instance + // + fn create_sieve_calculator() -> Box { + Box::new(CSieveCalculator::new()) + } + + // set_journal + // + // Handles Library Journaling + // * @param[in] file_name - Journal FileName + // + fn set_journal(_file_name : &str) { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs new file mode 100644 index 00000000..67541471 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs @@ -0,0 +1,74 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; + +// Stub struct to implement the Base trait +pub struct CBase { + last_error : Option +} + +impl Base for CBase { + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, _error_message : &mut String) -> bool { + match &self.last_error { + None => false, + Some(error_val) => { + *_error_message = error_val.clone(); + true + } + } + } + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.last_error = None; + } + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, _error_message : &str) { + self.last_error = Some(_error_message.to_string()); + } + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + 0 + } +} + + +impl CBase { + pub fn new() -> CBase { + CBase { + last_error : None + } + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs new file mode 100644 index 00000000..762fadbe --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs @@ -0,0 +1,128 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_base::CBase; + +// Stub struct to implement the Calculator trait +pub struct CCalculator { + parent : CBase, + value : u64, + progress_callback : ProgressCallback +} + +// Implementation of parent traits via parent + +impl Base for CCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + 1 + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl Calculator for CCalculator { + + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + self.value + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, _value : u64) { + self.value = _value; + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + unimplemented!(); + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, _progress_callback : ProgressCallback) { + self.progress_callback = _progress_callback + } +} + + +unsafe extern "C" fn dummy_progress(_par : f32, _val : *mut u8) { + +} + +impl CCalculator { + pub fn new() -> CCalculator { + CCalculator { + parent : CBase::new(), + value : 0, + progress_callback : dummy_progress + } + } + + pub fn progress_abort(&self) -> bool { + let mut res : u8 = 0; + unsafe { + (self.progress_callback)(0_f32, &mut res as *mut u8) + } + res != 0 + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs new file mode 100644 index 00000000..2a850705 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs @@ -0,0 +1,143 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_calculator::CCalculator; + +// Stub struct to implement the FactorizationCalculator trait +pub struct CFactorizationCalculator { + parent : CCalculator, + prime_factors : Vec +} + +// Implementation of parent traits via parent + +impl Calculator for CFactorizationCalculator { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + self.parent.get_value() + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64) { + self.parent.set_value(value) + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + self.prime_factors.clear(); + let mut val = self.get_value(); + let mut n = 0_u64; + while n < val { + if self.parent.progress_abort() { + return; + } + let mut prime_factor = PrimeFactor { + prime : n, + multiplicity : 0 + }; + while val % n == 0 { + val = val / n; + prime_factor.multiplicity += 1; + } + if prime_factor.multiplicity > 0 { + self.prime_factors.push(prime_factor) + } + n += 1 + } + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback) { + self.parent.set_progress_callback(progress_callback) + } +} +impl Base for CFactorizationCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + 2 + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl FactorizationCalculator for CFactorizationCalculator { + + + // get_prime_factors + // + // Returns the prime factors of this number (without multiplicity) + // * @param[out] prime_factors - The prime factors of this number + // + fn get_prime_factors(&mut self, _prime_factors : &mut Vec) { + *_prime_factors = self.prime_factors.clone() + } +} + +impl CFactorizationCalculator { + pub fn new() -> CFactorizationCalculator { + CFactorizationCalculator { + parent : CCalculator::new(), + prime_factors : Vec::new() + } + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs new file mode 100644 index 00000000..7e2c515f --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs @@ -0,0 +1,142 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_calculator::CCalculator; + +// Stub struct to implement the SieveCalculator trait +pub struct CSieveCalculator { + parent : CCalculator, + primes : Vec +} + +// Implementation of parent traits via parent + +impl Calculator for CSieveCalculator { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + self.parent.get_value() + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64) { + self.parent.set_value(value) + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + self.primes.clear(); + let val = self.get_value(); + let mut sieved : Vec = vec![false; (val+1) as usize]; + sieved[0] = true; + sieved[1] = true; + let val_sqrt = (val as f64).sqrt() as u64 + 1; + for i in 2_u64..val_sqrt { + if self.parent.progress_abort() { + return + } + if !sieved[i as usize] { + self.primes.push(i); + let mut mul : u64 = i*i; + while mul <= val { + sieved[mul as usize] = true; + mul *= i; + } + } + } + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback) { + self.parent.set_progress_callback(progress_callback) + } +} +impl Base for CSieveCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + 3 + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl SieveCalculator for CSieveCalculator { + + + // get_primes + // + // Returns all prime numbers lower or equal to the sieve's value + // * @param[out] primes - The primes lower or equal to the sieve's value + // + fn get_primes(&mut self, _primes : &mut Vec) { + *_primes = self.primes.clone() + } +} + +impl CSieveCalculator { + pub fn new() -> CSieveCalculator { + CSieveCalculator { + parent : CCalculator::new(), + primes : Vec::new() + } + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs new file mode 100644 index 00000000..629c58a9 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs @@ -0,0 +1,44 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] +#![feature(vec_into_raw_parts)] + +#[path = "Interfaces/libprimes_interfaces.rs"] +mod libprimes_interfaces; + +#[path = "Interfaces/libprimes_interface_wrapper.rs"] +mod libprimes_interface_wrapper; + +#[path = "Interfaces/libprimes_interface_handle.rs"] +mod libprimes_interface_handle; + +#[path = "Stub/libprimes.rs"] +mod libprimes; + +#[path = "Stub/libprimes_base.rs"] +mod libprimes_base; + +#[path = "Stub/libprimes_calculator.rs"] +mod libprimes_calculator; + +#[path = "Stub/libprimes_factorization_calculator.rs"] +mod libprimes_factorization_calculator; + +#[path = "Stub/libprimes_sieve_calculator.rs"] +mod libprimes_sieve_calculator; + diff --git a/Examples/Primes/libPrimes.xml b/Examples/Primes/libPrimes.xml index 6f83e41f..1d7ec1fb 100644 --- a/Examples/Primes/libPrimes.xml +++ b/Examples/Primes/libPrimes.xml @@ -19,6 +19,7 @@ + @@ -45,6 +46,9 @@ + + + @@ -74,7 +78,8 @@ + releasemethod="ReleaseInstance" versionmethod="GetVersion" errormethod="GetLastError" journalmethod="SetJournal" + classtypeidmethod="ClassTypeId"> diff --git a/Source/automaticcomponenttoolkit.go b/Source/automaticcomponenttoolkit.go index 760a3167..6e61a595 100644 --- a/Source/automaticcomponenttoolkit.go +++ b/Source/automaticcomponenttoolkit.go @@ -547,6 +547,29 @@ func createComponent(component ComponentDefinition, outfolderBase string, bindin } } + case "Rust": + { + outputFolderImplementationProject := outputFolderImplementations + "/Rust" + outputFolderImplementationRust := outputFolderImplementations + "/Rust/Interfaces" + outputFolderImplementationRustStub := outputFolderImplementations + "/Rust/Stub" + + err = os.MkdirAll(outputFolderImplementationRust, os.ModePerm) + if err != nil { + return err + } + + err = os.MkdirAll(outputFolderImplementationRustStub, os.ModePerm) + if err != nil { + return err + } + + err = BuildImplementationRust(component, outputFolderImplementationRust, outputFolderImplementationRustStub, + outputFolderImplementationProject, implementation) + if err != nil { + return err + } + } + case "Fortran": { log.Printf("Implementation in language \"%s\" is not yet supported.", implementation.Language) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go new file mode 100644 index 00000000..1aaf437f --- /dev/null +++ b/Source/buildimplementationrust.go @@ -0,0 +1,1059 @@ +/*++ + +Copyright (C) 2023 Autodesk Inc. (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// buildimplementationrust .go +// functions to generate Rust interface classes, implementation stubs and wrapper code that maps to +// the rust interfaces. +////////////////////////////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "log" + "path" + "path/filepath" + "strings" +) + +// BuildImplementationPascal builds Pascal interface classes, implementation stubs and wrapper code that maps to the Pascal header +func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubOutputFolder string, projectOutputFolder string, implementation ComponentDefinitionImplementation) error { + forceRebuild := true + LibraryName := component.LibraryName + BaseName := component.BaseName + modfiles := make([]string, 0) + indentString := getIndentationString(implementation.Indentation) + + stubIdentifier := "" + if len(implementation.StubIdentifier) > 0 { + stubIdentifier = "_" + strings.ToLower(implementation.StubIdentifier) + } + + InterfaceMod := BaseName + "_interfaces" + IntfFileName := InterfaceMod + ".rs" + IntfFilePath := path.Join(outputFolder, IntfFileName) + modfiles = append(modfiles, IntfFilePath) + log.Printf("Creating \"%s\"", IntfFilePath) + IntfRSFile, err := CreateLanguageFile(IntfFilePath, indentString) + if err != nil { + return err + } + IntfRSFile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated rust file in order to allow easy\ndevelopment of %s. The implementer of %s needs to\nderive concrete classes from the abstract classes in this header.", LibraryName, LibraryName), + true) + err = writeRustBaseTypeDefinitions(component, IntfRSFile, component.NameSpace, BaseName) + if err != nil { + return err + } + err = buildRustInterfaces(component, IntfRSFile) + if err != nil { + return err + } + + IntfWrapperFileName := BaseName + "_interface_wrapper.rs" + IntfWrapperFilePath := path.Join(outputFolder, IntfWrapperFileName) + modfiles = append(modfiles, IntfWrapperFilePath) + log.Printf("Creating \"%s\"", IntfWrapperFilePath) + IntfWrapperRSFile, err := CreateLanguageFile(IntfWrapperFilePath, indentString) + if err != nil { + return err + } + IntfWrapperRSFile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. The functions in this file need to be implemented. It needs to be generated only once.", LibraryName), + true) + err = buildRustWrapper(component, IntfWrapperRSFile, InterfaceMod) + if err != nil { + return err + } + + IntfHandleFileName := BaseName + "_interface_handle.rs" + IntfHandleFilePath := path.Join(outputFolder, IntfHandleFileName) + modfiles = append(modfiles, IntfHandleFilePath) + log.Printf("Creating \"%s\"", IntfHandleFilePath) + IntfHandleRSFile, err := CreateLanguageFile(IntfHandleFilePath, indentString) + if err != nil { + return err + } + IntfHandleRSFile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. The functions in this file need to be implemented. It needs to be generated only once.", LibraryName), + true) + err = buildRustHandle(component, IntfHandleRSFile, InterfaceMod) + if err != nil { + return err + } + + IntfWrapperStubName := path.Join(stubOutputFolder, BaseName+stubIdentifier+".rs") + modfiles = append(modfiles, IntfWrapperStubName) + if forceRebuild || !FileExists(IntfWrapperStubName) { + log.Printf("Creating \"%s\"", IntfWrapperStubName) + stubfile, err := CreateLanguageFile(IntfWrapperStubName, indentString) + if err != nil { + return err + } + stubfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + if err != nil { + return err + } + err = buildRustGlobalStubFile(component, stubfile, InterfaceMod) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of implementation stub \"%s\"", IntfWrapperStubName) + } + + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + StubBase := BaseName + stubIdentifier + StubClassName := path.Join(stubOutputFolder, StubBase+"_"+toSnakeCase(class.ClassName)+".rs") + modfiles = append(modfiles, StubClassName) + if forceRebuild || !FileExists(StubClassName) { + log.Printf("Creating \"%s\"", StubClassName) + stubfile, err := CreateLanguageFile(StubClassName, indentString) + if err != nil { + return err + } + stubfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + if err != nil { + return err + } + err = buildRustStubFile(component, class, stubfile, InterfaceMod, StubBase) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of implementation stub \"%s\"", StubClassName) + } + } + + if len(projectOutputFolder) > 0 { + IntfWrapperLibName := path.Join(projectOutputFolder, "lib.rs") + if forceRebuild || !FileExists(IntfWrapperLibName) { + log.Printf("Creating \"%s\"", IntfWrapperLibName) + libfile, err := CreateLanguageFile(IntfWrapperLibName, indentString) + if err != nil { + return err + } + libfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + err = buildRustGlobalLibFile(component, libfile, projectOutputFolder, modfiles) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of lib \"%s\"", IntfWrapperLibName) + } + + CargoFileName := path.Join(projectOutputFolder, "Cargo.toml") + if forceRebuild || !FileExists(CargoFileName) { + log.Printf("Creating Cargo file \"%s\" for Rust Implementation", CargoFileName) + CargoFile, err := CreateLanguageFile(CargoFileName, indentString) + if err != nil { + return err + } + CargoFile.WriteTomlLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Cargo file for the development of %s.", LibraryName), + true) + LibPath, err := filepath.Rel(projectOutputFolder, IntfWrapperLibName) + if err != nil { + return err + } + buildCargoForRustImplementation(component, CargoFile, LibPath) + } else { + log.Printf("Omitting recreation of Cargo file \"%s\" for Rust Implementation", CargoFileName) + } + } + + return nil +} + +func buildRustGlobalLibFile(component ComponentDefinition, w LanguageWriter, basedir string, modfiles []string) error { + w.Writeln("") + w.Writeln("#![feature(trait_upcasting)]") + w.Writeln("#![allow(incomplete_features)]") + w.Writeln("#![feature(vec_into_raw_parts)]") + w.Writeln("") + // Get all modules + for i := 0; i < len(modfiles); i++ { + modfile := modfiles[i] + relfile, err := filepath.Rel(basedir, modfile) + if err != nil { + return err + } + w.Writeln("#[path = \"%s\"]", strings.ReplaceAll(relfile, "\\", "/")) + IntfName := strings.TrimSuffix(filepath.Base(relfile), ".rs") + w.Writeln("mod %s;", IntfName) + w.Writeln("") + } + return nil +} + +func buildRustInterfaces(component ComponentDefinition, w LanguageWriter) error { + NameSpace := component.NameSpace + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Traits defined for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(component.Classes); i++ { + classinfo := component.Classes[i] + err := writeRustTrait(component, classinfo, w) + if err != nil { + return err + } + } + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Trait defined for global methods of %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + err := writeRustGlobalTrait(component, w) + if err != nil { + return err + } + return nil +} + +func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { + projectName := strings.ToLower(component.NameSpace) + if strings.HasPrefix(projectName, "lib") { + projectName = projectName[3:] + } + w.Writeln("[package]") + w.Writeln(" name = \"%s\"", projectName) + w.Writeln(" version = \"0.1.0\"") + w.Writeln("[lib]") + w.Writeln(" path = \"%s\"", strings.ReplaceAll(path, "\\", "/")) + w.Writeln(" crate-type = [\"cdylib\"]") + return nil +} + +func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter) error { + w.Writeln("// Trait for interface %s", class.ClassName) + w.Writeln("//") + if class.ClassDescription != "" { + w.Writeln("// %s", class.ClassDescription) + w.Writeln("//") + } + parentClassString := "" + if !component.isBaseClass(class) { + if class.ParentClass == "" { + parentClassString = fmt.Sprintf(": %s ", component.Global.BaseClassName) + } else { + parentClassString = fmt.Sprintf(": %s ", class.ParentClass) + } + } + w.Writeln("pub trait %s %s {", class.ClassName, parentClassString) + w.AddIndentationLevel(1) + methods := class.Methods + if component.isBaseClass(class) { + methods = append( + methods, + GetLastErrorMessageMethod(), + ClearErrorMessageMethod(), + RegisterErrorMessageMethod()) + } + + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, true, false, false, nil) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + w.Writeln("") + return nil +} + +func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool, hasImpl bool, hasImplParent bool, impl []string) error { + methodName := toSnakeCase(method.MethodName) + w.Writeln("// %s", methodName) + w.Writeln("//") + w.Writeln("// %s", method.MethodDescription) + parameterString := "" + parameterNames := "" + if hasSelf { + parameterString += "&mut self" + } + returnType := "" + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + RustParams, err := generateRustParameters(param, false) + if err != nil { + return err + } + RustParam := RustParams[0] + paramName := RustParam.ParamName + if hasImpl && !hasImplParent { + // For stubed out methods avoid warning about unued parameters + paramName = "_" + RustParam.ParamName + } + if param.ParamPass != "return" { + if parameterString == "" { + parameterString += fmt.Sprintf("%s : %s", paramName, RustParam.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", paramName, RustParam.ParamType) + } + if parameterNames == "" { + parameterNames += paramName + } else { + parameterNames += fmt.Sprintf(", %s", paramName) + } + } else { + returnType = RustParam.ParamType + } + w.Writeln("// %s", RustParam.ParamComment) + } + w.Writeln("//") + if !hasImpl { + if returnType == "" { + w.Writeln("fn %s(%s);", methodName, parameterString) + } else { + w.Writeln("fn %s(%s) -> %s;", methodName, parameterString, returnType) + } + } else { + if returnType == "" { + w.Writeln("fn %s(%s) {", methodName, parameterString) + } else { + w.Writeln("fn %s(%s) -> %s {", methodName, parameterString, returnType) + } + w.AddIndentationLevel(1) + if !hasImplParent { + if impl == nil { + w.Writeln("unimplemented!();") + } else { + for i := 0; i < len(impl); i++ { + w.Writeln(impl[i]) + } + } + } else { + w.Writeln("self.parent.%s(%s)", methodName, parameterNames) + } + w.AddIndentationLevel(-1) + w.Writeln("}") + } + return nil +} + +func writeRustGlobalTrait(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("// Wrapper trait for global methods") + w.Writeln("//") + w.Writeln("pub trait Wrapper {") + w.AddIndentationLevel(1) + methods := component.Global.Methods + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, false, false, false, nil) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + return nil +} + +func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("") + w.Writeln("// Wrapper struct to implement the wrapper trait for global methods") + w.Writeln("pub struct CWrapper;") + w.Writeln("") + w.Writeln("impl Wrapper for CWrapper {") + w.Writeln("") + w.AddIndentationLevel(1) + methods := component.Global.Methods + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, false, true, false, nil) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + return nil +} + +func getParentList(component ComponentDefinition, class ComponentDefinitionClass) ([]string, error) { + parents := make([]string, 0) + currClass := class + for !component.isBaseClass(currClass) { + parent := currClass.ParentClass + if parent == "" { + parent = component.baseClass().ClassName + } + parents = append(parents, parent) + parClass, err := getClass(component, parent) + if err != nil { + return parents, err + } + currClass = parClass + } + return parents, nil +} + +func getChildList(component ComponentDefinition, class ComponentDefinitionClass) []string { + children := make([]string, 0) + for i := 0; i < len(component.Classes); i++ { + child := component.Classes[i] + if child.ParentClass == class.ClassName { + children = append(children, child.ClassName) + children = append(children, getChildList(component, child)...) + } + } + return children +} + +func getClass(component ComponentDefinition, name string) (ComponentDefinitionClass, error) { + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + if class.ClassName == name { + return class, nil + } + } + return component.baseClass(), fmt.Errorf("Cannot find class %s", name) +} + +func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, InterfaceMod string, StubBase string) error { + Name := class.ClassName + parents, err := getParentList(component, class) + if err != nil { + return err + } + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + if len(parents) > 0 { + parentName := parents[0] + w.Writeln("use %s_%s::C%s;", StubBase, toSnakeCase(parentName), parentName) + } + w.Writeln("") + w.Writeln("// Stub struct to implement the %s trait", Name) + if len(parents) == 0 { + w.Writeln("pub struct C%s {", Name) + w.AddIndentationLevel(1) + w.Writeln("last_error : Option") + w.ResetIndentationLevel() + w.Writeln("}") + } else { + w.Writeln("pub struct C%s {", Name) + w.AddIndentationLevel(1) + w.Writeln("parent : C%s", parents[0]) + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + w.Writeln("// Implementation of parent traits via parent") + w.Writeln("") + for i := 0; i < len(parents); i++ { + parent := parents[i] + parentClass, err := getClass(component, parent) + if err != nil { + return err + } + w.Writeln("impl %s for C%s {", parent, Name) + w.AddIndentationLevel(1) + methods := parentClass.Methods + if component.isBaseClass(parentClass) { + methods = append( + methods, + GetLastErrorMessageMethod(), + ClearErrorMessageMethod(), + RegisterErrorMessageMethod()) + } + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, true, true, true, nil) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + } + } + w.Writeln("") + w.Writeln("impl %s for C%s {", Name, Name) + w.Writeln("") + w.AddIndentationLevel(1) + methods := class.Methods + if component.isBaseClass(class) { + writeRustStubGetLastErrorMessageMethod(class, w) + writeRustStubClearErrorMessageMethod(class, w) + writeRustStubRegisterErrorMessageMethod(class, w) + } + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, true, true, false, nil) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + return nil +} + +func writeRustStubGetLastErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := GetLastErrorMessageMethod() + impl := []string{ + "match &self.last_error {", + " None => false,", + " Some(error_val) => {", + " *_error_message = error_val.clone();", + " true", + " }", + "}"} + writeRustTraitFn(method, w, true, true, false, impl) +} +func writeRustStubClearErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := ClearErrorMessageMethod() + impl := []string{ + "self.last_error = None;"} + writeRustTraitFn(method, w, true, true, false, impl) +} +func writeRustStubRegisterErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := RegisterErrorMessageMethod() + impl := []string{ + "self.last_error = Some(_error_message.to_string());"} + writeRustTraitFn(method, w, true, true, false, impl) +} + +func buildRustWrapper(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + // Imports + ModName := strings.ToLower(component.NameSpace) + w.Writeln("") + w.Writeln("// Calls from the C-Interface to the Rust traits via the CWrapper") + w.Writeln("// These are the symbols exposed in the shared object interface") + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("use %s::CWrapper;", ModName) + w.Writeln("use std::ffi::{c_char, CStr};") + w.Writeln("") + cprefix := ModName + "_" + // Build the global methods + err := writeGlobalRustWrapper(component, w, cprefix) + if err != nil { + return err + } + // Build other methods + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + err := writeClassRustWrapper(component, class, w, cprefix) + if err != nil { + return err + } + } + return nil +} + +func buildRustHandle(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + w.Writeln("") + w.Writeln("// Handle passed through interface define the casting maps needed to extract") + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("") + w.Writeln("#[allow(dead_code)]") + w.Writeln("impl HandleImpl {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + writeRustHandleAs(component, w, class, false) + writeRustHandleAs(component, w, class, true) + w.Writeln("") + } + writeRustHandleIncRef(component, w) + writeRustHandleDecRef(component, w) + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class ComponentDefinitionClass, mut bool) error { + children := getChildList(component, class) + Name := class.ClassName + if !mut { + w.Writeln("pub fn as_%s(&self) -> Option<&dyn %s> {", toSnakeCase(Name), Name) + } else { + w.Writeln("pub fn as_mut_%s(&mut self) -> Option<&mut dyn %s> {", toSnakeCase(Name), Name) + } + w.AddIndentationLevel(1) + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(children); i++ { + child := children[i] + if !mut { + w.Writeln("HandleImpl::T%s(_, ptr) => Some(ptr.as_ref()),", child) + } else { + w.Writeln("HandleImpl::T%s(_, ptr) => Some(ptr.as_mut()),", child) + } + } + w.Writeln("_ => None") + w.AddIndentationLevel(-1) + w.Writeln("}") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustHandleIncRef(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("pub fn inc_ref_count(&mut self) {") + w.AddIndentationLevel(1) + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + w.Writeln("HandleImpl::T%s(count, _) => *count += 1,", class.ClassName) + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustHandleDecRef(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("pub fn dec_ref_count(&mut self) -> bool {") + w.AddIndentationLevel(1) + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + w.Writeln("HandleImpl::T%s(count, _) => {*count -= 1; *count == 0},", class.ClassName) + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cprefix string) error { + errorprefix := strings.ToUpper(component.NameSpace) + methods := component.Global.Methods + for i := 0; i < len(methods); i++ { + method := methods[i] + if method.MethodName == component.Global.AcquireMethod { + writeGlobalRustAquireWrapper(w, cprefix, errorprefix) + continue + } + if method.MethodName == component.Global.ReleaseMethod { + writeGlobalRustReleaseWrapper(w, cprefix, errorprefix) + continue + } + err := writeRustMethodWrapper(method, nil, w, cprefix, errorprefix) + if err != nil { + return err + } + w.Writeln("") + } + return nil +} + +func writeGlobalRustAquireWrapper(w LanguageWriter, cprefix string, errorprefix string) { + w.Writeln("#[no_mangle]") + w.Writeln("fn %sacquireinstance(instance : BaseHandle) -> i32 {", cprefix) + w.AddIndentationLevel(1) + w.Writeln("if instance.is_null() { return %s_ERROR_INVALIDPARAM; }", errorprefix) + w.Writeln("let _handle_instance = unsafe {&mut *instance};") + w.Writeln("_handle_instance.inc_ref_count();") + w.Writeln("%s_SUCCESS", errorprefix) + w.AddIndentationLevel(-1) + w.Writeln("}") +} + +func writeGlobalRustReleaseWrapper(w LanguageWriter, cprefix string, errorprefix string) { + w.Writeln("#[no_mangle]") + w.Writeln("fn %sreleaseinstance(instance : BaseHandle) -> i32 {", cprefix) + w.AddIndentationLevel(1) + w.Writeln("if instance.is_null() { return %s_ERROR_INVALIDPARAM; }", errorprefix) + w.Writeln("let _handle_instance = unsafe {&mut *instance};") + w.Writeln("let free = _handle_instance.dec_ref_count();") + w.Writeln("if free {") + w.Writeln(" unsafe { let _ = Box::from_raw(instance); }") + w.Writeln("}") + w.Writeln("%s_SUCCESS", errorprefix) + w.AddIndentationLevel(-1) + w.Writeln("}") +} + +func writeClassRustWrapper(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, cprefix string) error { + errorprefix := strings.ToUpper(component.NameSpace) + methods := class.Methods + classprefix := cprefix + strings.ToLower(class.ClassName) + "_" + for i := 0; i < len(methods); i++ { + method := methods[i] + err := writeRustMethodWrapper(method, &class, w, classprefix, errorprefix) + if err != nil { + return err + } + w.Writeln("") + } + return nil +} + +func writeRustMethodWrapper(method ComponentDefinitionMethod, optclass *ComponentDefinitionClass, w LanguageWriter, cprefix string, errorprefix string) error { + // Build up the parameter strings + parameterString := "" + returnName := "" + // Handle self parameter if non global + if optclass != nil { + SelfParam := ComponentDefinitionParam{ + ParamName: "self_", + ParamType: "class", + ParamClass: optclass.ClassName, + ParamPass: "in"} + SelfRustParams, err := generateRustParameters(SelfParam, true) + if err != nil { + return err + } + SelfRustParam := SelfRustParams[0] + parameterString += fmt.Sprintf("%s : %s", SelfRustParam.ParamName, SelfRustParam.ParamType) + } + // Handle method parameters + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + RustParams, err := generateRustParameters(param, true) + if err != nil { + return err + } + for i := 0; i < len(RustParams); i++ { + RustParam := RustParams[i] + if parameterString == "" { + parameterString += fmt.Sprintf("%s : %s", RustParam.ParamName, RustParam.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + } + } + } + w.Writeln("#[no_mangle]") + w.Writeln("pub fn %s%s(%s) -> i32 {", cprefix, strings.ToLower(method.MethodName), parameterString) + w.AddIndentationLevel(1) + // Convert self parameter if non global + ClassName := "" + if optclass != nil { + SelfParam := ComponentDefinitionParam{ + ParamName: "self_", + ParamType: "class", + ParamClass: optclass.ClassName, + ParamPass: "out"} + CName, err := writeRustParameterConversionArg(SelfParam, w, errorprefix) + if err != nil { + return err + } + ClassName = CName + } + // Convert method parameters + argsString := "" + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + OName, err := writeRustParameterConversionArg(param, w, errorprefix) + if err != nil { + return err + } + if OName != "" { + if argsString == "" { + argsString = OName + } else { + argsString += fmt.Sprintf(", %s", OName) + } + } else { + returnName = "_return_" + toSnakeCase(param.ParamName) + } + } + if ClassName != "" { + w.Writeln("// Call into trait class") + if returnName != "" { + w.Writeln("let %s = %s.%s(%s);", returnName, ClassName, toSnakeCase(method.MethodName), argsString) + } else { + w.Writeln("%s.%s(%s);", ClassName, toSnakeCase(method.MethodName), argsString) + } + } else { + w.Writeln("// Call into wrapper for global") + if returnName != "" { + w.Writeln("let %s = CWrapper::%s(%s);", returnName, toSnakeCase(method.MethodName), argsString) + } else { + w.Writeln("CWrapper::%s(%s);", toSnakeCase(method.MethodName), argsString) + } + } + w.Writeln("") + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + err := writeRustParameterConversionOutPost(param, w, errorprefix) + if err != nil { + return err + } + err = writeRustParameterConversionReturn(param, w, errorprefix) + if err != nil { + return err + } + } + w.Writeln("// All ok - return success") + w.Writeln("%s_SUCCESS", errorprefix) + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) (string, error) { + if param.ParamPass == "return" { + return "", nil + } + IName := toSnakeCase(param.ParamName) + w.Writeln("// Convert parameter %s to be used as an argument", IName) + OName := "_" + IName + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "functiontype": + if param.ParamPass == "in" { + w.Writeln("let %s = %s;", OName, IName) + } else { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&mut *%s};", OName, IName) + } + case "class", "optionalclass": + if param.ParamPass == "in" { + HName := "_handle_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&*%s};", HName, IName) + w.Writeln("let %s = %s.as_%s();", OpName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("if %s.is_none() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) + + } else { + HName := "_handle_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&mut *%s};", HName, IName) + w.Writeln("let %s = %s.as_mut_%s();", OpName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("if %s.is_none() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) + } + case "string": + if param.ParamPass == "in" { + SName := "_str_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe{ CStr::from_ptr(%s) };", SName, IName) + w.Writeln("let %s = %s.to_str();", OpName, SName) + w.Writeln("if %s.is_err() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) + } else { + SName := "_string_" + IName + w.Writeln("let mut %s = String::new();", SName) + w.Writeln("let %s = &mut %s;", OName, SName) + } + case "basicarray": + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "in" + basicTypeName, err := generateRustParameterType(basicParam, true) + if err != nil { + return "", err + } + if param.ParamPass == "in" { + BuffName := IName + "_buffer" + BuffSizeName := IName + "_buffer_size" + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("let %s = unsafe { std::slice::from_raw_parts(%s, %s) };", OName, BuffName, BuffSizeName) + } else { + AName := "_array_" + IName + w.Writeln("let mut %s : Vec<%s> = Vec::new();", AName, basicTypeName) + w.Writeln("let %s = &mut %s;", OName, AName) + } + case "structarray": + basicTypeName := param.ParamClass + if param.ParamPass == "in" { + BuffName := IName + "_buffer" + BuffSizeName := IName + "_buffer_size" + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("let %s = unsafe { std::slice::from_raw_parts(%s, %s) };", OName, BuffName, BuffSizeName) + } else { + AName := "_array_" + IName + w.Writeln("let mut %s : Vec<%s> = Vec::new();", AName, basicTypeName) + w.Writeln("let %s = &mut %s;", OName, AName) + } + case "struct", "pointer": + if param.ParamPass == "in" { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&*%s};", OName, IName) + } else { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&mut *%s};", OName, IName) + } + case "bool": + if param.ParamPass == "in" { + w.Writeln("let %s = %s != 0;", OName, IName) + } else { + w.Writeln("let %s = true;", OName) + } + default: + return "", fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + w.Writeln("") + return OName, nil +} + +func writeRustParameterConversionOutPost(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) error { + if param.ParamPass != "out" { + return nil + } + // Any remaining bit needed to wire out variables + IName := toSnakeCase(param.ParamName) + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "class", "optionalclass", "functiontype", "struct", "pointer": + return nil + case "string": + w.Writeln("// Pass the string %s via output parameters", IName) + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CNName := IName + "_needed_chars" + CNRName := "_" + CNName + SName := "_string_" + IName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_bytes();", SLName, SName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CNName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) + w.Writeln("*%s = %s.len();", CNRName, SLName) + w.Writeln("") + case "basicarray", "structarray": + w.Writeln("// Pass the array %s via output parameters", IName) + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CountName := IName + "_count" + CountRName := "_" + CountName + AName := "_array_" + IName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_slice();", SLName, AName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CountName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CountRName, CountName) + w.Writeln("*%s = %s.len();", CountRName, SLName) + w.Writeln("") + case "bool": + OName := "_" + IName + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s as u8;", RefName, OName) + default: + return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + return nil +} + +func writeRustParameterConversionReturn(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) error { + if param.ParamPass != "return" { + return nil + } + // Take the returned variable and send it to output pars + IName := toSnakeCase(param.ParamName) + w.Writeln("// Pass the return value %s via output parameters", IName) + RetName := "_return_" + IName + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "functiontype", "struct", "pointer": + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s;", RefName, RetName) + case "string": + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CNName := IName + "_needed_chars" + CNRName := "_" + CNName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_bytes();", SLName, RetName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CNName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) + w.Writeln("*%s = %s.len();", CNRName, SLName) + case "basicarray", "structarray": + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CountName := IName + "_count" + CountRName := "_" + CountName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_slice();", SLName, RetName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CountName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CountRName, CountName) + w.Writeln("*%s = %s.len();", CountRName, SLName) + w.Writeln("") + case "class", "optionalclass": + HName := "_handle_" + IName + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = Box::new(HandleImpl::T%s(1, %s));", HName, param.ParamClass, RetName) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = Box::into_raw(%s);", RefName, HName) + case "bool": + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s as u8;", RefName, RetName) + default: + return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + w.Writeln("") + return nil +} diff --git a/Source/languagerust.go b/Source/languagerust.go new file mode 100644 index 00000000..2c97d25c --- /dev/null +++ b/Source/languagerust.go @@ -0,0 +1,535 @@ +/*++ + +Copyright (C) 2023 Autodesk Inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// languagerust.go +// functions to generate the Rust-layer of a library's API (can be used in bindings or implementation) +////////////////////////////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "regexp" + "strings" +) + +func toSnakeCase(BaseType string) string { + var matchCapital = regexp.MustCompile("(.)([A-Z])") + underscored := matchCapital.ReplaceAllString(BaseType, "${1}_${2}") + return strings.ToLower(underscored) +} + +func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string) error { + w.Writeln("#[allow(unused_imports)]") + w.Writeln("use std::ffi::c_void;") + w.Writeln("") + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Version definition for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_VERSION_MAJOR : usize = %d;", strings.ToUpper(NameSpace), majorVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_VERSION_MINOR : usize = %d;", strings.ToUpper(NameSpace), minorVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_VERSION_MICRO : usize= %d;", strings.ToUpper(NameSpace), microVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_VERSION_PRERELEASEINFO : &str = \"%s\";", strings.ToUpper(NameSpace), preReleaseInfo(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_VERSION_BUILDINFO : &str = \"%s\";", strings.ToUpper(NameSpace), buildInfo(componentdefinition.Version)) + + w.Writeln("") + w.Writeln("") + + if len(componentdefinition.Errors.Errors) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Error constants for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub const %s_SUCCESS : i32 = 0;", strings.ToUpper(NameSpace)) + for i := 0; i < len(componentdefinition.Errors.Errors); i++ { + errorcode := componentdefinition.Errors.Errors[i] + w.Writeln("#[allow(dead_code)]") + if errorcode.Description != "" { + w.Writeln("pub const %s_ERROR_%s : i32 = %d; /** %s */", strings.ToUpper(NameSpace), errorcode.Name, errorcode.Code, errorcode.Description) + } else { + w.Writeln("pub const %s_ERROR_%s : i32 = %d;", strings.ToUpper(NameSpace), errorcode.Name, errorcode.Code) + } + } + w.Writeln("") + w.Writeln("") + } + + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Handle definiton for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("// Enum of all traits - this acts as a handle as we pass trait pointers through the interface") + w.Writeln("") + w.Writeln("#[allow(dead_code)]") + w.Writeln("pub enum HandleImpl {") + w.AddIndentationLevel(1) + for i := 0; i < len(componentdefinition.Classes); i++ { + class := componentdefinition.Classes[i] + if i != len(componentdefinition.Classes)-1 { + w.Writeln("T%s(u64, Box),", class.ClassName, class.ClassName) + } else { + w.Writeln("T%s(u64, Box)", class.ClassName, class.ClassName) + } + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.Writeln("") + w.Writeln("pub type Handle = *mut HandleImpl;") + for i := 0; i < len(componentdefinition.Classes); i++ { + class := componentdefinition.Classes[i] + w.Writeln("pub type %sHandle =Handle;", class.ClassName) + } + + if len(componentdefinition.Enums) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Enum definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Enums); i++ { + enuminfo := componentdefinition.Enums[i] + w.Writeln("#[repr(C, u16)]") + enumName := enuminfo.Name + w.Writeln("pub enum %s {", enumName) + for j := 0; j < len(enuminfo.Options); j++ { + option := enuminfo.Options[j] + sep := "," + if j == len(enuminfo.Options)-1 { + sep = "" + } + optionName := option.Name + w.Writeln(" pub %s = %d%s", optionName, option.Value, sep) + } + w.Writeln("}") + w.Writeln("") + } + } + + if len(componentdefinition.Structs) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Interface Struct definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Structs); i++ { + structinfo := componentdefinition.Structs[i] + structName := structinfo.Name + w.Writeln("#[repr(C)]") + w.Writeln("#[derive(Clone)]") + w.Writeln("pub struct %s {", structName) + for j := 0; j < len(structinfo.Members); j++ { + member := structinfo.Members[j] + last := j == len(structinfo.Members)-1 + err := writeRustMemberLine(member, w, structinfo.Name, last) + if err != nil { + return err + } + } + w.Writeln("}") + w.Writeln("") + } + } + + if len(componentdefinition.Functions) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Function type definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Functions); i++ { + funcinfo := componentdefinition.Functions[i] + w.Writeln("// %s", funcinfo.FunctionDescription) + w.Writeln("//") + parameterString := "" + for j := 0; j < len(funcinfo.Params); j++ { + RustParameters, err := generateRustParameters(funcinfo.Params[j], true) + RustParameter := RustParameters[0] + if err != nil { + return err + } + w.Writeln("// %s", RustParameter.ParamComment) + if j == 0 { + parameterString += fmt.Sprintf("%s : %s", RustParameter.ParamName, RustParameter.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", RustParameter.ParamName, RustParameter.ParamType) + } + } + w.Writeln("//") + funcName := funcinfo.FunctionName + w.Writeln("pub type %s = unsafe extern \"C\" fn(%s);", funcName, parameterString) + } + } + + return nil +} + +func writeRustMemberLine(member ComponentDefinitionMember, w LanguageWriter, StructName string, last bool) error { + suffix := "," + if last { + suffix = "" + } + arraysuffix := suffix + if member.Rows > 0 { + if member.Columns > 0 { + arraysuffix = fmt.Sprintf("[%d][%d]%s", member.Columns, member.Rows, suffix) + } else { + arraysuffix = fmt.Sprintf("[%d]%s", member.Rows, suffix) + } + } + memberName := toSnakeCase(member.Name) + switch member.Type { + case "uint8": + w.Writeln(" pub %s: u8%s", memberName, arraysuffix) + case "uint16": + w.Writeln(" pub %s: u16%s", memberName, arraysuffix) + case "uint32": + w.Writeln(" pub %s: u32%s", memberName, arraysuffix) + case "uint64": + w.Writeln(" pub %s: u64%s", memberName, arraysuffix) + case "int8": + w.Writeln(" pub %s: i8%s", memberName, arraysuffix) + case "int16": + w.Writeln(" pub %s: i16%s", memberName, arraysuffix) + case "int32": + w.Writeln(" pub %s: i32%s", memberName, arraysuffix) + case "int64": + w.Writeln(" pub %s: i64%s", memberName, arraysuffix) + case "bool": + w.Writeln(" pub %s: bool%s", memberName, arraysuffix) + case "single": + w.Writeln(" pub %s: f32%s", memberName, arraysuffix) + case "double": + w.Writeln(" pub %s: f64%s", memberName, arraysuffix) + case "pointer": + w.Writeln(" pub %s: c_void%s", memberName, arraysuffix) + case "string": + return fmt.Errorf("it is not possible for struct %s to contain a string value", StructName) + case "class", "optionalclass": + return fmt.Errorf("it is not possible for struct %s to contain a handle value", StructName) + case "enum": + w.Writeln(" pub %s: u16%s", memberName, arraysuffix) + } + return nil +} + +// CParameter is a handy representation of a function parameter in C +type RustParameter struct { + ParamType string + ParamName string + ParamComment string +} + +func generateRustParameters(param ComponentDefinitionParam, isPlain bool) ([]RustParameter, error) { + Params := make([]RustParameter, 1) + ParamTypeName, err := generateRustParameterType(param, isPlain) + if err != nil { + return nil, err + } + + if isPlain { + if param.ParamType == "string" { + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut usize" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_needed_chars" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written bytes, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = "*mut u8" + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[2].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } + } + + if param.ParamType == "basicarray" { + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "in" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) + if err != nil { + return nil, err + } + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut usize" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_count" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written elements, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = fmt.Sprintf("*mut %s", basicTypeName) + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } else { + Params = make([]RustParameter, 2) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = fmt.Sprintf("*const %s", basicTypeName) + Params[1].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[1].ParamComment = fmt.Sprintf("* @param[in] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } + } + + if param.ParamType == "structarray" { + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut usize" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_count" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written elements, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = fmt.Sprintf("*mut %s", param.ParamClass) + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } else { + Params = make([]RustParameter, 2) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = fmt.Sprintf("*const %s", param.ParamClass) + Params[1].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[1].ParamComment = fmt.Sprintf("* @param[in] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } + } + } + + Params[0].ParamType = ParamTypeName + Params[0].ParamName = toSnakeCase(param.ParamName) + Params[0].ParamComment = fmt.Sprintf("* @param[%s] %s - %s", param.ParamPass, Params[0].ParamName, param.ParamDescription) + + return Params, nil +} + +func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (string, error) { + RustParamTypeName := "" + ParamTypeName := param.ParamType + ParamClass := param.ParamClass + BasicType := false + switch ParamTypeName { + case "uint8": + RustParamTypeName = "u8" + BasicType = true + case "uint16": + RustParamTypeName = "u16" + BasicType = true + case "uint32": + RustParamTypeName = "u32" + BasicType = true + case "uint64": + RustParamTypeName = "u64" + BasicType = true + case "int8": + RustParamTypeName = "i8" + BasicType = true + case "int16": + RustParamTypeName = "i16" + BasicType = true + case "int32": + RustParamTypeName = "i32" + BasicType = true + case "int64": + RustParamTypeName = "i64" + BasicType = true + case "bool": + if isPlain { + RustParamTypeName = "u8" + } else { + RustParamTypeName = "bool" + } + BasicType = true + case "single": + RustParamTypeName = "f32" + BasicType = true + case "double": + RustParamTypeName = "f64" + BasicType = true + case "pointer": + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "return" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) + if err != nil { + basicTypeName = param.ParamClass + } + if isPlain { + if param.ParamPass == "in" { + RustParamTypeName = fmt.Sprintf("*const %s", basicTypeName) + } else { + RustParamTypeName = fmt.Sprintf("*mut %s", basicTypeName) + } + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut %s", basicTypeName) + case "in": + RustParamTypeName = fmt.Sprintf("&%s", basicTypeName) + case "return": + RustParamTypeName = fmt.Sprintf("%s", basicTypeName) + } + } + case "string": + if isPlain { + RustParamTypeName = "*const c_char" + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = "&mut String" + case "in": + RustParamTypeName = "&str" + case "return": + RustParamTypeName = "String" + } + } + + case "enum": + if isPlain { + RustParamTypeName = fmt.Sprintf("u16") + } else { + RustParamTypeName = ParamClass + } + BasicType = true + case "functiontype": + RustParamTypeName = fmt.Sprintf("%s", ParamClass) + BasicType = true + case "struct": + if isPlain { + if param.ParamPass == "in" { + RustParamTypeName = fmt.Sprintf("*const %s", ParamClass) + } else { + RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + } + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut %s", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("&%s", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("%s", ParamClass) + } + } + + case "basicarray": + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "return" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) + if err != nil { + return "", err + } + + if isPlain { + RustParamTypeName = fmt.Sprintf("*mut %s", basicTypeName) + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut Vec<%s>", basicTypeName) + case "in": + RustParamTypeName = fmt.Sprintf("&[%s]", basicTypeName) + case "return": + RustParamTypeName = fmt.Sprintf("Vec<%s>", basicTypeName) + } + } + + case "structarray": + if isPlain { + RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut Vec<%s>", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("&[%s]", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("Vec<%s>", ParamClass) + } + } + + case "class", "optionalclass": + if isPlain { + RustParamTypeName = fmt.Sprintf("%sHandle", ParamClass) + BasicType = true + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut dyn %s", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("& dyn %s", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("Box", ParamClass) + } + } + + default: + return "", fmt.Errorf("invalid parameter type \"%s\" for Rust parameter", ParamTypeName) + } + if BasicType { + if param.ParamPass == "out" { + if isPlain { + RustParamOutTypeName := fmt.Sprintf("*mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } else { + RustParamOutTypeName := fmt.Sprintf("&mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } + } + if param.ParamPass == "return" { + if isPlain { + RustParamOutTypeName := fmt.Sprintf("*mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } + } + } + return RustParamTypeName, nil +} diff --git a/Source/languagewriter.go b/Source/languagewriter.go index e2df7fa3..2e586ae1 100644 --- a/Source/languagewriter.go +++ b/Source/languagewriter.go @@ -42,10 +42,10 @@ import ( // LanguageWriter is a wrapper around a io.Writer that handles indentation type LanguageWriter struct { - Indentation int + Indentation int IndentString string - Writer io.Writer - CurrentLine string + Writer io.Writer + CurrentLine string } func max(x, y int) int { @@ -56,144 +56,147 @@ func max(x, y int) int { } // AddIndentationLevel adds number of indentation the writers output -func (writer *LanguageWriter) AddIndentationLevel (levels int) (error) { - writer.Indentation = max(writer.Indentation + levels, 0) +func (writer *LanguageWriter) AddIndentationLevel(levels int) error { + writer.Indentation = max(writer.Indentation+levels, 0) return nil } // ResetIndentationLevel adds indentation to all output -func (writer *LanguageWriter) ResetIndentationLevel () (error) { +func (writer *LanguageWriter) ResetIndentationLevel() error { writer.Indentation = 0 return nil } // Writeln formats a string and writes it to a line. Pairs of leading spaces will be replaced by the indent IndentString. -func (writer *LanguageWriter) Writeln (format string, a ...interface{}) (int, error) { +func (writer *LanguageWriter) Writeln(format string, a ...interface{}) (int, error) { - leadingSpaces := 0; + leadingSpaces := 0 for _, rune := range format { if rune == ' ' { - leadingSpaces = leadingSpaces + 1; + leadingSpaces = leadingSpaces + 1 } else { - break; + break } } - leadingIndents := leadingSpaces / 2; - - indentedFormat := strings.Repeat (writer.IndentString, leadingIndents + writer.Indentation) + format[leadingIndents * 2:]; - return fmt.Fprintf (writer.Writer, indentedFormat + "\n", a...); + leadingIndents := leadingSpaces / 2 + + indentedFormat := strings.Repeat(writer.IndentString, leadingIndents+writer.Indentation) + format[leadingIndents*2:] + return fmt.Fprintf(writer.Writer, indentedFormat+"\n", a...) } // Writelns writes multiple lines and processes indentation -func (writer *LanguageWriter) Writelns (prefix string, lines []string) (error) { +func (writer *LanguageWriter) Writelns(prefix string, lines []string) error { for idx := 0; idx < len(lines); idx++ { - _, err := writer.Writeln (prefix + lines[idx]); + _, err := writer.Writeln(prefix + lines[idx]) if err != nil { - return err; + return err } } - - return nil; + + return nil } - + // BeginLine clears the CurrentLine buffer -func (writer *LanguageWriter) BeginLine () () { - writer.CurrentLine = ""; +func (writer *LanguageWriter) BeginLine() { + writer.CurrentLine = "" } // Printf formats a string and appends it to the CurrentLine buffer -func (writer *LanguageWriter) Printf (format string, a ...interface{}) () { - writer.CurrentLine = writer.CurrentLine + fmt.Sprintf (format, a...); +func (writer *LanguageWriter) Printf(format string, a ...interface{}) { + writer.CurrentLine = writer.CurrentLine + fmt.Sprintf(format, a...) } // EndLine flushes the CurrentBuffer to the internal writer -func (writer *LanguageWriter) EndLine () (int, error) { - return writer.Writeln (writer.CurrentLine); +func (writer *LanguageWriter) EndLine() (int, error) { + return writer.Writeln(writer.CurrentLine) } - // WriteCMakeLicenseHeader writes a license header into a writer with CMake-style comments -func (writer *LanguageWriter) WriteCMakeLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "#[[", "\n]]"); +func (writer *LanguageWriter) WriteCMakeLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", "") } // WriteCLicenseHeader writes a license header into a writer with C-style comments -func (writer *LanguageWriter) WriteCLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/"); +func (writer *LanguageWriter) WriteCLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") } // WritePascalLicenseHeader writes a license header into a writer Pascal-style comments -func (writer *LanguageWriter) WritePascalLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "(*", "*)"); +func (writer *LanguageWriter) WritePascalLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "(*", "*)", "") } // WritePythonLicenseHeader writes a license header into a writer Python-style comments -func (writer *LanguageWriter) WritePythonLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "'''", "'''"); +func (writer *LanguageWriter) WritePythonLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "'''", "'''", "") } // WriteJavaLicenseHeader writes a license header into a writer Java-style comments -func (writer *LanguageWriter) WriteJavaLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/"); +func (writer *LanguageWriter) WriteJavaLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") +} + +// WriteTomlLicenseHeader writes a license header into a writer for TOML-style line prefix comments +func (writer *LanguageWriter) WriteTomlLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "# ") } // WritePlainLicenseHeader writes a license header into a writer without comments -func (writer *LanguageWriter) WritePlainLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", ""); +func (writer *LanguageWriter) WritePlainLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "") } // WriteLicenseHeader writes a license header into a writer with C-style comments -func WriteLicenseHeader (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (w, component, abstract, includeVersion, "/*", "*/"); +func WriteLicenseHeader(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(w, component, abstract, includeVersion, "/*", "*/", "") } // writeLicenseHeaderEx writes a license header into a writer. -func writeLicenseHeaderEx (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool, CommandStart string, CommandEnd string) { - ACTVersion := component.ACTVersion; - version := component.Version; - copyright := component.Copyright; - year := component.Year; - - if (len(CommandStart) > 0) { - fmt.Fprintf (w, "%s++\n", CommandStart); - fmt.Fprintf (w, "\n"); +func writeLicenseHeaderEx(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool, CommandStart string, CommandEnd string, prefix string) { + ACTVersion := component.ACTVersion + version := component.Version + copyright := component.Copyright + year := component.Year + + if len(CommandStart) > 0 { + fmt.Fprintf(w, "%s++\n", CommandStart) + fmt.Fprintf(w, "\n") } - fmt.Fprintf (w, "Copyright (C) %d %s\n", year, copyright); - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "%sCopyright (C) %d %s\n", prefix, year, copyright) + fmt.Fprintf(w, "%s\n", prefix) for i := 0; i < len(component.License.Lines); i++ { - line := component.License.Lines[i]; - fmt.Fprintf (w, "%s\n", line.Value); + line := component.License.Lines[i] + fmt.Fprintf(w, "%s%s\n", prefix, line.Value) } - fmt.Fprintf (w, "\n"); - if (includeVersion) { - fmt.Fprintf (w, "This file has been generated by the Automatic Component Toolkit (ACT) version %s.\n", ACTVersion) - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "%s\n", prefix) + if includeVersion { + fmt.Fprintf(w, "%sThis file has been generated by the Automatic Component Toolkit (ACT) version %s.\n", prefix, ACTVersion) + fmt.Fprintf(w, "%s\n", prefix) } - if (len(abstract) > 0 ) { - fmt.Fprintf (w, "Abstract: %s\n", abstract); - if (includeVersion) { - fmt.Fprintf (w, "\nInterface version: %d.%d.%d\n", majorVersion(version), minorVersion(version), microVersion(version)) + if len(abstract) > 0 { + fmt.Fprintf(w, "%sAbstract: %s\n", prefix, abstract) + if includeVersion { + fmt.Fprintf(w, "%s\n%sInterface version: %d.%d.%d\n", prefix, prefix, majorVersion(version), minorVersion(version), microVersion(version)) } } - fmt.Fprintf (w, "\n"); - if (len(CommandEnd) > 0) { - fmt.Fprintf (w, "%s\n", CommandEnd); - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "\n") + if len(CommandEnd) > 0 { + fmt.Fprintf(w, "%s\n", CommandEnd) + fmt.Fprintf(w, "\n") } } - // CreateLanguageFile creates a LanguageWriter and sets its indent string -func CreateLanguageFile (fileName string, indentString string) (LanguageWriter, error) { - var result LanguageWriter; - var err error; - +func CreateLanguageFile(fileName string, indentString string) (LanguageWriter, error) { + var result LanguageWriter + var err error + result.IndentString = indentString result.Indentation = 0 result.Writer, err = os.Create(fileName) if err != nil { - return result, err; + return result, err } - - return result, nil; + + return result, nil }