From 47f02b96d7101afc9802fefa2dead9f4706d9bc2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 8 Aug 2022 09:10:15 +0800 Subject: [PATCH 1/4] Add top-level wrappers. --- CHANGELOG.md | 4 ++ src/api/eval.rs | 34 +++++++++++----- src/api/files.rs | 80 +++++++++++++++++++++++++++++++++++-- src/api/run.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 5 ++- 5 files changed, 204 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f4dbfef..9b532505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ New features * Using a script-defined function's name (in place of a variable) implicitly creates a function pointer to the function. +### Top-level functions + +* Crate-level functions `rhai::eval`, `rhai::run`, `rhai::eval_file`, `rhai::run_file` are added as convenient wrappers. + Enhancements ------------ diff --git a/src/api/eval.rs b/src/api/eval.rs index cb351301..cfa24a8f 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -11,7 +11,7 @@ use std::any::type_name; use std::prelude::v1::*; impl Engine { - /// Evaluate a string. + /// Evaluate a string as a script, returning the result value or an error. /// /// # Example /// @@ -29,7 +29,7 @@ impl Engine { pub fn eval(&self, script: &str) -> RhaiResultOf { self.eval_with_scope(&mut Scope::new(), script) } - /// Evaluate a string with own scope. + /// Evaluate a string as a script with own scope, returning the result value or an error. /// /// ## Constants Propagation /// @@ -71,7 +71,7 @@ impl Engine { )?; self.eval_ast_with_scope(scope, &ast) } - /// Evaluate a string containing an expression. + /// Evaluate a string containing an expression, returning the result value or an error. /// /// # Example /// @@ -89,7 +89,7 @@ impl Engine { pub fn eval_expression(&self, script: &str) -> RhaiResultOf { self.eval_expression_with_scope(&mut Scope::new(), script) } - /// Evaluate a string containing an expression with own scope. + /// Evaluate a string containing an expression with own scope, returning the result value or an error. /// /// # Example /// @@ -130,7 +130,7 @@ impl Engine { self.eval_ast_with_scope(scope, &ast) } - /// Evaluate an [`AST`]. + /// Evaluate an [`AST`], returning the result value or an error. /// /// # Example /// @@ -152,7 +152,7 @@ impl Engine { pub fn eval_ast(&self, ast: &AST) -> RhaiResultOf { self.eval_ast_with_scope(&mut Scope::new(), ast) } - /// Evaluate an [`AST`] with own scope. + /// Evaluate an [`AST`] with own scope, returning the result value or an error. /// /// # Example /// @@ -162,9 +162,6 @@ impl Engine { /// /// let engine = Engine::new(); /// - /// // Compile a script to an AST and store it for later evaluation - /// let ast = engine.compile("x + 2")?; - /// /// // Create initialized scope /// let mut scope = Scope::new(); /// scope.push("x", 40_i64); @@ -209,7 +206,7 @@ impl Engine { ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into() }) } - /// Evaluate an [`AST`] with own scope. + /// Evaluate an [`AST`] with own scope, returning the result value or an error. #[inline] pub(crate) fn eval_ast_with_scope_raw<'a>( &self, @@ -274,3 +271,20 @@ impl Engine { self.eval_global_statements(scope, global, caches, statements, lib, level) } } + +/// Evaluate a string as a script, returning the result value or an error. +/// +/// # Example +/// +/// ``` +/// # fn main() -> Result<(), Box> { +/// let result: i64 = rhai::eval("40 + 2")?; +/// +/// assert_eq!(result, 42); +/// # Ok(()) +/// # } +/// ``` +#[inline(always)] +pub fn eval(script: &str) -> RhaiResultOf { + Engine::new().eval(script) +} diff --git a/src/api/files.rs b/src/api/files.rs index 28272345..9c452e0b 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -102,7 +102,7 @@ impl Engine { pub fn compile_file_with_scope(&self, scope: &Scope, path: PathBuf) -> RhaiResultOf { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } - /// Evaluate a script file. + /// Evaluate a script file, returning the result value or an error. /// /// Not available under `no_std` or `WASM`. /// @@ -123,7 +123,7 @@ impl Engine { pub fn eval_file(&self, path: PathBuf) -> RhaiResultOf { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } - /// Evaluate a script file with own scope. + /// Evaluate a script file with own scope, returning the result value or an error. /// /// Not available under `no_std` or `WASM`. /// @@ -159,14 +159,28 @@ impl Engine { ) -> RhaiResultOf { Self::read_file(path).and_then(|contents| self.eval_with_scope(scope, &contents)) } - /// Evaluate a file, returning any error (if any). + /// Evaluate a file. /// /// Not available under `no_std` or `WASM`. + /// + /// # Example + /// + /// ```no_run + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// // Notice that a PathBuf is required which can easily be constructed from a string. + /// engine.run_file("script.rhai".into())?; + /// # Ok(()) + /// # } + /// ``` #[inline] pub fn run_file(&self, path: PathBuf) -> RhaiResultOf<()> { Self::read_file(path).and_then(|contents| self.run(&contents)) } - /// Evaluate a file with own scope, returning any error (if any). + /// Evaluate a file with own scope. /// /// Not available under `no_std` or `WASM`. /// @@ -176,8 +190,66 @@ impl Engine { /// the scope are propagated throughout the script _including_ functions. /// /// This allows functions to be optimized based on dynamic global constants. + /// + /// # Example + /// + /// ```no_run + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Scope}; + /// + /// let engine = Engine::new(); + /// + /// // Create initialized scope + /// let mut scope = Scope::new(); + /// scope.push("x", 42_i64); + /// + /// // Notice that a PathBuf is required which can easily be constructed from a string. + /// engine.run_file_with_scope(&mut scope, "script.rhai".into())?; + /// # Ok(()) + /// # } + /// ``` #[inline] pub fn run_file_with_scope(&self, scope: &mut Scope, path: PathBuf) -> RhaiResultOf<()> { Self::read_file(path).and_then(|contents| self.run_with_scope(scope, &contents)) } } + +/// Evaluate a script file. +/// +/// Not available under `no_std` or `WASM`. +/// +/// # Example +/// +/// ```no_run +/// # fn main() -> Result<(), Box> { +/// // Notice that a PathBuf is required which can easily be constructed from a string. +/// let result: i64 = rhai::eval_file("script.rhai".into())?; +/// # Ok(()) +/// # } +/// ``` +#[inline] +pub fn eval_file(path: PathBuf) -> RhaiResultOf { + Engine::read_file(path).and_then(|contents| Engine::new().eval::(&contents)) +} + +/// Evaluate a file. +/// +/// Not available under `no_std` or `WASM`. +/// +/// # Example +/// +/// ```no_run +/// # fn main() -> Result<(), Box> { +/// use rhai::Engine; +/// +/// let engine = Engine::new(); +/// +/// // Notice that a PathBuf is required which can easily be constructed from a string. +/// rhai::run_file("script.rhai".into())?; +/// # Ok(()) +/// # } +/// ``` +#[inline] +pub fn run_file(path: PathBuf) -> RhaiResultOf<()> { + Engine::read_file(path).and_then(|contents| Engine::new().run(&contents)) +} diff --git a/src/api/run.rs b/src/api/run.rs index babb3295..8213791d 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -7,18 +7,52 @@ use crate::{Engine, Module, RhaiResultOf, Scope, AST}; use std::prelude::v1::*; impl Engine { - /// Evaluate a script, returning any error (if any). + /// Evaluate a string as a script. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// engine.run::("print(40 + 2);")?; + /// # Ok(()) + /// # } + /// ``` #[inline(always)] pub fn run(&self, script: &str) -> RhaiResultOf<()> { self.run_with_scope(&mut Scope::new(), script) } - /// Evaluate a script with own scope, returning any error (if any). + /// Evaluate a string as a script with own scope. /// /// ## Constants Propagation /// /// If not [`OptimizationLevel::None`][crate::OptimizationLevel::None], constants defined within - /// the scope are propagated throughout the script _including_ functions. This allows functions - /// to be optimized based on dynamic global constants. + /// the scope are propagated throughout the script _including_ functions. + /// + /// This allows functions to be optimized based on dynamic global constants. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Scope}; + /// + /// let engine = Engine::new(); + /// + /// // Create initialized scope + /// let mut scope = Scope::new(); + /// scope.push("x", 40_i64); + /// + /// engine.run_with_scope(&mut scope, "x += 2; print(x);")?; + /// + /// // The variable in the scope is modified + /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 44); + /// # Ok(()) + /// # } + /// ``` #[inline] pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> { let scripts = [script]; @@ -28,12 +62,53 @@ impl Engine { let ast = self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?; self.run_ast_with_scope(scope, &ast) } - /// Evaluate an [`AST`], returning any error (if any). + /// Evaluate an [`AST`]. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::Engine; + /// + /// let engine = Engine::new(); + /// + /// // Compile a script to an AST and store it for later evaluation + /// let ast = engine.compile("print(40 + 2);")?; + /// + /// // Evaluate it + /// engine.run_ast(&ast)?; + /// # Ok(()) + /// # } + /// ``` #[inline(always)] pub fn run_ast(&self, ast: &AST) -> RhaiResultOf<()> { self.run_ast_with_scope(&mut Scope::new(), ast) } - /// Evaluate an [`AST`] with own scope, returning any error (if any). + /// Evaluate an [`AST`] with own scope. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// use rhai::{Engine, Scope}; + /// + /// let engine = Engine::new(); + /// + /// // Create initialized scope + /// let mut scope = Scope::new(); + /// scope.push("x", 40_i64); + /// + /// // Compile a script to an AST and store it for later evaluation + /// let ast = engine.compile("x += 2; x")?; + /// + /// // Evaluate it + /// engine.run_ast_with_scope(&mut scope, &ast)?; + /// + /// // The variable in the scope is modified + /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 44); + /// # Ok(()) + /// # } + /// ``` #[inline] pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> { let caches = &mut Caches::new(); @@ -73,3 +148,18 @@ impl Engine { Ok(()) } } + +/// Evaluate a string as a script. +/// +/// # Example +/// +/// ``` +/// # fn main() -> Result<(), Box> { +/// rhai::run("print(40 + 2);")?; +/// # Ok(()) +/// # } +/// ``` +#[inline(always)] +pub fn run(script: &str) -> RhaiResultOf<()> { + Engine::new().run(script) +} diff --git a/src/lib.rs b/src/lib.rs index 9ad7317f..b39606d3 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::events::VarDefInfo; +#[cfg(not(feature = "no_std"))] +#[cfg(not(target_family = "wasm"))] +pub use api::files::{eval_file, run_file}; +pub use api::{eval::eval, events::VarDefInfo, run::run}; pub use ast::{FnAccess, AST}; pub use engine::{Engine, OP_CONTAINS, OP_EQUALS}; pub use eval::EvalContext; From 68c11060d9c78210537004b3b7bc7c515cf0657d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 9 Aug 2022 16:31:25 +0800 Subject: [PATCH 2/4] Merge branch 'main' of https://github.com/rhaiscript/rhai --- src/api/build_type.rs | 329 ++++++++++++++++++++++++++++++++++++++++++ src/api/mod.rs | 2 + src/lib.rs | 7 +- tests/build_type.rs | 118 +++++++++++++++ 4 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 src/api/build_type.rs create mode 100644 tests/build_type.rs diff --git a/src/api/build_type.rs b/src/api/build_type.rs new file mode 100644 index 00000000..98cc8645 --- /dev/null +++ b/src/api/build_type.rs @@ -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) { +/// 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> { +/// +/// let mut engine = Engine::new(); +/// +/// // 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: Variant + Clone { + /// Builds the custom type for use with the [`Engine`]. + /// i.e. register the type, getters, setters, methods, etc... + fn build(builder: TypeBuilder); +} + +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(&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, +} + +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(&mut self, name: N, method: F) -> &mut Self + where + N: AsRef + Into, + F: RegisterNativeFunction, + { + 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(&mut self, name: N, method: F) -> &mut Self + where + N: AsRef + Into, + F: RegisterNativeFunction>, + { + 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( + &mut self, + name: impl AsRef, + 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( + &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 + } + + /// 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( + &mut self, + name: impl AsRef, + 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( + &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 + } + + /// 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( + &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 + } +} + +#[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( + &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( + &mut self, + get_fn: impl Fn(&mut T, X) -> RhaiResultOf + 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( + &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( + &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( + &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::(name); + } else { + self.engine.register_type::(); + } + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index f24777c6..635082dd 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -28,6 +28,8 @@ pub mod custom_syntax; pub mod deprecated; +pub mod build_type; + #[cfg(feature = "metadata")] pub mod definitions; diff --git a/src/lib.rs b/src/lib.rs index b39606d3..9808ddce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,7 +169,12 @@ type InclusiveRange = std::ops::RangeInclusive; #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] 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 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(()) +} From d9a58907d90ae27090ab3f83217f3a16abb606ae Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 9 Aug 2022 17:07:45 +0800 Subject: [PATCH 3/4] Fix up tests and some coding style changes. --- src/api/build_type.rs | 102 ++++++++++++++++-------------------------- tests/build_type.rs | 92 +++++++++++++++++++------------------ 2 files changed, 86 insertions(+), 108 deletions(-) diff --git a/src/api/build_type.rs b/src/api/build_type.rs index 83533e41..02a5b3cd 100644 --- a/src/api/build_type.rs +++ b/src/api/build_type.rs @@ -1,12 +1,16 @@ -use core::marker::PhantomData; +//! Trait to build a custom type for use with [`Engine`]. +#![allow(deprecated)] use crate::{ func::SendSync, types::dynamic::Variant, Engine, Identifier, RegisterNativeFunction, RhaiResultOf, }; +use std::marker::PhantomData; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; -/// Trait to build a custom type for use with the [`Engine`]. -/// i.e. register the type and its getters, setters, methods, etc... +/// Trait to build a custom type for use with an [`Engine`] +/// (i.e. register the type and its getters, setters, methods, etc.). /// /// # Example /// @@ -58,49 +62,44 @@ use crate::{ /// # Ok(()) /// # } /// ``` +#[deprecated = "This trait is NOT deprecated, but it is considered volatile and may change in the future."] pub trait CustomType: Variant + Clone { /// Builds the custom type for use with the [`Engine`]. - /// i.e. register the type, getters, setters, methods, etc... + /// + /// Methods, property getters/setters, indexers etc. should be registered in this function. fn build(builder: TypeBuilder); } impl Engine { /// Build a custom type for use with the [`Engine`]. - /// i.e. register the type and its getters, setters, methods, etc... /// - /// See [`CustomType`]. + /// The custom type must implement [`CustomType`]. #[inline] - pub fn build_type(&mut self) -> &mut Self - where - T: CustomType, - { + pub fn build_type(&mut self) -> &mut Self { T::build(TypeBuilder::new(self)); self } } -/// Builder to build a custom type i.e. register this type and its getters, setters, methods, etc... +/// Builder to build a custom type for use with an [`Engine`]. /// /// 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, -{ +/// By default the type is registered with [`Engine::register_type`] (i.e. without a pretty name). +/// +/// To define a pretty name, call [`with_name`][`TypeBuilder::with_name`], +/// to use [`Engine::register_type_with_name`] instead. +#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."] +pub struct TypeBuilder<'a, T: Variant + Clone> { engine: &'a mut Engine, name: Option<&'static str>, _marker: PhantomData, } -impl<'a, T> TypeBuilder<'a, T> -where - T: Variant + Clone, -{ - #[inline] +impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { + #[inline(always)] fn new(engine: &'a mut Engine) -> Self { Self { engine, @@ -110,21 +109,16 @@ where } } -impl<'a, T> TypeBuilder<'a, T> -where - T: Variant + Clone, -{ +impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { /// 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] + #[inline(always)] 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] + #[inline(always)] pub fn with_fn(&mut self, name: N, method: F) -> &mut Self where N: AsRef + Into, @@ -135,8 +129,7 @@ where } /// Register a custom fallible function. - #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."] - #[inline] + #[inline(always)] pub fn with_result_fn(&mut self, name: N, method: F) -> &mut Self where N: AsRef + Into, @@ -148,17 +141,13 @@ where } #[cfg(not(feature = "no_object"))] -impl<'a, T> TypeBuilder<'a, T> -where - T: Variant + Clone, -{ +impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { /// 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] + #[inline(always)] pub fn with_get( &mut self, name: impl AsRef, @@ -173,8 +162,7 @@ where /// 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] + #[inline(always)] pub fn with_get_result( &mut self, name: impl AsRef, @@ -187,8 +175,7 @@ where /// 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] + #[inline(always)] pub fn with_set( &mut self, name: impl AsRef, @@ -201,8 +188,7 @@ where /// 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] + #[inline(always)] pub fn with_set_result( &mut self, name: impl AsRef, @@ -217,8 +203,7 @@ where /// 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] + #[inline(always)] pub fn with_get_set( &mut self, name: impl AsRef, @@ -231,17 +216,13 @@ where } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -impl<'a, T> TypeBuilder<'a, T> -where - T: Variant + Clone, -{ +impl<'a, T: Variant + Clone> TypeBuilder<'a, T> { /// 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] + #[inline(always)] pub fn with_indexer_get( &mut self, get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, @@ -255,8 +236,7 @@ where /// 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] + #[inline(always)] pub fn with_indexer_get_result( &mut self, get_fn: impl Fn(&mut T, X) -> RhaiResultOf + SendSync + 'static, @@ -268,8 +248,7 @@ where /// 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] + #[inline(always)] pub fn with_indexer_set( &mut self, set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, @@ -281,8 +260,7 @@ where /// 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] + #[inline(always)] pub fn with_indexer_set_result( &mut self, set_fn: impl Fn(&mut T, X, V) -> RhaiResultOf<()> + SendSync + 'static, @@ -294,8 +272,7 @@ where /// 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] + #[inline(always)] pub fn with_indexer_get_set( &mut self, get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, @@ -306,10 +283,7 @@ where } } -impl<'a, T> Drop for TypeBuilder<'a, T> -where - T: Variant + Clone, -{ +impl<'a, T: Variant + Clone> Drop for TypeBuilder<'a, T> { #[inline] fn drop(&mut self) { if let Some(name) = self.name { diff --git a/tests/build_type.rs b/tests/build_type.rs index 76d1bcac..9f8924d4 100644 --- a/tests/build_type.rs +++ b/tests/build_type.rs @@ -1,37 +1,38 @@ -use rhai::{CustomType, Engine, EvalAltResult, Position, TypeBuilder}; +#![cfg(not(feature = "no_object"))] +use rhai::{CustomType, Engine, EvalAltResult, Position, TypeBuilder, INT}; #[test] fn build_type() -> Result<(), Box> { #[derive(Debug, Clone, PartialEq)] struct Vec3 { - x: i64, - y: i64, - z: i64, + x: INT, + y: INT, + z: INT, } impl Vec3 { - fn new(x: i64, y: i64, z: i64) -> Self { + fn new(x: INT, y: INT, z: INT) -> Self { Self { x, y, z } } - fn get_x(&mut self) -> i64 { + fn get_x(&mut self) -> INT { self.x } - fn set_x(&mut self, x: i64) { + fn set_x(&mut self, x: INT) { self.x = x } - fn get_y(&mut self) -> i64 { + fn get_y(&mut self) -> INT { self.y } - fn set_y(&mut self, y: i64) { + fn set_y(&mut self, y: INT) { self.y = y } - fn get_z(&mut self) -> i64 { + fn get_z(&mut self) -> INT { self.z } - fn set_z(&mut self, z: i64) { + fn set_z(&mut self, z: INT) { self.z = z } - fn get_component(&mut self, idx: i64) -> Result> { + fn get_component(&mut self, idx: INT) -> Result> { match idx { 0 => Ok(self.x), 1 => Ok(self.y), @@ -51,8 +52,10 @@ fn build_type() -> Result<(), Box> { .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); + .with_get_set("z", Self::get_z, Self::set_z); + + #[cfg(not(feature = "no_index"))] + builder.with_indexer_get_result(Self::get_component); } } @@ -61,55 +64,56 @@ fn build_type() -> Result<(), Box> { assert_eq!( engine.eval::( - r#" - let v = vec3(1, 2, 3); - v -"#, + " + 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 -"#, + engine.eval::( + " + let v = vec3(1, 2, 3); + v.x + ", )?, 1, ); assert_eq!( - engine.eval::( - r#" - let v = vec3(1, 2, 3); - v.y -"#, + engine.eval::( + " + let v = vec3(1, 2, 3); + v.y + ", )?, 2, ); assert_eq!( - engine.eval::( - r#" - let v = vec3(1, 2, 3); - v.z -"#, + engine.eval::( + " + let v = vec3(1, 2, 3); + v.z + ", )?, 3, ); + #[cfg(not(feature = "no_index"))] assert!(engine.eval::( - r#" - let v = vec3(1, 2, 3); - v.x == v[0] && v.y == v[1] && v.z == v[2] -"#, + " + 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 -"#, + " + let v = vec3(1, 2, 3); + v.x = 5; + v.y = 6; + v.z = 7; + v + ", )?, Vec3::new(5, 6, 7), ); From f323644e20e1b737d7577191724cd9e7a1a57210 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 9 Aug 2022 17:24:24 +0800 Subject: [PATCH 4/4] Fix tests. --- src/api/run.rs | 6 +++--- src/lib.rs | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/api/run.rs b/src/api/run.rs index 8213791d..c6bd0b54 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -17,7 +17,7 @@ impl Engine { /// /// let engine = Engine::new(); /// - /// engine.run::("print(40 + 2);")?; + /// engine.run("print(40 + 2);")?; /// # Ok(()) /// # } /// ``` @@ -49,7 +49,7 @@ impl Engine { /// engine.run_with_scope(&mut scope, "x += 2; print(x);")?; /// /// // The variable in the scope is modified - /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 44); + /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 42); /// # Ok(()) /// # } /// ``` @@ -105,7 +105,7 @@ impl Engine { /// engine.run_ast_with_scope(&mut scope, &ast)?; /// /// // The variable in the scope is modified - /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 44); + /// assert_eq!(scope.get_value::("x").expect("variable x should exist"), 42); /// # Ok(()) /// # } /// ``` diff --git a/src/lib.rs b/src/lib.rs index 9808ddce..715dd979 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,15 +166,12 @@ type ExclusiveRange = std::ops::Range; /// An inclusive integer range. type InclusiveRange = std::ops::RangeInclusive; +#[allow(deprecated)] +pub use api::build_type::{CustomType, TypeBuilder}; #[cfg(not(feature = "no_std"))] #[cfg(not(target_family = "wasm"))] pub use api::files::{eval_file, run_file}; -pub use api::{ - build_type::{CustomType, TypeBuilder}, - eval::eval, - events::VarDefInfo, - run::run, -}; +pub use api::{eval::eval, events::VarDefInfo, run::run}; pub use ast::{FnAccess, AST}; pub use engine::{Engine, OP_CONTAINS, OP_EQUALS}; pub use eval::EvalContext;