export_fn: add return_raw attribute
This commit is contained in:
parent
c5937f990e
commit
07a4541949
@ -1,10 +1,14 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{parse::Parse, parse::ParseStream, spanned::Spanned};
|
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct ExportedFnParams {
|
pub(crate) struct ExportedFnParams {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
pub return_raw: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ExportedFnParams {
|
impl Parse for ExportedFnParams {
|
||||||
@ -12,25 +16,68 @@ impl Parse for ExportedFnParams {
|
|||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(ExportedFnParams::default());
|
return Ok(ExportedFnParams::default());
|
||||||
}
|
}
|
||||||
let assignment: syn::ExprAssign = args.parse()?;
|
|
||||||
|
|
||||||
let attr_name: syn::Ident = match assignment.left.as_ref() {
|
let arg_list = args.call(
|
||||||
syn::Expr::Path(syn::ExprPath { path: attr_path, .. }) => attr_path.get_ident().cloned()
|
syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_separated_nonempty,
|
||||||
.ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?,
|
)?;
|
||||||
x => return Err(syn::Error::new(x.span(), "expecting attribute name")),
|
|
||||||
};
|
let mut attrs: HashMap<proc_macro2::Ident, Option<syn::LitStr>> = HashMap::new();
|
||||||
if &attr_name != "name" {
|
for arg in arg_list {
|
||||||
return Err(syn::Error::new(attr_name.span(), format!("unknown attribute '{}'", &attr_name)));
|
let (left, right) = match arg {
|
||||||
|
syn::Expr::Assign(syn::ExprAssign {
|
||||||
|
ref left,
|
||||||
|
ref right,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let attr_name: syn::Ident = match left.as_ref() {
|
||||||
|
syn::Expr::Path(syn::ExprPath {
|
||||||
|
path: attr_path, ..
|
||||||
|
}) => attr_path.get_ident().cloned().ok_or_else(|| {
|
||||||
|
syn::Error::new(attr_path.span(), "expecting attribute name")
|
||||||
|
})?,
|
||||||
|
x => return Err(syn::Error::new(x.span(), "expecting attribute name")),
|
||||||
|
};
|
||||||
|
let attr_value = match right.as_ref() {
|
||||||
|
syn::Expr::Lit(syn::ExprLit {
|
||||||
|
lit: syn::Lit::Str(string),
|
||||||
|
..
|
||||||
|
}) => string.clone(),
|
||||||
|
x => return Err(syn::Error::new(x.span(), "expecting string literal")),
|
||||||
|
};
|
||||||
|
(attr_name, Some(attr_value))
|
||||||
|
}
|
||||||
|
syn::Expr::Path(syn::ExprPath {
|
||||||
|
path: attr_path, ..
|
||||||
|
}) => attr_path
|
||||||
|
.get_ident()
|
||||||
|
.cloned()
|
||||||
|
.map(|a| (a, None))
|
||||||
|
.ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?,
|
||||||
|
x => return Err(syn::Error::new(x.span(), "expecting identifier")),
|
||||||
|
};
|
||||||
|
attrs.insert(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
let attr_value: String = match assignment.right.as_ref() {
|
let mut name = None;
|
||||||
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(string), .. }) => string.value(),
|
let mut return_raw = false;
|
||||||
x => return Err(syn::Error::new(x.span(), "expecting string literal value")),
|
for (ident, value) in attrs.drain() {
|
||||||
};
|
match (ident.to_string().as_ref(), value) {
|
||||||
|
("name", Some(s)) => name = Some(s.value()),
|
||||||
|
("name", None) => return Err(syn::Error::new(ident.span(), "requires value")),
|
||||||
|
("return_raw", None) => return_raw = true,
|
||||||
|
("return_raw", Some(s)) => {
|
||||||
|
return Err(syn::Error::new(s.span(), "extraneous value"))
|
||||||
|
}
|
||||||
|
(attr, _) => {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
ident.span(),
|
||||||
|
format!("unknown attribute '{}'", attr),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ExportedFnParams {
|
Ok(ExportedFnParams { name, return_raw })
|
||||||
name: Some(attr_value),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,6 +408,19 @@ impl ExportedFn {
|
|||||||
unpack_stmts.push(arg0);
|
unpack_stmts.push(arg0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle "raw returns", aka cases where the result is a dynamic or an error.
|
||||||
|
//
|
||||||
|
// This allows skipping the Dynamic::from wrap.
|
||||||
|
let return_expr = if !self.params.return_raw {
|
||||||
|
quote! {
|
||||||
|
Ok(Dynamic::from(#name(#(#unpack_exprs),*)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
#name(#(#unpack_exprs),*)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
@ -373,7 +433,7 @@ impl ExportedFn {
|
|||||||
args.len(), #arg_count), Position::none())));
|
args.len(), #arg_count), Position::none())));
|
||||||
}
|
}
|
||||||
#(#unpack_stmts)*
|
#(#unpack_stmts)*
|
||||||
Ok(Dynamic::from(#name(#(#unpack_exprs),*)))
|
#return_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_method_call(&self) -> bool { #is_method_call }
|
fn is_method_call(&self) -> bool { #is_method_call }
|
||||||
|
@ -230,3 +230,44 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod raw_returning_fn {
|
||||||
|
use rhai::plugin::*;
|
||||||
|
use rhai::FLOAT;
|
||||||
|
|
||||||
|
#[export_fn(return_raw)]
|
||||||
|
pub fn distance_function(
|
||||||
|
x1: FLOAT,
|
||||||
|
y1: FLOAT,
|
||||||
|
x2: FLOAT,
|
||||||
|
y2: FLOAT,
|
||||||
|
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
||||||
|
Ok(Dynamic::from(
|
||||||
|
((y2 - y1).abs().powf(2.0) + (x2 - x1).abs().powf(2.0)).sqrt(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_returning_fn_test() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
||||||
|
let mut m = Module::new();
|
||||||
|
rhai::register_exported_fn!(
|
||||||
|
m,
|
||||||
|
"euclidean_distance".to_string(),
|
||||||
|
raw_returning_fn::distance_function
|
||||||
|
);
|
||||||
|
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 x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#
|
||||||
|
)?,
|
||||||
|
41.0
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ struct Point {
|
|||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_fn(unknown = true)]
|
#[export_fn(unknown = "thing")]
|
||||||
pub fn test_fn(input: Point) -> bool {
|
pub fn test_fn(input: Point) -> bool {
|
||||||
input.x > input.y
|
input.x > input.y
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
error: unknown attribute 'unknown'
|
error: unknown attribute 'unknown'
|
||||||
--> $DIR/export_fn_bad_attr.rs:9:13
|
--> $DIR/export_fn_bad_attr.rs:9:13
|
||||||
|
|
|
|
||||||
9 | #[export_fn(unknown = true)]
|
9 | #[export_fn(unknown = "thing")]
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find function `test_fn` in this scope
|
error[E0425]: cannot find function `test_fn` in this scope
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error: expecting string literal value
|
error: expecting string literal
|
||||||
--> $DIR/export_fn_bad_value.rs:9:20
|
--> $DIR/export_fn_bad_value.rs:9:20
|
||||||
|
|
|
|
||||||
9 | #[export_fn(name = true)]
|
9 | #[export_fn(name = true)]
|
||||||
|
24
codegen/ui_tests/export_fn_extra_value.rs
Normal file
24
codegen/ui_tests/export_fn_extra_value.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_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_fn(n) {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
11
codegen/ui_tests/export_fn_extra_value.stderr
Normal file
11
codegen/ui_tests/export_fn_extra_value.stderr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
error: extraneous value
|
||||||
|
--> $DIR/export_fn_extra_value.rs:9:26
|
||||||
|
|
|
||||||
|
9 | #[export_fn(return_raw = "yes")]
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `test_fn` in this scope
|
||||||
|
--> $DIR/export_fn_extra_value.rs:19:8
|
||||||
|
|
|
||||||
|
19 | if test_fn(n) {
|
||||||
|
| ^^^^^^^ not found in this scope
|
@ -1,4 +1,4 @@
|
|||||||
error: expected assignment expression
|
error: expecting identifier
|
||||||
--> $DIR/export_fn_junk_arg.rs:9:13
|
--> $DIR/export_fn_junk_arg.rs:9:13
|
||||||
|
|
|
|
||||||
9 | #[export_fn("wheeeee")]
|
9 | #[export_fn("wheeeee")]
|
||||||
|
24
codegen/ui_tests/export_fn_missing_value.rs
Normal file
24
codegen/ui_tests/export_fn_missing_value.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_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_fn(n) {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
11
codegen/ui_tests/export_fn_missing_value.stderr
Normal file
11
codegen/ui_tests/export_fn_missing_value.stderr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
error: requires value
|
||||||
|
--> $DIR/export_fn_missing_value.rs:9:13
|
||||||
|
|
|
||||||
|
9 | #[export_fn(name)]
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `test_fn` in this scope
|
||||||
|
--> $DIR/export_fn_missing_value.rs:19:8
|
||||||
|
|
|
||||||
|
19 | if test_fn(n) {
|
||||||
|
| ^^^^^^^ not found in this scope
|
24
codegen/ui_tests/export_fn_path_attr.rs
Normal file
24
codegen/ui_tests/export_fn_path_attr.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_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_fn(n) {
|
||||||
|
println!("yes");
|
||||||
|
} else {
|
||||||
|
println!("no");
|
||||||
|
}
|
||||||
|
}
|
11
codegen/ui_tests/export_fn_path_attr.stderr
Normal file
11
codegen/ui_tests/export_fn_path_attr.stderr
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
error: expecting attribute name
|
||||||
|
--> $DIR/export_fn_path_attr.rs:9:13
|
||||||
|
|
|
||||||
|
9 | #[export_fn(rhai::name = "thing")]
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0425]: cannot find function `test_fn` in this scope
|
||||||
|
--> $DIR/export_fn_path_attr.rs:19:8
|
||||||
|
|
|
||||||
|
19 | if test_fn(n) {
|
||||||
|
| ^^^^^^^ not found in this scope
|
Loading…
Reference in New Issue
Block a user