Add rhai_fn nested attribute and skip fn parameter

This commit is contained in:
J Henry Waugh 2020-08-15 21:51:14 -05:00
parent db9d8b81cf
commit bcf14025a7
15 changed files with 357 additions and 15 deletions

View File

@ -17,6 +17,15 @@ use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
pub(crate) struct ExportedFnParams { pub(crate) struct ExportedFnParams {
pub name: Option<String>, pub name: Option<String>,
pub return_raw: bool, pub return_raw: bool,
pub skip: bool,
}
impl ExportedFnParams {
pub fn skip() -> ExportedFnParams {
let mut skip = ExportedFnParams::default();
skip.skip = true;
skip
}
} }
impl Parse for ExportedFnParams { impl Parse for ExportedFnParams {
@ -68,6 +77,7 @@ impl Parse for ExportedFnParams {
let mut name = None; let mut name = None;
let mut return_raw = false; let mut return_raw = false;
let mut skip = false;
for (ident, value) in attrs.drain() { for (ident, value) in attrs.drain() {
match (ident.to_string().as_ref(), value) { match (ident.to_string().as_ref(), value) {
("name", Some(s)) => name = Some(s.value()), ("name", Some(s)) => name = Some(s.value()),
@ -76,6 +86,10 @@ impl Parse for ExportedFnParams {
("return_raw", Some(s)) => { ("return_raw", Some(s)) => {
return Err(syn::Error::new(s.span(), "extraneous value")) return Err(syn::Error::new(s.span(), "extraneous value"))
} }
("skip", None) => skip = true,
("skip", Some(s)) => {
return Err(syn::Error::new(s.span(), "extraneous value"))
}
(attr, _) => { (attr, _) => {
return Err(syn::Error::new( return Err(syn::Error::new(
ident.span(), ident.span(),
@ -85,7 +99,7 @@ impl Parse for ExportedFnParams {
} }
} }
Ok(ExportedFnParams { name, return_raw }) Ok(ExportedFnParams { name, return_raw, skip, ..Default::default() })
} }
} }
@ -95,7 +109,7 @@ pub(crate) struct ExportedFn {
signature: syn::Signature, signature: syn::Signature,
is_public: bool, is_public: bool,
mut_receiver: bool, mut_receiver: bool,
params: ExportedFnParams, pub params: ExportedFnParams,
} }
impl Parse for ExportedFn { impl Parse for ExportedFn {

View File

@ -1,7 +1,7 @@
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{parse::Parse, parse::ParseStream}; use syn::{parse::Parse, parse::ParseStream};
use crate::function::ExportedFn; use crate::function::{ExportedFn, ExportedFnParams};
use crate::rhai_module::ExportedConst; use crate::rhai_module::ExportedConst;
#[cfg(no_std)] #[cfg(no_std)]
@ -12,6 +12,22 @@ use std::vec as new_vec;
#[cfg(no_std)] #[cfg(no_std)]
use core::mem; use core::mem;
fn inner_fn_attributes(f: &mut syn::ItemFn) -> syn::Result<ExportedFnParams> {
if let Some(rhai_fn_idx) = f.attrs.iter().position(|a| {
a.path
.get_ident()
.map(|i| i.to_string() == "rhai_fn")
.unwrap_or(false)
}) {
let rhai_fn_attr = f.attrs.remove(rhai_fn_idx);
rhai_fn_attr.parse_args()
} else if let syn::Visibility::Public(_) = f.vis {
Ok(ExportedFnParams::default())
} else {
Ok(ExportedFnParams::skip())
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Module { pub(crate) struct Module {
mod_all: Option<syn::ItemMod>, mod_all: Option<syn::ItemMod>,
@ -21,25 +37,27 @@ pub(crate) struct Module {
impl Parse for Module { impl Parse for Module {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let mod_all: syn::ItemMod = input.parse()?; let mut mod_all: syn::ItemMod = input.parse()?;
let fns: Vec<_>; let fns: Vec<_>;
let consts: Vec<_>; let consts: Vec<_>;
if let Some((_, ref content)) = mod_all.content { if let Some((_, ref mut content)) = mod_all.content {
fns = content fns = content
.iter() .iter_mut()
.filter_map(|item| match item { .filter_map(|item| match item {
syn::Item::Fn(f) => { syn::Item::Fn(f) => Some(f),
if let syn::Visibility::Public(_) = f.vis {
Some(f)
} else {
None
}
}
_ => None, _ => None,
}) })
.try_fold(Vec::new(), |mut vec, itemfn| { .try_fold(Vec::new(), |mut vec, mut itemfn| {
let params = match inner_fn_attributes(&mut itemfn) {
Ok(p) => p,
Err(e) => return Err(e),
};
syn::parse2::<ExportedFn>(itemfn.to_token_stream()) syn::parse2::<ExportedFn>(itemfn.to_token_stream())
.map(|f| vec.push(f)) .map(|mut f| {
f.params = params;
f
})
.map(|f| if !f.params.skip { vec.push(f) })
.map(|_| vec) .map(|_| vec)
})?; })?;
consts = content consts = content
@ -217,6 +235,22 @@ mod module_tests {
assert!(item_mod.consts.is_empty()); assert!(item_mod.consts.is_empty());
} }
#[test]
fn one_skipped_fn_module() {
let input_tokens: TokenStream = quote! {
pub mod one_fn {
#[rhai_fn(skip)]
pub fn get_mystic_number() -> INT {
42
}
}
};
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.fns.is_empty());
assert!(item_mod.consts.is_empty());
}
#[test] #[test]
fn one_private_constant_module() { fn one_private_constant_module() {
let input_tokens: TokenStream = quote! { let input_tokens: TokenStream = quote! {
@ -552,6 +586,36 @@ mod generate_tests {
assert_streams_eq(item_mod.generate(), expected_tokens); assert_streams_eq(item_mod.generate(), expected_tokens);
} }
#[test]
fn one_skipped_fn_module() {
let input_tokens: TokenStream = quote! {
pub mod one_fn {
#[rhai_fn(skip)]
pub fn get_mystic_number() -> INT {
42
}
}
};
let expected_tokens = quote! {
pub mod one_fn {
pub fn get_mystic_number() -> INT {
42
}
#[allow(unused_imports)]
use super::*;
#[allow(unused_mut)]
pub fn rhai_module_generate() -> Module {
let mut m = Module::new();
m
}
}
};
let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert_streams_eq(item_mod.generate(), expected_tokens);
}
#[test] #[test]
fn one_private_constant_module() { fn one_private_constant_module() {
let input_tokens: TokenStream = quote! { let input_tokens: TokenStream = quote! {

View File

@ -0,0 +1,36 @@
use rhai::module_resolvers::*;
use rhai::{Engine, EvalAltResult, RegisterFn, FLOAT, INT};
pub mod one_fn_module_nested_attr {
use rhai::plugin::*;
#[export_module]
pub mod advanced_math {
use rhai::plugin::*;
use rhai::FLOAT;
#[rhai_fn(return_raw)]
pub fn get_mystic_number() -> Result<Dynamic, Box<EvalAltResult>> {
Ok(Dynamic::from(42.0 as FLOAT))
}
}
}
#[test]
fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new();
let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math);
let mut r = StaticModuleResolver::new();
r.insert("Math::Advanced".to_string(), m);
engine.set_module_resolver(Some(r));
assert_eq!(
engine.eval::<FLOAT>(
r#"import "Math::Advanced" as math;
let m = math::get_mystic_number();
m"#
)?,
42.0
);
Ok(())
}

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn(unknown = "thing")]
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");
}
}

View File

@ -0,0 +1,11 @@
error: unknown attribute 'unknown'
--> $DIR/rhai_fn_bad_attr.rs:11:11
|
11 | #[rhai_fn(unknown = "thing")]
| ^^^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_bad_attr.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn(name = true)]
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");
}
}

View File

@ -0,0 +1,11 @@
error: expecting string literal
--> $DIR/rhai_fn_bad_value.rs:11:18
|
11 | #[rhai_fn(name = true)]
| ^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_bad_value.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn(return_raw = "yes")]
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");
}
}

View File

@ -0,0 +1,11 @@
error: extraneous value
--> $DIR/rhai_fn_extra_value.rs:11:24
|
11 | #[rhai_fn(return_raw = "yes")]
| ^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_extra_value.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn("wheeeee")]
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");
}
}

View File

@ -0,0 +1,11 @@
error: expecting identifier
--> $DIR/rhai_fn_junk_arg.rs:11:11
|
11 | #[rhai_fn("wheeeee")]
| ^^^^^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_junk_arg.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn(name)]
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");
}
}

View File

@ -0,0 +1,11 @@
error: requires value
--> $DIR/rhai_fn_missing_value.rs:11:11
|
11 | #[rhai_fn(name)]
| ^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_missing_value.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`

View File

@ -0,0 +1,27 @@
use rhai::plugin::*;
#[derive(Clone)]
struct Point {
x: f32,
y: f32,
}
#[export_module]
pub mod test_module {
#[rhai_fn(rhai::name = "thing")]
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");
}
}

View File

@ -0,0 +1,11 @@
error: expecting attribute name
--> $DIR/rhai_fn_path_attr.rs:11:11
|
11 | #[rhai_fn(rhai::name = "thing")]
| ^^^^^^^^^^
error[E0433]: failed to resolve: use of undeclared type or module `test_module`
--> $DIR/rhai_fn_path_attr.rs:22:8
|
22 | if test_module::test_fn(n) {
| ^^^^^^^^^^^ use of undeclared type or module `test_module`