rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.73k stars 175 forks source link

BigDecimal crate input value evaluated to type name #753

Closed MaximeLassalleHub closed 1 year ago

MaximeLassalleHub commented 1 year ago

Hi there,

I'm integrating BigDecimal with Rhai in a project.

I'm perfectly able to call native functions with BigDecimal parameters.

The problem is the values returned from these functions in BigDecimal type seem to fail to be converted to Dynamic at some point.

I get the type name "bigdecimal::BigDecimal" in scope when getting the Dynamic value of a BigDecimal passed to the engine via engine.eval_expression_with_scope.

I've tried to build a custom type but it didn't help.

What am I missing please ?

Thanks

schungx commented 1 year ago

I don't think you're missing anything... a Dynamic can hold any data type including your BigDecimal.

So what you described is correct behaviour.

Maybe if you can share a snippet to demonstrate what you are trying to achieve.

MaximeLassalleHub commented 1 year ago

Hi @schungx ,

I've written a few tests to show you up the problem (sorry for the bad indentation due to copy / paste).

So it looks like evaluating a big decimal works if the return value is typed as BigDecimal but not as Dynamic.

On my side , I really need to execute evaluations in a generic manner and it's not an option to provide an explicit type unfortunately.

As you can observe , it works fine with RustDecimal even with Dynamic return value type (when enabling the "decimal" feature flag only).

I assume something needs to be done to make that work with a custom crate like BigDecimal.

Thanks for helping out,

Regards,

Maxime.

mod test_decimal {
// bigdecimal = { version = "0.4", features = [ "serde" ] }
use bigdecimal::BigDecimal;
// rhai = { version = "1.13.0", features = [ "decimal", "serde", "sync" ] }
use rhai::{serde::to_dynamic, Dynamic, Engine, Scope};

#[test]
// fails for bigdecimal when return value is Dynamic
fn evaluates_dynamic_big_decimal_correctly() {
let mut rhai_engine = Engine::new();
rhai_engine.register_fn("get_bigdec", |value: i64| BigDecimal::from(value));

let res: Result<Dynamic, Box<rhai::EvalAltResult>> =
rhai_engine.eval_expression_with_scope(&mut Scope::new(), "get_bigdec(14)");

println!("res {:?}", res);
// output:
// res Ok(bigdecimal::BigDecimal)
// displays the type name (why ?!)
// is there something I need to add to the engine so it can retrieve the actual bigdecimal value from the Dynamic result
assert_eq!(
res.unwrap().to_string(),
to_dynamic(BigDecimal::from(14)).unwrap().to_string()
);
}

#[test]
// works for rust_decimal when return value is Dynamic
fn evaluates_dynamic_rust_decimal_correctly() {
let mut rhai_engine = Engine::new();
rhai_engine.register_fn("get_dec", |value: i64| rust_decimal::Decimal::from(value));

let res: Result<Dynamic, Box<rhai::EvalAltResult>> =
rhai_engine.eval_expression_with_scope(&mut Scope::new(), "get_dec(14)");

println!("res {:?}", res);
// output:
// res Ok(14)

assert_eq!(
res.unwrap().to_string(),
to_dynamic(rust_decimal::Decimal::from(14))
.unwrap()
.to_string()
);
}

#[test]
// works for bigdecimal when return value is explicit BigDecimal
fn evaluates_explictly_typed_dynamic_big_decimal_correctly() {
let mut rhai_engine = Engine::new();
rhai_engine.register_fn("get_bigdec", |value: i64| BigDecimal::from(value));

let res: Result<BigDecimal, Box<rhai::EvalAltResult>> =
rhai_engine.eval_expression_with_scope(&mut Scope::new(), "get_bigdec(14)");

println!("res {:?}", res);
// output:
// res Ok(BigDecimal("14"))

assert_eq!(
res.unwrap().to_string(),
to_dynamic(BigDecimal::from(14)).unwrap().to_string()
);
}
}
schungx commented 1 year ago

Right away, I see that you're using to_dynamic which uses serde to serialize data. It is most likely NOT what you want, and you don't need to use serde to convert to Dynamic. Your problem probably came from no serde support for BigDecimal?

To convert a BigDecimal to Dynamic:

let value = BigDecimal::from(42);
let dynamic = Dynamic::from(value);

To convert a Dynamic into BigDecimal:

let dynamic = ....;  // get a Dynamic value, from script perhaps?

let value: BigDecimal = dynamic.cast();

// or...

let value: BigDecimal = dynamic.try_cast()?;

Now, printing a BigDecimal outside of Rhai is problematic, as you'd have to do it yourself. BigDecimal is treated as a custom type (since it is not a built-in Rhai data type). There is no facility in Dynamic to detect whether a custom type can be printed or not, so it defaults to just printing the name.

You need to get the value inside the Dynamic and print it yourself, most likely using Dynamic::read_lock.

For details on what you can use on Dynamic, see: https://rhai.rs/book/language/dynamic-rust.html

schungx commented 1 year ago

Is this issue resolved?

schungx commented 1 year ago

OK closing this now. If you have any further issues, feel free to reopen.