more efforts to automate rhai bindings

This commit is contained in:
timurgordon
2025-05-13 02:00:35 +03:00
parent 16ad4f5743
commit ec4769a6b0
14 changed files with 3174 additions and 52 deletions

View File

@@ -1,15 +1,23 @@
use rhai_wrapper::wrap_for_rhai;
use rhai_wrapper::{ToRhaiMap, FromRhaiMap};
use rhai::{CustomType, TypeBuilder, Engine, INT, FLOAT, Array};
use rhai_macros_derive::{ToRhaiMap as ToRhaiMapDerive, FromRhaiMap as FromRhaiMapDerive};
use rhai_macros_derive::{ToRhaiMap as ToRhaiMapDerive, FromRhaiMap as FromRhaiMapDerive, export_fn};
#[export_fn(rhai_name = "add_rhai")]
fn add(a: INT, b: INT) -> INT { a + b }
#[export_fn(rhai_name = "mul_rhai")]
fn mul(a: INT, b: INT) -> INT { a * b }
#[export_fn(rhai_name = "greet_rhai")]
fn greet(name: String) -> String { format!("Hello, {name}!") }
#[export_fn(rhai_name = "get_forty_two_rhai")]
fn get_forty_two() -> INT { 42 }
#[export_fn(rhai_name = "shout_rhai")]
fn shout() -> String { "HEY!".to_string() }
#[export_fn(rhai_name = "add_float_rhai")]
fn add_float(a: FLOAT, b: FLOAT) -> FLOAT { a + b }
#[export_fn(rhai_name = "is_even_rhai")]
fn is_even(n: INT) -> bool { n % 2 == 0 }
#[export_fn(rhai_name = "maybe_add_rhai")]
fn maybe_add(a: INT, b: INT, do_add: bool) -> Option<INT> { if do_add { Some(a + b) } else { None } }
// Renamed from sum_vec, takes rhai::Array
@@ -110,69 +118,83 @@ fn get_polygon_id_and_num_vertices(poly: Polygon) -> String {
#[test]
fn test_add() {
let mut engine = Engine::new();
engine.register_fn("add", wrap_for_rhai!(add));
let result = engine.eval::<INT>("add(2, 3)").unwrap();
engine.register_fn("add_rhai", add_rhai_wrapper);
let result = engine.eval::<INT>("add_rhai(2, 3)").unwrap();
assert_eq!(result, 5);
}
#[test]
fn test_mul() {
let mut engine = Engine::new();
engine.register_fn("mul", wrap_for_rhai!(mul));
let result = engine.eval::<INT>("mul(4, 5)").unwrap();
engine.register_fn("mul_rhai", mul_rhai_wrapper);
let result = engine.eval::<INT>("mul_rhai(4, 5)").unwrap();
assert_eq!(result, 20);
}
#[test]
fn test_greet() {
let mut engine = Engine::new();
engine.register_fn("greet", wrap_for_rhai!(greet));
let result = engine.eval::<String>(r#"greet("Alice")"#).unwrap();
engine.register_fn("greet_rhai", greet_rhai_wrapper);
let result = engine.eval::<String>(r#"greet_rhai("Alice")"#).unwrap();
assert_eq!(result, "Hello, Alice!");
}
#[test]
fn test_get_forty_two() {
let mut engine = Engine::new();
engine.register_fn("get_forty_two", wrap_for_rhai!(get_forty_two));
let result = engine.eval::<INT>("get_forty_two()").unwrap();
engine.register_fn("get_forty_two_rhai", get_forty_two_rhai_wrapper);
let result = engine.eval::<INT>("get_forty_two_rhai()").unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_shout() {
let mut engine = Engine::new();
engine.register_fn("shout", wrap_for_rhai!(shout));
let result = engine.eval::<String>("shout()").unwrap();
engine.register_fn("shout_rhai", shout_rhai_wrapper);
let result = engine.eval::<String>("shout_rhai()").unwrap();
assert_eq!(result, "HEY!");
}
#[test]
fn test_add_float() {
let mut engine = Engine::new();
engine.register_fn("add_float", wrap_for_rhai!(add_float));
let result = engine.eval::<FLOAT>("add_float(1.5, 2.25)").unwrap();
assert!((result - 3.75).abs() < 1e-8);
engine.register_fn("add_float_rhai", add_float_rhai_wrapper);
let result = engine.eval::<FLOAT>("add_float_rhai(2.5, 3.5)").unwrap();
assert_eq!(result, 6.0);
}
#[test]
fn test_is_even() {
let mut engine = Engine::new();
engine.register_fn("is_even", wrap_for_rhai!(is_even));
let result = engine.eval::<bool>("is_even(4)").unwrap();
assert!(result);
let result = engine.eval::<bool>("is_even(5)").unwrap();
assert!(!result);
engine.register_fn("is_even_rhai", is_even_rhai_wrapper);
let result_true = engine.eval::<bool>("is_even_rhai(4)").unwrap();
assert_eq!(result_true, true);
let result_false = engine.eval::<bool>("is_even_rhai(3)").unwrap();
assert_eq!(result_false, false);
}
#[test]
fn test_maybe_add() {
let mut engine = Engine::new();
engine.register_fn("maybe_add", wrap_for_rhai!(maybe_add));
let result = engine.eval::<Option<INT>>("maybe_add(2, 3, true)").unwrap();
assert_eq!(result, Some(5));
let result = engine.eval::<Option<INT>>("maybe_add(2, 3, false)").unwrap();
assert_eq!(result, None);
engine.register_fn("maybe_add_rhai", maybe_add_rhai_wrapper);
// Test case where None is returned (expecting an error or specific handling in Rhai)
// Rhai treats Option::None as an empty Dynamic, which can lead to type mismatch if not handled.
// For now, let's check if the script produces a specific type or if it can be evaluated to Dynamic.
// If the function returns None, eval might return an error if trying to cast to INT.
// Let's eval to Dynamic and check if it's empty (Rhai's representation of None).
let result_none = engine.eval::<rhai::Dynamic>("maybe_add_rhai(2, 3, false)").unwrap();
// Debug prints
println!("Debug [test_maybe_add]: result_none = {:?}", result_none);
println!("Debug [test_maybe_add]: result_none.type_name() = {}", result_none.type_name());
println!("Debug [test_maybe_add]: result_none.is::<()>() = {}", result_none.is::<()>());
assert!(result_none.is_unit(), "Expected Rhai None (unit Dynamic)");
// Test case where Some is returned
let result_some = engine.eval::<INT>("maybe_add_rhai(2, 3, true)").unwrap();
assert_eq!(result_some, 5);
}
#[test]
@@ -502,21 +524,20 @@ mod new_export_fn_tests {
assert_eq!(result, 15);
}
// #[test]
// fn test_export_fn_custom_type_arg_return() { // This test was commented out, keeping as is for now
// let mut engine = Engine::new();
// engine.build_type::<Point>();
// // engine.register_fn("offset_simple_point", offset_simple_point_rhai_wrapper);
#[test]
fn test_export_fn_custom_type_arg_return_new() {
let mut engine = Engine::new();
engine.build_type::<Point>();
engine.register_fn("offset_simple_point", offset_simple_point_rhai_wrapper);
// // let script = r#"
// // let p = #{ x: 10, y: 20 };
// // let p_offset = offset_simple_point(p, 5);
// // p_offset.x
// // "#;
// // let result = engine.eval::<INT>(script).unwrap();
// // assert_eq!(result, 15);
// }
let script = r#"
let p = #{ x: 10, y: 20 };
let p_offset = offset_simple_point(p, 5);
p_offset.x
"#;
let result = engine.eval::<INT>(script).unwrap();
assert_eq!(result, 15);
}
#[derive(Debug, Clone, PartialEq, FromRhaiMapDerive, ToRhaiMapDerive, CustomType)]
@@ -556,17 +577,4 @@ mod new_export_fn_tests {
assert_eq!(result_struct.optional_nested_vec.as_ref().unwrap().len(), 1);
assert_eq!(result_struct.optional_nested_vec.as_ref().unwrap()[0], SampleStruct { value: 3, name: "n3".to_string() });
}
#[test]
fn test_export_fn_custom_type_arg_return_new() {
let mut engine = Engine::new();
engine.build_type::<Point>();
engine.register_fn("offset_simple_point", offset_simple_point_rhai_wrapper);
let script = r#"
42
"#;
let result = engine.eval::<INT>(script).unwrap();
assert_eq!(result, 42);
}
}

View File

@@ -0,0 +1,709 @@
use rhai::{Engine, INT, FLOAT, Dynamic, Map, Array, EvalAltResult, CustomType, TypeBuilder};
use rhai_wrapper::{
wrap_option_return, wrap_vec_return, wrap_option_vec_return,
wrap_option_return_result, wrap_vec_return_result, wrap_option_vec_return_result,
wrap_option_method_result, wrap_vec_method_result, wrap_option_vec_method_result,
ToRhaiMap, FromRhaiMap
};
use rhai_macros_derive::{ToRhaiMap as ToRhaiMapDerive, FromRhaiMap as FromRhaiMapDerive};
use std::fmt;
// Test structs
#[derive(Debug, Clone, PartialEq, CustomType, ToRhaiMapDerive, FromRhaiMapDerive)]
struct User {
id: INT,
name: String,
age: INT,
}
impl User {
fn to_rhai_map(&self) -> Map {
let mut map = Map::new();
map.insert("id".into(), self.id.into());
map.insert("name".into(), self.name.clone().into());
map.insert("age".into(), self.age.into());
map
}
fn from_rhai_map(map: Map) -> Result<Self, String> {
let id = map.get("id")
.and_then(|d| d.as_int().ok())
.ok_or_else(|| "Field 'id' not found or not an INT".to_string())?;
let name = map.get("name")
.and_then(|d| d.clone().into_string().ok())
.ok_or_else(|| "Field 'name' not found or not a String".to_string())?;
let age = map.get("age")
.and_then(|d| d.as_int().ok())
.ok_or_else(|| "Field 'age' not found or not an INT".to_string())?;
Ok(User { id, name, age })
}
}
// Mock DB connection type
struct OurDB {
// Some mock state
error_mode: bool,
}
impl OurDB {
fn new(error_mode: bool) -> Self {
OurDB { error_mode }
}
}
// Custom error type for testing
#[derive(Debug)]
struct DBError {
message: String,
}
impl fmt::Display for DBError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DB Error: {}", self.message)
}
}
// Test functions for wrap_option_return
fn get_user_by_id(_db: &OurDB, id: INT) -> Option<User> {
if id > 0 {
Some(User { id, name: format!("User {}", id), age: 30 })
} else {
None
}
}
// Test functions for wrap_vec_return
fn get_all_users(_db: &OurDB) -> Vec<User> {
vec![
User { id: 1, name: "User 1".to_string(), age: 30 },
User { id: 2, name: "User 2".to_string(), age: 25 },
]
}
fn get_users_by_age(_db: &OurDB, min_age: INT) -> Vec<User> {
vec![
User { id: 1, name: "User 1".to_string(), age: 30 },
User { id: 2, name: "User 2".to_string(), age: 25 },
].into_iter().filter(|u| u.age >= min_age).collect()
}
fn get_all_user_ids() -> Vec<INT> {
vec![1, 2, 3]
}
// Test functions for wrap_option_vec_return
fn find_users_by_name(_db: &OurDB, name_part: String) -> Option<Vec<User>> {
if name_part.is_empty() {
None
} else {
Some(vec![
User { id: 1, name: format!("{} One", name_part), age: 30 },
User { id: 2, name: format!("{} Two", name_part), age: 25 },
])
}
}
// Test functions for result-returning wrappers
fn get_user_by_id_result(db: &OurDB, id: INT) -> Result<Option<User>, DBError> {
if db.error_mode {
Err(DBError { message: "DB connection error".to_string() })
} else if id > 0 {
Ok(Some(User { id, name: format!("User {}", id), age: 30 }))
} else {
Ok(None)
}
}
fn get_all_users_result(db: &OurDB) -> Result<Vec<User>, DBError> {
if db.error_mode {
Err(DBError { message: "DB connection error".to_string() })
} else {
Ok(vec![
User { id: 1, name: "User 1".to_string(), age: 30 },
User { id: 2, name: "User 2".to_string(), age: 25 },
])
}
}
fn find_users_by_name_result(db: &OurDB, name_part: String) -> Result<Option<Vec<User>>, DBError> {
if db.error_mode {
Err(DBError { message: "DB connection error".to_string() })
} else if name_part.is_empty() {
Ok(None)
} else {
Ok(Some(vec![
User { id: 1, name: format!("{} One", name_part), age: 30 },
User { id: 2, name: format!("{} Two", name_part), age: 25 },
]))
}
}
// Test methods for method wrappers
struct UserCollection {
users: Vec<User>,
error_mode: bool,
}
impl UserCollection {
fn new(error_mode: bool) -> Self {
UserCollection {
users: vec![
User { id: 1, name: "User 1".to_string(), age: 30 },
User { id: 2, name: "User 2".to_string(), age: 25 },
],
error_mode,
}
}
fn get_by_id(&self, id: INT) -> Result<Option<User>, DBError> {
if self.error_mode {
Err(DBError { message: "Collection error".to_string() })
} else {
Ok(self.users.iter().find(|u| u.id == id).cloned())
}
}
fn get_all(&self) -> Result<Vec<User>, DBError> {
if self.error_mode {
Err(DBError { message: "Collection error".to_string() })
} else {
Ok(self.users.clone())
}
}
fn find_by_name(&self, name_part: String) -> Result<Option<Vec<User>>, DBError> {
if self.error_mode {
Err(DBError { message: "Collection error".to_string() })
} else if name_part.is_empty() {
Ok(None)
} else {
Ok(Some(self.users.iter()
.filter(|u| u.name.contains(&name_part))
.cloned()
.collect()))
}
}
}
#[test]
fn test_wrap_option_return() {
let mut engine = Engine::new();
let db = OurDB::new(false);
// Register the wrapped function
engine.register_fn(
"get_user_by_id_rhai",
wrap_option_return!(get_user_by_id, &OurDB, INT => User)
);
// Register the User type
engine.build_type::<User>();
// Test with existing user
let script1 = r#"
let user = get_user_by_id_rhai(db, 1);
user.id
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"user.id",
(db.clone(), )
).unwrap();
assert_eq!(result1, 1);
// Test with non-existing user
let script2 = r#"
let user = get_user_by_id_rhai(db, 0);
if user == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if user == () { 42 } else { 0 }",
(db, )
).unwrap();
assert_eq!(result2, 42);
}
#[test]
fn test_wrap_vec_return() {
let mut engine = Engine::new();
let db = OurDB::new(false);
// Register the wrapped functions
engine.register_fn(
"get_all_users_rhai",
wrap_vec_return!(get_all_users, &OurDB => User)
);
engine.register_fn(
"get_users_by_age_rhai",
wrap_vec_return!(get_users_by_age, &OurDB, INT => User)
);
engine.register_fn(
"get_all_user_ids_rhai",
wrap_vec_return!(get_all_user_ids, () => INT)
);
// Register the User type
engine.build_type::<User>();
// Test get_all_users
let script1 = r#"
let users = get_all_users_rhai(db);
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(db.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test get_users_by_age
let script2 = r#"
let users = get_users_by_age_rhai(db, 30);
users.len()
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"users.len()",
(db, )
).unwrap();
assert_eq!(result2, 1);
// Test get_all_user_ids
let script3 = r#"
let ids = get_all_user_ids_rhai();
ids.len()
"#;
let result3 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script3,
"ids.len()",
()
).unwrap();
assert_eq!(result3, 3);
}
#[test]
fn test_wrap_option_vec_return() {
let mut engine = Engine::new();
let db = OurDB::new(false);
// Register the wrapped function
engine.register_fn(
"find_users_by_name_rhai",
wrap_option_vec_return!(find_users_by_name, &OurDB, String => User)
);
// Register the User type
engine.build_type::<User>();
// Test with found users
let script1 = r#"
let users = find_users_by_name_rhai(db, "User");
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(db.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test with no users found
let script2 = r#"
let users = find_users_by_name_rhai(db, "");
if users == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if users == () { 42 } else { 0 }",
(db, )
).unwrap();
assert_eq!(result2, 42);
}
#[test]
fn test_wrap_option_return_result() {
let mut engine = Engine::new();
let db_ok = OurDB::new(false);
let db_err = OurDB::new(true);
// Register the wrapped function
engine.register_result_fn(
"get_user_by_id_result_rhai",
wrap_option_return_result!(get_user_by_id_result, &OurDB, INT => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with existing user
let script1 = r#"
let user = get_user_by_id_result_rhai(db, 1);
user.id
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"user.id",
(db_ok.clone(), )
).unwrap();
assert_eq!(result1, 1);
// Test with non-existing user
let script2 = r#"
let user = get_user_by_id_result_rhai(db, 0);
if user == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if user == () { 42 } else { 0 }",
(db_ok.clone(), )
).unwrap();
assert_eq!(result2, 42);
// Test with error
let script3 = r#"
try {
let user = get_user_by_id_result_rhai(db, 1);
0
} catch(err) {
if err.contains("DB connection error") { 99 } else { 0 }
}
"#;
let result3 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script3,
"try_catch_block",
(db_err, )
).unwrap();
assert_eq!(result3, 99);
}
#[test]
fn test_wrap_vec_return_result() {
let mut engine = Engine::new();
let db_ok = OurDB::new(false);
let db_err = OurDB::new(true);
// Register the wrapped function
engine.register_result_fn(
"get_all_users_result_rhai",
wrap_vec_return_result!(get_all_users_result, &OurDB => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with successful result
let script1 = r#"
let users = get_all_users_result_rhai(db);
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(db_ok.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test with error
let script2 = r#"
try {
let users = get_all_users_result_rhai(db);
0
} catch(err) {
if err.contains("DB connection error") { 99 } else { 0 }
}
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"try_catch_block",
(db_err, )
).unwrap();
assert_eq!(result2, 99);
}
#[test]
fn test_wrap_option_vec_return_result() {
let mut engine = Engine::new();
let db_ok = OurDB::new(false);
let db_err = OurDB::new(true);
// Register the wrapped function
engine.register_result_fn(
"find_users_by_name_result_rhai",
wrap_option_vec_return_result!(find_users_by_name_result, &OurDB, String => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with found users
let script1 = r#"
let users = find_users_by_name_result_rhai(db, "User");
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(db_ok.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test with no users found
let script2 = r#"
let users = find_users_by_name_result_rhai(db, "");
if users == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if users == () { 42 } else { 0 }",
(db_ok.clone(), )
).unwrap();
assert_eq!(result2, 42);
// Test with error
let script3 = r#"
try {
let users = find_users_by_name_result_rhai(db, "User");
0
} catch(err) {
if err.contains("DB connection error") { 99 } else { 0 }
}
"#;
let result3 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script3,
"try_catch_block",
(db_err, )
).unwrap();
assert_eq!(result3, 99);
}
#[test]
fn test_wrap_option_method_result() {
let mut engine = Engine::new();
let collection_ok = UserCollection::new(false);
let collection_err = UserCollection::new(true);
// Register the wrapped method
engine.register_result_fn(
"get_by_id_rhai",
wrap_option_method_result!(get_by_id, &UserCollection, INT => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with existing user
let script1 = r#"
let user = get_by_id_rhai(collection, 1);
user.id
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"user.id",
(collection_ok.clone(), )
).unwrap();
assert_eq!(result1, 1);
// Test with non-existing user
let script2 = r#"
let user = get_by_id_rhai(collection, 999);
if user == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if user == () { 42 } else { 0 }",
(collection_ok.clone(), )
).unwrap();
assert_eq!(result2, 42);
// Test with error
let script3 = r#"
try {
let user = get_by_id_rhai(collection, 1);
0
} catch(err) {
if err.contains("Collection error") { 99 } else { 0 }
}
"#;
let result3 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script3,
"try_catch_block",
(collection_err, )
).unwrap();
assert_eq!(result3, 99);
}
#[test]
fn test_wrap_vec_method_result() {
let mut engine = Engine::new();
let collection_ok = UserCollection::new(false);
let collection_err = UserCollection::new(true);
// Register the wrapped method
engine.register_result_fn(
"get_all_rhai",
wrap_vec_method_result!(get_all, &UserCollection => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with successful result
let script1 = r#"
let users = get_all_rhai(collection);
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(collection_ok.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test with error
let script2 = r#"
try {
let users = get_all_rhai(collection);
0
} catch(err) {
if err.contains("Collection error") { 99 } else { 0 }
}
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"try_catch_block",
(collection_err, )
).unwrap();
assert_eq!(result2, 99);
}
#[test]
fn test_wrap_option_vec_method_result() {
let mut engine = Engine::new();
let collection_ok = UserCollection::new(false);
let collection_err = UserCollection::new(true);
// Register the wrapped method
engine.register_result_fn(
"find_by_name_rhai",
wrap_option_vec_method_result!(find_by_name, &UserCollection, String => User, DBError)
);
// Register the User type
engine.build_type::<User>();
// Test with found users
let script1 = r#"
let users = find_by_name_rhai(collection, "User");
users.len()
"#;
let result1 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script1,
"users.len()",
(collection_ok.clone(), )
).unwrap();
assert_eq!(result1, 2);
// Test with no users found
let script2 = r#"
let users = find_by_name_rhai(collection, "");
if users == () { 42 } else { 0 }
"#;
let result2 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script2,
"if users == () { 42 } else { 0 }",
(collection_ok.clone(), )
).unwrap();
assert_eq!(result2, 42);
// Test with error
let script3 = r#"
try {
let users = find_by_name_rhai(collection, "User");
0
} catch(err) {
if err.contains("Collection error") { 99 } else { 0 }
}
"#;
let result3 = engine.call_fn::<INT>(
&mut rhai::Scope::new(),
script3,
"try_catch_block",
(collection_err, )
).unwrap();
assert_eq!(result3, 99);
}