From 5e6d5e8e8016b91cdb40fd93ad1a6cd3c1195542 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 7 Aug 2020 11:10:38 +0800 Subject: [PATCH] Expand getter/setter/indexer API. --- RELEASES.md | 5 ++ src/api.rs | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 87cb5bdb..fcf498cf 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 0.19.0 ============== +New features +------------ + +* Adds `Engine::register_get_result`, `Engine::register_set_result`, `Engine::register_indexer_get_result`, `Engine::register_indexer_set_result` API. + Version 0.18.1 ============== diff --git a/src/api.rs b/src/api.rs index 34af07a1..8487370f 100644 --- a/src/api.rs +++ b/src/api.rs @@ -18,7 +18,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET}; #[cfg(not(feature = "no_object"))] use crate::{ engine::{make_getter, make_setter, Map}, - fn_register::RegisterFn, + fn_register::{RegisterFn, RegisterResultFn}, token::Token, }; @@ -224,6 +224,54 @@ impl Engine { self.register_fn(&make_getter(name), callback) } + /// Register a getter function for a member of a registered type with the `Engine`. + /// Returns `Result>`. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// # Example + /// + /// ``` + /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// + /// #[derive(Clone)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { TestStruct { field: 1 } } + /// + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self) -> Result> { + /// Ok(self.field.into()) + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// let mut engine = Engine::new(); + /// + /// // Register the custom type. + /// engine.register_type::(); + /// + /// engine.register_fn("new_ts", TestStruct::new); + /// + /// // Register a getter on a property (notice it doesn't have to be the same name). + /// engine.register_get_result("xyz", TestStruct::get_field); + /// + /// assert_eq!(engine.eval::("let a = new_ts(); a.xyz")?, 1); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_object"))] + pub fn register_get_result( + &mut self, + name: &str, + callback: impl Fn(&mut T) -> Result> + SendSync + 'static, + ) -> &mut Self { + self.register_result_fn(&make_getter(name), callback) + } + /// Register a setter function for a member of a registered type with the `Engine`. /// /// # Example @@ -273,6 +321,59 @@ impl Engine { self.register_fn(&make_setter(name), callback) } + /// Register a setter function for a member of a registered type with the `Engine`. + /// Returns `Result>`. + /// + /// # Example + /// + /// ``` + /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// + /// #[derive(Debug, Clone, Eq, PartialEq)] + /// struct TestStruct { + /// field: i64 + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { TestStruct { field: 1 } } + /// fn set_field(&mut self, new_val: i64) -> Result> { + /// self.field = new_val; + /// Ok(().into()) + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// let mut engine = Engine::new(); + /// + /// // Register the custom type. + /// engine.register_type::(); + /// + /// engine.register_fn("new_ts", TestStruct::new); + /// + /// // Register a setter on a property (notice it doesn't have to be the same name) + /// engine.register_set_result("xyz", TestStruct::set_field); + /// + /// // Notice that, with a getter, there is no way to get the property value + /// assert_eq!( + /// engine.eval::("let a = new_ts(); a.xyz = 42; a")?, + /// TestStruct { field: 42 } + /// ); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_object"))] + pub fn register_set_result( + &mut self, + name: &str, + callback: impl Fn(&mut T, U) -> Result> + SendSync + 'static, + ) -> &mut Self + where + T: Variant + Clone, + U: Variant + Clone, + { + self.register_result_fn(&make_setter(name), callback) + } + /// Shorthand for registering both getter and setter functions /// of a registered type with the `Engine`. /// @@ -375,6 +476,58 @@ impl Engine { self.register_fn(FN_IDX_GET, callback) } + /// Register an index getter for a registered type with the `Engine`. + /// Returns `Result>`. + /// + /// The function signature must start with `&mut self` and not `&self`. + /// + /// # Example + /// + /// ``` + /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// + /// #[derive(Clone)] + /// struct TestStruct { + /// fields: Vec + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// + /// // Even a getter must start with `&mut self` and not `&self`. + /// fn get_field(&mut self, index: i64) -> Result> { + /// Ok(self.fields[index as usize].into()) + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// let mut engine = Engine::new(); + /// + /// // Register the custom type. + /// engine.register_type::(); + /// + /// engine.register_fn("new_ts", TestStruct::new); + /// + /// // Register an indexer. + /// engine.register_indexer_get_result(TestStruct::get_field); + /// + /// assert_eq!(engine.eval::("let a = new_ts(); a[2]")?, 3); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] + pub fn register_indexer_get_result( + &mut self, + callback: impl Fn(&mut T, X) -> Result> + SendSync + 'static, + ) -> &mut Self + where + T: Variant + Clone, + X: Variant + Clone, + { + self.register_result_fn(FN_IDX_GET, callback) + } + /// Register an index setter for a registered type with the `Engine`. /// /// # Example @@ -424,6 +577,59 @@ impl Engine { self.register_fn(FN_IDX_SET, callback) } + /// Register an index setter for a registered type with the `Engine`. + /// Returns `Result>`. + /// + /// # Example + /// + /// ``` + /// use rhai::{Engine, Dynamic, EvalAltResult, RegisterFn}; + /// + /// #[derive(Clone)] + /// struct TestStruct { + /// fields: Vec + /// } + /// + /// impl TestStruct { + /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn set_field(&mut self, index: i64, value: i64) -> Result> { + /// self.fields[index as usize] = value; + /// Ok(().into()) + /// } + /// } + /// + /// # fn main() -> Result<(), Box> { + /// let mut engine = Engine::new(); + /// + /// // Register the custom type. + /// engine.register_type::(); + /// + /// engine.register_fn("new_ts", TestStruct::new); + /// + /// // Register an indexer. + /// engine.register_indexer_set_result(TestStruct::set_field); + /// + /// assert_eq!( + /// engine.eval::("let a = new_ts(); a[2] = 42; a")?.fields[2], + /// 42 + /// ); + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] + pub fn register_indexer_set_result( + &mut self, + callback: impl Fn(&mut T, X, U) -> Result> + SendSync + 'static, + ) -> &mut Self + where + T: Variant + Clone, + U: Variant + Clone, + X: Variant + Clone, + { + self.register_result_fn(FN_IDX_SET, callback) + } + /// Shorthand for register both index getter and setter functions for a registered type with the `Engine`. /// /// # Example