Ruby support #1

Open
jaroslaw wants to merge 165 commits from ruby into main
3 changed files with 36 additions and 17 deletions
Showing only changes of commit e15fb74e64 - Show all commits

View file

@ -358,7 +358,7 @@ pub trait Runtime: Resource + Default {
name: &str, name: &str,
script_data: &mut Self::ScriptData, script_data: &mut Self::ScriptData,
entity: Entity, entity: Entity,
args: impl for<'a> FuncArgs<'a, Self::Value, Self>, args: impl for<'a> FuncArgs<'a, Self::Value, Self> + Send + 'static,
) -> Result<Self::Value, ScriptingError>; ) -> Result<Self::Value, ScriptingError>;
/// Calls a function by value defined within the runtime in the context of the /// Calls a function by value defined within the runtime in the context of the

View file

@ -3,6 +3,7 @@
// TODO: add tests for every runtime for return value // TODO: add tests for every runtime for return value
// TODO: consider dropping magnus // TODO: consider dropping magnus
// TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions // TODO: unwinding https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions
// TODO: maybe unify api and call non thread methods non_send
use std::{ use std::{
collections::HashMap, collections::HashMap,
ffi::{c_void, CString}, ffi::{c_void, CString},
@ -131,10 +132,10 @@ pub struct RubyValue(magnus::value::Opaque<magnus::Value>);
impl RubyValue { impl RubyValue {
fn nil(ruby: &Ruby) -> Self { fn nil(ruby: &Ruby) -> Self {
Self::new(ruby.qnil().as_value(), ruby) Self::new(ruby.qnil().as_value())
} }
fn new(value: magnus::Value, ruby: &Ruby) -> Self { fn new(value: magnus::Value) -> Self {
Self(magnus::value::Opaque::from(value)) Self(magnus::value::Opaque::from(value))
} }
} }
@ -191,7 +192,7 @@ impl Runtime for RubyRuntime {
.unwrap() .unwrap()
.execute(Box::new(move |ruby| { .execute(Box::new(move |ruby| {
let value = ruby.eval::<magnus::value::Value>(&script).unwrap(); let value = ruby.eval::<magnus::value::Value>(&script).unwrap();
RubyValue::new(value, &ruby) RubyValue::new(value)
})); }));
Ok(RubyScriptData) Ok(RubyScriptData)
} }
@ -235,7 +236,7 @@ impl Runtime for RubyRuntime {
f( f(
(), (),
args.iter() args.iter()
.map(|arg| RubyValue::new(arg.into_value(), &ruby)) .map(|arg| RubyValue::new(arg.into_value()))
.collect(), .collect(),
) )
.expect("failed to call callback"); .expect("failed to call callback");
@ -258,7 +259,7 @@ impl Runtime for RubyRuntime {
name: &str, name: &str,
_script_data: &mut Self::ScriptData, _script_data: &mut Self::ScriptData,
_entity: bevy::prelude::Entity, _entity: bevy::prelude::Entity,
_args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self> + Send + 'static,
) -> Result<Self::Value, crate::ScriptingError> { ) -> Result<Self::Value, crate::ScriptingError> {
let name = name.to_string(); let name = name.to_string();
Ok(self Ok(self
@ -266,7 +267,12 @@ impl Runtime for RubyRuntime {
.as_ref() .as_ref()
.unwrap() .unwrap()
.execute(Box::new(move |ruby| { .execute(Box::new(move |ruby| {
let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap(); let args: Vec<_> = args
.parse(&ruby)
.into_iter()
.map(|a| ruby.get_inner(a.0))
.collect();
let _: magnus::Value = ruby.class_object().funcall(name, args.as_slice()).unwrap();
RubyValue::nil(&ruby) RubyValue::nil(&ruby)
}))) })))
} }
@ -297,8 +303,8 @@ impl<T: TryConvert> FromRuntimeValueWithEngine<'_, RubyRuntime> for T {
} }
impl<T: IntoValue> IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T { impl<T: IntoValue> IntoRuntimeValueWithEngine<'_, T, RubyRuntime> for T {
fn into_runtime_value_with_engine(value: T, engine: &magnus::Ruby) -> RubyValue { fn into_runtime_value_with_engine(value: T, _engine: &magnus::Ruby) -> RubyValue {
RubyValue::new(value.into_value(), engine) RubyValue::new(value.into_value())
} }
} }
@ -308,9 +314,11 @@ impl FuncArgs<'_, RubyValue, RubyRuntime> for () {
} }
} }
impl<T> FuncArgs<'_, RubyValue, RubyRuntime> for Vec<T> { impl<T: IntoValue> FuncArgs<'_, RubyValue, RubyRuntime> for Vec<T> {
fn parse(self, engine: &magnus::Ruby) -> Vec<RubyValue> { fn parse(self, engine: &magnus::Ruby) -> Vec<RubyValue> {
self.into_iter().map(|_x| RubyValue::nil(engine)).collect() self.into_iter()
.map(|x| RubyValue::new(x.into_value()))
.collect()
} }
} }

View file

@ -502,17 +502,28 @@ mod ruby_tests {
}) })
} }
fn assert_state_key_value_i32(_world: &World, _entity_id: Entity, _key: &str, _value: i32) { fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) {
todo!(); let runtime = world.get_resource::<RubyRuntime>().unwrap();
let key = key.to_string();
runtime.with_engine_thread(move |engine| {
let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap();
let res: i32 = state.funcall_public("[]", (key,)).unwrap();
assert_eq!(res, value)
})
} }
fn assert_state_key_value_string( fn assert_state_key_value_string(
_world: &World, world: &World,
_entity_id: Entity, _entity_id: Entity,
_key: &str, key: &str,
_value: &str, value: &str,
) { ) {
todo!(); let runtime = world.get_resource::<RubyRuntime>().unwrap();
runtime.with_engine(|engine| {
let state: magnus::value::Value = engine.class_object().const_get("STATE").unwrap();
let res: String = state.funcall_public("[]", (key,)).unwrap();
assert_eq!(res, value);
});
} }
} }