Merge pull request #304 from schungx/master
Print arrays and maps using to_debug.
This commit is contained in:
commit
344e99d2be
12
RELEASES.md
12
RELEASES.md
@ -4,6 +4,18 @@ Rhai Release Notes
|
|||||||
Version 0.19.7
|
Version 0.19.7
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Fixes compilation errors with certain feature flag combinations.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Property getters/setters and indexers defined in a plugin module are by default `#[rhai_fn(global)]`.
|
||||||
|
* `to_debug` is a new standard function for converting a value into debug format.
|
||||||
|
* Arrays and object maps now print values using `to_debug` (if available).
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.6
|
Version 0.19.6
|
||||||
==============
|
==============
|
||||||
|
@ -4,7 +4,8 @@ use quote::{quote, ToTokens};
|
|||||||
|
|
||||||
use crate::attrs::ExportScope;
|
use crate::attrs::ExportScope;
|
||||||
use crate::function::{
|
use crate::function::{
|
||||||
flatten_type_groups, print_type, ExportedFn, FnNamespaceAccess, FnSpecialAccess,
|
flatten_type_groups, print_type, ExportedFn, FnNamespaceAccess, FnSpecialAccess, FN_GET,
|
||||||
|
FN_IDX_GET, FN_IDX_SET, FN_SET,
|
||||||
};
|
};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
@ -81,7 +82,6 @@ pub(crate) fn generate_body(
|
|||||||
function.name().span(),
|
function.name().span(),
|
||||||
);
|
);
|
||||||
let reg_names = function.exported_names();
|
let reg_names = function.exported_names();
|
||||||
let mut namespace = FnNamespaceAccess::Internal;
|
|
||||||
|
|
||||||
let fn_input_names: Vec<String> = function
|
let fn_input_names: Vec<String> = function
|
||||||
.arg_list()
|
.arg_list()
|
||||||
@ -141,11 +141,27 @@ pub(crate) fn generate_body(
|
|||||||
.map(print_type)
|
.map(print_type)
|
||||||
.unwrap_or_else(|| "()".to_string());
|
.unwrap_or_else(|| "()".to_string());
|
||||||
|
|
||||||
if let Some(ns) = function.params().namespace {
|
|
||||||
namespace = ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
for fn_literal in reg_names {
|
for fn_literal in reg_names {
|
||||||
|
let mut namespace = FnNamespaceAccess::Internal;
|
||||||
|
|
||||||
|
match function.params().special {
|
||||||
|
FnSpecialAccess::None => {}
|
||||||
|
FnSpecialAccess::Index(_) | FnSpecialAccess::Property(_) => {
|
||||||
|
let reg_name = fn_literal.value();
|
||||||
|
if reg_name.starts_with(FN_GET)
|
||||||
|
|| reg_name.starts_with(FN_SET)
|
||||||
|
|| reg_name == FN_IDX_GET
|
||||||
|
|| reg_name == FN_IDX_SET
|
||||||
|
{
|
||||||
|
namespace = FnNamespaceAccess::Global;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ns) = function.params().namespace {
|
||||||
|
namespace = ns;
|
||||||
|
}
|
||||||
|
|
||||||
let ns_str = syn::Ident::new(
|
let ns_str = syn::Ident::new(
|
||||||
match namespace {
|
match namespace {
|
||||||
FnNamespaceAccess::Global => "Global",
|
FnNamespaceAccess::Global => "Global",
|
||||||
|
@ -1519,7 +1519,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]),
|
m.set_fn("get$square", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "u64"]),
|
||||||
&[core::any::TypeId::of::<u64>()],
|
&[core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
@ -1597,7 +1597,7 @@ mod generate_tests {
|
|||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
m.set_fn("square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
m.set_fn("get$square", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "u64"]), &[core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
}
|
}
|
||||||
@ -1672,7 +1672,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
m.set_fn("set$squared", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
||||||
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
@ -1752,7 +1752,7 @@ mod generate_tests {
|
|||||||
m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
||||||
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
m.set_fn("set$squared", FnNamespace::Global, FnAccess::Public, Some(&["x: &mut u64", "y: u64", "()"]),
|
||||||
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
&[core::any::TypeId::of::<u64>(), core::any::TypeId::of::<u64>()],
|
||||||
int_foo_token().into());
|
int_foo_token().into());
|
||||||
if flatten {} else {}
|
if flatten {} else {}
|
||||||
@ -1829,7 +1829,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public,
|
m.set_fn("index$get$", FnNamespace::Global, FnAccess::Public,
|
||||||
Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]),
|
Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]),
|
||||||
&[core::any::TypeId::of::<MyCollection>(),
|
&[core::any::TypeId::of::<MyCollection>(),
|
||||||
core::any::TypeId::of::<u64>()],
|
core::any::TypeId::of::<u64>()],
|
||||||
@ -1914,7 +1914,7 @@ mod generate_tests {
|
|||||||
&[core::any::TypeId::of::<MyCollection>(),
|
&[core::any::TypeId::of::<MyCollection>(),
|
||||||
core::any::TypeId::of::<u64>()],
|
core::any::TypeId::of::<u64>()],
|
||||||
get_by_index_token().into());
|
get_by_index_token().into());
|
||||||
m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public,
|
m.set_fn("index$get$", FnNamespace::Global, FnAccess::Public,
|
||||||
Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]),
|
Some(&["x: &mut MyCollection", "i: u64", "FLOAT"]),
|
||||||
&[core::any::TypeId::of::<MyCollection>(),
|
&[core::any::TypeId::of::<MyCollection>(),
|
||||||
core::any::TypeId::of::<u64>()],
|
core::any::TypeId::of::<u64>()],
|
||||||
@ -1994,7 +1994,7 @@ mod generate_tests {
|
|||||||
}
|
}
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||||
m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public,
|
m.set_fn("index$set$", FnNamespace::Global, FnAccess::Public,
|
||||||
Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]),
|
Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]),
|
||||||
&[core::any::TypeId::of::<MyCollection>(),
|
&[core::any::TypeId::of::<MyCollection>(),
|
||||||
core::any::TypeId::of::<u64>(),
|
core::any::TypeId::of::<u64>(),
|
||||||
@ -2083,7 +2083,7 @@ mod generate_tests {
|
|||||||
core::any::TypeId::of::<u64>(),
|
core::any::TypeId::of::<u64>(),
|
||||||
core::any::TypeId::of::<FLOAT>()],
|
core::any::TypeId::of::<FLOAT>()],
|
||||||
set_by_index_token().into());
|
set_by_index_token().into());
|
||||||
m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public,
|
m.set_fn("index$set$", FnNamespace::Global, FnAccess::Public,
|
||||||
Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]),
|
Some(&["x: &mut MyCollection", "i: u64", "item: FLOAT", "()"]),
|
||||||
&[core::any::TypeId::of::<MyCollection>(),
|
&[core::any::TypeId::of::<MyCollection>(),
|
||||||
core::any::TypeId::of::<u64>(),
|
core::any::TypeId::of::<u64>(),
|
||||||
|
@ -38,3 +38,5 @@ is an alias to `Rc<String>` or `Arc<String>` (depending on the [`sync`] feature)
|
|||||||
Any modification done to a Rhai string will cause the string to be cloned and the modifications made to the copy.
|
Any modification done to a Rhai string will cause the string to be cloned and the modifications made to the copy.
|
||||||
|
|
||||||
The `to_string` function converts a standard type into a [string] for display purposes.
|
The `to_string` function converts a standard type into a [string] for display purposes.
|
||||||
|
|
||||||
|
The `to_debug` function converts a standard type into a [string] in debug format.
|
||||||
|
@ -37,7 +37,7 @@ mod MyEnumModule {
|
|||||||
MyEnum::Baz(val1, val2)
|
MyEnum::Baz(val1, val2)
|
||||||
}
|
}
|
||||||
// Access to fields
|
// Access to fields
|
||||||
#[rhai_fn(global, get = "enum_type")]
|
#[rhai_fn(get = "enum_type")]
|
||||||
pub fn get_type(a: &mut MyEnum) -> String {
|
pub fn get_type(a: &mut MyEnum) -> String {
|
||||||
match a {
|
match a {
|
||||||
MyEnum::Foo => "Foo".to_string(),
|
MyEnum::Foo => "Foo".to_string(),
|
||||||
@ -45,7 +45,7 @@ mod MyEnumModule {
|
|||||||
MyEnum::Baz(_, _) => "Baz".to_string()
|
MyEnum::Baz(_, _) => "Baz".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(global, get = "field_0")]
|
#[rhai_fn(get = "field_0")]
|
||||||
pub fn get_field_0(a: &mut MyEnum) -> Dynamic {
|
pub fn get_field_0(a: &mut MyEnum) -> Dynamic {
|
||||||
match a {
|
match a {
|
||||||
MyEnum::Foo => Dynamic::UNIT,
|
MyEnum::Foo => Dynamic::UNIT,
|
||||||
@ -53,7 +53,7 @@ mod MyEnumModule {
|
|||||||
MyEnum::Baz(x, _) => Dynamic::from(x)
|
MyEnum::Baz(x, _) => Dynamic::from(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(global, get = "field_1")]
|
#[rhai_fn(get = "field_1")]
|
||||||
pub fn get_field_1(a: &mut MyEnum) -> Dynamic {
|
pub fn get_field_1(a: &mut MyEnum) -> Dynamic {
|
||||||
match a {
|
match a {
|
||||||
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
||||||
@ -61,7 +61,7 @@ mod MyEnumModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Printing
|
// Printing
|
||||||
#[rhai(global, name = "to_string", name = "print", name = "debug")]
|
#[rhai(global, name = "to_string", name = "print", name = "to_debug", name = "debug")]
|
||||||
pub fn to_string(a: &mut MyEnum) -> String {
|
pub fn to_string(a: &mut MyEnum) -> String {
|
||||||
format!("{:?}", a))
|
format!("{:?}", a))
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,33 @@ struct Handler {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Register API for Any Custom Type
|
||||||
|
|
||||||
|
[Custom types] are often used to hold state. The easiest way to register an entire API is via a [plugin module].
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
// A custom type to a hold state value.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
|
pub struct SomeType {
|
||||||
|
data: i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod SomeTypeAPI {
|
||||||
|
#[rhai_fn(global)]
|
||||||
|
pub func1(obj: &mut SomeType) -> bool { ... }
|
||||||
|
#[rhai_fn(global)]
|
||||||
|
pub func2(obj: &mut SomeType) -> bool { ... }
|
||||||
|
pub process(data: i64) -> i64 { ... }
|
||||||
|
#[rhai_fn(get = "value")]
|
||||||
|
pub get_value(obj: &mut SomeType) -> i64 { obj.data }
|
||||||
|
#[rhai_fn(set = "value")]
|
||||||
|
pub set_value(obj: &mut SomeType, value: i64) { obj.data = value; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Initialize Handler Object
|
### Initialize Handler Object
|
||||||
|
|
||||||
Steps to initialize the event handler:
|
Steps to initialize the event handler:
|
||||||
@ -70,16 +97,10 @@ impl Handler {
|
|||||||
pub new(path: impl Into<PathBuf>) -> Self {
|
pub new(path: impl Into<PathBuf>) -> Self {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
// Register API functions here
|
// Register custom types and API's
|
||||||
engine
|
engine
|
||||||
.register_fn("func1", func1)
|
|
||||||
.register_fn("func2", func2)
|
|
||||||
.register_fn("func3", func3)
|
|
||||||
.register_type_with_name::<SomeType>("SomeType")
|
.register_type_with_name::<SomeType>("SomeType")
|
||||||
.register_get_set("value",
|
.load_package(exported_module!(SomeTypeAPI));
|
||||||
|obj: &mut SomeType| obj.data,
|
|
||||||
|obj: &mut SomeType, value: i64| obj.data = value
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a custom 'Scope' to hold state
|
// Create a custom 'Scope' to hold state
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
@ -112,36 +133,30 @@ Mapping an event from the system into a scripted handler is straight-forward:
|
|||||||
impl Handler {
|
impl Handler {
|
||||||
// Say there are three events: 'start', 'end', 'update'.
|
// Say there are three events: 'start', 'end', 'update'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
pub fn on_event(&mut self, event_name: &str, event_data: i64) {
|
pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Result<(), Error> {
|
||||||
match event_name {
|
match event_name {
|
||||||
// The 'start' event maps to function 'start'.
|
// The 'start' event maps to function 'start'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
"start" =>
|
"start" => self.engine.call_fn(&mut self.scope, &self.ast, "start", (event_data,))?,
|
||||||
self.engine
|
|
||||||
.call_fn(&mut self.scope, &self.ast, "start", (event_data,)).unwrap(),
|
|
||||||
|
|
||||||
// The 'end' event maps to function 'end'.
|
// The 'end' event maps to function 'end'.
|
||||||
// In a real application you'd be handling errors...
|
// In a real application you'd be handling errors...
|
||||||
"end" =>
|
"end" => self.engine.call_fn(&mut self.scope, &self.ast, "end", (event_data,))?,
|
||||||
self.engine
|
|
||||||
.call_fn(&mut self.scope, &self.ast, "end", (event_data,)).unwrap(),
|
|
||||||
|
|
||||||
// The 'update' event maps to function 'update'.
|
// The 'update' event maps to function 'update'.
|
||||||
// This event provides a default implementation when the scripted function
|
// This event provides a default implementation when the scripted function
|
||||||
// is not found.
|
// is not found.
|
||||||
"update" =>
|
"update" => self.engine
|
||||||
self.engine
|
.call_fn(&mut self.scope, &self.ast, "update", (event_data,))
|
||||||
.call_fn(&mut self.scope, &self.ast, "update", (event_data,))
|
.or_else(|err| match *err {
|
||||||
.or_else(|err| match *err {
|
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "update" => {
|
// Default implementation of 'update' event handler
|
||||||
// Default implementation of 'update' event handler
|
self.scope.set_value("state2", SomeType::new(42));
|
||||||
self.scope.set_value("state2", SomeType::new(42));
|
// Turn function-not-found into a success
|
||||||
// Turn function-not-found into a success
|
Ok(Dynamic::UNIT)
|
||||||
Ok(Dynamic::UNIT)
|
}
|
||||||
}
|
_ => Err(err.into())
|
||||||
_ => Err(err.into())
|
})?
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,24 +174,25 @@ fn start(data) {
|
|||||||
if state1 {
|
if state1 {
|
||||||
throw "Already started!";
|
throw "Already started!";
|
||||||
}
|
}
|
||||||
if func1(state2) || func2() {
|
if state2.func1() || state2.func2() {
|
||||||
throw "Conditions not yet ready to start!";
|
throw "Conditions not yet ready to start!";
|
||||||
}
|
}
|
||||||
state1 = true;
|
state1 = true;
|
||||||
state2.value = 0;
|
state2.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(data) {
|
fn end(data) {
|
||||||
if !state1 {
|
if !state1 {
|
||||||
throw "Not yet started!";
|
throw "Not yet started!";
|
||||||
}
|
}
|
||||||
if func1(state2) || func2() {
|
if state2.func1() || state2.func2() {
|
||||||
throw "Conditions not yet ready to start!";
|
throw "Conditions not yet ready to start!";
|
||||||
}
|
}
|
||||||
state1 = false;
|
state1 = false;
|
||||||
|
state2.value = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(data) {
|
fn update(data) {
|
||||||
state2.value += func3(data);
|
state2.value += process(data);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -166,6 +166,9 @@ as well as all _type iterators_, are automatically exposed to the _global_ names
|
|||||||
[iteration]({{rootUrl}}/language/for.md), [getters/setters] and [indexers] for [custom types]
|
[iteration]({{rootUrl}}/language/for.md), [getters/setters] and [indexers] for [custom types]
|
||||||
can work as expected.
|
can work as expected.
|
||||||
|
|
||||||
|
In fact, the default for all [getters/setters] and [indexers] defined in a plugin module
|
||||||
|
is `#[rhai_fn(global)]` unless specifically overridden by `#[rhai_fn(internal)]`.
|
||||||
|
|
||||||
Therefore, in the example above, the `increment` method (defined with `#[rhai_fn(global)]`)
|
Therefore, in the example above, the `increment` method (defined with `#[rhai_fn(global)]`)
|
||||||
works fine when called in method-call style:
|
works fine when called in method-call style:
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ is `T: Display + Debug`):
|
|||||||
| ----------- | ---------------------------------------------- | ---------------------------- | -------------------------------------------------------------------- |
|
| ----------- | ---------------------------------------------- | ---------------------------- | -------------------------------------------------------------------- |
|
||||||
| `to_string` | <code>\|x: &mut T\| -> String</code> | `x.to_string()` | converts the custom type into a [string] |
|
| `to_string` | <code>\|x: &mut T\| -> String</code> | `x.to_string()` | converts the custom type into a [string] |
|
||||||
| `print` | <code>\|x: &mut T\| -> String</code> | `x.to_string()` | converts the custom type into a [string] for the [`print`] statement |
|
| `print` | <code>\|x: &mut T\| -> String</code> | `x.to_string()` | converts the custom type into a [string] for the [`print`] statement |
|
||||||
|
| `to_debug` | <code>\|x: &mut T\| -> String</code> | `format!("{:?}", x)` | converts the custom type into a [string] in debug format |
|
||||||
| `debug` | <code>\|x: &mut T\| -> String</code> | `format!("{:?}", x)` | converts the custom type into a [string] for the [`debug`] statement |
|
| `debug` | <code>\|x: &mut T\| -> String</code> | `format!("{:?}", x)` | converts the custom type into a [string] for the [`debug`] statement |
|
||||||
| `+` | <code>\|s: &str, x: T\| -> String</code> | `format!("{}{}", s, x)` | concatenates the custom type with another [string] |
|
| `+` | <code>\|s: &str, x: T\| -> String</code> | `format!("{}{}", s, x)` | concatenates the custom type with another [string] |
|
||||||
| `+` | <code>\|x: &mut T, s: &str\| -> String</code> | `x.to_string().push_str(s);` | concatenates another [string] with the custom type |
|
| `+` | <code>\|x: &mut T, s: &str\| -> String</code> | `x.to_string().push_str(s);` | concatenates another [string] with the custom type |
|
||||||
|
@ -134,7 +134,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
/// If `is_method` is [`true`], the first argument is assumed to be passed
|
/// If `is_method` is [`true`], the first argument is assumed to be passed
|
||||||
/// by reference and is not consumed.
|
/// by reference and is not consumed.
|
||||||
pub fn call_fn_dynamic_raw(
|
pub fn call_fn_dynamic_raw(
|
||||||
&mut self,
|
&self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
public_only: bool,
|
public_only: bool,
|
||||||
@ -262,7 +262,7 @@ impl FnPtr {
|
|||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
pub fn call_dynamic(
|
pub fn call_dynamic(
|
||||||
&self,
|
&self,
|
||||||
mut ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
@ -650,7 +650,7 @@ mod array_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "==", return_raw)]
|
#[rhai_fn(name = "==", return_raw)]
|
||||||
pub fn equals(
|
pub fn equals(
|
||||||
mut ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
arr1: &mut Array,
|
arr1: &mut Array,
|
||||||
mut arr2: Array,
|
mut arr2: Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
@ -45,7 +45,7 @@ mod map_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "==", return_raw)]
|
#[rhai_fn(name = "==", return_raw)]
|
||||||
pub fn equals(
|
pub fn equals(
|
||||||
mut ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
map1: &mut Map,
|
map1: &mut Map,
|
||||||
mut map2: Map,
|
mut map2: Map,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
@ -15,6 +15,9 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
const FUNC_TO_STRING: &'static str = "to_string";
|
||||||
|
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||||
|
|
||||||
type Unit = ();
|
type Unit = ();
|
||||||
|
|
||||||
macro_rules! gen_functions {
|
macro_rules! gen_functions {
|
||||||
@ -32,13 +35,14 @@ macro_rules! gen_functions {
|
|||||||
|
|
||||||
macro_rules! reg_print_functions {
|
macro_rules! reg_print_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
set_exported_fn!($mod_name, "to_string", $root::$arg_type::to_string_func);
|
set_exported_fn!($mod_name, FUNC_TO_STRING, $root::$arg_type::to_string_func);
|
||||||
set_exported_fn!($mod_name, KEYWORD_PRINT, $root::$arg_type::to_string_func);
|
set_exported_fn!($mod_name, KEYWORD_PRINT, $root::$arg_type::to_string_func);
|
||||||
)* }
|
)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reg_debug_functions {
|
macro_rules! reg_debug_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
|
set_exported_fn!($mod_name, FUNC_TO_DEBUG, $root::$arg_type::to_string_func);
|
||||||
set_exported_fn!($mod_name, KEYWORD_DEBUG, $root::$arg_type::to_string_func);
|
set_exported_fn!($mod_name, KEYWORD_DEBUG, $root::$arg_type::to_string_func);
|
||||||
)* }
|
)* }
|
||||||
}
|
}
|
||||||
@ -67,12 +71,6 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
reg_print_functions!(lib += print_float; f32, f64);
|
reg_print_functions!(lib += print_float; f32, f64);
|
||||||
reg_debug_functions!(lib += debug_float; f32, f64);
|
reg_debug_functions!(lib += debug_float; f32, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
{
|
|
||||||
reg_print_functions!(lib += print_array; Array);
|
|
||||||
reg_debug_functions!(lib += print_array; Array);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
||||||
@ -109,10 +107,18 @@ gen_functions!(print_float => to_string(f32, f64));
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
gen_functions!(debug_float => to_debug(f32, f64));
|
gen_functions!(debug_float => to_debug(f32, f64));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
gen_functions!(print_array => to_debug(Array));
|
|
||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[inline(always)]
|
||||||
|
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
||||||
|
match ctx.call_fn_dynamic_raw(fn_name, true, false, &mut [value], None) {
|
||||||
|
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
||||||
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod print_debug_functions {
|
mod print_debug_functions {
|
||||||
#[rhai_fn(name = "print", name = "debug")]
|
#[rhai_fn(name = "print", name = "debug")]
|
||||||
@ -132,13 +138,50 @@ mod print_debug_functions {
|
|||||||
to_string(f)
|
to_string(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
pub mod array_functions {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "print", name = "to_string", name = "to_debug", name = "debug")]
|
||||||
|
pub fn format_array(ctx: NativeCallContext, arr: &mut Array) -> ImmutableString {
|
||||||
|
let mut result = crate::stdlib::string::String::with_capacity(16);
|
||||||
|
result.push_str("[");
|
||||||
|
|
||||||
|
let len = arr.len();
|
||||||
|
|
||||||
|
arr.iter_mut().enumerate().for_each(|(i, x)| {
|
||||||
|
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
|
||||||
|
if i < len - 1 {
|
||||||
|
result.push_str(", ");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.push_str("]");
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub mod map_functions {
|
pub mod map_functions {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[rhai_fn(name = "print", name = "debug", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string", name = "to_debug", name = "debug")]
|
||||||
pub fn format_map(x: &mut Map) -> ImmutableString {
|
pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
|
||||||
format!("#{:?}", x).into()
|
let mut result = crate::stdlib::string::String::with_capacity(16);
|
||||||
|
result.push_str("#{");
|
||||||
|
|
||||||
|
let len = map.len();
|
||||||
|
|
||||||
|
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
||||||
|
result.push_str(k);
|
||||||
|
result.push_str(": ");
|
||||||
|
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, v));
|
||||||
|
if i < len - 1 {
|
||||||
|
result.push_str(", ");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
result.push_str("}");
|
||||||
|
result.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult};
|
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -30,3 +30,49 @@ fn test_print() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
|
struct MyStruct {
|
||||||
|
field: INT,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MyStruct {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "hello: {}", self.field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[test]
|
||||||
|
fn test_print_custom_type() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_type_with_name::<MyStruct>("MyStruct")
|
||||||
|
.register_fn("to_debug", |x: &mut MyStruct| x.to_string())
|
||||||
|
.register_fn("debug", |x: &mut MyStruct| x.to_string())
|
||||||
|
.register_fn("new_ts", || MyStruct { field: 42 });
|
||||||
|
|
||||||
|
engine.consume("let x = new_ts(); debug(x);")?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(
|
||||||
|
r#"
|
||||||
|
let x = [ 123, true, (), "world", new_ts() ];
|
||||||
|
x.to_string()
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
r#"[123, true, (), "world", hello: 42]"#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(engine
|
||||||
|
.eval::<String>(
|
||||||
|
r#"
|
||||||
|
let x = #{ a:123, b:true, c:(), d:"world", e:new_ts() };
|
||||||
|
x.to_string()
|
||||||
|
"#
|
||||||
|
)?
|
||||||
|
.contains("e: hello: 42"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user