diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index dc4b219b..3b334479 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,3 @@ FROM mcr.microsoft.com/devcontainers/rust:latest RUN apt-get update && apt-get -y install libgl1-mesa-dev libasound2-dev -RUN rustup component add --toolchain nightly-x86_64-unknown-linux-gnu rustfmt diff --git a/core/common/src/abstractions/assets.rs b/core/common/src/abstractions/assets.rs index 5e388149..5a9399d9 100644 --- a/core/common/src/abstractions/assets.rs +++ b/core/common/src/abstractions/assets.rs @@ -23,19 +23,6 @@ pub struct AssetContext { dependencies: Graph, } -impl AssetContext { - /// Builds a new asset context from a root asset ID. - fn from_asset_id(asset_id: AssetId) -> Self { - let mut dependencies = Graph::default(); - let current_node = dependencies.add_node(asset_id); - - Self { - current_node, - dependencies, - } - } -} - /// Represents an asset that can be loaded and resolved. pub trait Asset { /// Resolves the dependencies of the asset. diff --git a/core/common/src/abstractions/callbacks.rs b/core/common/src/abstractions/callbacks.rs index 9b634222..dbd1b1fc 100644 --- a/core/common/src/abstractions/callbacks.rs +++ b/core/common/src/abstractions/callbacks.rs @@ -1,7 +1,14 @@ -use std::{marker::PhantomData, panic::RefUnwindSafe}; +use std::{ + fmt::{Debug, Formatter}, + marker::PhantomData, + panic::RefUnwindSafe, + sync::Arc, +}; use crate::{FromVariant, ToVariant, Variant}; +use super::VariantError; + /// An error when calling a script callback. #[derive(Debug)] pub enum CallbackError { @@ -9,7 +16,63 @@ pub enum CallbackError { InvalidArgument, } -/// A callback that can be called from a foreign environment. +/// A boxed callable function. +/// +/// This is a wrapper around a boxed function that can be called with a list of +/// [`Variant`] arguments and returns a [`Variant`] result. +#[derive(Clone)] +pub struct Callable(Arc Result>); + +impl Callable { + /// Creates a new boxed callable function from the given function. + pub fn new(function: impl Fn(&[Variant]) -> Result + 'static) -> Self { + Self(Arc::new(function)) + } + + /// Creates a new boxed callable function from the given [`Callback`]. + pub fn from_callback(callback: impl Callback + 'static) -> Self { + Self(Arc::new(move |args| callback.call(args))) + } + + /// Calls the boxed callable function with the given arguments. + pub fn call(&self, args: &[Variant]) -> Result { + let callable = self.0.as_ref(); + + callable(args) + } +} + +impl PartialEq for Callable { + #[inline] + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +impl Debug for Callable { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + write!(formatter, "Callable") + } +} + +impl ToVariant for Callable { + #[inline] + fn to_variant(&self) -> Variant { + Variant::Callable(self.clone()) + } +} + +impl FromVariant for Callable { + #[inline] + fn from_variant(variant: Variant) -> Result { + match variant { + Variant::Callable(callable) => Ok(callable), + _ => Err(VariantError::InvalidConversion), + } + } +} + +/// Represents a function signature that is callable. pub trait Callback: RefUnwindSafe { /// Calls the callback with the given arguments. fn call(&self, args: &[Variant]) -> Result; @@ -135,3 +198,16 @@ where Ok(result.to_variant()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_callable_function_creation_and_execution() { + let callable = Callable::from_callback(|a: u32, b: u32| a + b); + let result = callable.call(&[Variant::U32(1), Variant::U32(2)]).unwrap(); + + assert_eq!(result, Variant::U32(3)); + } +} diff --git a/core/common/src/abstractions/variant.rs b/core/common/src/abstractions/variant.rs index 8783407c..2ccf1729 100644 --- a/core/common/src/abstractions/variant.rs +++ b/core/common/src/abstractions/variant.rs @@ -1,6 +1,6 @@ -use std::{any::Any, cmp::Ordering}; +use std::{any::Any, cmp::Ordering, fmt::Debug}; -use crate::{Color, Color32, Pointer, Quat, StringName, Vec2, Vec3, Vec4}; +use crate::{Callable, Color, Color32, Pointer, Quat, StringName, Vec2, Vec3, Vec4}; /// An error that can occur when working with variants. #[derive(Debug)] @@ -47,6 +47,7 @@ pub enum VariantKind { Quat, Color, Color32, + Callable, UserData, } @@ -55,7 +56,7 @@ pub enum VariantKind { /// This is an abstraction over the different primitive types that are often /// shuffled around in the engine. It allows for a more generic API that can /// handle any type of value. -#[derive(Default, Debug)] +#[derive(Default, Debug, PartialEq)] pub enum Variant { #[default] Null, @@ -79,45 +80,10 @@ pub enum Variant { Quat(Quat), Color(Color), Color32(Color32), + Callable(Callable), UserData(Pointer), } -impl PartialEq for Variant { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Variant::Null, Variant::Null) => true, - (Variant::Bool(a), Variant::Bool(b)) => a == b, - (Variant::Char(a), Variant::Char(b)) => a == b, - (Variant::U8(a), Variant::U8(b)) => a == b, - (Variant::U16(a), Variant::U16(b)) => a == b, - (Variant::U32(a), Variant::U32(b)) => a == b, - (Variant::U64(a), Variant::U64(b)) => a == b, - (Variant::I8(a), Variant::I8(b)) => a == b, - (Variant::I16(a), Variant::I16(b)) => a == b, - (Variant::I32(a), Variant::I32(b)) => a == b, - (Variant::I64(a), Variant::I64(b)) => a == b, - (Variant::F32(a), Variant::F32(b)) => a == b, - (Variant::F64(a), Variant::F64(b)) => a == b, - (Variant::String(a), Variant::String(b)) => a == b, - (Variant::StringName(a), Variant::StringName(b)) => a == b, - (Variant::Vec2(a), Variant::Vec2(b)) => a == b, - (Variant::Vec3(a), Variant::Vec3(b)) => a == b, - (Variant::Vec4(a), Variant::Vec4(b)) => a == b, - (Variant::Quat(a), Variant::Quat(b)) => a == b, - (Variant::Color(a), Variant::Color(b)) => a == b, - (Variant::Color32(a), Variant::Color32(b)) => a == b, - (Variant::UserData(a), Variant::UserData(b)) => { - // pointer comparison - let ptr_a = &**a as *const dyn Any; - let ptr_b = &**b as *const dyn Any; - - std::ptr::addr_eq(ptr_a, ptr_b) - } - _ => false, - } - } -} - impl Clone for Variant { fn clone(&self) -> Self { match self { @@ -142,6 +108,7 @@ impl Clone for Variant { Variant::Quat(value) => Variant::Quat(value.clone()), Variant::Color(value) => Variant::Color(value.clone()), Variant::Color32(value) => Variant::Color32(value.clone()), + Variant::Callable(value) => Variant::Callable(value.clone()), Variant::UserData(value) => Variant::UserData(value.clone()), } } @@ -172,6 +139,7 @@ impl Variant { Variant::Quat(_) => VariantKind::Quat, Variant::Color(_) => VariantKind::Color, Variant::Color32(_) => VariantKind::Color32, + Variant::Callable(_) => VariantKind::Callable, Variant::UserData(_) => VariantKind::UserData, } } diff --git a/core/common/src/io/formats/binary.rs b/core/common/src/io/formats/binary.rs index ce258f31..fa50e500 100644 --- a/core/common/src/io/formats/binary.rs +++ b/core/common/src/io/formats/binary.rs @@ -189,7 +189,8 @@ impl FileFormat for BinaryFileFormat { stream.write_u8(value.b)?; stream.write_u8(value.a)?; } - Variant::UserData(_) => {} + Variant::Callable(_) => todo!(), + Variant::UserData(_) => todo!(), } } Chunk::Sequence(sequence) => { diff --git a/core/common/src/io/formats/json.rs b/core/common/src/io/formats/json.rs index 86e03e54..b6e79b55 100644 --- a/core/common/src/io/formats/json.rs +++ b/core/common/src/io/formats/json.rs @@ -60,7 +60,8 @@ impl FileFormat for JsonFileFormat { Variant::Color32(value) => { stream.write_string(&format!("[{}, {}, {}, {}]", value.r, value.g, value.b, value.a))?; } - Variant::UserData(_) => {} + Variant::Callable(_) => todo!(), + Variant::UserData(_) => todo!(), }, Chunk::Sequence(sequence) => { stream.write_string("[")?; diff --git a/core/common/src/lua.rs b/core/common/src/lua.rs index 0c11bc3c..89d0d023 100644 --- a/core/common/src/lua.rs +++ b/core/common/src/lua.rs @@ -6,8 +6,7 @@ pub use mlua::prelude::*; use crate::{ - Callback, Color, Color32, FromVariant, Pointer, Quat, ToStringName, ToVariant, ToVirtualPath, Variant, Vec2, Vec3, - Vec4, + Callable, Callback, CallbackError, Color, Color32, FromVariant, Pointer, Quat, ToStringName, ToVariant, ToVirtualPath, Variant, Vec2, Vec3, Vec4 }; /// A Lua scripting engine. @@ -136,6 +135,23 @@ impl<'lua> IntoLua<'lua> for Variant { Variant::Quat(value) => LuaQuat(value).into_lua(lua)?, Variant::Color(value) => LuaColor(value).into_lua(lua)?, Variant::Color32(value) => LuaColor32(value).into_lua(lua)?, + Variant::Callable(callable) => { + // create a Lua function that calls the callable + let function = lua.create_function(move |lua, args: LuaMultiValue| { + let args = args + .into_iter() + .map(|value| Variant::from_lua(value, lua)) + .collect::>>()?; + + let result = callable.call(&args).map_err(|error| { + LuaError::RuntimeError(format!("An error occurred calling a function, {:?}", error)) + })?; + + Ok(result.into_lua(lua)?) + })?; + + LuaValue::Function(function) + }, Variant::UserData(value) => LuaValue::LightUserData(LuaLightUserData(value.into_void())), }) } @@ -151,6 +167,15 @@ impl<'lua> FromLua<'lua> for Variant { LuaValue::Number(value) => Variant::F64(value), LuaValue::String(value) => Variant::String(value.to_str()?.to_string()), LuaValue::Table(value) => Variant::UserData(Pointer::new(value.into_owned())), + LuaValue::Function(function) => { + // create a callable that calls the Lua function + let function = function.into_owned(); + let callable = Callable::new(move |args| { + function.call(args).map_err(|error| CallbackError::ExecutionError(error.to_string())) + }); + + Variant::Callable(callable) + }, LuaValue::LightUserData(value) => Variant::UserData(Pointer::from_raw_mut(value.0)), LuaValue::UserData(value) => match () { _ if value.is::() => Variant::Vec2(value.borrow::()?.0), diff --git a/core/common/src/memory/pointer.rs b/core/common/src/memory/pointer.rs index 6cb1bfc2..a3ecdddd 100644 --- a/core/common/src/memory/pointer.rs +++ b/core/common/src/memory/pointer.rs @@ -98,6 +98,13 @@ impl Pointer { } } +/// Pointer value equality. +impl PartialEq for Pointer { + fn eq(&self, other: &Self) -> bool { + std::ptr::addr_eq(self.ptr, other.ptr) + } +} + /// Allow printing of the pointer. impl Debug for Pointer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {