commit
ecce9c5477
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -52,7 +52,7 @@ jobs:
|
|||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --all ${{matrix.flags}}
|
args: ${{matrix.flags}}
|
||||||
# no-std builds are a bit more extensive to test
|
# no-std builds are a bit more extensive to test
|
||||||
no_std_build:
|
no_std_build:
|
||||||
name: NoStdBuild
|
name: NoStdBuild
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -1,8 +1,14 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
".",
|
||||||
|
"codegen"
|
||||||
|
]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
homepage = "https://github.com/jonathandturner/rhai"
|
homepage = "https://github.com/jonathandturner/rhai"
|
||||||
repository = "https://github.com/jonathandturner/rhai"
|
repository = "https://github.com/jonathandturner/rhai"
|
||||||
@ -38,7 +44,7 @@ internals = [] # expose internal data structures
|
|||||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||||
|
|
||||||
# compiling for no-std
|
# compiling for no-std
|
||||||
no_std = [ "no_closure", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
|
no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
@ -20,10 +20,6 @@ Bug fixes
|
|||||||
* Imported modules now work inside closures.
|
* Imported modules now work inside closures.
|
||||||
* Closures that capture now work under `no_object`.
|
* Closures that capture now work under `no_object`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.18.2
|
|
||||||
==============
|
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968", "Stephen Chung"]
|
authors = ["jhwgh1968"]
|
||||||
description = "Proceducral macro support package for Rhai, a scripting language for Rust"
|
description = "Proceducral macro support package for Rhai, a scripting language for Rust"
|
||||||
homepage = "https://github.com/jonathandturner/rhai"
|
homepage = "https://github.com/jonathandturner/rhai"
|
||||||
repository = "https://github.com/jonathandturner/rhai"
|
repository = "https://github.com/jonathandturner/rhai"
|
||||||
|
@ -1,5 +1,18 @@
|
|||||||
use syn::{parse::ParseStream, parse::Parser, spanned::Spanned};
|
use syn::{parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ExportScope {
|
||||||
|
PubOnly,
|
||||||
|
Prefix(String),
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExportScope {
|
||||||
|
fn default() -> ExportScope {
|
||||||
|
ExportScope::PubOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ExportedParams: Sized {
|
pub trait ExportedParams: Sized {
|
||||||
fn parse_stream(args: ParseStream) -> syn::Result<Self>;
|
fn parse_stream(args: ParseStream) -> syn::Result<Self>;
|
||||||
fn no_attrs() -> Self;
|
fn no_attrs() -> Self;
|
||||||
|
@ -10,14 +10,16 @@ use alloc::format;
|
|||||||
#[cfg(not(no_std))]
|
#[cfg(not(no_std))]
|
||||||
use std::format;
|
use std::format;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||||
|
|
||||||
use crate::attrs::{ExportInfo, ExportedParams};
|
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct ExportedFnParams {
|
pub(crate) struct ExportedFnParams {
|
||||||
pub name: Option<String>,
|
pub name: Option<Vec<String>>,
|
||||||
pub return_raw: bool,
|
pub return_raw: bool,
|
||||||
pub skip: bool,
|
pub skip: bool,
|
||||||
pub span: Option<proc_macro2::Span>,
|
pub span: Option<proc_macro2::Span>,
|
||||||
@ -53,11 +55,12 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_info(
|
fn from_info(info: crate::attrs::ExportInfo) -> syn::Result<Self> {
|
||||||
info: crate::attrs::ExportInfo,
|
let ExportInfo {
|
||||||
) -> syn::Result<Self> {
|
item_span: span,
|
||||||
let ExportInfo { item_span: span, items: attrs } = info;
|
items: attrs,
|
||||||
let mut name = None;
|
} = info;
|
||||||
|
let mut name = Vec::new();
|
||||||
let mut return_raw = false;
|
let mut return_raw = false;
|
||||||
let mut skip = false;
|
let mut skip = false;
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
@ -71,15 +74,15 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
"Rhai function names may not contain dot",
|
"Rhai function names may not contain dot",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
name = Some(s.value())
|
name.push(s.value())
|
||||||
}
|
}
|
||||||
("get", Some(s)) => name = Some(make_getter(&s.value())),
|
("get", Some(s)) => name.push(make_getter(&s.value())),
|
||||||
("set", Some(s)) => name = Some(make_setter(&s.value())),
|
("set", Some(s)) => name.push(make_setter(&s.value())),
|
||||||
("get", None) | ("set", None) | ("name", None) => {
|
("get", None) | ("set", None) | ("name", None) => {
|
||||||
return Err(syn::Error::new(key.span(), "requires value"))
|
return Err(syn::Error::new(key.span(), "requires value"))
|
||||||
}
|
}
|
||||||
("index_get", None) => name = Some(FN_IDX_GET.to_string()),
|
("index_get", None) => name.push(FN_IDX_GET.to_string()),
|
||||||
("index_set", None) => name = Some(FN_IDX_SET.to_string()),
|
("index_set", None) => name.push(FN_IDX_SET.to_string()),
|
||||||
("return_raw", None) => return_raw = true,
|
("return_raw", None) => return_raw = true,
|
||||||
("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => {
|
("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => {
|
||||||
return Err(syn::Error::new(s.span(), "extraneous value"))
|
return Err(syn::Error::new(s.span(), "extraneous value"))
|
||||||
@ -96,7 +99,7 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExportedFnParams {
|
Ok(ExportedFnParams {
|
||||||
name,
|
name: if name.is_empty() { None } else { Some(name) },
|
||||||
return_raw,
|
return_raw,
|
||||||
skip,
|
skip,
|
||||||
span: Some(span),
|
span: Some(span),
|
||||||
@ -222,10 +225,24 @@ impl ExportedFn {
|
|||||||
&self.params
|
&self.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_scope(&mut self, parent_scope: &ExportScope) {
|
||||||
|
let keep = match (self.params.skip, parent_scope) {
|
||||||
|
(true, _) => false,
|
||||||
|
(_, ExportScope::PubOnly) => self.is_public,
|
||||||
|
(_, ExportScope::Prefix(s)) => self.name().to_string().starts_with(s),
|
||||||
|
(_, ExportScope::All) => true,
|
||||||
|
};
|
||||||
|
self.params.skip = !keep;
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn skipped(&self) -> bool {
|
pub(crate) fn skipped(&self) -> bool {
|
||||||
self.params.skip
|
self.params.skip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn signature(&self) -> &syn::Signature {
|
||||||
|
&self.signature
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn mutable_receiver(&self) -> bool {
|
pub(crate) fn mutable_receiver(&self) -> bool {
|
||||||
self.mut_receiver
|
self.mut_receiver
|
||||||
}
|
}
|
||||||
@ -242,6 +259,14 @@ impl ExportedFn {
|
|||||||
&self.signature.ident
|
&self.signature.ident
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exported_name<'n>(&'n self) -> Cow<'n, str> {
|
||||||
|
if let Some(ref name) = self.params.name {
|
||||||
|
Cow::Borrowed(name.last().unwrap().as_str())
|
||||||
|
} else {
|
||||||
|
Cow::Owned(self.signature.ident.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
||||||
self.signature.inputs.iter()
|
self.signature.inputs.iter()
|
||||||
}
|
}
|
||||||
@ -322,7 +347,9 @@ impl ExportedFn {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let return_span = self.return_type().map(|r| r.span())
|
let return_span = self
|
||||||
|
.return_type()
|
||||||
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
||||||
if !self.params.return_raw {
|
if !self.params.return_raw {
|
||||||
quote_spanned! { return_span=>
|
quote_spanned! { return_span=>
|
||||||
@ -369,11 +396,10 @@ impl ExportedFn {
|
|||||||
|
|
||||||
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream {
|
||||||
let sig_name = self.name().clone();
|
let sig_name = self.name().clone();
|
||||||
let name = self
|
let name = self.params.name.as_ref().map_or_else(
|
||||||
.params
|
|| self.name().to_string(),
|
||||||
.name
|
|names| names.last().unwrap().clone(),
|
||||||
.clone()
|
);
|
||||||
.unwrap_or_else(|| self.name().to_string());
|
|
||||||
|
|
||||||
let arg_count = self.arg_count();
|
let arg_count = self.arg_count();
|
||||||
let is_method_call = self.mutable_receiver();
|
let is_method_call = self.mutable_receiver();
|
||||||
@ -494,7 +520,9 @@ impl ExportedFn {
|
|||||||
// Handle "raw returns", aka cases where the result is a dynamic or an error.
|
// Handle "raw returns", aka cases where the result is a dynamic or an error.
|
||||||
//
|
//
|
||||||
// This allows skipping the Dynamic::from wrap.
|
// This allows skipping the Dynamic::from wrap.
|
||||||
let return_span = self.return_type().map(|r| r.span())
|
let return_span = self
|
||||||
|
.return_type()
|
||||||
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
||||||
let return_expr = if !self.params.return_raw {
|
let return_expr = if !self.params.return_raw {
|
||||||
quote_spanned! { return_span=>
|
quote_spanned! { return_span=>
|
||||||
@ -529,522 +557,3 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod function_tests {
|
|
||||||
use super::ExportedFn;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn minimal_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_nothing() { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "do_nothing");
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_something(x: usize) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "do_something");
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 1);
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().next().unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { x: usize }).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_something(x: usize, y: f32) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "do_something");
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 2);
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().next().unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { x: usize }).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().nth(1).unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { y: f32 }).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn usize_returning_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn get_magic_number() -> usize { 42 }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "get_magic_number");
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 0);
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.return_type().unwrap(),
|
|
||||||
&syn::Type::Path(syn::TypePath {
|
|
||||||
qself: None,
|
|
||||||
path: syn::parse2::<syn::Path>(quote! { usize }).unwrap()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ref_returning_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn get_magic_phrase() -> &'static str { "open sesame" }
|
|
||||||
};
|
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
|
||||||
assert_eq!(format!("{}", err), "cannot return a reference to Rhai");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ptr_returning_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn get_magic_phrase() -> *const str { "open sesame" }
|
|
||||||
};
|
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
|
||||||
assert_eq!(format!("{}", err), "cannot return a pointer to Rhai");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ref_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn greet(who: &Person) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", err),
|
|
||||||
"references from Rhai in this position must be mutable"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ref_second_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn greet(count: usize, who: &Person) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", err),
|
|
||||||
"this type in this position passes from Rhai by value"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mut_ref_second_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn give(item_name: &str, who: &mut Person) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", err),
|
|
||||||
"this type in this position passes from Rhai by value"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn str_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn log(message: &str) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "log");
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 1);
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().next().unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { message: &str }).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn str_second_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn log(level: usize, message: &str) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "log");
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 2);
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().next().unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { level: usize }).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
item_fn.arg_list().nth(1).unwrap(),
|
|
||||||
&syn::parse2::<syn::FnArg>(quote! { message: &str }).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn private_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
fn do_nothing() { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "do_nothing");
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert!(!item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn receiver_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn act_upon(&mut self) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "act_upon");
|
|
||||||
assert!(item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn immutable_receiver_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn act_upon(&self) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_eq!(&item_fn.name().to_string(), "act_upon");
|
|
||||||
assert!(item_fn.mutable_receiver());
|
|
||||||
assert!(item_fn.is_public());
|
|
||||||
assert!(item_fn.return_type().is_none());
|
|
||||||
assert_eq!(item_fn.arg_list().count(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod generate_tests {
|
|
||||||
use super::ExportedFn;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
fn assert_streams_eq(actual: TokenStream, expected: TokenStream) {
|
|
||||||
let actual = actual.to_string();
|
|
||||||
let expected = expected.to_string();
|
|
||||||
if &actual != &expected {
|
|
||||||
let mut counter = 0;
|
|
||||||
let iter = actual
|
|
||||||
.chars()
|
|
||||||
.zip(expected.chars())
|
|
||||||
.inspect(|_| counter += 1)
|
|
||||||
.skip_while(|(a, e)| *a == *e);
|
|
||||||
let (actual_diff, expected_diff) = {
|
|
||||||
let mut actual_diff = String::new();
|
|
||||||
let mut expected_diff = String::new();
|
|
||||||
for (a, e) in iter.take(50) {
|
|
||||||
actual_diff.push(a);
|
|
||||||
expected_diff.push(e);
|
|
||||||
}
|
|
||||||
(actual_diff, expected_diff)
|
|
||||||
};
|
|
||||||
eprintln!("actual != expected, diverge at char {}", counter);
|
|
||||||
}
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn minimal_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_nothing() { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub mod rhai_fn_do_nothing {
|
|
||||||
use super::*;
|
|
||||||
struct Token();
|
|
||||||
impl PluginFunction for Token {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
|
||||||
Ok(Dynamic::from(do_nothing()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn token_callable() -> CallableFunction {
|
|
||||||
CallableFunction::from_plugin(Token())
|
|
||||||
}
|
|
||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub fn dynamic_result_fn() -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::do_nothing()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_arg_usize_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_something(x: usize) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub mod rhai_fn_do_something {
|
|
||||||
use super::*;
|
|
||||||
struct Token();
|
|
||||||
impl PluginFunction for Token {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn token_callable() -> CallableFunction {
|
|
||||||
CallableFunction::from_plugin(Token())
|
|
||||||
}
|
|
||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::do_something(x)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn one_arg_usize_fn_impl() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn do_something(x: usize) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
impl PluginFunction for MyType {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(MyType()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_streams_eq(item_fn.generate_impl("MyType"), expected_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn two_arg_returning_usize_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn add_together(x: usize, y: usize) -> usize { x + y }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub mod rhai_fn_add_together {
|
|
||||||
use super::*;
|
|
||||||
struct Token();
|
|
||||||
impl PluginFunction for Token {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>(),
|
|
||||||
TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn token_callable() -> CallableFunction {
|
|
||||||
CallableFunction::from_plugin(Token())
|
|
||||||
}
|
|
||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::add_together(x, y)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mut_arg_usize_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn increment(x: &mut usize, y: usize) { *x += y; }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub mod rhai_fn_increment {
|
|
||||||
use super::*;
|
|
||||||
struct Token();
|
|
||||||
impl PluginFunction for Token {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<usize>().unwrap();
|
|
||||||
Ok(Dynamic::from(increment(arg0, arg1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { true }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<usize>(),
|
|
||||||
TypeId::of::<usize>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn token_callable() -> CallableFunction {
|
|
||||||
CallableFunction::from_plugin(Token())
|
|
||||||
}
|
|
||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::increment(x, y)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert!(item_fn.mutable_receiver());
|
|
||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn str_arg_fn() {
|
|
||||||
let input_tokens: TokenStream = quote! {
|
|
||||||
pub fn special_print(message: &str) { eprintln!("----{}----", message); }
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected_tokens = quote! {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub mod rhai_fn_special_print {
|
|
||||||
use super::*;
|
|
||||||
struct Token();
|
|
||||||
impl PluginFunction for Token {
|
|
||||||
fn call(&self,
|
|
||||||
args: &mut [&mut Dynamic], pos: Position
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<ImmutableString>();
|
|
||||||
Ok(Dynamic::from(special_print(&arg0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { false }
|
|
||||||
fn is_varadic(&self) -> bool { false }
|
|
||||||
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
|
||||||
fn input_types(&self) -> Box<[TypeId]> {
|
|
||||||
new_vec![TypeId::of::<ImmutableString>()].into_boxed_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn token_callable() -> CallableFunction {
|
|
||||||
CallableFunction::from_plugin(Token())
|
|
||||||
}
|
|
||||||
pub fn token_input_types() -> Box<[TypeId]> {
|
|
||||||
Token().input_types()
|
|
||||||
}
|
|
||||||
type EvalBox = Box<EvalAltResult>;
|
|
||||||
pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, EvalBox> {
|
|
||||||
Ok(Dynamic::from(super::special_print(message)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
|
||||||
assert!(!item_fn.mutable_receiver());
|
|
||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -102,6 +102,9 @@ mod module;
|
|||||||
mod register;
|
mod register;
|
||||||
mod rhai_module;
|
mod rhai_module;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn export_fn(
|
pub fn export_fn(
|
||||||
args: proc_macro::TokenStream,
|
args: proc_macro::TokenStream,
|
||||||
@ -124,10 +127,18 @@ pub fn export_fn(
|
|||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn export_module(
|
pub fn export_module(
|
||||||
_args: proc_macro::TokenStream,
|
args: proc_macro::TokenStream,
|
||||||
input: proc_macro::TokenStream,
|
input: proc_macro::TokenStream,
|
||||||
) -> proc_macro::TokenStream {
|
) -> proc_macro::TokenStream {
|
||||||
let module_def = parse_macro_input!(input as module::Module);
|
let parsed_params = match crate::attrs::outer_item_attributes(args.into(), "export_module") {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(err) => return proc_macro::TokenStream::from(err.to_compile_error()),
|
||||||
|
};
|
||||||
|
let mut module_def = parse_macro_input!(input as module::Module);
|
||||||
|
if let Err(e) = module_def.set_params(parsed_params) {
|
||||||
|
return e.to_compile_error().into();
|
||||||
|
}
|
||||||
|
|
||||||
let tokens = module_def.generate();
|
let tokens = module_def.generate();
|
||||||
proc_macro::TokenStream::from(tokens)
|
proc_macro::TokenStream::from(tokens)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,15 +2,17 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
|
use crate::attrs::ExportScope;
|
||||||
use crate::function::ExportedFn;
|
use crate::function::ExportedFn;
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
pub(crate) type ExportedConst = (String, syn::Expr);
|
pub(crate) type ExportedConst = (String, syn::Expr);
|
||||||
|
|
||||||
pub(crate) fn generate_body(
|
pub(crate) fn generate_body(
|
||||||
fns: &[ExportedFn],
|
fns: &mut [ExportedFn],
|
||||||
consts: &[ExportedConst],
|
consts: &[ExportedConst],
|
||||||
submodules: &[Module],
|
submodules: &mut [Module],
|
||||||
|
parent_scope: &ExportScope,
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
let mut set_fn_stmts: Vec<syn::Stmt> = Vec::new();
|
let mut set_fn_stmts: Vec<syn::Stmt> = Vec::new();
|
||||||
let mut set_const_stmts: Vec<syn::Stmt> = Vec::new();
|
let mut set_const_stmts: Vec<syn::Stmt> = Vec::new();
|
||||||
@ -28,6 +30,7 @@ pub(crate) fn generate_body(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for itemmod in submodules {
|
for itemmod in submodules {
|
||||||
|
itemmod.update_scope(&parent_scope);
|
||||||
if itemmod.skipped() {
|
if itemmod.skipped() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -56,6 +59,7 @@ pub(crate) fn generate_body(
|
|||||||
// NB: these are token streams, because reparsing messes up "> >" vs ">>"
|
// NB: these are token streams, because reparsing messes up "> >" vs ">>"
|
||||||
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||||
for function in fns {
|
for function in fns {
|
||||||
|
function.update_scope(&parent_scope);
|
||||||
if function.skipped() {
|
if function.skipped() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -63,12 +67,12 @@ pub(crate) fn generate_body(
|
|||||||
&format!("{}_token", function.name().to_string()),
|
&format!("{}_token", function.name().to_string()),
|
||||||
function.name().span(),
|
function.name().span(),
|
||||||
);
|
);
|
||||||
let reg_name = function
|
let reg_names = function
|
||||||
.params()
|
.params()
|
||||||
.name
|
.name
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| function.name().to_string());
|
.unwrap_or_else(|| vec![function.name().to_string()]);
|
||||||
let fn_literal = syn::LitStr::new(®_name, proc_macro2::Span::call_site());
|
|
||||||
let fn_input_types: Vec<syn::Expr> = function
|
let fn_input_types: Vec<syn::Expr> = function
|
||||||
.arg_list()
|
.arg_list()
|
||||||
.map(|fnarg| match fnarg {
|
.map(|fnarg| match fnarg {
|
||||||
@ -106,13 +110,17 @@ pub(crate) fn generate_body(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
set_fn_stmts.push(
|
for reg_name in reg_names {
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
let fn_literal = syn::LitStr::new(®_name, proc_macro2::Span::call_site());
|
||||||
m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
|
|
||||||
CallableFunction::from_plugin(#fn_token_name()));
|
set_fn_stmts.push(
|
||||||
})
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
.unwrap(),
|
m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
|
||||||
);
|
CallableFunction::from_plugin(#fn_token_name()));
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
gen_fn_tokens.push(quote! {
|
gen_fn_tokens.push(quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -151,29 +159,33 @@ pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::
|
|||||||
let mut renames = HashMap::<String, proc_macro2::Span>::new();
|
let mut renames = HashMap::<String, proc_macro2::Span>::new();
|
||||||
let mut names = HashMap::<String, proc_macro2::Span>::new();
|
let mut names = HashMap::<String, proc_macro2::Span>::new();
|
||||||
for itemfn in fns.iter() {
|
for itemfn in fns.iter() {
|
||||||
if let Some(ref name) = itemfn.params().name {
|
if let Some(ref names) = itemfn.params().name {
|
||||||
let current_span = itemfn.params().span.as_ref().unwrap();
|
for name in names {
|
||||||
let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| {
|
let current_span = itemfn.params().span.as_ref().unwrap();
|
||||||
let type_string: String = match fnarg {
|
let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| {
|
||||||
syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"),
|
let type_string: String = match fnarg {
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
syn::FnArg::Receiver(_) => {
|
||||||
ty.as_ref().to_token_stream().to_string()
|
unimplemented!("receiver rhai_fns not implemented")
|
||||||
}
|
}
|
||||||
};
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
||||||
argstr.push('.');
|
ty.as_ref().to_token_stream().to_string()
|
||||||
argstr.push_str(&type_string);
|
}
|
||||||
argstr
|
};
|
||||||
});
|
argstr.push('.');
|
||||||
if let Some(other_span) = renames.insert(key, *current_span) {
|
argstr.push_str(&type_string);
|
||||||
let mut err = syn::Error::new(
|
argstr
|
||||||
*current_span,
|
});
|
||||||
format!("duplicate Rhai signature for '{}'", &name),
|
if let Some(other_span) = renames.insert(key, *current_span) {
|
||||||
);
|
let mut err = syn::Error::new(
|
||||||
err.combine(syn::Error::new(
|
*current_span,
|
||||||
other_span,
|
format!("duplicate Rhai signature for '{}'", &name),
|
||||||
format!("duplicated function renamed '{}'", &name),
|
);
|
||||||
));
|
err.combine(syn::Error::new(
|
||||||
return Err(err);
|
other_span,
|
||||||
|
format!("duplicated function renamed '{}'", &name),
|
||||||
|
));
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ident = itemfn.name();
|
let ident = itemfn.name();
|
||||||
|
518
codegen/src/test/function.rs
Normal file
518
codegen/src/test/function.rs
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod function_tests {
|
||||||
|
use crate::function::ExportedFn;
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minimal_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_nothing() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "do_nothing");
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(x: usize) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "do_something");
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 1);
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().next().unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { x: usize }).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(x: usize, y: f32) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "do_something");
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 2);
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().next().unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { x: usize }).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().nth(1).unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { y: f32 }).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn usize_returning_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn get_magic_number() -> usize { 42 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "get_magic_number");
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.return_type().unwrap(),
|
||||||
|
&syn::Type::Path(syn::TypePath {
|
||||||
|
qself: None,
|
||||||
|
path: syn::parse2::<syn::Path>(quote! { usize }).unwrap()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ref_returning_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn get_magic_phrase() -> &'static str { "open sesame" }
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
|
assert_eq!(format!("{}", err), "cannot return a reference to Rhai");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ptr_returning_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn get_magic_phrase() -> *const str { "open sesame" }
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
|
assert_eq!(format!("{}", err), "cannot return a pointer to Rhai");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ref_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn greet(who: &Person) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", err),
|
||||||
|
"references from Rhai in this position must be mutable"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ref_second_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn greet(count: usize, who: &Person) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", err),
|
||||||
|
"this type in this position passes from Rhai by value"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mut_ref_second_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn give(item_name: &str, who: &mut Person) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = syn::parse2::<ExportedFn>(input_tokens).unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", err),
|
||||||
|
"this type in this position passes from Rhai by value"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn log(message: &str) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "log");
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 1);
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().next().unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { message: &str }).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_second_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn log(level: usize, message: &str) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "log");
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 2);
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().next().unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { level: usize }).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
item_fn.arg_list().nth(1).unwrap(),
|
||||||
|
&syn::parse2::<syn::FnArg>(quote! { message: &str }).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn private_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
fn do_nothing() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "do_nothing");
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert!(!item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn receiver_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn act_upon(&mut self) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "act_upon");
|
||||||
|
assert!(item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn immutable_receiver_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn act_upon(&self) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_eq!(&item_fn.name().to_string(), "act_upon");
|
||||||
|
assert!(item_fn.mutable_receiver());
|
||||||
|
assert!(item_fn.is_public());
|
||||||
|
assert!(item_fn.return_type().is_none());
|
||||||
|
assert_eq!(item_fn.arg_list().count(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod generate_tests {
|
||||||
|
use crate::function::ExportedFn;
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
fn assert_streams_eq(actual: TokenStream, expected: TokenStream) {
|
||||||
|
let actual = actual.to_string();
|
||||||
|
let expected = expected.to_string();
|
||||||
|
if &actual != &expected {
|
||||||
|
let mut counter = 0;
|
||||||
|
let iter = actual
|
||||||
|
.chars()
|
||||||
|
.zip(expected.chars())
|
||||||
|
.inspect(|_| counter += 1)
|
||||||
|
.skip_while(|(a, e)| *a == *e);
|
||||||
|
let (actual_diff, expected_diff) = {
|
||||||
|
let mut actual_diff = String::new();
|
||||||
|
let mut expected_diff = String::new();
|
||||||
|
for (a, e) in iter.take(50) {
|
||||||
|
actual_diff.push(a);
|
||||||
|
expected_diff.push(e);
|
||||||
|
}
|
||||||
|
(actual_diff, expected_diff)
|
||||||
|
};
|
||||||
|
eprintln!("actual != expected, diverge at char {}", counter);
|
||||||
|
}
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minimal_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_nothing() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_do_nothing {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
|
Ok(Dynamic::from(do_nothing()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
CallableFunction::from_plugin(Token())
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
type EvalBox = Box<EvalAltResult>;
|
||||||
|
pub fn dynamic_result_fn() -> Result<Dynamic, EvalBox> {
|
||||||
|
Ok(Dynamic::from(super::do_nothing()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_arg_usize_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(x: usize) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_do_something {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
||||||
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
CallableFunction::from_plugin(Token())
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
type EvalBox = Box<EvalAltResult>;
|
||||||
|
pub fn dynamic_result_fn(x: usize) -> Result<Dynamic, EvalBox> {
|
||||||
|
Ok(Dynamic::from(super::do_something(x)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_arg_usize_fn_impl() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(x: usize) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
impl PluginFunction for MyType {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
||||||
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(MyType()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_fn.generate_impl("MyType"), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_arg_returning_usize_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn add_together(x: usize, y: usize) -> usize { x + y }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_add_together {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
||||||
|
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
||||||
|
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>(),
|
||||||
|
TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
CallableFunction::from_plugin(Token())
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
type EvalBox = Box<EvalAltResult>;
|
||||||
|
pub fn dynamic_result_fn(x: usize, y: usize) -> Result<Dynamic, EvalBox> {
|
||||||
|
Ok(Dynamic::from(super::add_together(x, y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mut_arg_usize_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn increment(x: &mut usize, y: usize) { *x += y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_increment {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
|
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
||||||
|
let arg0: &mut _ = &mut args[0usize].write_lock::<usize>().unwrap();
|
||||||
|
Ok(Dynamic::from(increment(arg0, arg1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { true }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>(),
|
||||||
|
TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
CallableFunction::from_plugin(Token())
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
type EvalBox = Box<EvalAltResult>;
|
||||||
|
pub fn dynamic_result_fn(x: &mut usize, y: usize) -> Result<Dynamic, EvalBox> {
|
||||||
|
Ok(Dynamic::from(super::increment(x, y)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert!(item_fn.mutable_receiver());
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn str_arg_fn() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn special_print(message: &str) { eprintln!("----{}----", message); }
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_special_print {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self,
|
||||||
|
args: &mut [&mut Dynamic], pos: Position
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).clone().cast::<ImmutableString>();
|
||||||
|
Ok(Dynamic::from(special_print(&arg0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_varadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<ImmutableString>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
CallableFunction::from_plugin(Token())
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
type EvalBox = Box<EvalAltResult>;
|
||||||
|
pub fn dynamic_result_fn(message: &str) -> Result<Dynamic, EvalBox> {
|
||||||
|
Ok(Dynamic::from(super::special_print(message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert!(!item_fn.mutable_receiver());
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
}
|
2
codegen/src/test/mod.rs
Normal file
2
codegen/src/test/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod function;
|
||||||
|
mod module;
|
1243
codegen/src/test/module.rs
Normal file
1243
codegen/src/test/module.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -221,3 +221,225 @@ fn duplicate_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(&output_array[1].as_int().unwrap(), &43);
|
assert_eq!(&output_array[1].as_int().unwrap(), &43);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod multiple_fn_rename {
|
||||||
|
use rhai::plugin::*;
|
||||||
|
#[export_module]
|
||||||
|
pub mod my_adds {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
pub fn get_mystic_number() -> FLOAT {
|
||||||
|
42.0
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "add", name = "+", name = "add_together")]
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2 * 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "add", name = "+", name = "add_together")]
|
||||||
|
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2 * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "prop", get = "prop")]
|
||||||
|
pub fn get_prop(x: FLOAT) -> FLOAT {
|
||||||
|
x * 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "idx", index_get)]
|
||||||
|
pub fn index(x: FLOAT, i: INT) -> FLOAT {
|
||||||
|
x + (i as FLOAT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds);
|
||||||
|
engine.load_package(m);
|
||||||
|
|
||||||
|
let output_array = engine.eval::<Array>(
|
||||||
|
r#"
|
||||||
|
let fx = get_mystic_number();
|
||||||
|
let fy1 = add(fx, 1.0);
|
||||||
|
let fy2 = add_together(fx, 1.0);
|
||||||
|
let fy3 = fx + 1.0;
|
||||||
|
let p1 = fx.prop;
|
||||||
|
let p2 = prop(fx);
|
||||||
|
let idx1 = fx[1];
|
||||||
|
let idx2 = idx(fx, 1);
|
||||||
|
let ix = 42;
|
||||||
|
let iy1 = add(ix, 1);
|
||||||
|
let iy2 = add_together(ix, 1);
|
||||||
|
let iy3 = ix + 1;
|
||||||
|
[fy1, fy2, fy3, iy1, iy2, iy3, p1, p2, idx1, idx2]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
assert_eq!(&output_array[0].as_float().unwrap(), &44.0);
|
||||||
|
assert_eq!(&output_array[1].as_float().unwrap(), &44.0);
|
||||||
|
assert_eq!(&output_array[2].as_float().unwrap(), &44.0);
|
||||||
|
assert_eq!(&output_array[3].as_int().unwrap(), &44);
|
||||||
|
assert_eq!(&output_array[4].as_int().unwrap(), &44);
|
||||||
|
assert_eq!(&output_array[5].as_int().unwrap(), &44);
|
||||||
|
assert_eq!(&output_array[6].as_float().unwrap(), &84.0);
|
||||||
|
assert_eq!(&output_array[7].as_float().unwrap(), &84.0);
|
||||||
|
assert_eq!(&output_array[8].as_float().unwrap(), &43.0);
|
||||||
|
assert_eq!(&output_array[9].as_float().unwrap(), &43.0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
mod export_by_prefix {
|
||||||
|
use rhai::plugin::*;
|
||||||
|
#[export_module(export_prefix = "foo_")]
|
||||||
|
pub mod my_adds {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
#[rhai_fn(name = "foo_add_f")]
|
||||||
|
pub fn foo_add1(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "bar_add_i")]
|
||||||
|
fn foo_add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "foo_add_float2")]
|
||||||
|
pub fn add_float2(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foo_m(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo_n(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bar_m(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let m = rhai::exported_module!(crate::export_by_prefix::my_adds);
|
||||||
|
let mut r = StaticModuleResolver::new();
|
||||||
|
r.insert("Math::Advanced".to_string(), m);
|
||||||
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
|
let output_array = engine.eval::<Array>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::foo_add_f(ex, 1.0);
|
||||||
|
let gx = math::foo_m(41.0, 1.0);
|
||||||
|
let ei = 41;
|
||||||
|
let fi = math::bar_add_i(ei, 1);
|
||||||
|
let gi = math::foo_n(41, 1);
|
||||||
|
[fx, gx, fi, gi]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[2].as_int().unwrap(), &42);
|
||||||
|
assert_eq!(&output_array[3].as_int().unwrap(), &42);
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::foo_add_float2(ex, 1.0);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::foo_add_float2 (f64, f64)"
|
||||||
|
&& p == rhai::Position::new(3, 23)));
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::bar_m(ex, 1.0);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::bar_m (f64, f64)"
|
||||||
|
&& p == rhai::Position::new(3, 23)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
mod export_all {
|
||||||
|
use rhai::plugin::*;
|
||||||
|
#[export_module(export_all)]
|
||||||
|
pub mod my_adds {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
#[rhai_fn(name = "foo_add_f")]
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "foo_add_i")]
|
||||||
|
fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(skip)]
|
||||||
|
pub fn add_float2(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foo_m(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo_n(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(skip)]
|
||||||
|
fn foo_p(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 * i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_all_test() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let m = rhai::exported_module!(crate::export_all::my_adds);
|
||||||
|
let mut r = StaticModuleResolver::new();
|
||||||
|
r.insert("Math::Advanced".to_string(), m);
|
||||||
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
|
let output_array = engine.eval::<Array>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::foo_add_f(ex, 1.0);
|
||||||
|
let gx = math::foo_m(41.0, 1.0);
|
||||||
|
let ei = 41;
|
||||||
|
let fi = math::foo_add_i(ei, 1);
|
||||||
|
let gi = math::foo_n(41, 1);
|
||||||
|
[fx, gx, fi, gi]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[2].as_int().unwrap(), &42);
|
||||||
|
assert_eq!(&output_array[3].as_int().unwrap(), &42);
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<INT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41;
|
||||||
|
let fx = math::foo_p(ex, 1);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::foo_p (i64, i64)"
|
||||||
|
&& p == rhai::Position::new(3, 23)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rhai::module_resolvers::*;
|
use rhai::module_resolvers::*;
|
||||||
use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT, INT};
|
use rhai::{Array, Engine, EvalAltResult, RegisterFn, FLOAT, INT};
|
||||||
|
|
||||||
pub mod one_fn_module_nested_attr {
|
pub mod one_fn_module_nested_attr {
|
||||||
use rhai::plugin::*;
|
use rhai::plugin::*;
|
||||||
@ -70,3 +70,133 @@ fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod export_nested_by_prefix {
|
||||||
|
use rhai::plugin::*;
|
||||||
|
#[export_module(export_prefix = "foo_")]
|
||||||
|
|
||||||
|
pub mod my_adds {
|
||||||
|
pub mod foo_first_adders {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod foo_second_adders {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_mod(name = "foo_third_adders")]
|
||||||
|
pub mod baz_third_adders {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod bar_fourth_adders {
|
||||||
|
use rhai::{FLOAT, INT};
|
||||||
|
|
||||||
|
pub fn add_float(f1: FLOAT, f2: FLOAT) -> FLOAT {
|
||||||
|
f1 + f2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_int(i1: INT, i2: INT) -> INT {
|
||||||
|
i1 + i2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds);
|
||||||
|
let mut r = StaticModuleResolver::new();
|
||||||
|
r.insert("Math::Advanced".to_string(), m);
|
||||||
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
|
let output_array = engine.eval::<Array>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::foo_first_adders::add_float(ex, 1.0);
|
||||||
|
|
||||||
|
let ei = 41;
|
||||||
|
let fi = math::foo_first_adders::add_int(ei, 1);
|
||||||
|
|
||||||
|
let gx = 41.0;
|
||||||
|
let hx = math::foo_second_adders::add_float(gx, 1.0);
|
||||||
|
|
||||||
|
let gi = 41;
|
||||||
|
let hi = math::foo_second_adders::add_int(gi, 1);
|
||||||
|
|
||||||
|
[fx, hx, fi, hi]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
assert_eq!(&output_array[0].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[1].as_float().unwrap(), &42.0);
|
||||||
|
assert_eq!(&output_array[2].as_int().unwrap(), &42);
|
||||||
|
assert_eq!(&output_array[3].as_int().unwrap(), &42);
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::foo_third_adders::add_float(ex, 1.0);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::foo_third_adders::add_float (f64, f64)"
|
||||||
|
&& p == rhai::Position::new(3, 41)));
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41;
|
||||||
|
let fx = math::foo_third_adders::add_int(ex, 1);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::foo_third_adders::add_int (i64, i64)"
|
||||||
|
&& p == rhai::Position::new(3, 41)));
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41;
|
||||||
|
let fx = math::bar_fourth_adders::add_int(ex, 1);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::bar_fourth_adders::add_int (i64, i64)"
|
||||||
|
&& p == rhai::Position::new(3, 42)));
|
||||||
|
|
||||||
|
|
||||||
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
|
r#"import "Math::Advanced" as math;
|
||||||
|
let ex = 41.0;
|
||||||
|
let fx = math::bar_fourth_adders::add_float(ex, 1.0);
|
||||||
|
fx
|
||||||
|
"#).unwrap_err(),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(s, p)
|
||||||
|
if s == "math::bar_fourth_adders::add_float (f64, f64)"
|
||||||
|
&& p == rhai::Position::new(3, 42)));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0277]: the trait bound `NonClonable: std::clone::Clone` is not satisfied
|
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||||
--> $DIR/non_clonable.rs:11:23
|
--> $DIR/non_clonable.rs:11:23
|
||||||
|
|
|
|
||||||
11 | pub fn test_fn(input: NonClonable) -> bool {
|
11 | pub fn test_fn(input: NonClonable) -> bool {
|
||||||
| ^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `NonClonable`
|
| ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0277]: the trait bound `NonClonable: std::clone::Clone` is not satisfied
|
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||||
--> $DIR/non_clonable_second.rs:11:27
|
--> $DIR/non_clonable_second.rs:11:27
|
||||||
|
|
|
|
||||||
11 | pub fn test_fn(a: u32, b: NonClonable) -> bool {
|
11 | pub fn test_fn(a: u32, b: NonClonable) -> bool {
|
||||||
| ^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `NonClonable`
|
| ^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
27
codegen/ui_tests/rhai_fn_non_clonable_return.rs
Normal file
27
codegen/ui_tests/rhai_fn_non_clonable_return.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
struct NonClonable {
|
||||||
|
a: f32,
|
||||||
|
b: u32,
|
||||||
|
c: char,
|
||||||
|
d: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_fn]
|
||||||
|
pub fn test_fn(input: f32) -> NonClonable {
|
||||||
|
NonClonable {
|
||||||
|
a: input,
|
||||||
|
b: 10,
|
||||||
|
c: 'a',
|
||||||
|
d: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = test_fn(20.0);
|
||||||
|
if n.c == 'a' {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
10
codegen/ui_tests/rhai_fn_non_clonable_return.stderr
Normal file
10
codegen/ui_tests/rhai_fn_non_clonable_return.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||||
|
--> $DIR/rhai_fn_non_clonable_return.rs:11:8
|
||||||
|
|
|
||||||
|
11 | pub fn test_fn(input: f32) -> NonClonable {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/src/any.rs
|
||||||
|
|
|
||||||
|
| pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
|
| ----- required by this bound in `rhai::Dynamic::from`
|
@ -0,0 +1,30 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod test_module {
|
||||||
|
pub use super::Point;
|
||||||
|
#[rhai_fn(name = "foo", get = "bar")]
|
||||||
|
pub fn test_fn(input: Point) -> bool {
|
||||||
|
input.x > input.y
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(get = "bar")]
|
||||||
|
pub fn foo(input: Point) -> bool {
|
||||||
|
input.x < input.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = Point { x: 0.0, y: 10.0 };
|
||||||
|
if test_module::test_fn(n) {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
error: duplicate Rhai signature for 'get$bar'
|
||||||
|
--> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:17:15
|
||||||
|
|
|
||||||
|
17 | #[rhai_fn(get = "bar")]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: duplicated function renamed 'get$bar'
|
||||||
|
--> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:12:15
|
||||||
|
|
|
||||||
|
12 | #[rhai_fn(name = "foo", get = "bar")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
|
||||||
|
--> $DIR/rhai_fn_rename_collision_oneattr_multiple.rs:25:8
|
||||||
|
|
|
||||||
|
25 | if test_module::test_fn(n) {
|
||||||
|
| ^^^^^^^^^^^ use of undeclared type or module `test_module`
|
25
codegen/ui_tests/rhai_fn_rename_collision_with_itself.rs
Normal file
25
codegen/ui_tests/rhai_fn_rename_collision_with_itself.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod test_module {
|
||||||
|
pub use super::Point;
|
||||||
|
#[rhai_fn(name = "foo", name = "bar", name = "foo")]
|
||||||
|
pub fn test_fn(input: Point) -> bool {
|
||||||
|
input.x > input.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = Point { x: 0.0, y: 10.0 };
|
||||||
|
if test_module::test_fn(n) {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
17
codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr
Normal file
17
codegen/ui_tests/rhai_fn_rename_collision_with_itself.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error: duplicate Rhai signature for 'foo'
|
||||||
|
--> $DIR/rhai_fn_rename_collision_with_itself.rs:12:15
|
||||||
|
|
|
||||||
|
12 | #[rhai_fn(name = "foo", name = "bar", name = "foo")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: duplicated function renamed 'foo'
|
||||||
|
--> $DIR/rhai_fn_rename_collision_with_itself.rs:12:15
|
||||||
|
|
|
||||||
|
12 | #[rhai_fn(name = "foo", name = "bar", name = "foo")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
|
||||||
|
--> $DIR/rhai_fn_rename_collision_with_itself.rs:20:8
|
||||||
|
|
|
||||||
|
20 | if test_module::test_fn(n) {
|
||||||
|
| ^^^^^^^^^^^ use of undeclared type or module `test_module`
|
29
codegen/ui_tests/rhai_mod_non_clonable_return.rs
Normal file
29
codegen/ui_tests/rhai_mod_non_clonable_return.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
struct NonClonable {
|
||||||
|
a: f32,
|
||||||
|
b: u32,
|
||||||
|
c: char,
|
||||||
|
d: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
pub mod test_mod {
|
||||||
|
pub fn test_fn(input: f32) -> NonClonable {
|
||||||
|
NonClonable {
|
||||||
|
a: input,
|
||||||
|
b: 10,
|
||||||
|
c: 'a',
|
||||||
|
d: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = test_mod::test_fn(20.0);
|
||||||
|
if n.c == 'a' {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
10
codegen/ui_tests/rhai_mod_non_clonable_return.stderr
Normal file
10
codegen/ui_tests/rhai_mod_non_clonable_return.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||||
|
--> $DIR/rhai_mod_non_clonable_return.rs:12:12
|
||||||
|
|
|
||||||
|
12 | pub fn test_fn(input: f32) -> NonClonable {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
|
|
||||||
|
::: $WORKSPACE/src/any.rs
|
||||||
|
|
|
||||||
|
| pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
|
| ----- required by this bound in `rhai::Dynamic::from`
|
@ -46,8 +46,8 @@ The Rhai Scripting Language
|
|||||||
2. [Load a Plugin Module as a Package](rust/packages/plugin.md)
|
2. [Load a Plugin Module as a Package](rust/packages/plugin.md)
|
||||||
3. [Manually Create a Custom Package](rust/packages/create.md)
|
3. [Manually Create a Custom Package](rust/packages/create.md)
|
||||||
10. [Plugins](plugins/index.md)
|
10. [Plugins](plugins/index.md)
|
||||||
1. [Create a Plugin Module](plugins/module.md)
|
1. [Export a Rust Module](plugins/module.md)
|
||||||
2. [Create a Plugin Function](plugins/function.md)
|
2. [Export a Rust Function](plugins/function.md)
|
||||||
5. [Rhai Language Reference](language/index.md)
|
5. [Rhai Language Reference](language/index.md)
|
||||||
1. [Comments](language/comments.md)
|
1. [Comments](language/comments.md)
|
||||||
2. [Values and Types](language/values-and-types.md)
|
2. [Values and Types](language/values-and-types.md)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Create a Plugin Function
|
Export a Rust Function to Rhai
|
||||||
========================
|
=============================
|
||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
@ -11,12 +11,11 @@ individual functions instead of a full-blown [plugin module].
|
|||||||
Macros
|
Macros
|
||||||
------
|
------
|
||||||
|
|
||||||
| Macro | Apply to | Behavior |
|
| Macro | Apply to | Behavior |
|
||||||
| ------------------------ | ------------------------------------------------------------- | --------------------------------------------------------- |
|
| ----------------------- | --------------------------------------------------------------- | -------------------------------------------------------- |
|
||||||
| `#[export_fn]` | Rust function defined in module | Export the function |
|
| `#[export_fn]` | Rust function defined in a Rust module | Export the function |
|
||||||
| `#[rhai_fn(return_raw)]` | Rust function returning `Result<Dynamic, Box<EvalAltResult>>` | Specify that this is a [fallible function] |
|
| `register_exported_fn!` | [`Engine`] instance, register name string, use path to function | Register function into an [`Engine`] under specific name |
|
||||||
| `register_exported_fn!` | [`Engine`] instance, register name, function name | Register function into the [`Engine`] under specific name |
|
| `set_exported_fn!` | [`Module`] instance, register name string, use path to function | Register function into an [`Module`] under specific name |
|
||||||
| `set_exported_fn!` | [`Module`], register name, function name | Register function into the [`Module`] under specific name |
|
|
||||||
|
|
||||||
|
|
||||||
`#[export_fn]` and `register_exported_fn!`
|
`#[export_fn]` and `register_exported_fn!`
|
||||||
|
@ -3,9 +3,11 @@ Plugins
|
|||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
Rhai contains a robust _plugin_ system that greatly simplifies registration of custom functions.
|
Rhai contains a robust _plugin_ system that greatly simplifies registration of custom
|
||||||
|
functionality.
|
||||||
|
|
||||||
Instead of the large `Engine::register_XXX` API, and the parallel `Module::set_fn_XXX` API,
|
Instead of using the large `Engine::register_XXX` API or the parallel `Module::set_fn_XXX` API,
|
||||||
a _plugin_ simplifies the work of creating and registering multiple functions into an [`Engine`].
|
a _plugin_ simplifies the work of creating and registering new functionality in an [`Engine`].
|
||||||
|
|
||||||
Plugins are processed via a set of procedural macros under the `rhai::plugins` module.
|
Plugins are processed via a set of procedural macros under the `rhai::plugins` module. These
|
||||||
|
allow registering Rust functions directly in the Engine, or adding Rust modules as packages.
|
||||||
|
@ -1,43 +1,26 @@
|
|||||||
Create a Plugin Module
|
Export a Rust Module to Rhai
|
||||||
======================
|
============================
|
||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
|
||||||
The core of creating a plugin [module] is the `#[export_module]` attribute.
|
When applied to a Rust module, the `#[export_module]` attribute generates the necessary
|
||||||
|
code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions. This code
|
||||||
|
is exactly what would need to be written by hand to achieve the same goal, and is custom fit
|
||||||
|
to each exported item.
|
||||||
|
|
||||||
When applied on a module definition, `#[export_module]` automatically generates Rhai-acceptable
|
This Rust module can then either be loaded into an [`Engine`] as a normal [module] or
|
||||||
functions from all `pub` functions defined within.
|
registered as a [custom package]. This is done by using the `exported_module!` macro.
|
||||||
|
|
||||||
The resulting module can then be loaded into an [`Engine`] as a normal [module],
|
|
||||||
or as a [custom package].
|
|
||||||
|
|
||||||
|
|
||||||
Macros
|
Using`#[export_module]` and `exported_module!`
|
||||||
------
|
---------------------------------------------
|
||||||
|
|
||||||
| Macro | Apply to | Behavior |
|
Apply `#[export_module]` onto a Rust module to convert all `pub` functions into Rhai plugin
|
||||||
| --------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------- |
|
functions.
|
||||||
| `#[export_module]` | Rust module | Export all `pub` functions |
|
|
||||||
| `#[rhai_fn(skip)]` | Function in Rust module | Do not export this function |
|
|
||||||
| `#[rhai_fn(return_raw)]` | `pub` function in Rust module returning `Result<Dynamic, Box<EvalAltResult>>` | Specify that this is a [fallible function] |
|
|
||||||
| `#[rhai_fn(name = "...")]` | `pub` function in Rust module | Register function under specific name |
|
|
||||||
| `#[rhai_fn(get = "...")]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a property getter under specific name |
|
|
||||||
| `#[rhai_fn(set = "...")]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a property setter under specific name |
|
|
||||||
| `#[rhai_fn(index_get]` | `pub` function in Rust module (first parameter must be `&mut`) | Register an index getter |
|
|
||||||
| `#[rhai_fn(index_set)]` | `pub` function in Rust module (first parameter must be `&mut`) | Register an index setter |
|
|
||||||
| `#[rhai_mod(name = "...")]` | `pub` sub-module in Rust module | Export the sub-module under specific name |
|
|
||||||
| `exported_module!` | Rust module name | Create a [module] containing exported functions |
|
|
||||||
|
|
||||||
|
|
||||||
`#[export_module]` and `exported_module!`
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
Apply `#[export_module]` onto a standard module to convert all `pub` functions
|
|
||||||
into Rhai plugin functions.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::plugins::*; // import macros
|
use rhai::plugins::*; // a "prelude" import for macros
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod my_module {
|
mod my_module {
|
||||||
@ -58,22 +41,24 @@ mod my_module {
|
|||||||
42
|
42
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to load this into an [`Engine`], use the `load_package` method on the exported module:
|
||||||
|
|
||||||
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
// 'exported_module!' creates the plugin module.
|
// The macro call creates the Rhai module.
|
||||||
let module = exported_module!(my_module);
|
let module = exported_module!(my_module);
|
||||||
|
|
||||||
// A module can simply be loaded as a custom package.
|
// A module can simply be loaded, registering all public its contents.
|
||||||
engine.load_package(module);
|
engine.load_package(module);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above automatically defines a plugin module named `my_module` which can be converted into
|
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`)
|
||||||
a Rhai [module] via `exported_module!`. The functions contained within the module definition
|
are automatically registered into the [`Engine`] when `Engine::load_package` is called.
|
||||||
(i.e. `greet`, `get_num` and `increment`) are automatically registered into the [`Engine`] when
|
|
||||||
`Engine::load_package` is called.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = greet("world");
|
let x = greet("world");
|
||||||
@ -89,6 +74,47 @@ increment(x);
|
|||||||
x == 43;
|
x == 43;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Registering this as a custom package is almost the same, except that a module resolver must
|
||||||
|
point to the module, rather than being loaded directly. See the [module] section for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
|
||||||
|
Function Overloading and Operators
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Operators and overloaded functions can be specified via applying the `#[rhai_fn(name = "...")]`
|
||||||
|
attribute to individual functions.
|
||||||
|
|
||||||
|
The text string given as the `name` parameter to `#[rhai_fn]` is used to register the function with
|
||||||
|
the [`Engine`], disregarding the actual name of the function.
|
||||||
|
|
||||||
|
With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai, so long as they have different parameters.
|
||||||
|
|
||||||
|
Operators (which require function names that are not valid for Rust) can also be registered this way.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::plugins::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod my_module {
|
||||||
|
// This is the '+' operator for 'MyType'.
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
pub fn add(obj: &mut MyType, value: i64) {
|
||||||
|
obj.prop += value;
|
||||||
|
}
|
||||||
|
// This function is 'calc (i64)'.
|
||||||
|
#[rhai_fn(name = "calc")]
|
||||||
|
pub fn calc_with_default(num: i64) -> i64 {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
// This function is 'calc (i64, bool)'.
|
||||||
|
#[rhai_fn(name = "calc")]
|
||||||
|
pub fn calc_with_option(num: i64, option: bool) -> i64 {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Getters, Setters and Indexers
|
Getters, Setters and Indexers
|
||||||
-----------------------------
|
-----------------------------
|
||||||
@ -97,7 +123,7 @@ Functions can be marked as [getters/setters] and [indexers] for [custom types] v
|
|||||||
attribute, which is applied on a function level.
|
attribute, which is applied on a function level.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::plugins::*; // import macros
|
use rhai::plugins::*; // a "prelude" import for macros
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod my_module {
|
mod my_module {
|
||||||
@ -129,43 +155,6 @@ mod my_module {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Function Overloading and Operators
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
Operators and overloaded functions can be specified via `#[rhai_fn(name = "...")]` applied upon
|
|
||||||
individual functions.
|
|
||||||
|
|
||||||
The text string given as the `name` parameter to `#[rhai_fn]` is used to register the function with
|
|
||||||
the [`Engine`], disregarding the actual name of the function.
|
|
||||||
|
|
||||||
With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai.
|
|
||||||
|
|
||||||
Operators (which require function names that are not valid for Rust) can also be registered this way.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use rhai::plugins::*; // import macros
|
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
mod my_module {
|
|
||||||
// This is the '+' operator for 'MyType'.
|
|
||||||
#[rhai_fn(name = "+")]
|
|
||||||
pub fn add(obj: &mut MyType, value: i64) {
|
|
||||||
obj.prop += value;
|
|
||||||
}
|
|
||||||
// This function is 'calc (i64)'.
|
|
||||||
#[rhai_fn(name = "calc")]
|
|
||||||
pub fn calc_with_default(num: i64) -> i64 {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
// This function is 'calc (i64, bool)'.
|
|
||||||
#[rhai_fn(name = "calc")]
|
|
||||||
pub fn calc_with_option(num: i64, option: bool) -> i64 {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Fallible Functions
|
Fallible Functions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@ -176,7 +165,7 @@ A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does n
|
|||||||
have the appropriate return type.
|
have the appropriate return type.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::plugins::*; // import macros
|
use rhai::plugins::*; // a "prelude" import for macros
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod my_module {
|
mod my_module {
|
||||||
@ -192,3 +181,35 @@ mod my_module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`#[export_module]` Parameters
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Parameters can be applied to the `#[export_module]` attribute to override its default behavior.
|
||||||
|
|
||||||
|
| Parameter | Behavior |
|
||||||
|
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| _None_ | Export only public (i.e. `pub`) functions |
|
||||||
|
| `export_all` | Export all functions (including private, non-`pub` functions); use `#[rhai_fn(skip)]` on individual functions to avoid export |
|
||||||
|
| `export_prefix = "..."` | Export functions (including private, non-`pub` functions) with names starting with a specific prefix |
|
||||||
|
|
||||||
|
|
||||||
|
Inner Attributes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Inner attributes can be applied to the inner items of a module to tweak the export process.
|
||||||
|
|
||||||
|
`#[rhai_fn]` is applied to functions, while `#[rhai_mod]` is applied to sub-modules.
|
||||||
|
|
||||||
|
Parameters should be set on inner attributes to specify the desired behavior.
|
||||||
|
|
||||||
|
| Attribute Parameter | Use with | Apply to | Behavior |
|
||||||
|
| ------------------- | --------------------------- | -------------------------------------------------------- | ----------------------------------------------------- |
|
||||||
|
| `skip` | `#[rhai_fn]`, `#[rhai_mod]` | Function or sub-module | Do not export this function/sub-module |
|
||||||
|
| `name = "..."` | `#[rhai_fn]`, `#[rhai_mod]` | Function or sub-module | Register function/sub-module under the specified name |
|
||||||
|
| `get = "..."` | `#[rhai_fn]` | Function with `&mut` first parameter | Register a getter for the named property |
|
||||||
|
| `set = "..."` | `#[rhai_fn]` | Function with `&mut` first parameter | Register a setter for the named property |
|
||||||
|
| `index_get` | `#[rhai_fn]` | Function with `&mut` first parameter | Register an index getter |
|
||||||
|
| `index_set` | `#[rhai_fn]` | Function with `&mut` first parameter | Register an index setter |
|
||||||
|
| `return_raw` | `#[rhai_fn]` | Function returning `Result<Dynamic, Box<EvalAltResult>>` | Mark this as a [fallible function] |
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
cargo-features = ["named-profiles"]
|
cargo-features = ["named-profiles"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "no_std_test"
|
name = "no_std_test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -86,24 +86,16 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod array_functions {
|
mod array_functions {
|
||||||
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(list: &mut Array) -> INT {
|
pub fn len(list: &mut Array) -> INT {
|
||||||
list.len() as INT
|
list.len() as INT
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "len")]
|
#[rhai_fn(name = "append", name = "+=")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn len_prop(list: &mut Array) -> INT {
|
|
||||||
len(list)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn append(x: &mut Array, y: Array) {
|
pub fn append(x: &mut Array, y: Array) {
|
||||||
x.extend(y);
|
x.extend(y);
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+=")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn append_operator(x: &mut Array, y: Array) {
|
|
||||||
append(x, y)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn concat(mut x: Array, y: Array) -> Array {
|
pub fn concat(mut x: Array, y: Array) -> Array {
|
||||||
|
@ -5,10 +5,13 @@ use crate::plugin::*;
|
|||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
|
|
||||||
def_package!(crate:EvalPackage:"Disable 'eval'.", lib, {
|
def_package!(crate:EvalPackage:"Disable 'eval'.", lib, {
|
||||||
set_exported_fn!(lib, "eval", eval_override);
|
lib.combine_flatten(exported_module!(eval_override));
|
||||||
});
|
});
|
||||||
|
|
||||||
#[export_fn(return_raw)]
|
#[export_module]
|
||||||
fn eval_override(_script: ImmutableString) -> Result<Dynamic, Box<EvalAltResult>> {
|
mod eval_override {
|
||||||
Err("eval is evil!".into())
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn eval(_script: ImmutableString) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
Err("eval is evil!".into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,9 @@ def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod fn_ptr_functions {
|
mod fn_ptr_functions {
|
||||||
|
#[rhai_fn(name = "name", get = "name")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
||||||
f.get_fn_name().clone()
|
f.get_fn_name().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(get = "name")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn name_prop(f: &mut FnPtr) -> ImmutableString {
|
|
||||||
name(f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,33 +18,25 @@ mod map_functions {
|
|||||||
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
||||||
map.contains_key(&prop)
|
map.contains_key(&prop)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(map: &mut Map) -> INT {
|
pub fn len(map: &mut Map) -> INT {
|
||||||
map.len() as INT
|
map.len() as INT
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "len")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn len_prop(map: &mut Map) -> INT {
|
|
||||||
len(map)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(map: &mut Map) {
|
pub fn clear(map: &mut Map) {
|
||||||
map.clear();
|
map.clear();
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn remove(x: &mut Map, name: ImmutableString) -> Dynamic {
|
pub fn remove(x: &mut Map, name: ImmutableString) -> Dynamic {
|
||||||
x.remove(&name).unwrap_or_else(|| ().into())
|
x.remove(&name).unwrap_or_else(|| ().into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "mixin", name = "+=")]
|
||||||
pub fn mixin(map1: &mut Map, map2: Map) {
|
pub fn mixin(map1: &mut Map, map2: Map) {
|
||||||
map2.into_iter().for_each(|(key, value)| {
|
map2.into_iter().for_each(|(key, value)| {
|
||||||
map1.insert(key, value);
|
map1.insert(key, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+=")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn mixin_operator(map1: &mut Map, map2: Map) {
|
|
||||||
mixin(map1, map2)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn merge(mut map1: Map, map2: Map) -> Map {
|
pub fn merge(mut map1: Map, map2: Map) -> Map {
|
||||||
map2.into_iter().for_each(|(key, value)| {
|
map2.into_iter().for_each(|(key, value)| {
|
||||||
|
@ -157,78 +157,46 @@ mod float_functions {
|
|||||||
pub fn log10(x: FLOAT) -> FLOAT {
|
pub fn log10(x: FLOAT) -> FLOAT {
|
||||||
x.log10()
|
x.log10()
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "floor", get = "floor")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn floor(x: FLOAT) -> FLOAT {
|
pub fn floor(x: FLOAT) -> FLOAT {
|
||||||
x.floor()
|
x.floor()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "floor")]
|
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn floor_prop(x: FLOAT) -> FLOAT {
|
|
||||||
floor(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn ceiling(x: FLOAT) -> FLOAT {
|
pub fn ceiling(x: FLOAT) -> FLOAT {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "ceiling")]
|
#[rhai_fn(name = "round", get = "round")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn ceiling_prop(x: FLOAT) -> FLOAT {
|
|
||||||
ceiling(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn round(x: FLOAT) -> FLOAT {
|
pub fn round(x: FLOAT) -> FLOAT {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "round")]
|
#[rhai_fn(name = "int", get = "int")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn round_prop(x: FLOAT) -> FLOAT {
|
|
||||||
ceiling(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn int(x: FLOAT) -> FLOAT {
|
pub fn int(x: FLOAT) -> FLOAT {
|
||||||
x.trunc()
|
x.trunc()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "int")]
|
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn int_prop(x: FLOAT) -> FLOAT {
|
|
||||||
int(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn fraction(x: FLOAT) -> FLOAT {
|
pub fn fraction(x: FLOAT) -> FLOAT {
|
||||||
x.fract()
|
x.fract()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "fraction")]
|
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn fraction_prop(x: FLOAT) -> FLOAT {
|
|
||||||
fraction(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_nan(x: FLOAT) -> bool {
|
pub fn is_nan(x: FLOAT) -> bool {
|
||||||
x.is_nan()
|
x.is_nan()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "is_nan")]
|
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_nan_prop(x: FLOAT) -> bool {
|
|
||||||
is_nan(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_finite(x: FLOAT) -> bool {
|
pub fn is_finite(x: FLOAT) -> bool {
|
||||||
x.is_finite()
|
x.is_finite()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "is_finite")]
|
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_finite_prop(x: FLOAT) -> bool {
|
|
||||||
is_finite(x)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_infinite(x: FLOAT) -> bool {
|
pub fn is_infinite(x: FLOAT) -> bool {
|
||||||
x.is_infinite()
|
x.is_infinite()
|
||||||
}
|
}
|
||||||
#[rhai_fn(get = "is_infinite")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_infinite_prop(x: FLOAT) -> bool {
|
|
||||||
is_infinite(x)
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn f32_to_int(x: f32) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
@ -140,6 +140,7 @@ mod string_functions {
|
|||||||
pub fn add_prepend_unit(_x: (), s: ImmutableString) -> ImmutableString {
|
pub fn add_prepend_unit(_x: (), s: ImmutableString) -> ImmutableString {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+=")]
|
#[rhai_fn(name = "+=")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn append_char(s: &mut ImmutableString, ch: char) {
|
pub fn append_char(s: &mut ImmutableString, ch: char) {
|
||||||
@ -151,17 +152,12 @@ mod string_functions {
|
|||||||
*s += &add;
|
*s += &add;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(s: &mut ImmutableString) -> INT {
|
pub fn len(s: &mut ImmutableString) -> INT {
|
||||||
s.chars().count() as INT
|
s.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(get = "len")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn len_prop(s: &mut ImmutableString) -> INT {
|
|
||||||
len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(s: &mut ImmutableString) {
|
pub fn clear(s: &mut ImmutableString) {
|
||||||
s.make_mut().clear();
|
s.make_mut().clear();
|
||||||
|
@ -30,7 +30,8 @@ mod time_functions {
|
|||||||
pub fn timestamp() -> Instant {
|
pub fn timestamp() -> Instant {
|
||||||
Instant::now()
|
Instant::now()
|
||||||
}
|
}
|
||||||
#[rhai_fn(return_raw)]
|
|
||||||
|
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
||||||
pub fn elapsed(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn elapsed(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
@ -52,12 +53,6 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(get = "elapsed", return_raw)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn elapsed_prop(timestamp: &mut Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
elapsed(timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(return_raw, name = "-")]
|
#[rhai_fn(return_raw, name = "-")]
|
||||||
pub fn time_diff(ts1: Instant, ts2: Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn time_diff(ts1: Instant, ts2: Instant) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -12,6 +12,8 @@ mod test {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub mod feature {
|
pub mod feature {
|
||||||
|
use rhai::{Array, Dynamic, EvalAltResult};
|
||||||
|
|
||||||
#[rhai_fn(get = "foo", return_raw)]
|
#[rhai_fn(get = "foo", return_raw)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn foo(array: &mut Array) -> Result<Dynamic, Box<EvalAltResult>> {
|
pub fn foo(array: &mut Array) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -19,7 +21,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "test")]
|
#[rhai_fn(name = "test", name = "hi")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(array: &mut Array, mul: INT) -> INT {
|
pub fn len(array: &mut Array, mul: INT) -> INT {
|
||||||
(array.len() as INT) * mul
|
(array.len() as INT) * mul
|
||||||
@ -72,6 +74,8 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||||
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; hi(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user