From a8b252a7678fa54681827b34f2ee9b8f5ca34e24 Mon Sep 17 00:00:00 2001 From: Raymond Date: Tue, 18 Apr 2023 23:44:06 +0330 Subject: [PATCH] Improve rs_port stage 4 --- source/ports/rs_port/Cargo.toml | 17 +- source/ports/rs_port/Dockerfile | 59 +- source/ports/rs_port/README.md | 16 +- source/ports/rs_port/build.rs | 2 +- source/ports/rs_port/inline/Cargo.toml | 2 +- source/ports/rs_port/src/helpers.rs | 115 ++++ source/ports/rs_port/src/hooks.rs | 11 +- source/ports/rs_port/src/lib.rs | 89 ++- source/ports/rs_port/src/loaders.rs | 28 +- source/ports/rs_port/src/macros.rs | 179 ++++-- source/ports/rs_port/src/metacall.rs | 52 +- source/ports/rs_port/src/parsers.rs | 173 +++--- .../rs_port/src/prelude/metacall_class.rs | 158 ------ .../rs_port/src/prelude/metacall_error.rs | 68 --- .../rs_port/src/prelude/metacall_function.rs | 75 --- .../rs_port/src/prelude/metacall_future.rs | 107 ---- .../rs_port/src/prelude/metacall_object.rs | 128 ----- .../rs_port/src/prelude/metacall_pointer.rs | 35 -- .../rs_port/src/prelude/object_protocol.rs | 536 ------------------ .../ports/rs_port/src/types/metacall_class.rs | 223 ++++++++ .../ports/rs_port/src/types/metacall_error.rs | 100 ++++ .../{prelude => types}/metacall_exception.rs | 126 +++- .../rs_port/src/types/metacall_function.rs | 92 +++ .../rs_port/src/types/metacall_future.rs | 155 +++++ .../src/{prelude => types}/metacall_null.rs | 2 + .../rs_port/src/types/metacall_object.rs | 170 ++++++ .../rs_port/src/types/metacall_pointer.rs | 98 ++++ .../ports/rs_port/src/types/metacall_value.rs | 421 ++++++++++++++ .../rs_port/src/{prelude => types}/mod.rs | 4 +- .../tests/{inline_test.rs => inlines.rs} | 2 +- ...alid_loader_test.rs => invalid_loaders.rs} | 14 +- .../tests/{loaders_test.rs => loaders.rs} | 4 +- .../{return_type_test.rs => metacall.rs} | 251 +++++--- .../scripts/{return_type_test.c => script.c} | 3 - .../{return_type_test.js => script.js} | 36 +- .../{return_type_test.py => script.py} | 10 +- 36 files changed, 2089 insertions(+), 1472 deletions(-) create mode 100644 source/ports/rs_port/src/helpers.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_class.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_error.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_function.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_future.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_object.rs delete mode 100644 source/ports/rs_port/src/prelude/metacall_pointer.rs delete mode 100644 source/ports/rs_port/src/prelude/object_protocol.rs create mode 100644 source/ports/rs_port/src/types/metacall_class.rs create mode 100644 source/ports/rs_port/src/types/metacall_error.rs rename source/ports/rs_port/src/{prelude => types}/metacall_exception.rs (50%) create mode 100644 source/ports/rs_port/src/types/metacall_function.rs create mode 100644 source/ports/rs_port/src/types/metacall_future.rs rename source/ports/rs_port/src/{prelude => types}/metacall_null.rs (88%) create mode 100644 source/ports/rs_port/src/types/metacall_object.rs create mode 100644 source/ports/rs_port/src/types/metacall_pointer.rs create mode 100644 source/ports/rs_port/src/types/metacall_value.rs rename source/ports/rs_port/src/{prelude => types}/mod.rs (89%) rename source/ports/rs_port/tests/{inline_test.rs => inlines.rs} (93%) rename source/ports/rs_port/tests/{invalid_loader_test.rs => invalid_loaders.rs} (65%) rename source/ports/rs_port/tests/{loaders_test.rs => loaders.rs} (95%) rename source/ports/rs_port/tests/{return_type_test.rs => metacall.rs} (53%) rename source/ports/rs_port/tests/scripts/{return_type_test.c => script.c} (87%) rename source/ports/rs_port/tests/scripts/{return_type_test.js => script.js} (52%) rename source/ports/rs_port/tests/scripts/{return_type_test.py => script.py} (72%) diff --git a/source/ports/rs_port/Cargo.toml b/source/ports/rs_port/Cargo.toml index 2664af3c47..30dbe14ce2 100644 --- a/source/ports/rs_port/Cargo.toml +++ b/source/ports/rs_port/Cargo.toml @@ -1,23 +1,22 @@ [package] -name = "metacall" -version = "0.3.1" -repository = "https://github.com/metacall/core/tree/develop/source/ports/rs_port" -authors = ["Vicente Eduardo Ferrer Garcia ", "Swarnim Arun "] +authors = ["Mahdi Sharifi ", "Vicente Eduardo Ferrer Garcia ", "Swarnim Arun "] +description = "Call NodeJS, TypeScript, Python, C#, Ruby... functions from Rust (a Rust Port for MetaCall)." edition = "2021" +keywords = ["programming-language", "ffi", "polyglot", "metacall", "function-mesh", "inter-language", "polyglot-programming"] license = "Apache-2.0" +name = "metacall" readme = "README.md" -description = "Call NodeJS, TypeScript, Python, C#, Ruby... functions from Rust (a Rust Port for MetaCall)." +repository = "https://github.com/metacall/core/tree/develop/source/ports/rs_port" +version = "0.4.0" [lib] name = "metacall" -crate-type = ["lib"] # TODO: Once this is unified with the loader, we should use cdylib type +crate-type = ["lib"] path = "src/lib.rs" edition = "2021" [dependencies] -concat-idents = "1.1.4" -dyn-clone = "1.0.11" -metacall-inline = { path = "./inline", version = "0.1.1" } +metacall-inline = { path = "./inline", version = "0.2.0" } [build-dependencies] bindgen = { version = "0.64.0", default-features = false, features = ["runtime", "logging", "which-rustfmt"]} diff --git a/source/ports/rs_port/Dockerfile b/source/ports/rs_port/Dockerfile index 7b14856293..788721c70a 100644 --- a/source/ports/rs_port/Dockerfile +++ b/source/ports/rs_port/Dockerfile @@ -1,39 +1,34 @@ -# docker build -t metacall/rs-port . -# docker run --rm -it metacall/rs-port - -FROM metacall/core:dev AS develop +FROM devraymondsh/ubuntu-docker-rust # Install dependencies RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - clang-11 clang-format-11 libclang-11-dev libtcc-dev valgrind libdw-dev libbfd-dev libdwarf-dev libffi-dev \ - && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ - && . "/root/.cargo/env" \ - && rustup component add rustfmt \ - && rustup toolchain add nightly \ - && rustup component add clippy - -ENV PATH="${PATH}:/root/.cargo/bin" + && apt-get install -y --no-install-recommends build-essential cmake ca-certificates git nodejs npm pkg-config clang-11 clang-format-11 libclang-11-dev libtcc-dev cmake valgrind libdw-dev libbfd-dev libdwarf-dev libffi-dev python3 libpython3-dev python3-pip +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10 -RUN cd build \ - && cmake \ - -DCMAKE_BUILD_TYPE=Debug \ - -DOPTION_BUILD_DETOURS=Off \ - -DOPTION_BUILD_EXAMPLES=Off \ - -DOPTION_BUILD_LOADERS_C=On \ - -DOPTION_BUILD_LOADERS_NODE=On \ - -DOPTION_BUILD_LOADERS_PY=On \ - -DOPTION_BUILD_LOADERS_TS=On \ - -DOPTION_BUILD_SCRIPTS=Off \ - -DOPTION_BUILD_SERIALS_RAPID_JSON=On \ - -DOPTION_BUILD_TESTS=Off \ - .. \ - && cmake --build . --target install \ - && cd /usr/local/lib \ - && ldconfig +WORKDIR /root/metacall-polyglot +RUN git clone https://github.com/metacall/core +RUN mkdir core/build -WORKDIR /usr/local/metacall/source/ports/rs_port +WORKDIR /root/metacall-polyglot/core/build +RUN cmake \ + -DCMAKE_BUILD_TYPE=Debug \ + -DOPTION_BUILD_DETOURS=Off \ + -DOPTION_BUILD_EXAMPLES=Off \ + -DOPTION_BUILD_LOADERS_C=On \ + -DOPTION_BUILD_LOADERS_NODE=On \ + -DOPTION_BUILD_LOADERS_PY=On \ + -DOPTION_BUILD_LOADERS_TS=On \ + -DOPTION_BUILD_SCRIPTS=Off \ + -DOPTION_BUILD_SERIALS_RAPID_JSON=On \ + -DOPTION_BUILD_TESTS=Off \ + .. +RUN cmake --build . --target install +RUN cd /usr/local/lib && ldconfig -COPY . . +RUN rustup component add rustfmt +RUN rustup toolchain add nightly +RUN rustup component add clippy +RUN cargo install cargo-valgrind -CMD ["cargo", "test"] +WORKDIR /root/metacall-polyglot +CMD ["cargo", "test"] \ No newline at end of file diff --git a/source/ports/rs_port/README.md b/source/ports/rs_port/README.md index b51fce5a52..e4444114aa 100644 --- a/source/ports/rs_port/README.md +++ b/source/ports/rs_port/README.md @@ -26,16 +26,18 @@ export function sum(a: number, b: number): number { `main.rs` ``` rust -use metacall::{hooks, loaders, structs::Any, metacall}; +use metacall::{hooks, metacall, loaders}; fn main() { - // Metacall automatically shuts down when it goes out of scope - let _ = hooks::initialize().unwrap(); + // Initialize Metacall at the top + let _metacall = hooks::initialize().unwrap(); + + // Load the file + loaders::from_single_file("ts", "sum.ts").unwrap(); - loaders::from_file("ts", "sum.ts").unwrap(); + // Call the sum function + let sum = metacall::("sum", [1.0, 2.0]).unwrap(); - let sum = metacall("sum", [Any::Double(1.0), Any::Double(2.0)]).unwrap(); - - println!("sum: {:?}", sum); + assert_eq!(sum, 3.0); } ``` diff --git a/source/ports/rs_port/build.rs b/source/ports/rs_port/build.rs index e516cf3611..d66425a56c 100644 --- a/source/ports/rs_port/build.rs +++ b/source/ports/rs_port/build.rs @@ -34,7 +34,7 @@ fn generate_bindings(headers: &[&str]) { fn main() { // When running from CMake - if let Ok(_) = env::var("CMAKE_BINDGEN") { + if env::var("CMAKE_BINDGEN").is_ok() { const HEADERS: [&str; 3] = [ "include/metacall/metacall.h", "include/metacall/metacall_value.h", diff --git a/source/ports/rs_port/inline/Cargo.toml b/source/ports/rs_port/inline/Cargo.toml index d5c82b6422..c48ff53d28 100644 --- a/source/ports/rs_port/inline/Cargo.toml +++ b/source/ports/rs_port/inline/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "metacall-inline" -version = "0.1.1" +version = "0.2.0" repository = "https://github.com/metacall/core/tree/develop/source/ports/rs_port" edition = "2021" license = "Apache-2.0" diff --git a/source/ports/rs_port/src/helpers.rs b/source/ports/rs_port/src/helpers.rs new file mode 100644 index 0000000000..68941d180b --- /dev/null +++ b/source/ports/rs_port/src/helpers.rs @@ -0,0 +1,115 @@ +use crate::types::MetacallValue; +use std::any::Any; + +pub trait MetacallDowncast: Any { + fn into_any(self: Box) -> Box; + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; +} +impl MetacallDowncast for T { + fn into_any(self: Box) -> Box { + self + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} +impl dyn MetacallValue { + /// Checks if the trait object is having the given type. + pub fn is(&self) -> bool { + MetacallDowncast::as_any(self).is::() + } + + /// Downcasts the inner value of the trait object and returns the ownership. + pub fn downcast(self: Box) -> Result> { + if self.is::() { + Ok(*MetacallDowncast::into_any(self).downcast::().unwrap()) + } else { + Err(self) + } + } + + /// Downcasts the inner value of the trait object and returns a reference. + pub fn downcast_ref(&self) -> Option<&T> { + MetacallDowncast::as_any(self).downcast_ref::() + } + + /// Downcasts the inner value of the trait object and returns a mutable reference. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + MetacallDowncast::as_any_mut(self).downcast_mut::() + } +} + +pub trait MetacallSealed {} +impl MetacallSealed for T {} +impl MetacallSealed for str {} +impl MetacallSealed for [T] {} + +pub fn clone_box(t: &T) -> Box +where + T: ?Sized + MetacallClone, +{ + unsafe { + let mut fat_ptr = t as *const T; + let data_ptr = &mut fat_ptr as *mut *const T as *mut *mut (); + + assert_eq!(*data_ptr as *const (), t as *const T as *const ()); + + *data_ptr = ::clone_box(t); + + Box::from_raw(fat_ptr as *mut T) + } +} + +pub trait MetacallClone: MetacallSealed { + fn clone_box(&self) -> *mut (); +} +impl MetacallClone for T +where + T: Clone, +{ + fn clone_box(&self) -> *mut () { + Box::::into_raw(Box::new(self.clone())) as *mut () + } +} + +impl MetacallClone for str { + fn clone_box(&self) -> *mut () { + Box::::into_raw(Box::from(self)) as *mut () + } +} +impl MetacallClone for [T] +where + T: Clone, +{ + fn clone_box(&self) -> *mut () { + Box::<[T]>::into_raw(self.iter().cloned().collect()) as *mut () + } +} +impl<'c> Clone for Box { + fn clone(&self) -> Self { + clone_box(&**self) + } +} +impl<'c> Clone for Box { + fn clone(&self) -> Self { + clone_box(&**self) + } +} +impl<'c> Clone for Box { + fn clone(&self) -> Self { + clone_box(&**self) + } +} +impl<'c> Clone for Box { + fn clone(&self) -> Self { + clone_box(&**self) + } +} + +pub fn metacall_implementer_to_traitobj(v: impl MetacallValue) -> Box { + Box::new(v) as Box +} diff --git a/source/ports/rs_port/src/hooks.rs b/source/ports/rs_port/src/hooks.rs index 4c3aff980c..e99d96e025 100644 --- a/source/ports/rs_port/src/hooks.rs +++ b/source/ports/rs_port/src/hooks.rs @@ -1,6 +1,6 @@ use crate::{ bindings::{metacall_destroy, metacall_initialize}, - prelude::MetacallInitError, + types::MetacallInitError, }; use std::ffi::c_int; @@ -18,6 +18,15 @@ impl Drop for MetacallAutoDestroy { } } +/// Initializes Metacall. Always remember to store the output in a variable to avoid instant drop. +/// For example: ... +/// ``` +/// // Initialize metacall at the top of your main function before loading your codes or +/// // calling any function. +/// let _metacall = metacall::initialize().unwrap(); +/// +/// +/// ``` pub fn initialize() -> Result { if initialize_manually() != 0 { return Err(MetacallInitError::new()); diff --git a/source/ports/rs_port/src/lib.rs b/source/ports/rs_port/src/lib.rs index af405766ef..60fc6707a1 100644 --- a/source/ports/rs_port/src/lib.rs +++ b/source/ports/rs_port/src/lib.rs @@ -1,5 +1,10 @@ #![warn(clippy::all)] -#![allow(clippy::not_unsafe_ptr_arg_deref, clippy::boxed_local)] +#![allow( + clippy::not_unsafe_ptr_arg_deref, + clippy::boxed_local, + clippy::tabs_in_doc_comments, + clippy::needless_doctest_main +)] /* * MetaCall Library by Parra Studios * A library for providing a foreign function interface calls. @@ -20,19 +25,93 @@ * */ -pub mod hooks; -pub mod loaders; -pub(crate) mod macros; +//! [METACALL](https://github.com/metacall/core) is a library that allows calling functions, +//! methods or procedures between programming languages. With METACALL you can transparently +//! execute code from / to any programming language, for example, call TypeScript code from Rust. +//! Click [here](https://github.com/metacall/install) for installation guide. +//! +//! General usage example: +//! Let's consider we have the following Typescript code: +//! `sum.ts` +//! ``` javascript +//! export function sum(a: number, b: number): number { +//! return a + b; +//! } +//! ``` +//! Now let's jump into Rust: +//! +//! ``` +//! use metacall::{hooks, metacall, loaders}; +//! +//! fn main() { +//! // Initialize Metacall at the top +//! let _metacall = hooks::initialize().unwrap(); +//! +//! // Load the file (Checkout the loaders module for loading multiple files +//! // or loading from string) +//! loaders::from_single_file("ts", "sum.ts").unwrap(); +//! +//! // Call the sum function (Also checkout other metacall functions) +//! let sum = metacall::("sum", [1.0, 2.0]).unwrap(); +//! +//! assert_eq!(sum, 3.0); +//! } +//! +//! ``` + +pub(crate) mod helpers; pub(crate) mod parsers; -pub mod prelude; +pub(crate) use macros::private_macros::*; + +/// Contains Metacall loaders from file and memory. Usage example: ... +/// ``` +/// // Loading a single file with Nodejs. +/// metacall::loaders::from_single_file("node", "index.js").unwrap(); +/// +/// // Loading multiple files with Nodejs. +/// metacall::loaders::from_file("node", ["index.js", "main.js"]).unwrap(); +/// +/// // Loading a string with Nodejs. +/// let script = "function greet() { return 'hi there!' }; module.exports = { greet };"; +/// metacall::loaders::from_memory("node", script).unwrap(); +/// ``` +pub mod loaders; + +mod types; +pub use hooks::initialize; + +#[doc(hidden)] +pub mod macros; + +#[doc(hidden)] +pub mod hooks; +pub use types::*; #[path = "metacall.rs"] mod metacall_mod; pub use metacall_mod::*; +/// Contains Metacall language inliners. Usage example: ... +/// ``` +/// // Python +/// py! { +/// print("hello world") +/// } +/// +/// // Nodejs +/// node! { +/// console.log("hello world"); +/// } +/// +/// // Typescript +/// ts! { +/// console.log("hello world"); +/// } +/// ``` pub mod inline { pub use metacall_inline::*; } #[allow(warnings)] +#[doc(hidden)] pub mod bindings; diff --git a/source/ports/rs_port/src/loaders.rs b/source/ports/rs_port/src/loaders.rs index 8ac9db4909..cf0cec127f 100644 --- a/source/ports/rs_port/src/loaders.rs +++ b/source/ports/rs_port/src/loaders.rs @@ -1,7 +1,7 @@ use crate::{ bindings::{metacall_load_from_file, metacall_load_from_memory}, cstring_enum, - prelude::MetacallLoaderError, + types::MetacallLoaderError, }; use std::{ ffi::CString, @@ -9,10 +9,23 @@ use std::{ ptr, }; -pub fn from_file(tag: impl ToString, script: impl AsRef) -> Result<(), MetacallLoaderError> { - from_files(tag, [script]) +/// Loads a script from a single file. Usage example: ... +/// ``` +/// // A Nodejs script +/// metacall::loaders::from_single_file("node", "index.js").unwrap(); +/// ``` +pub fn from_single_file( + tag: impl ToString, + script: impl AsRef, +) -> Result<(), MetacallLoaderError> { + from_file(tag, [script]) } -pub fn from_files( +/// Loads a script from file. Usage example: ... +/// ``` +/// // A Nodejs script +/// metacall::loaders::from_file("node", ["index.js", "main.js"]).unwrap(); +/// ``` +pub fn from_file( tag: impl ToString, scripts: impl IntoIterator>, ) -> Result<(), MetacallLoaderError> { @@ -53,6 +66,13 @@ pub fn from_files( Ok(()) } +/// Loads a script from memory. Usage example: ... +/// ``` +/// let script = "function greet() { return 'hi there!' }; module.exports = { greet };"; +/// +/// // A Nodejs script +/// metacall::loaders::from_memory("node", script).unwrap(); +/// ``` pub fn from_memory(tag: impl ToString, script: impl ToString) -> Result<(), MetacallLoaderError> { let script = script.to_string(); let c_tag = cstring_enum!(tag, MetacallLoaderError)?; diff --git a/source/ports/rs_port/src/macros.rs b/source/ports/rs_port/src/macros.rs index de467866c5..31a96caf65 100644 --- a/source/ports/rs_port/src/macros.rs +++ b/source/ports/rs_port/src/macros.rs @@ -1,51 +1,114 @@ -#[macro_export] -macro_rules! cstring_enum { - ($var:ident, $enum:ident) => {{ - let var = $var.to_string(); - match ::std::ffi::CString::new(var.clone()) { - Ok(str) => Ok(str), - Err(err) => Err($enum::UnexpectedCStringConversionErr( - $crate::prelude::MetacallStringConversionError::new(var, err), - )), - } - }}; +// Used for documentation. +#[allow(unused_imports)] +use crate::MetacallValue; - ($var:expr, $enum:ident) => {{ - let var = $var; - cstring_enum!(var, $enum) - }}; -} +pub(crate) mod private_macros { + macro_rules! cstring_enum { + ($var:ident, $enum:ident) => {{ + let var = $var.to_string(); + match ::std::ffi::CString::new(var.clone()) { + Ok(str) => Ok(str), + Err(err) => Err($enum::UnexpectedCStringConversionErr( + $crate::MetacallStringConversionError::new(var, err), + )), + } + }}; -#[macro_export] -macro_rules! cstring { - ($var:ident) => {{ - let var = $var.to_string(); - match ::std::ffi::CString::new(var.clone()) { - Ok(str) => Ok(str), - Err(err) => Err($crate::prelude::MetacallStringConversionError::new( - var, err, - )), - } - }}; + ($var:expr, $enum:ident) => {{ + let var = $var; + cstring_enum!(var, $enum) + }}; + } - ($var:expr) => {{ - let var = $var; - cstring!(var) - }}; -} + macro_rules! cstring { + ($var:ident) => {{ + let var = $var.to_string(); + match ::std::ffi::CString::new(var.clone()) { + Ok(str) => Ok(str), + Err(err) => Err($crate::MetacallStringConversionError::new(var, err)), + } + }}; -#[macro_export] -macro_rules! match_object_protocol_all { - ($any:expr, $var:ident, $action:expr, $($type: ty),*) => { - match_object_protocol!($any, { - $( $var: $type => $action, )* - _ => panic!("The object protocol") - }) - }; + ($var:expr) => {{ + let var = $var; + cstring!(var) + }}; + } + + macro_rules! match_metacall_value_all { + ($any:expr, $var:ident, $action:expr, [ $($type: ty),* ]) => {{ + use std::{collections::HashMap, vec::Vec}; + + match_metacall_value!($any, { + $( $var: $type => $action, )* + + // Support up to 5 dimensional vectors for type casting + $( $var: Vec<$type> => $action, )* + $( $var: Vec> => $action, )* + $( $var: Vec>> => $action, )* + $( $var: Vec>>> => $action, )* + $( $var: Vec>>>> => $action, )* + $( $var: Vec>>>>> => $action, )* + + // Support up to 5 dimensional hashmaps for type casting + $( $var: HashMap:: => $action, )* + $( $var: HashMap::> => $action, )* + $( $var: HashMap::>> => $action, )* + $( + $var: HashMap::> + > + > => $action, + )* + $( + $var: HashMap::> + > + > + > => $action, + )* + $( + $var: HashMap::> + > + > + > + > => $action, + )* + + _ => panic!("Unkown type: {:#?}", $any) + }) + }}; + } + + pub(crate) use cstring; + pub(crate) use cstring_enum; + pub(crate) use match_metacall_value_all; } #[macro_export] -macro_rules! match_object_protocol { +/// Matches [MetacallValue](MetacallValue) trait object. For example: ... +/// ``` +/// use metacall::{metacall_untyped_no_arg, match_metacall_value}; +/// +/// let value = metacall_untyped_no_arg("returns_string_or_number").unwrap(); +/// match_metacall_value!(value, { +/// str: String => str, +/// num: i16 => num.to_string(), +/// num: i32 => num.to_string(), +/// num: i64 => num.to_string(), +/// num: f32 => num.to_string(), +/// num: f64 => num.to_string(), +/// _ => String::from("Invalid output!") +/// }); +/// ``` +macro_rules! match_metacall_value { ( $any:expr, { $( $var:ident : $type:ty => $arm:expr ),*, _ => $default:expr } ) => ( $( if $any.is::<$type>() { @@ -59,7 +122,22 @@ macro_rules! match_object_protocol { ) } #[macro_export] -macro_rules! match_object_protocol_ref { +/// Same as [match_metacall_value](match_metacall_value) but gives a reference. For example: ... +/// ``` +/// use metacall::{metacall_untyped_no_arg, match_metacall_value_ref}; +/// +/// let value = metacall_untyped_no_arg("returns_string_or_number").unwrap(); +/// match_metacall_value_ref!(value, { +/// str: String => str.to_owned(), +/// num: i16 => num.to_string(), +/// num: i32 => num.to_string(), +/// num: i64 => num.to_string(), +/// num: f32 => num.to_string(), +/// num: f64 => num.to_string(), +/// _ => String::from("Invalid output!") +/// }); +/// ``` +macro_rules! match_metacall_value_ref { ( $any:expr, { $( $var:ident : $type:ty => $arm:expr ),*, _ => $default:expr } ) => ( $( if $any.is::<$type>() { @@ -73,7 +151,22 @@ macro_rules! match_object_protocol_ref { ) } #[macro_export] -macro_rules! match_object_protocol_mut { +/// Same as [match_metacall_value](match_metacall_value) but gives a mutable reference. For example: ... +/// ``` +/// use metacall::{metacall_untyped_no_arg, match_metacall_value_mut}; +/// +/// let mut value = metacall_untyped_no_arg("returns_string_or_number").unwrap(); +/// match_metacall_value_mut!(value, { +/// str: String => str.to_owned(), +/// num: i16 => num.to_string(), +/// num: i32 => num.to_string(), +/// num: i64 => num.to_string(), +/// num: f32 => num.to_string(), +/// num: f64 => num.to_string(), +/// _ => String::from("Invalid output!") +/// }); +/// ``` +macro_rules! match_metacall_value_mut { ( $any:expr, { $( $var:ident : $type:ty => $arm:expr ),*, _ => $default:expr } ) => ( $( if $any.is::<$type>() { diff --git a/source/ports/rs_port/src/metacall.rs b/source/ports/rs_port/src/metacall.rs index 2dbf7b0dbe..72d7eb234e 100644 --- a/source/ports/rs_port/src/metacall.rs +++ b/source/ports/rs_port/src/metacall.rs @@ -1,13 +1,17 @@ use crate::{ - bindings::{metacall_function, metacallfv_s}, + bindings::{metacall_function, metacall_value_destroy, metacallfv_s}, cstring_enum, parsers, - prelude::{MetacallError, MetacallNull, MetacallObjectProtocol}, + types::{MetacallError, MetacallNull, MetacallValue}, }; use std::ffi::c_void; +// Used for documentation. +#[allow(unused_imports)] +use crate::match_metacall_value; + fn metacall_inner( func: impl ToString, - args: impl IntoIterator, + args: impl IntoIterator, ) -> Result<*mut c_void, MetacallError> { let c_function = cstring_enum!(func, MetacallError)?; let c_func = unsafe { metacall_function(c_function.as_ptr()) }; @@ -16,32 +20,60 @@ fn metacall_inner( return Err(MetacallError::FunctionNotFound); } - let (mut c_args, cleanup) = parsers::metacallobj_to_raw_args(args); + let mut c_args = parsers::metacallobj_to_raw_args(args); let args_length = c_args.len(); let ret = unsafe { metacallfv_s(c_func, c_args.as_mut_ptr(), args_length) }; - cleanup(); + for c_arg in c_args { + unsafe { metacall_value_destroy(c_arg) }; + } Ok(ret) } +/// Calls a function same as [metacall](metacall) but returns a trait object +/// of [MetacallValue](MetacallValue). This is useful when you don't know the return +/// type of that function or the function may return multiple types. Checkout +/// [match_metacall_value](match_metacall_value) for unwrapping the inner value. For example: ... +/// ``` +/// let sum = metacall::metacall_untyped("sum", [1, 2]).unwrap(); +/// ``` pub fn metacall_untyped( func: impl ToString, - args: impl IntoIterator, -) -> Result, MetacallError> { + args: impl IntoIterator, +) -> Result, MetacallError> { Ok(parsers::raw_to_metacallobj_untyped(metacall_inner( func, args, )?)) } -pub fn metacall( +/// Calls a function same as [metacall_untyped](metacall_untyped) without passing any arguments. For example: ... +/// ``` +/// let greet = metacall::metacall_untyped_no_arg("sum").unwrap(); +/// ``` +pub fn metacall_untyped_no_arg( + func: impl ToString, +) -> Result, MetacallError> { + metacall_untyped(func, [] as [MetacallNull; 0]) +} +/// Calls a function with arguments. The generic parameter is the return type of the function +/// you're calling. Checkout [MetacallValue](MetacallValue) for possible types. +/// For example: ... +/// ``` +/// let sum = metacall::metacall::("sum", [1, 2]).unwrap(); +/// ``` +pub fn metacall( func: impl ToString, - args: impl IntoIterator, + args: impl IntoIterator, ) -> Result { match parsers::raw_to_metacallobj::(metacall_inner(func, args)?) { Ok(ret) => Ok(ret), Err(original) => Err(MetacallError::FailedCasting(original)), } } -pub fn metacall_no_arg(func: impl ToString) -> Result { +/// Calls a function same as [metacall](metacall) without passing any arguments. For example: ... +/// ``` +/// let greet = metacall::metacall_no_arg::("greet").unwrap(); +/// ``` +pub fn metacall_no_arg(func: impl ToString) -> Result { metacall::(func, [] as [MetacallNull; 0]) } diff --git a/source/ports/rs_port/src/parsers.rs b/source/ports/rs_port/src/parsers.rs index 0aeac58326..f20b2a8da8 100644 --- a/source/ports/rs_port/src/parsers.rs +++ b/source/ports/rs_port/src/parsers.rs @@ -1,33 +1,27 @@ use crate::{ bindings::metacall_value_id, - match_object_protocol, - prelude::{ + types::{ MetacallClass, MetacallException, MetacallFunction, MetacallFuture, MetacallNull, - MetacallObject, MetacallObjectProtocol, MetacallPointer, MetacallThrowable, + MetacallObject, MetacallPointer, MetacallThrowable, MetacallValue, }, }; use std::{collections::HashMap, ffi::c_void}; -pub fn metacallobj_wrap( - v: Result>, -) -> Box { +fn metacallobj_result_wrap( + v: Result>, +) -> Box { match v { - Ok(obj) => Box::new(obj) as Box, + Ok(obj) => Box::new(obj) as Box, Err(original) => original, } } -pub fn implementer_to_traitobj(v: impl MetacallObjectProtocol) -> Box { - Box::new(v) as Box -} -pub fn raw_to_metacallobj( - ret: *mut c_void, -) -> Result> { +pub fn raw_to_metacallobj(ret: *mut c_void) -> Result> { let null = MetacallNull(); if ret.is_null() { if ::get_metacall_id() != 14 { - return Err(metacallobj_wrap(Ok(null))); + return Err(metacallobj_result_wrap(Ok(null))); } else { return Ok(::from_metacall_raw(ret).unwrap()); } @@ -39,85 +33,90 @@ pub fn raw_to_metacallobj( Err(raw_to_metacallobj_untyped(ret)) } } -pub fn raw_to_metacallobj_untyped(ret: *mut c_void) -> Box { +pub fn raw_to_metacallobj_leak( + ret: *mut c_void, +) -> Result> { + let null = MetacallNull(); + + if ret.is_null() { + if ::get_metacall_id() != 14 { + return Err(metacallobj_result_wrap(Ok(null))); + } else { + return Ok(::from_metacall_raw(ret).unwrap()); + } + } + + if unsafe { metacall_value_id(ret) } == T::get_metacall_id() { + ::from_metacall_raw_leak(ret) + } else { + Err(raw_to_metacallobj_untyped_leak(ret)) + } +} + +pub fn raw_to_metacallobj_untyped(ret: *mut c_void) -> Box { match (ret.is_null(), unsafe { metacall_value_id(ret) }) { - (true, _) => metacallobj_wrap(MetacallNull::from_metacall_raw(ret)), - (_, 0) => metacallobj_wrap(bool::from_metacall_raw(ret)), - (_, 1) => metacallobj_wrap(char::from_metacall_raw(ret)), - (_, 2) => metacallobj_wrap(i16::from_metacall_raw(ret)), - (_, 3) => metacallobj_wrap(i32::from_metacall_raw(ret)), - (_, 4) => metacallobj_wrap(i64::from_metacall_raw(ret)), - (_, 5) => metacallobj_wrap(f32::from_metacall_raw(ret)), - (_, 6) => metacallobj_wrap(f64::from_metacall_raw(ret)), - (_, 7) => metacallobj_wrap(String::from_metacall_raw(ret)), - (_, 8) => metacallobj_wrap(>::from_metacall_raw(ret)), - (_, 9) => metacallobj_wrap(>>::from_metacall_raw( - ret, - )), - (_, 10) => metacallobj_wrap( - >>::from_metacall_raw(ret), + (true, _) => metacallobj_result_wrap(MetacallNull::from_metacall_raw(ret)), + (_, 0) => metacallobj_result_wrap(bool::from_metacall_raw(ret)), + (_, 1) => metacallobj_result_wrap(char::from_metacall_raw(ret)), + (_, 2) => metacallobj_result_wrap(i16::from_metacall_raw(ret)), + (_, 3) => metacallobj_result_wrap(i32::from_metacall_raw(ret)), + (_, 4) => metacallobj_result_wrap(i64::from_metacall_raw(ret)), + (_, 5) => metacallobj_result_wrap(f32::from_metacall_raw(ret)), + (_, 6) => metacallobj_result_wrap(f64::from_metacall_raw(ret)), + (_, 7) => metacallobj_result_wrap(String::from_metacall_raw(ret)), + (_, 8) => metacallobj_result_wrap(>::from_metacall_raw(ret)), + (_, 9) => metacallobj_result_wrap(>>::from_metacall_raw(ret)), + (_, 10) => metacallobj_result_wrap( + >>::from_metacall_raw(ret), ), - (_, 11) => metacallobj_wrap(::from_metacall_raw(ret)), - (_, 12) => metacallobj_wrap(MetacallFuture::from_metacall_raw(ret)), - (_, 13) => metacallobj_wrap(MetacallFunction::from_metacall_raw(ret)), - (_, 14) => metacallobj_wrap(MetacallNull::from_metacall_raw(ret)), - (_, 15) => metacallobj_wrap(MetacallClass::from_metacall_raw(ret)), - (_, 16) => metacallobj_wrap(MetacallObject::from_metacall_raw(ret)), - (_, 17) => metacallobj_wrap(MetacallException::from_metacall_raw(ret)), - (_, 18) => metacallobj_wrap(MetacallThrowable::from_metacall_raw(ret)), - _ => metacallobj_wrap(MetacallNull::from_metacall_raw(ret)), + (_, 11) => metacallobj_result_wrap(::from_metacall_raw(ret)), + (_, 12) => metacallobj_result_wrap(MetacallFuture::from_metacall_raw(ret)), + (_, 13) => metacallobj_result_wrap(MetacallFunction::from_metacall_raw(ret)), + (_, 14) => metacallobj_result_wrap(MetacallNull::from_metacall_raw(ret)), + (_, 15) => metacallobj_result_wrap(MetacallClass::from_metacall_raw(ret)), + (_, 16) => metacallobj_result_wrap(MetacallObject::from_metacall_raw(ret)), + (_, 17) => metacallobj_result_wrap(MetacallException::from_metacall_raw(ret)), + (_, 18) => metacallobj_result_wrap(MetacallThrowable::from_metacall_raw(ret)), + _ => metacallobj_result_wrap(MetacallNull::from_metacall_raw(ret)), } } -pub fn metacallobj_to_raw(arg: impl MetacallObjectProtocol) -> (*mut c_void, impl FnOnce()) { - let (ptr, value_drop) = arg.into_metacall_raw(); - (ptr, move || { - if let Some(value_drop) = value_drop { - value_drop(); - } - }) -} -pub fn metacallobj_to_raw_args( - args: impl IntoIterator, -) -> (Vec<*mut c_void>, impl FnOnce()) { - let mut value_drops = Vec::new(); - let ptr = args - .into_iter() - .map(|arg| { - let (ptr, value_drop) = arg.into_metacall_raw(); - - if let Some(value_drop) = value_drop { - value_drops.push(value_drop); - } - - ptr - }) - .collect::>(); - - (ptr.clone(), move || { - for value_drop in value_drops.into_iter() { - value_drop(); +pub fn raw_to_metacallobj_untyped_leak(ret: *mut c_void) -> Box { + match (ret.is_null(), unsafe { metacall_value_id(ret) }) { + (true, _) => metacallobj_result_wrap(MetacallNull::from_metacall_raw_leak(ret)), + (_, 0) => metacallobj_result_wrap(bool::from_metacall_raw_leak(ret)), + (_, 1) => metacallobj_result_wrap(char::from_metacall_raw_leak(ret)), + (_, 2) => metacallobj_result_wrap(i16::from_metacall_raw_leak(ret)), + (_, 3) => metacallobj_result_wrap(i32::from_metacall_raw_leak(ret)), + (_, 4) => metacallobj_result_wrap(i64::from_metacall_raw_leak(ret)), + (_, 5) => metacallobj_result_wrap(f32::from_metacall_raw_leak(ret)), + (_, 6) => metacallobj_result_wrap(f64::from_metacall_raw_leak(ret)), + (_, 7) => metacallobj_result_wrap(String::from_metacall_raw_leak(ret)), + (_, 8) => metacallobj_result_wrap(>::from_metacall_raw_leak(ret)), + (_, 9) => { + metacallobj_result_wrap(>>::from_metacall_raw_leak(ret)) } - }) -} - -pub fn hashmap_key_with_downcast(key: Box) -> String { - match_object_protocol!(key, { - str: String => str, - num: i16 => num.to_string(), - num: i32 => num.to_string(), - num: i64 => num.to_string(), - num: f32 => num.to_string(), - num: f64 => num.to_string(), - _ => String::from("Invalid key!") - }) + (_, 10) => metacallobj_result_wrap( + >>::from_metacall_raw_leak(ret), + ), + (_, 11) => metacallobj_result_wrap(::from_metacall_raw_leak(ret)), + (_, 12) => metacallobj_result_wrap(MetacallFuture::from_metacall_raw_leak(ret)), + (_, 13) => metacallobj_result_wrap(MetacallFunction::from_metacall_raw_leak(ret)), + (_, 14) => metacallobj_result_wrap(MetacallNull::from_metacall_raw_leak(ret)), + (_, 15) => metacallobj_result_wrap(MetacallClass::from_metacall_raw_leak(ret)), + (_, 16) => metacallobj_result_wrap(MetacallObject::from_metacall_raw_leak(ret)), + (_, 17) => metacallobj_result_wrap(MetacallException::from_metacall_raw_leak(ret)), + (_, 18) => metacallobj_result_wrap(MetacallThrowable::from_metacall_raw_leak(ret)), + _ => metacallobj_result_wrap(MetacallNull::from_metacall_raw_leak(ret)), + } } -pub fn new_raw_pointer(pointer: T) -> *mut T { - Box::into_raw(Box::new(pointer)) -} -pub fn new_void_pointer(pointer: T) -> *mut c_void { - new_raw_pointer(pointer) as *mut c_void +pub fn metacallobj_to_raw(arg: impl MetacallValue) -> *mut c_void { + arg.into_metacall_raw() } -pub fn pointer_to_box(pointer: *mut c_void) -> Box { - unsafe { Box::from_raw(pointer as *mut T) } +pub fn metacallobj_to_raw_args( + args: impl IntoIterator, +) -> Vec<*mut c_void> { + args.into_iter() + .map(|arg| arg.into_metacall_raw()) + .collect::>() } diff --git a/source/ports/rs_port/src/prelude/metacall_class.rs b/source/ports/rs_port/src/prelude/metacall_class.rs deleted file mode 100644 index 6252ea61d9..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_class.rs +++ /dev/null @@ -1,158 +0,0 @@ -use super::{ - MetacallError, MetacallGetAttributeError, MetacallNull, MetacallObject, MetacallObjectProtocol, - MetacallSetAttributeError, MetacallStringConversionError, -}; -use crate::{bindings::*, cstring, cstring_enum, parsers}; -use std::{ffi::c_void, sync::Arc}; - -#[derive(Clone, Debug)] -pub struct MetacallClass { - ptr: Arc<*mut c_void>, -} -unsafe impl Send for MetacallClass {} -unsafe impl Sync for MetacallClass {} - -impl MetacallClass { - pub fn from(ptr: *mut c_void) -> Self { - Self { ptr: Arc::new(ptr) } - } - - pub fn from_name(name: impl ToString) -> Result { - let c_name = cstring!(name)?; - - Ok(Self { - ptr: Arc::new(unsafe { metacall_class(c_name.as_ptr()) }), - }) - } - - fn value_to_class(&self) -> *mut c_void { - unsafe { metacall_value_to_class(*self.ptr) } - } - - pub fn create_object( - &self, - name: impl ToString, - constructor_args: impl IntoIterator, - ) -> Result { - let c_name = cstring!(name)?; - let (mut c_args, cleanup) = parsers::metacallobj_to_raw_args(constructor_args); - let obj = unsafe { - metacall_class_new( - self.value_to_class(), - c_name.as_ptr(), - c_args.as_mut_ptr(), - c_args.len(), - ) - }; - - cleanup(); - - Ok(MetacallObject::new(obj)) - } - pub fn create_object_no_arg( - &self, - name: impl ToString, - ) -> Result { - self.create_object::(name, []) - } - - fn get_attribute_inner( - &self, - name: impl ToString, - ) -> Result<*mut c_void, MetacallGetAttributeError> { - let c_name = cstring_enum!(name, MetacallGetAttributeError)?; - - Ok(unsafe { metacall_class_static_get(self.value_to_class(), c_name.as_ptr()) }) - } - pub fn get_attribute_untyped( - &self, - name: impl ToString, - ) -> Result, MetacallGetAttributeError> { - Ok(parsers::raw_to_metacallobj_untyped( - self.get_attribute_inner(name)?, - )) - } - pub fn get_attribute( - &self, - name: impl ToString, - ) -> Result { - match parsers::raw_to_metacallobj::(self.get_attribute_inner(name)?) { - Ok(ret) => Ok(ret), - Err(original) => Err(MetacallGetAttributeError::FailedCasting(original)), - } - } - - pub fn set_attribute( - &self, - key: impl ToString, - value: impl MetacallObjectProtocol, - ) -> Result<(), MetacallSetAttributeError> { - let c_key = cstring_enum!(key, MetacallSetAttributeError)?; - - let (c_args, cleanup) = parsers::metacallobj_to_raw(value); - if unsafe { metacall_class_static_set(self.value_to_class(), c_key.as_ptr(), c_args) } != 0 - { - return Err(MetacallSetAttributeError::SetAttributeFailure); - } - - cleanup(); - - Ok(()) - } - - fn call_method_inner( - &self, - name: impl ToString, - args: impl IntoIterator, - ) -> Result<*mut c_void, MetacallError> { - let c_key = cstring_enum!(name, MetacallError)?; - let (mut args, cleanup) = parsers::metacallobj_to_raw_args(args); - let ret = unsafe { - metacallv_class( - self.value_to_class(), - c_key.as_ptr(), - args.as_mut_ptr(), - args.len(), - ) - }; - - cleanup(); - - Ok(ret) - } - pub fn call_method_untyped( - &self, - name: impl ToString, - args: impl IntoIterator, - ) -> Result, MetacallError> { - Ok(parsers::raw_to_metacallobj_untyped( - self.call_method_inner::(name, args)?, - )) - } - pub fn call_method( - &self, - name: impl ToString, - args: impl IntoIterator, - ) -> Result { - match parsers::raw_to_metacallobj::(self.call_method_inner::(name, args)?) { - Ok(ret) => Ok(ret), - Err(original) => Err(MetacallError::FailedCasting(original)), - } - } - pub fn call_method_no_arg( - &self, - name: impl ToString, - ) -> Result { - self.call_method::(name, []) - } - - pub fn into_raw(self) -> *mut c_void { - self.value_to_class() - } -} - -impl Drop for MetacallClass { - fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) } - } -} diff --git a/source/ports/rs_port/src/prelude/metacall_error.rs b/source/ports/rs_port/src/prelude/metacall_error.rs deleted file mode 100644 index 8f81285a97..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_error.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::MetacallObjectProtocol; -use std::{ffi::NulError, path::PathBuf}; - -#[derive(Debug, Clone)] -pub struct MetacallInitError(pub String); -impl MetacallInitError { - pub fn new() -> Self { - Self(String::from("Failed to initialize Metacall!")) - } -} -impl Default for MetacallInitError { - fn default() -> Self { - MetacallInitError::new() - } -} -impl ToString for MetacallInitError { - fn to_string(&self) -> String { - self.0.clone() - } -} - -#[derive(Debug, Clone)] -pub struct MetacallStringConversionError { - pub original_string: String, - pub nul_error: NulError, -} -impl MetacallStringConversionError { - pub fn new(original_string: impl ToString, nul_error: NulError) -> Self { - Self { - original_string: original_string.to_string(), - nul_error, - } - } -} -impl ToString for MetacallStringConversionError { - fn to_string(&self) -> String { - self.original_string.clone() - } -} - -#[derive(Debug, Clone)] -pub enum MetacallError { - FunctionNotFound, - FailedCasting(Box), - UnexpectedCStringConversionErr(MetacallStringConversionError), -} - -#[derive(Debug, Clone)] -pub enum MetacallSetAttributeError { - SetAttributeFailure, - FailedCasting(Box), - UnexpectedCStringConversionErr(MetacallStringConversionError), -} - -#[derive(Debug, Clone)] -pub enum MetacallGetAttributeError { - FailedCasting(Box), - UnexpectedCStringConversionErr(MetacallStringConversionError), -} - -#[derive(Debug, Clone)] -pub enum MetacallLoaderError { - FileNotFound(PathBuf), - FromFileFailure, - FromMemoryFailure, - NotAFileOrPermissionDenied(PathBuf), - UnexpectedCStringConversionErr(MetacallStringConversionError), -} diff --git a/source/ports/rs_port/src/prelude/metacall_function.rs b/source/ports/rs_port/src/prelude/metacall_function.rs deleted file mode 100644 index 413844b23a..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_function.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::{MetacallError, MetacallNull, MetacallObjectProtocol}; -use crate::{ - bindings::{ - metacall_value_create_function, metacall_value_destroy, metacall_value_to_function, - metacallfv_s, - }, - parsers, -}; -use std::{ffi::c_void, sync::Arc}; - -#[derive(Clone, Debug)] -pub struct MetacallFunction { - ptr: Arc<*mut c_void>, -} -unsafe impl Send for MetacallFunction {} -unsafe impl Sync for MetacallFunction {} - -impl MetacallFunction { - pub fn new_raw(ptr: *mut c_void) -> Self { - Self { ptr: Arc::new(ptr) } - } - - pub fn new(func: T) -> Self { - unsafe { - Self::new_raw(metacall_value_create_function( - Box::into_raw(Box::new(func)) as *mut c_void, - )) - } - } - - fn value_to_function(&self) -> *mut c_void { - unsafe { metacall_value_to_function(*self.ptr) } - } - - fn call_inner( - &self, - args: impl IntoIterator, - ) -> *mut c_void { - let (mut c_args, cleanup) = parsers::metacallobj_to_raw_args(args); - let ret: *mut c_void = - unsafe { metacallfv_s(self.value_to_function(), c_args.as_mut_ptr(), 0) }; - - cleanup(); - - ret - } - pub fn call_untyped( - &self, - args: impl IntoIterator, - ) -> Box { - parsers::raw_to_metacallobj_untyped(self.call_inner(args)) - } - pub fn call( - &self, - args: impl IntoIterator, - ) -> Result { - match parsers::raw_to_metacallobj::(self.call_inner(args)) { - Ok(ret) => Ok(ret), - Err(original) => Err(MetacallError::FailedCasting(original)), - } - } - pub fn call_no_arg(&self) -> Result { - self.call::([]) - } - - pub fn into_raw(self) -> *mut c_void { - self.value_to_function() - } -} - -impl Drop for MetacallFunction { - fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) }; - } -} diff --git a/source/ports/rs_port/src/prelude/metacall_future.rs b/source/ports/rs_port/src/prelude/metacall_future.rs deleted file mode 100644 index a75981be33..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_future.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::{MetacallNull, MetacallObjectProtocol}; -use crate::{ - bindings::{metacall_await_future, metacall_value_destroy, metacall_value_to_future}, - parsers, -}; -use std::{ffi::c_void, ptr, sync::Arc}; - -#[derive(Clone, Debug)] -pub struct MetacallFuture { - ptr: Arc<*mut c_void>, -} -unsafe impl Send for MetacallFuture {} -unsafe impl Sync for MetacallFuture {} - -pub type MetacallFutureResolve = - fn(Box, Box); -pub type MetacallFutureReject = - fn(Box, Box); - -struct MetacallHandlers { - pub resolve: Option, - pub reject: Option, - pub user_data: Option>, -} - -unsafe extern "C" fn resolver(resolve_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void { - let handlers: Box = parsers::pointer_to_box(upper_data); - - if let Some(resolve) = handlers.resolve { - let data = if let Some(data) = handlers.user_data { - data - } else { - Box::new(MetacallNull()) - }; - - resolve(parsers::raw_to_metacallobj_untyped(resolve_data), data); - } - - ptr::null_mut() -} -unsafe extern "C" fn rejecter(reject_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void { - let handlers: Box = parsers::pointer_to_box(upper_data); - - if let Some(reject) = handlers.reject { - let data = if let Some(data) = handlers.user_data { - data - } else { - Box::new(MetacallNull()) - }; - - reject(parsers::raw_to_metacallobj_untyped(reject_data), data); - } - - ptr::null_mut() -} - -impl MetacallFuture { - pub fn new(ptr: *mut c_void) -> Self { - Self { ptr: Arc::new(ptr) } - } - - pub fn await_fut( - self, - resolve: Option, - reject: Option, - user_data: Option, - ) { - let future = self.into_raw(); - let user_data = match user_data { - Some(user_data) => Some(Box::new(user_data) as Box), - None => None, - }; - let handlers = parsers::new_void_pointer(MetacallHandlers { - resolve, - reject, - user_data, - }); - - unsafe { - match (resolve.is_some(), reject.is_some()) { - (true, true) => { - metacall_await_future(future, Some(resolver), Some(rejecter), handlers) - } - (true, false) => metacall_await_future(future, Some(resolver), None, handlers), - (false, true) => metacall_await_future(future, None, Some(rejecter), handlers), - (false, false) => metacall_await_future(future, None, None, ptr::null_mut()), - } - }; - } - pub fn await_fut_no_data( - self, - resolve: Option, - reject: Option, - ) { - self.await_fut(resolve, reject, None::) - } - - pub fn into_raw(self) -> *mut c_void { - unsafe { metacall_value_to_future(*self.ptr) } - } -} - -impl Drop for MetacallFuture { - fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) }; - } -} diff --git a/source/ports/rs_port/src/prelude/metacall_object.rs b/source/ports/rs_port/src/prelude/metacall_object.rs deleted file mode 100644 index 309bb9564c..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_object.rs +++ /dev/null @@ -1,128 +0,0 @@ -use super::{ - MetacallError, MetacallGetAttributeError, MetacallNull, MetacallObjectProtocol, - MetacallSetAttributeError, -}; -use crate::{ - bindings::{ - metacall_object_get, metacall_object_set, metacall_value_destroy, metacall_value_to_object, - metacallv_object, - }, - cstring_enum, parsers, -}; -use std::{ffi::c_void, sync::Arc}; - -#[derive(Clone, Debug)] -pub struct MetacallObject { - ptr: Arc<*mut c_void>, -} -unsafe impl Send for MetacallObject {} -unsafe impl Sync for MetacallObject {} - -impl MetacallObject { - pub fn new(ptr: *mut c_void) -> Self { - Self { ptr: Arc::new(ptr) } - } - - fn value_to_object(&self) -> *mut c_void { - unsafe { metacall_value_to_object(*self.ptr) } - } - - fn get_attribute_inner( - &self, - name: impl ToString, - ) -> Result<*mut c_void, MetacallGetAttributeError> { - let c_name = cstring_enum!(name, MetacallGetAttributeError)?; - - Ok(unsafe { metacall_object_get(self.value_to_object(), c_name.as_ptr()) }) - } - pub fn get_attribute_untyped( - &self, - name: impl ToString, - ) -> Result, MetacallGetAttributeError> { - Ok(parsers::raw_to_metacallobj_untyped( - self.get_attribute_inner(name)?, - )) - } - pub fn get_attribute( - &self, - name: impl ToString, - ) -> Result { - match parsers::raw_to_metacallobj::(self.get_attribute_inner(name)?) { - Ok(ret) => Ok(ret), - Err(original) => Err(MetacallGetAttributeError::FailedCasting(original)), - } - } - - pub fn set_attribute( - &self, - key: impl ToString, - value: impl MetacallObjectProtocol, - ) -> Result<(), MetacallSetAttributeError> { - let c_key = cstring_enum!(key, MetacallSetAttributeError)?; - - let (c_args, cleanup) = parsers::metacallobj_to_raw(value); - if unsafe { metacall_object_set(self.value_to_object(), c_key.as_ptr(), c_args) } != 0 { - return Err(MetacallSetAttributeError::SetAttributeFailure); - } - - cleanup(); - - Ok(()) - } - - fn call_method_inner( - &self, - key: impl ToString, - args: impl IntoIterator, - ) -> Result<*mut c_void, MetacallError> { - let c_key = cstring_enum!(key, MetacallError)?; - let (mut args, cleanup) = parsers::metacallobj_to_raw_args(args); - let ret = unsafe { - metacallv_object( - self.value_to_object(), - c_key.as_ptr(), - args.as_mut_ptr(), - args.len(), - ) - }; - - cleanup(); - - Ok(ret) - } - pub fn call_method_untyped( - &self, - key: impl ToString, - args: impl IntoIterator, - ) -> Result, MetacallError> { - Ok(parsers::raw_to_metacallobj_untyped( - self.call_method_inner::(key, args)?, - )) - } - pub fn call_method( - &self, - key: impl ToString, - args: impl IntoIterator, - ) -> Result { - match parsers::raw_to_metacallobj::(self.call_method_inner::(key, args)?) { - Ok(ret) => Ok(ret), - Err(original) => Err(MetacallError::FailedCasting(original)), - } - } - pub fn call_method_no_arg( - &self, - key: impl ToString, - ) -> Result { - self.call_method::(key, []) - } - - pub fn into_raw(self) -> *mut c_void { - self.value_to_object() - } -} - -impl Drop for MetacallObject { - fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) } - } -} diff --git a/source/ports/rs_port/src/prelude/metacall_pointer.rs b/source/ports/rs_port/src/prelude/metacall_pointer.rs deleted file mode 100644 index 7ee77cb1c6..0000000000 --- a/source/ports/rs_port/src/prelude/metacall_pointer.rs +++ /dev/null @@ -1,35 +0,0 @@ -use super::MetacallObjectProtocol; -use crate::bindings::{metacall_value_create_ptr, metacall_value_destroy, metacall_value_to_ptr}; -use std::{ffi::c_void, sync::Arc}; - -#[derive(Clone, Debug)] -pub struct MetacallPointer { - ptr: Arc<*mut c_void>, -} -unsafe impl Send for MetacallPointer {} -unsafe impl Sync for MetacallPointer {} - -impl MetacallPointer { - pub fn new_raw(ptr: *mut c_void) -> Result> { - Ok(Self { ptr: Arc::new(ptr) }) - } - - pub fn new( - ptr: Box, - ) -> Result> { - let ptr = Box::into_raw(ptr) as *mut _ as *mut c_void; - Ok(Self { - ptr: Arc::new(unsafe { metacall_value_create_ptr(ptr) }), - }) - } - - pub fn into_raw(self) -> *mut c_void { - unsafe { metacall_value_to_ptr(*self.ptr) } - } -} - -impl Drop for MetacallPointer { - fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) } - } -} diff --git a/source/ports/rs_port/src/prelude/object_protocol.rs b/source/ports/rs_port/src/prelude/object_protocol.rs deleted file mode 100644 index 62aa001eee..0000000000 --- a/source/ports/rs_port/src/prelude/object_protocol.rs +++ /dev/null @@ -1,536 +0,0 @@ -use super::{ - MetacallClass, MetacallException, MetacallFunction, MetacallFuture, MetacallNull, - MetacallObject, MetacallPointer, MetacallThrowable, -}; -use crate::{bindings::*, cstring, match_object_protocol, match_object_protocol_all, parsers}; -use dyn_clone::DynClone; -use std::{ - any::Any, - collections::HashMap, - ffi::{c_char, c_int, c_void, CStr, CString}, - fmt::Debug, - slice, -}; - -pub trait Downcast: Any { - fn into_any(self: Box) -> Box; - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; -} -impl Downcast for T { - fn into_any(self: Box) -> Box { - self - } - fn as_any(&self) -> &dyn Any { - self - } - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } -} - -pub trait MetacallObjectProtocol: Downcast + Debug + DynClone { - // It tries to convert the raw pointer to the value and return a trait object on failure - fn from_metacall_raw(v: *mut c_void) -> Result> - where - Self: Sized; - // It returns the enum index of Metacall Protocol in the core - fn get_metacall_id() -> u32 - where - Self: Sized; - // It returns a tuple. The first element is a raw pointer to the value parsed with metacall_value_to_X - // and the second element is a closure that drops the pointer (if it exists depending on the type) - fn into_metacall_raw(self) -> (*mut c_void, Option>); -} -dyn_clone::clone_trait_object!(MetacallObjectProtocol); - -impl dyn MetacallObjectProtocol { - #[inline] - pub fn is(&self) -> bool { - Downcast::as_any(self).is::() - } - - #[inline] - pub fn downcast(self: Box) -> Result> { - if self.is::() { - Ok(*Downcast::into_any(self).downcast::().unwrap()) - } else { - Err(self) - } - } - - #[inline] - pub fn downcast_ref(&self) -> Option<&T> { - Downcast::as_any(self).downcast_ref::() - } - - #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { - Downcast::as_any_mut(self).downcast_mut::() - } -} - -impl MetacallObjectProtocol for bool { - fn get_metacall_id() -> u32 { - 0 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_bool(v) != 0 }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_bool((self as c_int).try_into().unwrap()) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr); - })), - ) - } -} -impl MetacallObjectProtocol for char { - fn get_metacall_id() -> u32 { - 1 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_char(v) as u8 as char }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_char(self as c_char) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for i16 { - fn get_metacall_id() -> u32 { - 2 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_short(v) }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_short(self) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for i32 { - fn get_metacall_id() -> u32 { - 3 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_int(v) }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_int(self) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for i64 { - fn get_metacall_id() -> u32 { - 4 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_long(v) }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_long(self) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for f32 { - fn get_metacall_id() -> u32 { - 5 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_float(v) }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_float(self) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for f64 { - fn get_metacall_id() -> u32 { - 6 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let value = unsafe { metacall_value_to_double(v) }; - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let value_ptr = unsafe { metacall_value_create_double(self) }; - - ( - value_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(value_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for String { - fn get_metacall_id() -> u32 { - 7 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let c_str = unsafe { CStr::from_ptr(metacall_value_to_string(v)) }; - let value = String::from(c_str.to_str().unwrap()); - - unsafe { metacall_value_destroy(v) }; - - Ok(value) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let raw = cstring!(self.as_str()).unwrap().into_raw(); - let new_raw = unsafe { metacall_value_create_string(raw.cast(), self.len()) }; - - ( - new_raw, - Some(Box::new(move || unsafe { - drop(CString::from_raw(raw)); - - metacall_value_destroy(new_raw); - })), - ) - } -} -impl MetacallObjectProtocol for Vec { - fn get_metacall_id() -> u32 { - 8 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - unsafe { - let buf = slice::from_raw_parts( - metacall_value_to_buffer(v) as *mut c_char, - metacall_value_size(v), - ) - .to_vec(); - - metacall_value_destroy(v); - - Ok(buf) - } - } - fn into_metacall_raw(mut self) -> (*mut c_void, Option>) { - let new_raw = - unsafe { metacall_value_create_buffer(self.as_mut_ptr() as *mut c_void, self.len()) }; - let raw = self.as_mut_ptr(); - - ( - new_raw, - Some(Box::new(move || unsafe { - metacall_value_destroy(new_raw); - - raw.drop_in_place(); - })), - ) - } -} -impl MetacallObjectProtocol for Vec { - fn get_metacall_id() -> u32 { - 9 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - let count = unsafe { metacall_value_count(v) }; - let array = unsafe { metacall_value_to_array(v) }; - let vec = unsafe { slice::from_raw_parts_mut(array, count).to_vec() }; - let vec = vec - .into_iter() - .map(|element| parsers::raw_to_metacallobj(element)) - .collect::, Box>>()?; - - // unsafe { metacall_value_destroy() }; - - Ok(vec) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let mut value_drops = Vec::with_capacity(self.capacity()); - let mut array = self - .into_iter() - .map(|element| { - let (element, value_drop) = element.into_metacall_raw(); - - if let Some(value_drop) = value_drop { - value_drops.push(value_drop); - } - - element as *const c_void - }) - .collect::>(); - - let raw = array.as_mut_ptr(); - let new_raw = unsafe { metacall_value_create_array(raw, array.len()) }; - - ( - new_raw, - Some(Box::new(move || unsafe { - for value_drop in value_drops { - value_drop(); - } - // raw.drop_in_place(); - - metacall_value_destroy(new_raw); - })), - ) - } -} -impl MetacallObjectProtocol for HashMap { - fn get_metacall_id() -> u32 { - 10 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - unsafe { - let map = metacall_value_to_map(v); - let count = metacall_value_count(v); - let map = slice::from_raw_parts(map, count); - - let mut hashmap = HashMap::new(); - for map_value in map.iter() { - let m_pair = metacall_value_to_array(*map_value); - let m_pair = slice::from_raw_parts(m_pair, 2); - - let key = parsers::hashmap_key_with_downcast(parsers::raw_to_metacallobj_untyped( - m_pair[0], - )); - let val = match parsers::raw_to_metacallobj::(m_pair[1]) { - Ok(parsed) => parsed, - Err(original) => { - return Err(original); - } - }; - - hashmap.insert(key, val); - } - - metacall_value_destroy(v); - - Ok(hashmap) - } - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let mut value_drops = Vec::with_capacity(self.len()); - let mut hashmap: Vec<*const c_void> = self - .into_iter() - .map(|(key, value)| { - let (key, value_drop1) = key.into_metacall_raw(); - let (value, value_drop2) = value.into_metacall_raw(); - - if let Some(value_drop) = value_drop1 { - value_drops.push(value_drop); - } - if let Some(value_drop) = value_drop2 { - value_drops.push(value_drop); - } - - parsers::new_void_pointer((key, value)) as *const c_void - }) - .collect(); - - let new_raw = unsafe { metacall_value_create_map(hashmap.as_mut_ptr(), hashmap.len()) }; - let raw = hashmap.as_mut_ptr(); - - ( - new_raw, - Some(Box::new(move || unsafe { - for value_drop in value_drops { - value_drop(); - } - - metacall_value_destroy(new_raw); - - raw.drop_in_place() - })), - ) - } -} -impl MetacallObjectProtocol for MetacallPointer { - fn get_metacall_id() -> u32 { - 11 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Self::new_raw(v) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallFuture { - fn get_metacall_id() -> u32 { - 12 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallFuture::new(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallFunction { - fn get_metacall_id() -> u32 { - 13 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallFunction::new_raw(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallNull { - fn get_metacall_id() -> u32 { - 14 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - if !v.is_null() { - unsafe { metacall_value_destroy(v) }; - } - - Ok(MetacallNull()) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - let null_ptr = self.into_raw(); - - ( - null_ptr, - Some(Box::new(move || unsafe { - metacall_value_destroy(null_ptr) - })), - ) - } -} -impl MetacallObjectProtocol for MetacallClass { - fn get_metacall_id() -> u32 { - 15 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallClass::from(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallObject { - fn get_metacall_id() -> u32 { - 16 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallObject::new(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallException { - fn get_metacall_id() -> u32 { - 17 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallException::from(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for MetacallThrowable { - fn get_metacall_id() -> u32 { - 18 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(MetacallThrowable::new(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - (self.into_raw(), None) - } -} -impl MetacallObjectProtocol for Box { - fn get_metacall_id() -> u32 { - // Something random - 100 - } - fn from_metacall_raw(v: *mut c_void) -> Result> { - Ok(parsers::raw_to_metacallobj_untyped(v)) - } - fn into_metacall_raw(self) -> (*mut c_void, Option>) { - match_object_protocol_all!( - self, - x, - x.into_metacall_raw(), - bool, - char, - i16, - i32, - i64, - f32, - f64, - String, - Vec, - Vec>, - HashMap>, - MetacallPointer, - MetacallFuture, - MetacallFunction, - MetacallNull, - MetacallClass, - MetacallObject, - MetacallException, - MetacallThrowable, - Box - ) - } -} diff --git a/source/ports/rs_port/src/types/metacall_class.rs b/source/ports/rs_port/src/types/metacall_class.rs new file mode 100644 index 0000000000..6054e5d213 --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_class.rs @@ -0,0 +1,223 @@ +use super::{ + MetacallClassFromNameError, MetacallError, MetacallGetAttributeError, MetacallNull, + MetacallObject, MetacallSetAttributeError, MetacallStringConversionError, MetacallValue, +}; +use crate::{bindings::*, cstring, cstring_enum, parsers}; +use std::ffi::c_void; + +#[derive(Debug)] +/// Represents Metacall Class. You can get this type when returned by a function or get a class by its +/// name with [from_name](#method.from_name). +pub struct MetacallClass { + found_by_name: bool, + leak: bool, + value: *mut c_void, +} +unsafe impl Send for MetacallClass {} +unsafe impl Sync for MetacallClass {} +impl Clone for MetacallClass { + fn clone(&self) -> Self { + Self { + found_by_name: self.found_by_name, + leak: true, + value: unsafe { metacall_value_copy(self.value) }, + } + } +} + +impl MetacallClass { + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Self { + Self { + found_by_name: false, + leak: false, + value, + } + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Self { + Self { + found_by_name: false, + leak: true, + value, + } + } + + /// Gets a class by its name. + pub fn from_name(name: impl ToString) -> Result { + let c_name = cstring_enum!(name, MetacallClassFromNameError)?; + let class = unsafe { metacall_class(c_name.as_ptr()) }; + + if class.is_null() { + return Err(MetacallClassFromNameError::ClassNotFound); + } + + Ok(Self { + found_by_name: true, + leak: true, + value: class, + }) + } + + fn value_to_class(&self) -> *mut c_void { + if self.found_by_name { + self.value + } else { + unsafe { metacall_value_to_class(self.value) } + } + } + + /// Creates an [object](MetacallObject) of the class wtih constructor arguments. + pub fn create_object( + &self, + name: impl ToString, + constructor_args: impl IntoIterator, + ) -> Result { + let c_name = cstring!(name)?; + let mut c_args = parsers::metacallobj_to_raw_args(constructor_args); + let obj = unsafe { + metacall_class_new( + self.value_to_class(), + c_name.as_ptr(), + c_args.as_mut_ptr(), + c_args.len(), + ) + }; + + for c_arg in c_args { + unsafe { metacall_value_destroy(c_arg) }; + } + + Ok(MetacallObject::new_raw(obj)) + } + /// Creates an [object](MetacallObject) of the class wtihout constructor arguments. + pub fn create_object_no_arg( + &self, + name: impl ToString, + ) -> Result { + self.create_object::(name, []) + } + + fn get_attribute_inner( + &self, + name: impl ToString, + ) -> Result<*mut c_void, MetacallGetAttributeError> { + let c_name = cstring_enum!(name, MetacallGetAttributeError)?; + + Ok(unsafe { metacall_class_static_get(self.value_to_class(), c_name.as_ptr()) }) + } + /// Gets static attribute from a class without type casting([MetacallValue](MetacallValue)). + pub fn get_attribute_untyped( + &self, + name: impl ToString, + ) -> Result, MetacallGetAttributeError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.get_attribute_inner(name)?, + )) + } + /// Gets static attribute from a class. + pub fn get_attribute( + &self, + name: impl ToString, + ) -> Result { + match parsers::raw_to_metacallobj::(self.get_attribute_inner(name)?) { + Ok(ret) => Ok(ret), + Err(original) => Err(MetacallGetAttributeError::FailedCasting(original)), + } + } + + /// Sets static class attribute. + pub fn set_attribute( + &self, + key: impl ToString, + value: impl MetacallValue, + ) -> Result<(), MetacallSetAttributeError> { + let c_key = cstring_enum!(key, MetacallSetAttributeError)?; + + let c_arg = parsers::metacallobj_to_raw(value); + if unsafe { metacall_class_static_set(self.value_to_class(), c_key.as_ptr(), c_arg) } != 0 { + return Err(MetacallSetAttributeError::SetAttributeFailure); + } + + unsafe { metacall_value_destroy(c_arg) }; + + Ok(()) + } + + fn call_method_inner( + &self, + name: impl ToString, + args: impl IntoIterator, + ) -> Result<*mut c_void, MetacallError> { + let c_key = cstring_enum!(name, MetacallError)?; + let mut c_args = parsers::metacallobj_to_raw_args(args); + let ret = unsafe { + metacallv_class( + self.value_to_class(), + c_key.as_ptr(), + c_args.as_mut_ptr(), + c_args.len(), + ) + }; + + for c_arg in c_args { + unsafe { metacall_value_destroy(c_arg) }; + } + + Ok(ret) + } + /// Calls a static class method witout type casting([MetacallValue](MetacallValue)). + pub fn call_method_untyped( + &self, + name: impl ToString, + args: impl IntoIterator, + ) -> Result, MetacallError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.call_method_inner::(name, args)?, + )) + } + /// Calls a static class method witout type casting([MetacallValue](MetacallValue)) and + /// without passing arguments. + pub fn call_method_untyped_no_arg( + &self, + name: impl ToString, + ) -> Result, MetacallError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.call_method_inner::(name, [])?, + )) + } + /// Calls a static class method. + pub fn call_method( + &self, + name: impl ToString, + args: impl IntoIterator, + ) -> Result { + match parsers::raw_to_metacallobj::(self.call_method_inner::(name, args)?) { + Ok(ret) => Ok(ret), + Err(original) => Err(MetacallError::FailedCasting(original)), + } + } + /// Calls a static class method without passing arguments. + pub fn call_method_no_arg( + &self, + name: impl ToString, + ) -> Result { + self.call_method::(name, []) + } + + #[doc(hidden)] + pub fn into_raw(mut self) -> *mut c_void { + self.leak = true; + + self.value + } +} + +impl Drop for MetacallClass { + fn drop(&mut self) { + if !self.leak { + unsafe { metacall_value_destroy(self.value) } + } + } +} diff --git a/source/ports/rs_port/src/types/metacall_error.rs b/source/ports/rs_port/src/types/metacall_error.rs new file mode 100644 index 0000000000..690b9c16da --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_error.rs @@ -0,0 +1,100 @@ +use super::MetacallValue; +use std::{ffi::NulError, path::PathBuf}; + +#[derive(Debug, Clone)] +/// This error happens when it's not possible to initialize the Metacall core. You can check +/// your logs for more information. +pub struct MetacallInitError; +impl MetacallInitError { + #[doc(hidden)] + pub fn new() -> Self { + Self + } +} +impl Default for MetacallInitError { + fn default() -> Self { + MetacallInitError::new() + } +} +impl ToString for MetacallInitError { + fn to_string(&self) -> String { + String::from("Failed to initialize Metacall!") + } +} + +#[derive(Debug, Clone)] +/// This error may happen when passing contains a null character. You can access the +/// original string and the NulError throughout this struct. +pub struct MetacallStringConversionError { + pub original_string: String, + pub nul_error: NulError, +} +impl MetacallStringConversionError { + #[doc(hidden)] + pub fn new(original_string: impl ToString, nul_error: NulError) -> Self { + Self { + original_string: original_string.to_string(), + nul_error, + } + } +} +impl ToString for MetacallStringConversionError { + fn to_string(&self) -> String { + self.original_string.clone() + } +} + +#[derive(Debug, Clone)] +/// This error may happen when trying to call a function. +pub enum MetacallError { + /// Function not found. + FunctionNotFound, + /// Failed to cast the return type as the type requested. + FailedCasting(Box), + /// Null character detected. + UnexpectedCStringConversionErr(MetacallStringConversionError), +} + +#[derive(Debug, Clone)] +/// This error may happen when trying to set a class/object attribute. Check your logs +/// if you get `SetAttributeFailure` error variant. +pub enum MetacallSetAttributeError { + /// Failed to set the attribute. + SetAttributeFailure, + /// Null character detected. + UnexpectedCStringConversionErr(MetacallStringConversionError), +} + +#[derive(Debug, Clone)] +/// This error may happen when trying to get a class/object attribute. +pub enum MetacallGetAttributeError { + /// Failed to cast the attribute as the type requested. + FailedCasting(Box), + /// Null character detected. + UnexpectedCStringConversionErr(MetacallStringConversionError), +} + +#[derive(Debug, Clone)] +/// This error may happen when loading a code. Check your logs for more information if you +/// get `FromFileFailure` or `FromMemoryFailure` error variant. +pub enum MetacallLoaderError { + /// File not found. + FileNotFound(PathBuf), + /// Failed to load from file. + FromFileFailure, + /// Failed to load from memory. + FromMemoryFailure, + /// Not a file or permission denied. + NotAFileOrPermissionDenied(PathBuf), + /// Null character detected. + UnexpectedCStringConversionErr(MetacallStringConversionError), +} + +#[derive(Debug, Clone)] +/// This error may happen when trying to get a class by its name. +pub enum MetacallClassFromNameError { + /// Class not found. + ClassNotFound, + /// Null character detected. + UnexpectedCStringConversionErr(MetacallStringConversionError), +} diff --git a/source/ports/rs_port/src/prelude/metacall_exception.rs b/source/ports/rs_port/src/types/metacall_exception.rs similarity index 50% rename from source/ports/rs_port/src/prelude/metacall_exception.rs rename to source/ports/rs_port/src/types/metacall_exception.rs index 9f3882d22a..9f1585f589 100644 --- a/source/ports/rs_port/src/prelude/metacall_exception.rs +++ b/source/ports/rs_port/src/types/metacall_exception.rs @@ -1,25 +1,37 @@ -use super::{MetacallObjectProtocol, MetacallStringConversionError}; +use super::{MetacallStringConversionError, MetacallValue}; use crate::{ bindings::{ metacall_exception_type, metacall_throwable_value, metacall_value_create_exception, metacall_value_destroy, metacall_value_to_exception, metacall_value_to_throwable, }, - cstring, match_object_protocol, parsers, + cstring, helpers, match_metacall_value, parsers, }; use std::{ ffi::{c_char, c_void, CStr}, sync::Arc, }; -#[derive(Clone, Debug)] +#[derive(Debug)] +/// Represents Metacall exception. You can create an exception with [new](#method.new). pub struct MetacallException { exception_struct: Arc, - ptr: Arc<*mut c_void>, + leak: bool, + value: *mut c_void, } unsafe impl Send for MetacallException {} unsafe impl Sync for MetacallException {} +impl Clone for MetacallException { + fn clone(&self) -> Self { + Self { + exception_struct: self.exception_struct.clone(), + leak: true, + value: self.value, + } + } +} impl MetacallException { + /// Creates a new exception. pub fn new( message: impl ToString, label: impl ToString, @@ -41,15 +53,28 @@ impl MetacallException { Ok(Self { exception_struct: Arc::new(exception_struct), - ptr: Arc::new(exception_ptr), + value: exception_ptr, + leak: false, }) } - pub fn from(ptr: *mut c_void) -> Self { - let exception = unsafe { metacall_value_to_exception(ptr) }; + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Self { + let exception = unsafe { metacall_value_to_exception(value) }; + Self { + exception_struct: Arc::new(unsafe { *(exception as *mut metacall_exception_type) }), + leak: false, + value, + } + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Self { + let exception = unsafe { metacall_value_to_exception(value) }; Self { exception_struct: Arc::new(unsafe { *(exception as *mut metacall_exception_type) }), - ptr: Arc::new(ptr), + leak: true, + value, } } @@ -57,65 +82,104 @@ impl MetacallException { String::from(unsafe { CStr::from_ptr(string) }.to_str().unwrap()) } + /// Gets the exception message. pub fn get_message(&self) -> String { Self::string_convertor(self.exception_struct.message) } + /// Gets the exception label. pub fn get_label(&self) -> String { Self::string_convertor(self.exception_struct.label) } + /// Gets the exception stacktrace. pub fn get_stacktrace(&self) -> String { Self::string_convertor(self.exception_struct.stacktrace) } + /// Gets the exception code. pub fn get_code(&self) -> i64 { self.exception_struct.code } + #[doc(hidden)] pub fn into_raw(self) -> *mut c_void { - unsafe { metacall_value_to_exception(*self.ptr) } + self.value } } -#[derive(Clone, Debug)] -pub enum MetacallThrowableValue { +#[derive(Debug, Clone)] +/// Different types of Throwable value. +pub enum MetacallThrowableValue { + /// Exception. Exception(MetacallException), - Other(Box), + /// Other types. + Other(Box), + /// Specified. Specified(T), } -#[derive(Clone, Debug)] +#[derive(Debug)] +/// Represents Metacall throwable. pub struct MetacallThrowable { - value: Arc<*mut c_void>, - ptr: Arc<*mut c_void>, + leak: bool, + value_ptr: *mut c_void, + value: *mut c_void, } unsafe impl Send for MetacallThrowable {} unsafe impl Sync for MetacallThrowable {} +impl Clone for MetacallThrowable { + fn clone(&self) -> Self { + Self { + leak: true, + value: self.value, + value_ptr: self.value_ptr, + } + } +} impl MetacallThrowable { - pub fn new(ptr: *mut c_void) -> Self { - let throwable_value = unsafe { metacall_throwable_value(metacall_value_to_throwable(ptr)) }; + #[doc(hidden)] + pub fn new_raw(value_ptr: *mut c_void) -> Self { + let throwable_value = + unsafe { metacall_throwable_value(metacall_value_to_throwable(value_ptr)) }; Self { - ptr: Arc::new(ptr), - value: Arc::new(throwable_value), + leak: false, + value: throwable_value, + value_ptr, } } - pub fn get_value_untyped(&self) -> Box { - match parsers::raw_to_metacallobj::(*self.value) { - Ok(value) => parsers::implementer_to_traitobj(value), + #[doc(hidden)] + pub fn new_raw_leak(value_ptr: *mut c_void) -> Self { + let throwable_value = + unsafe { metacall_throwable_value(metacall_value_to_throwable(value_ptr)) }; + Self { + leak: true, + value: throwable_value, + value_ptr, + } + } + + /// Gets the throwable value without type casting([MetacallValue](MetacallValue)). + pub fn get_value_untyped(&self) -> Box { + match parsers::raw_to_metacallobj::(self.value) { + Ok(mut value) => { + value.leak = true; + + helpers::metacall_implementer_to_traitobj(value) + } Err(original) => original, } } - pub fn get_value( - &self, - ) -> Result> { + /// Gets the throwable value. + pub fn get_value(&self) -> Result> { match self.get_value_untyped().downcast::() { Ok(value) => Ok(value), Err(original) => Err(original), } } + #[doc(hidden)] pub fn into_raw(self) -> *mut c_void { - unsafe { metacall_value_to_throwable(*self.ptr) } + self.value_ptr } } @@ -133,7 +197,7 @@ impl ToString for MetacallThrowable { let throwable_value = self.get_value_untyped(); format!( "[Throwable]: {}", - match_object_protocol!(throwable_value, { + match_metacall_value!(throwable_value, { exception: MetacallException => exception.to_string(), _ => format!("{:#?}", throwable_value) }) @@ -143,13 +207,17 @@ impl ToString for MetacallThrowable { impl Drop for MetacallException { fn drop(&mut self) { - unsafe { metacall_value_destroy(*self.ptr) } + if !self.leak { + unsafe { metacall_value_destroy(self.value) } + } } } impl Drop for MetacallThrowable { fn drop(&mut self) { unsafe { - metacall_value_destroy(*self.ptr); + if !self.leak { + metacall_value_destroy(self.value_ptr); + } } } } diff --git a/source/ports/rs_port/src/types/metacall_function.rs b/source/ports/rs_port/src/types/metacall_function.rs new file mode 100644 index 0000000000..4c3328fff4 --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_function.rs @@ -0,0 +1,92 @@ +use super::{MetacallError, MetacallNull, MetacallValue}; +use crate::{ + bindings::{metacall_value_destroy, metacall_value_to_function, metacallfv_s}, + parsers, +}; +use std::ffi::c_void; + +#[derive(Debug)] +/// Represents Metacall function. +pub struct MetacallFunction { + leak: bool, + value: *mut c_void, +} +unsafe impl Send for MetacallFunction {} +unsafe impl Sync for MetacallFunction {} +impl Clone for MetacallFunction { + fn clone(&self) -> Self { + Self { + leak: true, + value: self.value, + } + } +} + +impl MetacallFunction { + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Self { + Self { leak: false, value } + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Self { + Self { leak: true, value } + } + + fn value_to_function(&self) -> *mut c_void { + unsafe { metacall_value_to_function(self.value) } + } + + fn call_inner(&self, args: impl IntoIterator) -> *mut c_void { + let mut c_args = parsers::metacallobj_to_raw_args(args); + let ret: *mut c_void = + unsafe { metacallfv_s(self.value_to_function(), c_args.as_mut_ptr(), 0) }; + + for c_arg in c_args { + unsafe { metacall_value_destroy(c_arg) }; + } + + ret + } + /// Calls the function with arguments and witout type casting([MetacallValue](MetacallValue)). + pub fn call_untyped( + &self, + args: impl IntoIterator, + ) -> Box { + parsers::raw_to_metacallobj_untyped(self.call_inner(args)) + } + /// Calls the function without passing arguments and witout type + /// casting([MetacallValue](MetacallValue)). + pub fn call_untyped_no_arg(&self) -> Box { + parsers::raw_to_metacallobj_untyped(self.call_inner([] as [MetacallNull; 0])) + } + /// Calls the function with arguments. + pub fn call( + &self, + args: impl IntoIterator, + ) -> Result { + match parsers::raw_to_metacallobj::(self.call_inner(args)) { + Ok(ret) => Ok(ret), + Err(original) => Err(MetacallError::FailedCasting(original)), + } + } + /// Calls the function without arguments. + pub fn call_no_arg(&self) -> Result { + self.call::([]) + } + + #[doc(hidden)] + pub fn into_raw(mut self) -> *mut c_void { + self.leak = true; + + self.value + } +} + +impl Drop for MetacallFunction { + fn drop(&mut self) { + if !self.leak { + unsafe { metacall_value_destroy(self.value) } + } + } +} diff --git a/source/ports/rs_port/src/types/metacall_future.rs b/source/ports/rs_port/src/types/metacall_future.rs new file mode 100644 index 0000000000..504e933f47 --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_future.rs @@ -0,0 +1,155 @@ +use super::{MetacallNull, MetacallValue}; +use crate::{ + bindings::{metacall_await_future, metacall_value_destroy, metacall_value_to_future}, + helpers, parsers, +}; +use std::{ffi::c_void, ptr}; + +/// Function pointer type used for resolving/rejecting Metacall futures. The first argument is the result +/// and the second argument is the data that you may want to access when the function gets called. +/// Checkout [MetacallFuture resolve](MetacallFuture#method.then) or +/// [MetacallFuture reject](MetacallFuture#method.catch) for usage. +pub type MetacallFutureHandler = fn(Box, Box); + +#[derive(Debug)] +/// Represents MetacallFuture. Usage example: ... +/// ``` +/// use metacall::{MetacallValue, MetacallFuture, metacall}; +/// +/// fn resolve(result: impl MetacallValue, data: impl MetacallValue) { +/// println!("Resolve:: result: {:#?}, data: {:#?}", result, data); +/// } +/// +/// fn reject(result: impl MetacallValue, data: impl MetacallValue) { +/// println!("Reject:: result: {:#?}, data: {:#?}", result, data); +/// } +/// +/// let future = metacall::("async_function", [1]).unwrap(); +/// future.then(resolve).catch(reject).await_fut(); +/// ``` +#[repr(C)] +pub struct MetacallFuture { + data: *mut dyn MetacallValue, + leak: bool, + reject: Option, + resolve: Option, + value: *mut c_void, +} +unsafe impl Send for MetacallFuture {} +unsafe impl Sync for MetacallFuture {} +impl Clone for MetacallFuture { + fn clone(&self) -> Self { + Self { + data: self.data, + leak: true, + reject: self.reject, + resolve: self.resolve, + value: self.value, + } + } +} + +unsafe extern "C" fn resolver(resolve_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void { + let self_struct = *Box::from_raw(upper_data as *mut MetacallFuture); + let user_data = Box::from_raw(self_struct.data); + + (self_struct.resolve.unwrap())(parsers::raw_to_metacallobj_untyped(resolve_data), user_data); + + ptr::null_mut() +} +unsafe extern "C" fn rejecter(reject_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void { + let self_struct = *Box::from_raw(upper_data as *mut MetacallFuture); + let user_data = Box::from_raw(self_struct.data); + + (self_struct.reject.unwrap())(parsers::raw_to_metacallobj_untyped(reject_data), user_data); + + if !self_struct.leak { + unsafe { metacall_value_destroy(self_struct.value) }; + } + + ptr::null_mut() +} + +impl MetacallFuture { + fn create_null_data() -> *mut dyn MetacallValue { + Box::into_raw(helpers::metacall_implementer_to_traitobj(MetacallNull())) + } + + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Self { + Self { + data: Self::create_null_data(), + leak: false, + reject: None, + resolve: None, + value, + } + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Self { + Self { + data: Self::create_null_data(), + leak: true, + reject: None, + resolve: None, + value, + } + } + + /// Adds a resolve callback. + pub fn then(mut self, resolve: MetacallFutureHandler) -> Self { + self.resolve = Some(resolve); + + self + } + + /// Adds a reject callback. + pub fn catch(mut self, reject: MetacallFutureHandler) -> Self { + self.reject = Some(reject); + + self + } + + /// Adds data. + pub fn data(mut self, data: impl MetacallValue) -> Self { + unsafe { drop(Box::from_raw(self.data)) }; + + self.data = Box::into_raw(Box::new(data) as Box); + + self + } + + /// Awaits the future. + pub fn await_fut(self) { + let resolve_is_some = self.resolve.is_some(); + let reject_is_some = self.reject.is_some(); + let value = self.value; + + unsafe { + metacall_value_destroy(metacall_await_future( + metacall_value_to_future(value), + if resolve_is_some { + Some(resolver) + } else { + None + }, + if reject_is_some { Some(rejecter) } else { None }, + Box::into_raw(Box::new(self)) as *mut c_void, + )) + }; + } + + #[doc(hidden)] + pub fn into_raw(self) -> *mut c_void { + self.value + } +} + +// impl Drop for MetacallFuture { +// fn drop(&mut self) { +// if !self.leak { +// unsafe { metacall_value_destroy(self.value) }; +// } +// } +// } diff --git a/source/ports/rs_port/src/prelude/metacall_null.rs b/source/ports/rs_port/src/types/metacall_null.rs similarity index 88% rename from source/ports/rs_port/src/prelude/metacall_null.rs rename to source/ports/rs_port/src/types/metacall_null.rs index 2c594002fd..b20ba51688 100644 --- a/source/ports/rs_port/src/prelude/metacall_null.rs +++ b/source/ports/rs_port/src/types/metacall_null.rs @@ -2,11 +2,13 @@ use crate::bindings::metacall_value_create_null; use std::ffi::c_void; #[derive(Clone, Debug)] +/// Represents NULL. pub struct MetacallNull(); unsafe impl Send for MetacallNull {} unsafe impl Sync for MetacallNull {} impl MetacallNull { + #[doc(hidden)] pub fn into_raw(self) -> *mut c_void { unsafe { metacall_value_create_null() } } diff --git a/source/ports/rs_port/src/types/metacall_object.rs b/source/ports/rs_port/src/types/metacall_object.rs new file mode 100644 index 0000000000..2c0ecb692a --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_object.rs @@ -0,0 +1,170 @@ +use super::{ + MetacallError, MetacallGetAttributeError, MetacallNull, MetacallSetAttributeError, + MetacallValue, +}; +use crate::{ + bindings::{ + metacall_object_get, metacall_object_set, metacall_value_destroy, metacall_value_to_object, + metacallv_object, + }, + cstring_enum, parsers, +}; +use std::ffi::c_void; + +// Used for documentation. +#[allow(unused_imports)] +use super::MetacallClass; + +#[derive(Debug)] +/// Represents Metacall Object. You can get this type when returned by a function or create one from +/// a class with [create_object](MetacallClass#method.create_object). +pub struct MetacallObject { + value: *mut c_void, + leak: bool, +} +unsafe impl Send for MetacallObject {} +unsafe impl Sync for MetacallObject {} +impl Clone for MetacallObject { + fn clone(&self) -> Self { + Self { + leak: true, + value: self.value, + } + } +} + +impl MetacallObject { + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Self { + Self { value, leak: false } + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Self { + Self { value, leak: true } + } + + fn get_attribute_inner( + &self, + name: impl ToString, + ) -> Result<*mut c_void, MetacallGetAttributeError> { + let c_name = cstring_enum!(name, MetacallGetAttributeError)?; + + Ok(unsafe { metacall_object_get(metacall_value_to_object(self.value), c_name.as_ptr()) }) + } + /// Gets attribute from an object without type casting([MetacallValue](MetacallValue)). + pub fn get_attribute_untyped( + &self, + name: impl ToString, + ) -> Result, MetacallGetAttributeError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.get_attribute_inner(name)?, + )) + } + /// Gets attribute from an object. + pub fn get_attribute( + &self, + name: impl ToString, + ) -> Result { + match parsers::raw_to_metacallobj::(self.get_attribute_inner(name)?) { + Ok(ret) => Ok(ret), + Err(original) => Err(MetacallGetAttributeError::FailedCasting(original)), + } + } + + /// Sets object attribute. + pub fn set_attribute( + &self, + key: impl ToString, + value: impl MetacallValue, + ) -> Result<(), MetacallSetAttributeError> { + let c_key = cstring_enum!(key, MetacallSetAttributeError)?; + let c_arg = parsers::metacallobj_to_raw(value); + if unsafe { + metacall_object_set(metacall_value_to_object(self.value), c_key.as_ptr(), c_arg) + } != 0 + { + return Err(MetacallSetAttributeError::SetAttributeFailure); + } + + unsafe { metacall_value_destroy(c_arg) }; + + Ok(()) + } + + fn call_method_inner( + &self, + key: impl ToString, + args: impl IntoIterator, + ) -> Result<*mut c_void, MetacallError> { + let c_key = cstring_enum!(key, MetacallError)?; + let mut c_args = parsers::metacallobj_to_raw_args(args); + let ret = unsafe { + metacallv_object( + metacall_value_to_object(self.value), + c_key.as_ptr(), + c_args.as_mut_ptr(), + c_args.len(), + ) + }; + + for c_arg in c_args { + unsafe { metacall_value_destroy(c_arg) }; + } + + Ok(ret) + } + /// Calls an object method witout type casting([MetacallValue](MetacallValue)). + pub fn call_method_untyped( + &self, + key: impl ToString, + args: impl IntoIterator, + ) -> Result, MetacallError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.call_method_inner::(key, args)?, + )) + } + /// Calls an object method witout type casting([MetacallValue](MetacallValue)) and + /// without passing arguments. + pub fn call_method_untyped_no_arg( + &self, + key: impl ToString, + ) -> Result, MetacallError> { + Ok(parsers::raw_to_metacallobj_untyped( + self.call_method_inner::(key, [])?, + )) + } + /// Calls an object method. + pub fn call_method( + &self, + key: impl ToString, + args: impl IntoIterator, + ) -> Result { + match parsers::raw_to_metacallobj::(self.call_method_inner::(key, args)?) { + Ok(ret) => Ok(ret), + Err(original) => Err(MetacallError::FailedCasting(original)), + } + } + /// Calls an object method without passing arguments. + pub fn call_method_no_arg( + &self, + key: impl ToString, + ) -> Result { + self.call_method::(key, []) + } + + #[doc(hidden)] + pub fn into_raw(mut self) -> *mut c_void { + self.leak = true; + + self.value + } +} + +impl Drop for MetacallObject { + fn drop(&mut self) { + if !self.leak { + unsafe { metacall_value_destroy(self.value) } + } + } +} diff --git a/source/ports/rs_port/src/types/metacall_pointer.rs b/source/ports/rs_port/src/types/metacall_pointer.rs new file mode 100644 index 0000000000..283d2200d3 --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_pointer.rs @@ -0,0 +1,98 @@ +use super::MetacallValue; +use crate::bindings::{metacall_value_create_ptr, metacall_value_destroy, metacall_value_to_ptr}; +use std::ffi::c_void; + +#[derive(Debug)] +/// Represents Metacall pointer. This type cannot be used in other languages. This type is highly +/// unsafe so be careful! +pub struct MetacallPointer { + leak: bool, + rust_value: *mut Box, + rust_value_leak: bool, + value: *mut c_void, +} +unsafe impl Send for MetacallPointer {} +unsafe impl Sync for MetacallPointer {} +impl Clone for MetacallPointer { + fn clone(&self) -> Self { + Self { + leak: true, + rust_value: self.rust_value.clone(), + rust_value_leak: true, + value: self.value, + } + } +} + +impl MetacallPointer { + fn get_rust_value_ptr(value: *mut c_void) -> *mut Box { + unsafe { metacall_value_to_ptr(value) as *mut Box } + } + + #[doc(hidden)] + pub fn new_raw(value: *mut c_void) -> Result> { + Ok(Self { + leak: false, + rust_value: Self::get_rust_value_ptr(value), + rust_value_leak: false, + value, + }) + } + + #[doc(hidden)] + pub fn new_raw_leak(value: *mut c_void) -> Result> { + Ok(Self { + leak: true, + rust_value: Self::get_rust_value_ptr(value), + rust_value_leak: false, + value, + }) + } + + /// Creates a new Metacall pointer and casts it to T. + pub fn new(ptr: impl MetacallValue) -> Result> { + let rust_value = Box::into_raw(Box::new(Box::new(ptr) as Box)); + + Ok(Self { + leak: false, + rust_value, + rust_value_leak: false, + value: unsafe { metacall_value_create_ptr(rust_value.cast()) }, + }) + } + + /// Consumes the Metacall pointer and returns ownership of the value without type + /// casting([MetacallValue](MetacallValue)). + pub fn get_value_untyped(mut self) -> Box { + self.rust_value_leak = true; + + unsafe { *Box::from_raw(self.rust_value) } + } + /// Consumes the Metacall pointer and returns ownership of the value. + pub fn get_value(self) -> Result> { + match self.get_value_untyped().downcast::() { + Ok(rust_value) => Ok(rust_value), + Err(original) => Err(original), + } + } + + #[doc(hidden)] + pub fn into_raw(mut self) -> *mut c_void { + self.leak = true; + self.rust_value_leak = true; + + self.value + } +} + +impl Drop for MetacallPointer { + fn drop(&mut self) { + if !self.leak { + unsafe { metacall_value_destroy(self.value) } + } + + if !self.rust_value_leak { + unsafe { drop(Box::from_raw(self.rust_value)) } + } + } +} diff --git a/source/ports/rs_port/src/types/metacall_value.rs b/source/ports/rs_port/src/types/metacall_value.rs new file mode 100644 index 0000000000..590635d10e --- /dev/null +++ b/source/ports/rs_port/src/types/metacall_value.rs @@ -0,0 +1,421 @@ +use super::{ + MetacallClass, MetacallException, MetacallFunction, MetacallFuture, MetacallNull, + MetacallObject, MetacallPointer, MetacallThrowable, +}; +use crate::{ + bindings::*, + cstring, + helpers::{MetacallClone, MetacallDowncast}, + match_metacall_value, match_metacall_value_all, parsers, +}; +use std::{ + collections::HashMap, + ffi::{c_char, c_int, c_void, CStr}, + fmt::Debug, + slice, +}; + +/// Trait of any possible object in Metacall. +/// Checkout [match_metacall_value](match_metacall_value) macro for +/// matching trait objects of this trait. +/// Also check [std implementors](#foreign-impls) and [other implementors](#implementors). +pub trait MetacallValue: MetacallClone + MetacallDowncast + Debug { + // It tries to convert the raw pointer to the value and return a trait object on failure + #[doc(hidden)] + fn from_metacall_raw(v: *mut c_void) -> Result> + where + Self: Sized, + { + let value = Self::from_metacall_raw_leak(v)?; + + unsafe { metacall_value_destroy(v) }; + + Ok(value) + } + // Same as `from_metacall_raw` but doesn't free the memory on drop and leaks + #[doc(hidden)] + fn from_metacall_raw_leak(v: *mut c_void) -> Result> + where + Self: Sized; + // It returns the enum index of Metacall Protocol in the core + #[doc(hidden)] + fn get_metacall_id() -> u32 + where + Self: Sized; + // It converts the value to a raw value known by the metacall core + #[doc(hidden)] + fn into_metacall_raw(self) -> *mut c_void; +} +#[doc = "Equivalent to Metacall boolean type."] +impl MetacallValue for bool { + fn get_metacall_id() -> u32 { + 0 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_bool(v) != 0 }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_bool((self as c_int).try_into().unwrap()) } + } +} +#[doc = "Equivalent to Metacall char type."] +impl MetacallValue for char { + fn get_metacall_id() -> u32 { + 1 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_char(v) as u8 as char }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_char(self as c_char) } + } +} +#[doc = "Equivalent to Metacall short type."] +impl MetacallValue for i16 { + fn get_metacall_id() -> u32 { + 2 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_short(v) }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_short(self) } + } +} +#[doc = "Equivalent to Metacall int type."] +impl MetacallValue for i32 { + fn get_metacall_id() -> u32 { + 3 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_int(v) }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_int(self) } + } +} +#[doc = "Equivalent to Metacall long type."] +impl MetacallValue for i64 { + fn get_metacall_id() -> u32 { + 4 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_long(v) }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_long(self) } + } +} +#[doc = "Equivalent to Metacall float type."] +impl MetacallValue for f32 { + fn get_metacall_id() -> u32 { + 5 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_float(v) }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_float(self) } + } +} +#[doc = "Equivalent to Metacall double type."] +impl MetacallValue for f64 { + fn get_metacall_id() -> u32 { + 6 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let value = unsafe { metacall_value_to_double(v) }; + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + unsafe { metacall_value_create_double(self) } + } +} +#[doc = "Equivalent to Metacall string type."] +impl MetacallValue for String { + fn get_metacall_id() -> u32 { + 7 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let c_str = unsafe { CStr::from_ptr(metacall_value_to_string(v)) }; + let value = String::from(c_str.to_str().unwrap()); + + Ok(value) + } + fn into_metacall_raw(self) -> *mut c_void { + let raw = cstring!(self.as_str()).unwrap(); + + unsafe { metacall_value_create_string(raw.as_ptr(), self.len()) } + } +} +#[doc = "Equivalent to Metacall buffer type."] +impl MetacallValue for Vec { + fn get_metacall_id() -> u32 { + 8 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(unsafe { + slice::from_raw_parts( + metacall_value_to_buffer(v) as *mut c_char, + metacall_value_size(v), + ) + .to_vec() + }) + } + fn into_metacall_raw(mut self) -> *mut c_void { + unsafe { metacall_value_create_buffer(self.as_mut_ptr() as *mut c_void, self.len()) } + } +} +#[doc = "Equivalent to Metacall array type."] +impl MetacallValue for Vec { + fn get_metacall_id() -> u32 { + 9 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + let count = unsafe { metacall_value_count(v) }; + let vec = unsafe { slice::from_raw_parts(metacall_value_to_array(v), count) } + .iter() + .map(|element| parsers::raw_to_metacallobj_leak(*element)) + .collect::, Box>>()?; + + Ok(vec) + } + fn into_metacall_raw(self) -> *mut c_void { + let mut array = self + .into_iter() + .map(|element| element.into_metacall_raw().cast_const()) + .collect::>(); + + unsafe { metacall_value_create_array(array.as_mut_ptr(), array.len()) } + } +} +#[doc = "Equivalent to Metacall map type."] +impl MetacallValue for HashMap { + fn get_metacall_id() -> u32 { + 10 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + unsafe { + let mut hashmap = HashMap::new(); + for map_value in + slice::from_raw_parts(metacall_value_to_map(v), metacall_value_count(v)).iter() + { + let m_pair = slice::from_raw_parts(metacall_value_to_array(*map_value), 2); + let key = match_metacall_value!(parsers::raw_to_metacallobj_untyped_leak(m_pair[0]), { + str: String => str, + num: i16 => num.to_string(), + num: i32 => num.to_string(), + num: i64 => num.to_string(), + num: f32 => num.to_string(), + num: f64 => num.to_string(), + _ => String::from("Invalid key!") + }); + let val = match parsers::raw_to_metacallobj_leak::(m_pair[1]) { + Ok(parsed) => parsed, + Err(original) => { + return Err(original); + } + }; + + hashmap.insert(key, val); + } + + Ok(hashmap) + } + } + fn into_metacall_raw(self) -> *mut c_void { + let mut hashmap: Vec<*const c_void> = self + .into_iter() + .map(|(key, value)| { + let key = key.into_metacall_raw(); + let value = value.into_metacall_raw(); + + unsafe { + metacall_value_create_array( + vec![key as *const c_void, value as *const c_void].as_mut_ptr(), + 2, + ) as *const c_void + } + }) + .collect(); + + unsafe { metacall_value_create_map(hashmap.as_mut_ptr(), hashmap.len()) } + } +} +#[doc = "Equivalent to Metacall pointer type."] +impl MetacallValue for MetacallPointer { + fn get_metacall_id() -> u32 { + 11 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Self::new_raw_leak(v) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Self::new_raw(v) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall future type."] +impl MetacallValue for MetacallFuture { + fn get_metacall_id() -> u32 { + 12 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall function type."] +impl MetacallValue for MetacallFunction { + fn get_metacall_id() -> u32 { + 13 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall null type."] +impl MetacallValue for MetacallNull { + fn get_metacall_id() -> u32 { + 14 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + unsafe { metacall_value_destroy(v) }; + + Ok(MetacallNull()) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Self::from_metacall_raw_leak(v) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall class type."] +impl MetacallValue for MetacallClass { + fn get_metacall_id() -> u32 { + 15 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall object type."] +impl MetacallValue for MetacallObject { + fn get_metacall_id() -> u32 { + 16 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall exception type."] +impl MetacallValue for MetacallException { + fn get_metacall_id() -> u32 { + 17 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc = "Equivalent to Metacall throwable type."] +impl MetacallValue for MetacallThrowable { + fn get_metacall_id() -> u32 { + 18 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(Self::new_raw_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(Self::new_raw(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + self.into_raw() + } +} +#[doc(hidden)] +impl MetacallValue for Box { + fn get_metacall_id() -> u32 { + // Something random + 100 + } + fn from_metacall_raw_leak(v: *mut c_void) -> Result> { + Ok(parsers::raw_to_metacallobj_untyped_leak(v)) + } + fn from_metacall_raw(v: *mut c_void) -> Result> { + Ok(parsers::raw_to_metacallobj_untyped(v)) + } + fn into_metacall_raw(self) -> *mut c_void { + match_metacall_value_all!( + self, + x, + x.into_metacall_raw(), + [ + bool, + char, + i16, + i32, + i64, + f32, + f64, + String, + Vec, + Vec>, + HashMap>, + MetacallPointer, + MetacallFuture, + MetacallFunction, + MetacallNull, + MetacallClass, + MetacallObject, + MetacallException, + MetacallThrowable, + Box + ] + ) + } +} diff --git a/source/ports/rs_port/src/prelude/mod.rs b/source/ports/rs_port/src/types/mod.rs similarity index 89% rename from source/ports/rs_port/src/prelude/mod.rs rename to source/ports/rs_port/src/types/mod.rs index ddc7ecd52b..cb04911de0 100644 --- a/source/ports/rs_port/src/prelude/mod.rs +++ b/source/ports/rs_port/src/types/mod.rs @@ -6,7 +6,7 @@ mod metacall_future; mod metacall_null; mod metacall_object; mod metacall_pointer; -mod object_protocol; +mod metacall_value; pub use metacall_class::*; pub use metacall_error::*; @@ -16,4 +16,4 @@ pub use metacall_future::*; pub use metacall_null::*; pub use metacall_object::*; pub use metacall_pointer::*; -pub use object_protocol::*; +pub use metacall_value::*; diff --git a/source/ports/rs_port/tests/inline_test.rs b/source/ports/rs_port/tests/inlines.rs similarity index 93% rename from source/ports/rs_port/tests/inline_test.rs rename to source/ports/rs_port/tests/inlines.rs index 93d543492e..54b50f68a7 100644 --- a/source/ports/rs_port/tests/inline_test.rs +++ b/source/ports/rs_port/tests/inlines.rs @@ -4,7 +4,7 @@ use metacall::{ }; #[test] -fn test_inline() { +fn inlines() { let _d = hooks::initialize().unwrap(); py! { diff --git a/source/ports/rs_port/tests/invalid_loader_test.rs b/source/ports/rs_port/tests/invalid_loaders.rs similarity index 65% rename from source/ports/rs_port/tests/invalid_loader_test.rs rename to source/ports/rs_port/tests/invalid_loaders.rs index a9b990ed5d..260af2b2be 100644 --- a/source/ports/rs_port/tests/invalid_loader_test.rs +++ b/source/ports/rs_port/tests/invalid_loaders.rs @@ -1,21 +1,25 @@ -use metacall::{hooks, loaders, prelude::MetacallLoaderError}; +use metacall::{hooks, loaders, MetacallLoaderError}; use std::env; #[test] -fn invalid_loader_test() { +fn invalid_loaders() { let _d = hooks::initialize().unwrap(); let scripts_dir = env::current_dir().unwrap().join("tests/scripts"); let inavlid_file = scripts_dir.join("whatever.yeet"); - let valid_file = scripts_dir.join("return_type_test.js"); + let valid_file = scripts_dir.join("script.js"); - if let Err(MetacallLoaderError::FileNotFound(_)) = loaders::from_file("random", inavlid_file) { + if let Err(MetacallLoaderError::FileNotFound(_)) = + loaders::from_single_file("random", inavlid_file) + { // Everything Ok } else { panic!("Expected the loader fail with `FileNotFound` error variant!"); } - if let Err(MetacallLoaderError::FromFileFailure) = loaders::from_file("random", valid_file) { + if let Err(MetacallLoaderError::FromFileFailure) = + loaders::from_single_file("random", valid_file) + { // Everything Ok } else { panic!("Expected the loader fail with `FromFileFailure` error variant!"); diff --git a/source/ports/rs_port/tests/loaders_test.rs b/source/ports/rs_port/tests/loaders.rs similarity index 95% rename from source/ports/rs_port/tests/loaders_test.rs rename to source/ports/rs_port/tests/loaders.rs index 3b79535961..e915f71881 100644 --- a/source/ports/rs_port/tests/loaders_test.rs +++ b/source/ports/rs_port/tests/loaders.rs @@ -40,7 +40,7 @@ fn load_from_file_test() { temp_js.write_all(SCRIPT2.as_bytes()).unwrap(); temp_js.flush().unwrap(); - loaders::from_file("node", temp_js_path).unwrap(); + loaders::from_single_file("node", temp_js_path).unwrap(); call_greet("load_from_file", 2); @@ -49,7 +49,7 @@ fn load_from_file_test() { } #[test] -fn loaders_test() { +fn loaders() { let _d = hooks::initialize().unwrap(); // Testing load_from_memory diff --git a/source/ports/rs_port/tests/return_type_test.rs b/source/ports/rs_port/tests/metacall.rs similarity index 53% rename from source/ports/rs_port/tests/return_type_test.rs rename to source/ports/rs_port/tests/metacall.rs index 1711eda3be..8b3e5e53c9 100644 --- a/source/ports/rs_port/tests/return_type_test.rs +++ b/source/ports/rs_port/tests/metacall.rs @@ -1,41 +1,38 @@ use metacall::{ - hooks, loaders, - prelude::{ - MetacallClass, MetacallException, MetacallFuture, MetacallNull, MetacallObject, - MetacallObjectProtocol, MetacallPointer, MetacallThrowable, - }, + hooks, loaders, MetacallClass, MetacallException, MetacallFunction, MetacallFuture, + MetacallNull, MetacallObject, MetacallPointer, MetacallThrowable, MetacallValue, }; use std::{collections::HashMap, env, fmt::Debug}; -fn generate_test( +fn generate_test( name: impl ToString, expected: T, -) { - let test = - if !(Box::new(expected.clone()) as Box).is::() { - ::metacall::metacall::(name, [expected.clone()]) - } else { - ::metacall::metacall_no_arg::(name) - }; +) -> T { + let test = if !(Box::new(expected.clone()) as Box).is::() { + ::metacall::metacall::(name, [expected.clone()]) + } else { + ::metacall::metacall_no_arg::(name) + }; match test { Ok(v) => { if v != expected { - invalid_return_value(expected, v); + invalid_return_value(expected.clone(), v); } } - Err(err) => invalid_return_type(expected, err), + Err(err) => invalid_return_type(expected.clone(), err), }; + + expected } -fn generate_test_custom_validation( +fn generate_test_custom_validation( name: impl ToString, expected_type: impl ToString, - expected_value: impl MetacallObjectProtocol + Clone, - validator: impl Fn(T), + expected_value: impl MetacallValue + Clone, + validator: impl FnOnce(T), ) { - let test = if !(Box::new(expected_value.clone()) as Box) - .is::() - { + let expected_value = Box::new(expected_value) as Box; + let test = if !expected_value.is::() { ::metacall::metacall::(name, [expected_value]) } else { ::metacall::metacall_no_arg::(name) @@ -61,8 +58,8 @@ fn invalid_return_value(expected: impl Debug, received: impl Debug) { } fn test_bool() { - generate_test::("test_bool", false); - generate_test::("test_bool", true); + generate_test::("return_the_argument_py", false); + generate_test::("return_the_argument_py", true); } fn test_char() { generate_test::("test_char", 'A'); @@ -84,11 +81,14 @@ fn test_double() { generate_test::("test_double", 1.2345 as f64); } fn test_string() { - generate_test::("test_string", String::from("hi there!")); + generate_test::( + "return_the_argument_py", + generate_test::("return_the_argument_py", String::from("hi there!")), + ); } fn test_buffer() { generate_test::>( - "test_buffer", + "return_the_argument_py", String::from("hi there!") .as_bytes() .iter() @@ -102,7 +102,7 @@ fn test_map() { expected_hashmap.insert(String::from("hello"), String::from("world")); generate_test_custom_validation::>( - "test_map", + "return_the_argument_py", "map", expected_hashmap.clone(), move |hashmap| { @@ -122,9 +122,9 @@ fn test_array() { let expected_array = vec![String::from("hi"), String::from("there!")]; generate_test_custom_validation::>( - "test_array", + "return_the_argument_py", "array", - MetacallNull(), + expected_array.clone(), |array| { for (index, expected_value) in expected_array.iter().enumerate() { if &array[index] != expected_value { @@ -138,17 +138,23 @@ fn test_array() { ); } fn test_pointer() { - metacall::metacall::( - "test_pointer", - [MetacallPointer::new(Box::new(String::from("hi there!"))).unwrap()], - ) - .unwrap(); + let expected_value = String::from("hi there!"); + + generate_test_custom_validation::( + "return_the_argument_py", + "pointer", + MetacallPointer::new(expected_value.clone()).unwrap(), + |pointer| { + let receieved_value = pointer.get_value::().unwrap(); + + if receieved_value != expected_value { + invalid_return_value(expected_value, receieved_value) + } + }, + ); } fn test_future() { - fn validate( - upper_result: Box, - upper_data: Box, - ) { + fn validate(upper_result: Box, upper_data: Box) { match upper_data.downcast::() { Ok(ret) => { if ret.as_str() != "data" { @@ -176,44 +182,64 @@ fn test_future() { "test_future_resolve", "future", MetacallNull(), - |future| { - fn resolve( - result: Box, - data: Box, - ) { - validate(result, data); - } + |upper_future| { + generate_test_custom_validation::( + "return_the_argument_py", + "future", + upper_future, + move |future| { + fn resolve(result: Box, data: Box) { + validate(result, data); + } - future.await_fut(Some(resolve), None, Some(String::from("data"))); + future.then(resolve).data(String::from("data")).await_fut(); + }, + ); }, ); generate_test_custom_validation::( "test_future_reject", "future", MetacallNull(), - |future| { - fn reject( - result: Box, - data: Box, - ) { - validate(result, data); - } + |upper_future| { + generate_test_custom_validation::( + "return_the_argument_py", + "future", + upper_future, + move |future| { + fn reject(result: Box, data: Box) { + validate(result, data); + } - future.await_fut(None, Some(reject), Some(String::from("data"))); + future.catch(reject).data(String::from("data")).await_fut(); + }, + ); + }, + ); +} +fn test_function() { + generate_test_custom_validation::( + "test_function", + "function", + MetacallNull(), + |upper_function| { + generate_test_custom_validation::( + "return_the_argument_py", + "function", + upper_function, + move |function| { + let ret = function.call_no_arg::().unwrap(); + if ret.as_str() == "hi there!" { + } else { + invalid_return_value("hi there!", ret); + } + }, + ); }, ); } -// fn test_function() { -// generate_test_custom_validation::("test_function", "function", |function| { -// let ret = function.call_no_arg::().unwrap(); -// if ret.as_str() == "hi there!" { -// } else { -// invalid_return_value("hi there!", ret); -// } -// }); -// } fn test_null() { - metacall::metacall_no_arg::("test_null").unwrap(); + metacall::metacall::("return_the_argument_py", [MetacallNull()]).unwrap(); } fn class_test_inner(class: MetacallClass) { let attr = class.get_attribute::("hi").unwrap(); @@ -242,7 +268,14 @@ fn test_class() { "test_class", "class", MetacallNull(), - class_test_inner, + |upper_class| { + generate_test_custom_validation::( + "return_the_argument_py", + "class", + upper_class, + class_test_inner, + ); + }, ); class_test_inner(MetacallClass::from_name("TestClass").unwrap()); @@ -267,10 +300,36 @@ fn test_object() { "test_object", "object", MetacallNull(), - object_test_inner, + |upper_object| { + generate_test_custom_validation::( + "return_the_argument_py", + "object", + upper_object, + object_test_inner, + ); + }, ); } fn test_exception() { + // generate_test_custom_validation::( + // "test_exception", + // "exception", + // MetacallNull(), + // |upper_exception| { + // generate_test_custom_validation::( + // "return_the_argument_js", + // "exception", + // upper_exception, + // move |exception| { + // let exception_message = exception.get_message(); + // if exception_message.as_str() != "hi there!" { + // invalid_return_value("hi there!", exception_message); + // } + // }, + // ); + // }, + // ); + generate_test_custom_validation::( "test_exception", "exception", @@ -284,6 +343,28 @@ fn test_exception() { ); } fn test_throwable() { + // generate_test_custom_validation::( + // "test_throwable", + // "throwable", + // MetacallNull(), + // |upper_throwable: MetacallThrowable| { + // generate_test_custom_validation::( + // "return_the_argument_py", + // "throwable", + // upper_throwable, + // |throwable| { + // let exception_message = throwable + // .get_value::() + // .unwrap() + // .get_message(); + // if exception_message.as_str() != "hi there!" { + // invalid_return_value("hi there!", exception_message); + // } + // }, + // ); + // }, + // ); + generate_test_custom_validation::( "test_throwable", "throwable", @@ -301,37 +382,37 @@ fn test_throwable() { } #[test] -fn return_type_test() { +fn metacall() { let _d = hooks::initialize().unwrap(); let tests_dir = env::current_dir().unwrap().join("tests/scripts"); - let js_test_file = tests_dir.join("return_type_test.js"); - let c_test_file = tests_dir.join("return_type_test.c"); - let py_test_file = tests_dir.join("return_type_test.py"); + let js_test_file = tests_dir.join("script.js"); + let c_test_file = tests_dir.join("script.c"); + let py_test_file = tests_dir.join("script.py"); - if let Ok(_) = loaders::from_file("c", c_test_file) { - test_char(); - test_short(); - test_int(); - test_long(); - test_float(); - test_double(); - } - if let Ok(_) = loaders::from_file("py", py_test_file) { + if let Ok(_) = loaders::from_single_file("py", py_test_file) { + test_buffer(); test_class(); test_object(); - test_buffer(); test_pointer(); } - if let Ok(_) = loaders::from_file("node", js_test_file) { - test_bool(); - test_string(); + if let Ok(_) = loaders::from_single_file("c", c_test_file) { + test_char(); + test_double(); + test_float(); + test_int(); + test_long(); + test_short(); + } + if let Ok(_) = loaders::from_single_file("node", js_test_file) { test_array(); + test_bool(); + test_exception(); + test_function(); test_map(); - // test_function(); test_null(); - test_exception(); + test_string(); test_throwable(); - test_future(); + // test_future(); } } diff --git a/source/ports/rs_port/tests/scripts/return_type_test.c b/source/ports/rs_port/tests/scripts/script.c similarity index 87% rename from source/ports/rs_port/tests/scripts/return_type_test.c rename to source/ports/rs_port/tests/scripts/script.c index 5e24c3089d..9d7a2391ea 100644 --- a/source/ports/rs_port/tests/scripts/return_type_test.c +++ b/source/ports/rs_port/tests/scripts/script.c @@ -1,6 +1,3 @@ -#include -#include - char test_char(char character) { return character; diff --git a/source/ports/rs_port/tests/scripts/return_type_test.js b/source/ports/rs_port/tests/scripts/script.js similarity index 52% rename from source/ports/rs_port/tests/scripts/return_type_test.js rename to source/ports/rs_port/tests/scripts/script.js index d3564e65c2..c2845773b9 100644 --- a/source/ports/rs_port/tests/scripts/return_type_test.js +++ b/source/ports/rs_port/tests/scripts/script.js @@ -1,51 +1,27 @@ -function test_bool(bool) { - return bool; -} - -function test_string(string) { - return string; -} - -function test_map(map) { - return map; -} - -function test_array(array) { - return ["hi", "there!"]; -} - async function test_future_resolve() { return "hi there!"; } async function test_future_reject() { return Promise.reject("hi there!").catch(() => {}); } - -function test_function(function_a) { - return function_a; -} - -function test_null(null_a) { - return null_a; +function test_function() { + return () => "hi there!"; } - function test_exception() { return Error("hi there!"); } - function test_throwable() { throw new Error("hi there!"); } +function return_the_argument_js(argument) { + return argument; +} module.exports = { - test_array, - test_bool, + return_the_argument_js, test_exception, test_function, test_future_reject, test_future_resolve, - test_map, - test_null, - test_string, test_throwable, }; diff --git a/source/ports/rs_port/tests/scripts/return_type_test.py b/source/ports/rs_port/tests/scripts/script.py similarity index 72% rename from source/ports/rs_port/tests/scripts/return_type_test.py rename to source/ports/rs_port/tests/scripts/script.py index afd3fb1848..507fa6cd65 100644 --- a/source/ports/rs_port/tests/scripts/return_type_test.py +++ b/source/ports/rs_port/tests/scripts/script.py @@ -5,18 +5,12 @@ class TestClass: @staticmethod def hi_function(): return "there!"; - def get_hi(self): return self.hi; def test_class(): return TestClass - def test_object(): return TestClass() - -def test_pointer(pointer): - return pointer - -def test_buffer(buffer): - return buffer \ No newline at end of file +def return_the_argument_py(argument): + return argument \ No newline at end of file