579 lines
17 KiB
Rust
579 lines
17 KiB
Rust
use super::{TryFromValue, TryIntoValue};
|
|
use crate::{ContextRef, Error, ValueRef, ValueResult};
|
|
use std::marker::PhantomData;
|
|
|
|
pub trait IntoRustFunctionResult {
|
|
fn into_res(self, ctx: &ContextRef) -> ValueResult;
|
|
}
|
|
|
|
impl<T: TryIntoValue> IntoRustFunctionResult for T {
|
|
fn into_res(self, ctx: &ContextRef) -> ValueResult {
|
|
self.try_into_value(ctx)
|
|
}
|
|
}
|
|
|
|
impl<T: TryIntoValue, E: std::fmt::Display> IntoRustFunctionResult for core::result::Result<T, E> {
|
|
fn into_res(self, ctx: &ContextRef) -> ValueResult {
|
|
match self {
|
|
Ok(v) => v.try_into_value(ctx),
|
|
Err(e) => Err(Error::RustFunctionError(e.to_string())),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait RustFunction<F> {
|
|
fn argument_count() -> usize;
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult;
|
|
}
|
|
|
|
impl<R, F> RustFunction<PhantomData<(&R, &F)>> for F
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
F: Fn(&ContextRef) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
0
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != 0 {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let res = self(context);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, FN> RustFunction<PhantomData<(&R, &A, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
FN: Fn(&ContextRef, A) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
1
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let res = self(context, va);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, FN> RustFunction<PhantomData<(&R, &A, &B, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
2
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let res = self(context, va, vb);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
3
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let res = self(context, va, vb, vc);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, D, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
D: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C, D) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
4
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let vd = D::try_from_value(args[3], &context)?;
|
|
let res = self(context, va, vb, vc, vd);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, D, E, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
D: TryFromValue,
|
|
E: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C, D, E) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
5
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let vd = D::try_from_value(args[3], &context)?;
|
|
let ve = E::try_from_value(args[4], &context)?;
|
|
let res = self(context, va, vb, vc, vd, ve);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, D, E, F, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
D: TryFromValue,
|
|
E: TryFromValue,
|
|
F: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C, D, E, F) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
6
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let vd = D::try_from_value(args[3], &context)?;
|
|
let ve = E::try_from_value(args[4], &context)?;
|
|
let vf = F::try_from_value(args[5], &context)?;
|
|
let res = self(context, va, vb, vc, vd, ve, vf);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, D, E, F, G, FN> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &G, &FN)>>
|
|
for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
D: TryFromValue,
|
|
E: TryFromValue,
|
|
F: TryFromValue,
|
|
G: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C, D, E, F, G) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
7
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let vd = D::try_from_value(args[3], &context)?;
|
|
let ve = E::try_from_value(args[4], &context)?;
|
|
let vf = F::try_from_value(args[5], &context)?;
|
|
let vg = G::try_from_value(args[6], &context)?;
|
|
let res = self(context, va, vb, vc, vd, ve, vf, vg);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
impl<R, A, B, C, D, E, F, G, H, FN>
|
|
RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F, &G, &H, &FN)>> for FN
|
|
where
|
|
R: IntoRustFunctionResult,
|
|
A: TryFromValue,
|
|
B: TryFromValue,
|
|
C: TryFromValue,
|
|
D: TryFromValue,
|
|
E: TryFromValue,
|
|
F: TryFromValue,
|
|
G: TryFromValue,
|
|
H: TryFromValue,
|
|
FN: Fn(&ContextRef, A, B, C, D, E, F, G, H) -> R + Sized,
|
|
{
|
|
fn argument_count() -> usize {
|
|
8
|
|
}
|
|
|
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
|
if args.len() != Self::argument_count() {
|
|
return Err(Error::ArgumentCountMismatch {
|
|
expected: Self::argument_count(),
|
|
received: args.len(),
|
|
});
|
|
}
|
|
|
|
let va = A::try_from_value(args[0], &context)?;
|
|
let vb = B::try_from_value(args[1], &context)?;
|
|
let vc = C::try_from_value(args[2], &context)?;
|
|
let vd = D::try_from_value(args[3], &context)?;
|
|
let ve = E::try_from_value(args[4], &context)?;
|
|
let vf = F::try_from_value(args[5], &context)?;
|
|
let vg = G::try_from_value(args[6], &context)?;
|
|
let vh = H::try_from_value(args[7], &context)?;
|
|
let res = self(context, va, vb, vc, vd, ve, vf, vg, vh);
|
|
res.into_res(context)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::{Context, Error, EvalFlags, Result, Runtime};
|
|
use assert_matches::assert_matches;
|
|
|
|
#[test]
|
|
fn no_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(|_: &ContextRef| -> Result<f32> { Ok(0.0) })
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function()", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("0"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn one_argument() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(|_: &ContextRef, a: f32| -> Result<f32> { Ok(a) })
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("1"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function()", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn two_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(|_: &ContextRef, a: f32, b: f32| -> Result<f32> { Ok(a + b) })
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("3"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn three_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(|_: &ContextRef, a: f32, b: f32, c: f32| -> Result<f32> { Ok(a + b + c) })
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("6"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn four_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(
|
|
|_: &ContextRef, a: f32, b: f32, c: f32, d: f32| -> Result<f32> {
|
|
Ok(a + b + c + d)
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3,4)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("10"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2,3)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn five_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(
|
|
|_: &ContextRef, a: f32, b: f32, c: f32, d: f32, e: f32| -> Result<f32> {
|
|
Ok(a + b + c + d + e)
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("15"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn six_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(
|
|
|_: &ContextRef, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32| -> Result<f32> {
|
|
Ok(a + b + c + d + e + f)
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("21"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn seven_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(
|
|
|_: &ContextRef,
|
|
a: f32,
|
|
b: f32,
|
|
c: f32,
|
|
d: f32,
|
|
e: f32,
|
|
f: f32,
|
|
g: f32|
|
|
-> Result<f32> { Ok(a + b + c + d + e + f + g) },
|
|
)
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("28"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6,7,8)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
|
|
#[test]
|
|
fn eight_arguments() {
|
|
let rt = Runtime::new();
|
|
let ctx = Context::new(rt);
|
|
|
|
let the_function = ctx
|
|
.new_fn(
|
|
|_: &ContextRef,
|
|
a: f32,
|
|
b: f32,
|
|
c: f32,
|
|
d: f32,
|
|
e: f32,
|
|
f: f32,
|
|
g: f32,
|
|
h: f32|
|
|
-> Result<f32> { Ok(a + b + c + d + e + f + g + h) },
|
|
)
|
|
.unwrap();
|
|
|
|
ctx.global_object()
|
|
.unwrap()
|
|
.set_property(&ctx, "the_function", &the_function)
|
|
.expect("Should be able to set the function");
|
|
|
|
let val = ctx
|
|
.eval("the_function(1,2,3,4,5,6,7,8)", "script", EvalFlags::NONE)
|
|
.unwrap();
|
|
|
|
assert_eq!(String::from("36"), val.to_string(&ctx).unwrap());
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6,7)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
|
|
let val = ctx.eval("the_function(1,2,3,4,5,6,7,8,9)", "script", EvalFlags::NONE);
|
|
assert_matches!(val, Err(Error::Exception(..)));
|
|
}
|
|
}
|