Merge branch 'type_builder'

This commit is contained in:
Tristan Guichaoua 2022-08-09 10:18:49 +02:00
commit eabce6da83
3 changed files with 393 additions and 13 deletions

View File

@ -1,12 +1,17 @@
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`]. /// Trait to build a custom type for use with the [`Engine`].
/// i.e. register the type, getters, setters, methods, etc... /// i.e. register the type and its getters, setters, methods, etc...
/// ///
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use rhai::{Engine, CustomType}; /// use rhai::{CustomType, TypeBuilder, Engine};
/// ///
/// #[derive(Debug, Clone, Eq, PartialEq)] /// #[derive(Debug, Clone, Eq, PartialEq)]
/// struct TestStruct { /// struct TestStruct {
@ -20,14 +25,21 @@ use crate::Engine;
/// fn update(&mut self, offset: i64) { /// fn update(&mut self, offset: i64) {
/// self.field += offset; /// self.field += offset;
/// } /// }
/// fn get_value(&mut self) -> i64 {
/// self.field
/// }
/// fn set_value(&mut self, value: i64) {
/// self.field = value;
/// }
/// } /// }
/// ///
/// impl CustomType for TestStruct { /// impl CustomType for TestStruct {
/// fn build(engine: &mut Engine) { /// fn build(mut builder: TypeBuilder<Self>) {
/// engine /// builder
/// .register_type::<Self>() /// .with_name("TestStruct")
/// .register_fn("new_ts", Self::new) /// .with_fn("new_ts", Self::new)
/// .register_fn("update", Self::update); /// .with_fn("update", Self::update)
/// .with_get_set("value", Self::get_value, Self::set_value);
/// } /// }
/// } /// }
/// ///
@ -38,30 +50,277 @@ use crate::Engine;
/// // Register API for the custom type. /// // Register API for the custom type.
/// engine.build_type::<TestStruct>(); /// engine.build_type::<TestStruct>();
/// ///
///
/// # #[cfg(not(feature = "no_object"))] /// # #[cfg(not(feature = "no_object"))]
/// assert_eq!( /// assert_eq!(
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?, /// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
/// TestStruct { field: 42 } /// TestStruct { field: 42 }
/// ); /// );
///
/// # #[cfg(not(feature = "no_object"))]
/// assert_eq!(
/// engine.eval::<TestStruct>("let x = new_ts(); x.value = 5 + x.value; x")?,
/// TestStruct { field: 6 }
/// );
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub trait CustomType { pub trait CustomType: Variant + Clone {
/// Builds the custom type for use with the [`Engine`]. /// Builds the custom type for use with the [`Engine`].
/// i.e. register the type, getters, setters, methods, etc... /// i.e. register the type, getters, setters, methods, etc...
fn build(engine: &mut Engine); fn build(builder: TypeBuilder<Self>);
} }
impl Engine { impl Engine {
/// Build a custom type for use with the [`Engine`]. /// Build a custom type for use with the [`Engine`].
/// i.e. register the type, getters, setters, methods, etc... /// i.e. register the type and its getters, setters, methods, etc...
/// ///
/// See [`CustomType`]. /// See [`CustomType`].
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn build_type<T>(&mut self) -> &mut Self pub fn build_type<T>(&mut self) -> &mut Self
where where
T: CustomType, T: CustomType,
{ {
T::build(self); T::build(TypeBuilder::new(self));
self self
} }
} }
/// Builder to build a custom type i.e. register this type and its getters, setters, methods, etc...
///
/// The type is automatically registered when this builder is dropped.
///
/// ## Pretty name
/// By default the type is registered with [`Engine::register_type`] i.e. without a pretty name.
///
/// To define a pretty name call `.with_name`, in this case [`Engine::register_type_with_name`] will be used.
pub struct TypeBuilder<'a, T>
where
T: Variant + Clone,
{
engine: &'a mut Engine,
name: Option<&'static str>,
_marker: PhantomData<T>,
}
impl<'a, T> TypeBuilder<'a, T>
where
T: Variant + Clone,
{
#[inline]
fn new(engine: &'a mut Engine) -> Self {
Self {
engine,
name: None,
_marker: PhantomData::default(),
}
}
}
impl<'a, T> TypeBuilder<'a, T>
where
T: Variant + Clone,
{
/// Sets a pretty-print name for the `type_of` function.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
pub fn with_name(&mut self, name: &'static str) -> &mut Self {
self.name = Some(name);
self
}
/// Register a custom function.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
pub fn with_fn<N, A, F>(&mut self, name: N, method: F) -> &mut Self
where
N: AsRef<str> + Into<Identifier>,
F: RegisterNativeFunction<A, ()>,
{
self.engine.register_fn(name, method);
self
}
/// Register a custom fallible function.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
pub fn with_result_fn<N, A, F, R>(&mut self, name: N, method: F) -> &mut Self
where
N: AsRef<str> + Into<Identifier>,
F: RegisterNativeFunction<A, RhaiResultOf<R>>,
{
self.engine.register_result_fn(name, method);
self
}
}
#[cfg(not(feature = "no_object"))]
impl<'a, T> TypeBuilder<'a, T>
where
T: Variant + Clone,
{
/// Register a getter function.
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_get<V: Variant + Clone>(
&mut self,
name: impl AsRef<str>,
get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
) -> &mut Self {
self.engine.register_get(name, get_fn);
self
}
/// Register a fallible getter function.
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_get_result<V: Variant + Clone>(
&mut self,
name: impl AsRef<str>,
get_fn: impl Fn(&mut T) -> RhaiResultOf<V> + SendSync + 'static,
) -> &mut Self {
self.engine.register_get_result(name, get_fn);
self
}
/// Register a setter function.
///
/// Not available under `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_set<V: Variant + Clone>(
&mut self,
name: impl AsRef<str>,
set_fn: impl Fn(&mut T, V) + SendSync + 'static,
) -> &mut Self {
self.engine.register_set(name, set_fn);
self
}
/// Register a fallible setter function.
///
/// Not available under `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_set_result<V: Variant + Clone>(
&mut self,
name: impl AsRef<str>,
set_fn: impl Fn(&mut T, V) -> RhaiResultOf<()> + SendSync + 'static,
) -> &mut Self {
self.engine.register_set_result(name, set_fn);
self
}
/// Short-hand for registering both getter and setter functions.
///
/// All function signatures must start with `&mut self` and not `&self`.
///
/// Not available under `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_get_set<V: Variant + Clone>(
&mut self,
name: impl AsRef<str>,
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
}
}
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
impl<'a, T> TypeBuilder<'a, T>
where
T: Variant + Clone,
{
/// Register an index getter.
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under both `no_index` and `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_indexer_get<X: Variant + Clone, V: Variant + Clone>(
&mut self,
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
) -> &mut Self {
self.engine.register_indexer_get(get_fn);
self
}
/// Register an fallible index getter.
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under both `no_index` and `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_indexer_get_result<X: Variant + Clone, V: Variant + Clone>(
&mut self,
get_fn: impl Fn(&mut T, X) -> RhaiResultOf<V> + SendSync + 'static,
) -> &mut Self {
self.engine.register_indexer_get_result(get_fn);
self
}
/// Register an index setter.
///
/// Not available under both `no_index` and `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_indexer_set<X: Variant + Clone, V: Variant + Clone>(
&mut self,
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
) -> &mut Self {
self.engine.register_indexer_set(set_fn);
self
}
/// Register an fallible index setter.
///
/// Not available under both `no_index` and `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_indexer_set_result<X: Variant + Clone, V: Variant + Clone>(
&mut self,
set_fn: impl Fn(&mut T, X, V) -> RhaiResultOf<()> + SendSync + 'static,
) -> &mut Self {
self.engine.register_indexer_set_result(set_fn);
self
}
/// Short-hand for registering both index getter and setter functions.
///
/// Not available under both `no_index` and `no_object`.
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
#[inline]
pub fn with_indexer_get_set<X: Variant + Clone, V: Variant + Clone>(
&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,
{
#[inline]
fn drop(&mut self) {
if let Some(name) = self.name {
self.engine.register_type_with_name::<T>(name);
} else {
self.engine.register_type::<T>();
}
}
}

View File

@ -166,7 +166,10 @@ type ExclusiveRange = std::ops::Range<INT>;
/// An inclusive integer range. /// An inclusive integer range.
type InclusiveRange = std::ops::RangeInclusive<INT>; type InclusiveRange = std::ops::RangeInclusive<INT>;
pub use api::{build_type::CustomType, events::VarDefInfo}; pub use api::{
build_type::{CustomType, TypeBuilder},
events::VarDefInfo,
};
pub use ast::{FnAccess, AST}; pub use ast::{FnAccess, AST};
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS}; pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
pub use eval::EvalContext; pub use eval::EvalContext;

118
tests/build_type.rs Normal file
View File

@ -0,0 +1,118 @@
use rhai::{CustomType, Engine, EvalAltResult, Position, TypeBuilder};
#[test]
fn build_type() -> Result<(), Box<EvalAltResult>> {
#[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<i64, Box<EvalAltResult>> {
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<Self>) {
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::<Vec3>();
assert_eq!(
engine.eval::<Vec3>(
r#"
let v = vec3(1, 2, 3);
v
"#,
)?,
Vec3::new(1, 2, 3),
);
assert_eq!(
engine.eval::<i64>(
r#"
let v = vec3(1, 2, 3);
v.x
"#,
)?,
1,
);
assert_eq!(
engine.eval::<i64>(
r#"
let v = vec3(1, 2, 3);
v.y
"#,
)?,
2,
);
assert_eq!(
engine.eval::<i64>(
r#"
let v = vec3(1, 2, 3);
v.z
"#,
)?,
3,
);
assert!(engine.eval::<bool>(
r#"
let v = vec3(1, 2, 3);
v.x == v[0] && v.y == v[1] && v.z == v[2]
"#,
)?);
assert_eq!(
engine.eval::<Vec3>(
r#"
let v = vec3(1, 2, 3);
v.x = 5;
v.y = 6;
v.z = 7;
v
"#,
)?,
Vec3::new(5, 6, 7),
);
Ok(())
}