This commit is contained in:
Stephen Chung 2022-08-09 16:31:25 +08:00
parent 47f02b96d7
commit 68c11060d9
4 changed files with 455 additions and 1 deletions

329
src/api/build_type.rs Normal file
View File

@ -0,0 +1,329 @@
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 and its getters, setters, methods, etc...
///
/// # Example
///
/// ```
/// use rhai::{CustomType, TypeBuilder, Engine};
///
/// #[derive(Debug, Clone, Eq, PartialEq)]
/// struct TestStruct {
/// field: i64
/// }
///
/// impl TestStruct {
/// fn new() -> Self {
/// Self { field: 1 }
/// }
/// 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 CustomType for TestStruct {
/// fn build(mut builder: TypeBuilder<Self>) {
/// 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);
/// }
/// }
///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
///
/// let mut engine = Engine::new();
///
/// // Register API for the custom type.
/// engine.build_type::<TestStruct>();
///
///
/// # #[cfg(not(feature = "no_object"))]
/// assert_eq!(
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
/// 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(())
/// # }
/// ```
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(builder: TypeBuilder<Self>);
}
impl Engine {
/// Build a custom type for use with the [`Engine`].
/// i.e. register the type and its getters, setters, methods, etc...
///
/// 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
where
T: CustomType,
{
T::build(TypeBuilder::new(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."]
#[inline]
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."]
#[inline]
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."]
#[inline]
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

@ -28,6 +28,8 @@ pub mod custom_syntax;
pub mod deprecated; pub mod deprecated;
pub mod build_type;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
pub mod definitions; pub mod definitions;

View File

@ -169,7 +169,12 @@ type InclusiveRange = std::ops::RangeInclusive<INT>;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub use api::files::{eval_file, run_file}; pub use api::files::{eval_file, run_file};
pub use api::{eval::eval, events::VarDefInfo, run::run}; pub use api::{
build_type::{CustomType, TypeBuilder},
eval::eval,
events::VarDefInfo,
run::run,
};
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(())
}