Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions source/ports/rs_port/src/types/metacall_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,157 @@ impl MetaCallValue for f64 {
unsafe { metacall_value_create_double(self) }
}
}

//Equivalent to MetaCall long type.
impl MetaCallValue for u8 {
fn get_metacall_id() -> metacall_value_id {
metacall_value_id::METACALL_LONG
}

fn from_metacall_raw_leak(v: *mut c_void) -> Result<Self, Box<dyn MetaCallValue>> {
let id = unsafe { metacall_value_id(v) };

let value_i64 = match id {
metacall_value_id::METACALL_SHORT => unsafe {
metacall_value_to_short(v) as i64
},
metacall_value_id::METACALL_INT => unsafe {
metacall_value_to_int(v) as i64
},
metacall_value_id::METACALL_LONG => unsafe {
metacall_value_to_long(v)
},
_ => return Err(Box::new(0u8)),
};


if value_i64 < 0 || value_i64 > u8::MAX as i64 {
return Err(Box::new(value_i64));
}

Ok(value_i64 as u8)
}

fn into_metacall_raw(self) -> *mut c_void {
// safer to send as LONG (i64)
unsafe { metacall_value_create_long(self as i64) }
}
}

//Equivalent to MetaCall long type.
impl MetaCallValue for u16 {
fn get_metacall_id() -> metacall_value_id {
metacall_value_id::METACALL_LONG
}

fn from_metacall_raw_leak(v: *mut c_void) -> Result<Self, Box<dyn MetaCallValue>> {
let id = unsafe { metacall_value_id(v) };

let value_i64 = match id {
metacall_value_id::METACALL_SHORT => unsafe {
metacall_value_to_short(v) as i64
},
metacall_value_id::METACALL_INT => unsafe {
metacall_value_to_int(v) as i64
},
metacall_value_id::METACALL_LONG => unsafe {
metacall_value_to_long(v)
},
_ => return Err(Box::new(0u16)),
};


if value_i64 < 0 || value_i64 > u16::MAX as i64 {
return Err(Box::new(value_i64));
}

Ok(value_i64 as u16)
}

fn into_metacall_raw(self) -> *mut c_void {
// safer to send as LONG (i64)
unsafe { metacall_value_create_long(self as i64) }
}
}

//Equivalent to MetaCall long type.
impl MetaCallValue for u32 {
fn get_metacall_id() -> metacall_value_id {
metacall_value_id::METACALL_LONG
}

fn from_metacall_raw_leak(v: *mut c_void) -> Result<Self, Box<dyn MetaCallValue>> {
let id = unsafe { metacall_value_id(v) };

let value_i64 = match id {
metacall_value_id::METACALL_INT => unsafe {
metacall_value_to_int(v) as i64
},
metacall_value_id::METACALL_LONG => unsafe {
metacall_value_to_long(v)
},
_ => {
return Err(Box::new(0u32));
}
};

if value_i64 < 0 {
return Err(Box::new(value_i64));
}

if value_i64 > u32::MAX as i64 {
return Err(Box::new(value_i64));
}

Ok(value_i64 as u32)
}

fn into_metacall_raw(self) -> *mut c_void {
// safer to send as LONG (i64)
unsafe { metacall_value_create_long(self as i64) }
}
}

// TODO:
// MetaCall does not provide an unsigned 64-bit type. Since METACALL_LONG maps to i64, values above i64::MAX, cannot be represented safely.
// Proper support for u64 may require: Adding i128 support in MetaCall
// For now, u64 support is intentionally not implemented. Only emits warning when value exceeds i64::MAX
impl MetaCallValue for u64 {
fn get_metacall_id() -> metacall_value_id {
metacall_value_id::METACALL_LONG
}

fn from_metacall_raw_leak(v: *mut c_void) -> Result<Self, Box<dyn MetaCallValue>> {
let id = unsafe { metacall_value_id(v) };

let value_i64 = match id {
metacall_value_id::METACALL_INT => unsafe {
metacall_value_to_int(v) as i64
},
metacall_value_id::METACALL_LONG => unsafe {
metacall_value_to_long(v)
},
_ => {
return Err(Box::new(0u64));
}
};

if value_i64 < 0 {
return Err(Box::new(value_i64));
}

Ok(value_i64 as u64)
}

fn into_metacall_raw(self) -> *mut c_void {
if self > i64::MAX as u64 {
eprintln!("Warning: u64 value exceeds i64::MAX, precision loss possible");
}

unsafe { metacall_value_create_long(self as i64) }
}
}

/// Equivalent to MetaCall string type.
impl MetaCallValue for String {
fn get_metacall_id() -> metacall_value_id {
Expand Down
84 changes: 84 additions & 0 deletions source/ports/rs_port/tests/metacall_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ use metacall::{
};
use std::{any::Any, collections::HashMap, env, fmt::Debug};

use std::sync::Once;

static INIT: Once = Once::new();

fn ensure_runtime() {
INIT.call_once(|| {
initialize().unwrap();

load::from_file(
Tag::Python,
["tests/scripts/script.py"],
None,
)
.unwrap();
});
}

fn generate_test<T: MetaCallValue + PartialEq + Debug + Clone>(
name: impl ToString,
expected: T,
Expand Down Expand Up @@ -82,6 +99,73 @@ fn test_float() {
fn test_double() {
generate_test::<f64>("test_double", 1.2345_f64);
}

#[test]
fn test_u8_valid() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u8>("return_small_number");
assert_eq!(result.unwrap(), 42u8);
}

#[test]
fn test_u8_negative() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u8>("return_negative_int");
assert!(result.is_err(), "Negative value should not convert to u8");
}

#[test]
fn test_u16_valid() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u16>("return_small_number");
assert_eq!(result.unwrap(), 42u16);
}

#[test]
fn test_u16_overflow() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u16>("return_large_number");
assert!(result.is_err(), "Overflow should not convert to u16");
}

#[test]
fn test_u32_overflow() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u32>("return_large_number");
assert!(result.is_err());
}


#[test]
fn test_u32_negative() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u32>("return_negative_int");

assert!(result.is_err(), "Negative C int should not convert to u32");
}

#[test]
fn test_u64_valid() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u64>("return_large_number");
assert_eq!(result.unwrap(), i64::MAX as u64);
}

#[test]
fn test_u64_negative() {
ensure_runtime();

let result = ::metacall::metacall_no_arg::<u64>("return_negative_int");
assert!(result.is_err(), "Negative value should not convert to u64");
}

fn test_mixed_numbers() {
let result = ::metacall::metacall::<i64>(
"test_mixed_numbers",
Expand Down
8 changes: 7 additions & 1 deletion source/ports/rs_port/tests/scripts/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ def test_class():
def test_object():
return TestClass()
def return_the_argument_py(argument):
return argument
return argument
def return_small_number():
return 42
def return_negative_int():
return -1
def return_large_number():
return 9223372036854775807 # i64::MAX
Loading