diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 13e05c4e..dc84f80d 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -1,4 +1,9 @@ -use crate::Engine; +use core::marker::PhantomData; + +use crate::{ + func::SendSync, types::dynamic::Variant, Engine, Identifier, RegisterNativeFunction, + RhaiResultOf, +}; /// Trait to build a custom type for use with the [`Engine`]. /// i.e. register the type, getters, setters, methods, etc... @@ -6,7 +11,7 @@ use crate::Engine; /// # Example /// /// ``` -/// use rhai::{Engine, RhaiCustomType}; +/// use rhai::{CustomType, TypeBuilder, Engine}; /// /// #[derive(Debug, Clone, Eq, PartialEq)] /// struct TestStruct { @@ -20,14 +25,21 @@ use crate::Engine; /// fn update(&mut self, offset: i64) { /// self.field += offset; /// } +/// fn get_value(&mut self) -> i64 { +/// self.field +/// } +/// fn set_value(&mut self, value: i64) { +/// self.field = value; +/// } /// } /// -/// impl RhaiCustomType for TestStruct { -/// fn build(engine: &mut Engine) { -/// engine -/// .register_type::() -/// .register_fn("new_ts", Self::new) -/// .register_fn("update", Self::update); +/// impl CustomType for TestStruct { +/// fn build(mut builder: TypeBuilder) { +/// builder +/// .with_name("TestStruct") +/// .with_fn("new_ts", Self::new) +/// .with_fn("update", Self::update) +/// .with_get_set("value", Self::get_value, Self::set_value); /// } /// } /// @@ -38,18 +50,25 @@ use crate::Engine; /// // Register API for the custom type. /// engine.build_type::(); /// +/// /// # #[cfg(not(feature = "no_object"))] /// assert_eq!( /// engine.eval::("let x = new_ts(); x.update(41); x")?, /// TestStruct { field: 42 } /// ); +/// +/// # #[cfg(not(feature = "no_object"))] +/// assert_eq!( +/// engine.eval::("let x = new_ts(); x.value = 5 + x.value; x")?, +/// TestStruct { field: 6 } +/// ); /// # Ok(()) /// # } /// ``` -pub trait CustomType { +pub trait CustomType: Variant + Clone { /// Builds the custom type for use with the [`Engine`]. /// i.e. register the type, getters, setters, methods, etc... - fn build(engine: &mut Engine); + fn build(builder: TypeBuilder); } impl Engine { @@ -61,7 +80,158 @@ impl Engine { where T: CustomType, { - T::build(self); + T::build(TypeBuilder::new(self)); self } } + +#[allow(missing_docs)] // TODO: add docs +pub struct TypeBuilder<'a, T> +where + T: Variant + Clone, +{ + engine: &'a mut Engine, + name: Option<&'static str>, + _marker: PhantomData, +} + +#[allow(missing_docs)] // TODO: add docs +impl<'a, T> TypeBuilder<'a, T> +where + T: Variant + Clone, +{ + pub(crate) fn new(engine: &'a mut Engine) -> Self { + Self { + engine, + name: None, + _marker: PhantomData::default(), + } + } + + pub fn build(self) { + /* empty */ + } + + pub fn with_name(&mut self, name: &'static str) -> &mut Self { + self.name = Some(name); + self + } + + pub fn with_fn(&mut self, name: N, method: F) -> &mut Self + where + N: AsRef + Into, + F: RegisterNativeFunction, + { + self.engine.register_fn(name, method); + self + } + + pub fn with_result_fn(&mut self, name: N, method: F) -> &mut Self + where + N: AsRef + Into, + F: RegisterNativeFunction>, + { + self.engine.register_result_fn(name, method); + self + } + + pub fn with_get( + &mut self, + name: impl AsRef, + get_fn: impl Fn(&mut T) -> V + SendSync + 'static, + ) -> &mut Self { + self.engine.register_get(name, get_fn); + self + } + + pub fn with_get_result( + &mut self, + name: impl AsRef, + get_fn: impl Fn(&mut T) -> RhaiResultOf + SendSync + 'static, + ) -> &mut Self { + self.engine.register_get_result(name, get_fn); + self + } + + pub fn with_set( + &mut self, + name: impl AsRef, + set_fn: impl Fn(&mut T, V) + SendSync + 'static, + ) -> &mut Self { + self.engine.register_set(name, set_fn); + self + } + + pub fn with_set_result( + &mut self, + name: impl AsRef, + set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static, + ) -> &mut Self { + self.engine.register_set_result(name, set_fn); + self + } + + pub fn with_get_set( + &mut self, + name: impl AsRef, + get_fn: impl Fn(&mut T) -> V + SendSync + 'static, + set_fn: impl Fn(&mut T, V) + SendSync + 'static, + ) -> &mut Self { + self.engine.register_get_set(name, get_fn, set_fn); + self + } + + pub fn with_indexer_get( + &mut self, + get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, + ) -> &mut Self { + self.engine.register_indexer_get(get_fn); + self + } + + pub fn with_indexer_get_result( + &mut self, + get_fn: impl Fn(&mut T, X) -> RhaiResultOf + SendSync + 'static, + ) -> &mut Self { + self.engine.register_indexer_get_result(get_fn); + self + } + + pub fn with_indexer_set( + &mut self, + set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, + ) -> &mut Self { + self.engine.register_indexer_set(set_fn); + self + } + + pub fn with_indexer_set_result( + &mut self, + set_fn: impl Fn(&mut T, X, V) -> RhaiResultOf<()> + SendSync + 'static, + ) -> &mut Self { + self.engine.register_indexer_set_result(set_fn); + self + } + + pub fn with_indexer_get_set( + &mut self, + get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, + set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, + ) -> &mut Self { + self.engine.register_indexer_get_set(get_fn, set_fn); + self + } +} + +impl<'a, T> Drop for TypeBuilder<'a, T> +where + T: Variant + Clone, +{ + fn drop(&mut self) { + if let Some(name) = self.name { + self.engine.register_type_with_name::(name); + } else { + self.engine.register_type::(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index cf7d0a04..875e1cad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,7 +166,10 @@ type ExclusiveRange = std::ops::Range; /// An inclusive integer range. type InclusiveRange = std::ops::RangeInclusive; -pub use api::{build_type::CustomType, events::VarDefInfo}; +pub use api::{ + build_type::{CustomType, TypeBuilder}, + events::VarDefInfo, +}; pub use ast::{FnAccess, AST}; pub use engine::{Engine, OP_CONTAINS, OP_EQUALS}; pub use eval::EvalContext; diff --git a/tests/build_type.rs b/tests/build_type.rs new file mode 100644 index 00000000..76d1bcac --- /dev/null +++ b/tests/build_type.rs @@ -0,0 +1,118 @@ +use rhai::{CustomType, Engine, EvalAltResult, Position, TypeBuilder}; + +#[test] +fn build_type() -> Result<(), Box> { + #[derive(Debug, Clone, PartialEq)] + struct Vec3 { + x: i64, + y: i64, + z: i64, + } + + impl Vec3 { + fn new(x: i64, y: i64, z: i64) -> Self { + Self { x, y, z } + } + fn get_x(&mut self) -> i64 { + self.x + } + fn set_x(&mut self, x: i64) { + self.x = x + } + fn get_y(&mut self) -> i64 { + self.y + } + fn set_y(&mut self, y: i64) { + self.y = y + } + fn get_z(&mut self) -> i64 { + self.z + } + fn set_z(&mut self, z: i64) { + self.z = z + } + fn get_component(&mut self, idx: i64) -> Result> { + match idx { + 0 => Ok(self.x), + 1 => Ok(self.y), + 2 => Ok(self.z), + _ => Err(Box::new(EvalAltResult::ErrorIndexNotFound( + idx.into(), + Position::NONE, + ))), + } + } + } + + impl CustomType for Vec3 { + fn build(mut builder: TypeBuilder) { + builder + .with_name("Vec3") + .with_fn("vec3", Self::new) + .with_get_set("x", Self::get_x, Self::set_x) + .with_get_set("y", Self::get_y, Self::set_y) + .with_get_set("z", Self::get_z, Self::set_z) + .with_indexer_get_result(Self::get_component); + } + } + + let mut engine = Engine::new(); + engine.build_type::(); + + assert_eq!( + engine.eval::( + r#" + let v = vec3(1, 2, 3); + v +"#, + )?, + Vec3::new(1, 2, 3), + ); + assert_eq!( + engine.eval::( + r#" + let v = vec3(1, 2, 3); + v.x +"#, + )?, + 1, + ); + assert_eq!( + engine.eval::( + r#" + let v = vec3(1, 2, 3); + v.y +"#, + )?, + 2, + ); + assert_eq!( + engine.eval::( + r#" + let v = vec3(1, 2, 3); + v.z +"#, + )?, + 3, + ); + assert!(engine.eval::( + r#" + let v = vec3(1, 2, 3); + v.x == v[0] && v.y == v[1] && v.z == v[2] +"#, + )?); + assert_eq!( + engine.eval::( + r#" + let v = vec3(1, 2, 3); + v.x = 5; + v.y = 6; + v.z = 7; + v +"#, + )?, + Vec3::new(5, 6, 7), + ); + + Ok(()) +}