Skip to content

Commit

Permalink
Merge pull request #423 from devraymondsh/develop
Browse files Browse the repository at this point in the history
Improve rs_port stage 4
  • Loading branch information
viferga authored Apr 18, 2023
2 parents 1168467 + 1ba74dd commit 740022c
Show file tree
Hide file tree
Showing 35 changed files with 2,062 additions and 1,440 deletions.
17 changes: 8 additions & 9 deletions source/ports/rs_port/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <vic798@gmail.com>", "Swarnim Arun <swarnimarun11@gmail.com>"]
authors = ["Mahdi Sharifi <devraymondsh@gmail.com>", "Vicente Eduardo Ferrer Garcia <vic798@gmail.com>", "Swarnim Arun <swarnimarun11@gmail.com>"]
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"]}
16 changes: 9 additions & 7 deletions source/ports/rs_port/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<f64>("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);
}
```
2 changes: 1 addition & 1 deletion source/ports/rs_port/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion source/ports/rs_port/inline/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
115 changes: 115 additions & 0 deletions source/ports/rs_port/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::types::MetacallValue;
use std::any::Any;

pub trait MetacallDowncast: Any {
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: Any> MetacallDowncast for T {
fn into_any(self: Box<Self>) -> Box<dyn Any> {
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<T: MetacallValue>(&self) -> bool {
MetacallDowncast::as_any(self).is::<T>()
}

/// Downcasts the inner value of the trait object and returns the ownership.
pub fn downcast<T: MetacallValue>(self: Box<Self>) -> Result<T, Box<Self>> {
if self.is::<T>() {
Ok(*MetacallDowncast::into_any(self).downcast::<T>().unwrap())
} else {
Err(self)
}
}

/// Downcasts the inner value of the trait object and returns a reference.
pub fn downcast_ref<T: MetacallValue>(&self) -> Option<&T> {
MetacallDowncast::as_any(self).downcast_ref::<T>()
}

/// Downcasts the inner value of the trait object and returns a mutable reference.
pub fn downcast_mut<T: MetacallValue>(&mut self) -> Option<&mut T> {
MetacallDowncast::as_any_mut(self).downcast_mut::<T>()
}
}

pub trait MetacallSealed {}
impl<T: Clone> MetacallSealed for T {}
impl MetacallSealed for str {}
impl<T: Clone> MetacallSealed for [T] {}

pub fn clone_box<T>(t: &T) -> Box<T>
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 = <T as MetacallClone>::clone_box(t);

Box::from_raw(fat_ptr as *mut T)
}
}

pub trait MetacallClone: MetacallSealed {
fn clone_box(&self) -> *mut ();
}
impl<T> MetacallClone for T
where
T: Clone,
{
fn clone_box(&self) -> *mut () {
Box::<T>::into_raw(Box::new(self.clone())) as *mut ()
}
}

impl MetacallClone for str {
fn clone_box(&self) -> *mut () {
Box::<str>::into_raw(Box::from(self)) as *mut ()
}
}
impl<T> 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<dyn MetacallValue + 'c> {
fn clone(&self) -> Self {
clone_box(&**self)
}
}
impl<'c> Clone for Box<dyn MetacallValue + Send + 'c> {
fn clone(&self) -> Self {
clone_box(&**self)
}
}
impl<'c> Clone for Box<dyn MetacallValue + Sync + 'c> {
fn clone(&self) -> Self {
clone_box(&**self)
}
}
impl<'c> Clone for Box<dyn MetacallValue + Send + Sync + 'c> {
fn clone(&self) -> Self {
clone_box(&**self)
}
}

pub fn metacall_implementer_to_traitobj(v: impl MetacallValue) -> Box<dyn MetacallValue> {
Box::new(v) as Box<dyn MetacallValue>
}
11 changes: 10 additions & 1 deletion source/ports/rs_port/src/hooks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
bindings::{metacall_destroy, metacall_initialize},
prelude::MetacallInitError,
types::MetacallInitError,
};
use std::ffi::c_int;

Expand All @@ -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<MetacallAutoDestroy, MetacallInitError> {
if initialize_manually() != 0 {
return Err(MetacallInitError::new());
Expand Down
89 changes: 84 additions & 5 deletions source/ports/rs_port/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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::<f64>("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;
28 changes: 24 additions & 4 deletions source/ports/rs_port/src/loaders.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
use crate::{
bindings::{metacall_load_from_file, metacall_load_from_memory},
cstring_enum,
prelude::MetacallLoaderError,
types::MetacallLoaderError,
};
use std::{
ffi::CString,
path::{Path, PathBuf},
ptr,
};

pub fn from_file(tag: impl ToString, script: impl AsRef<Path>) -> 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<Path>,
) -> 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<Item = impl AsRef<Path>>,
) -> Result<(), MetacallLoaderError> {
Expand Down Expand Up @@ -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)?;
Expand Down
Loading

0 comments on commit 740022c

Please sign in to comment.