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 IntoRustFunctionResult for T { fn into_res(self, ctx: &ContextRef) -> ValueResult { self.try_into_value(ctx) } } impl IntoRustFunctionResult for core::result::Result { 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 { fn argument_count() -> usize; fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult; } impl RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 RustFunction> 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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(..))); } }