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 F where R: IntoRustFunctionResult, A: TryFromValue, F: 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 F where R: IntoRustFunctionResult, A: TryFromValue, B: TryFromValue, F: 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 F where R: IntoRustFunctionResult, A: TryFromValue, B: TryFromValue, C: TryFromValue, F: 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 F where R: IntoRustFunctionResult, A: TryFromValue, B: TryFromValue, C: TryFromValue, D: TryFromValue, F: 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 F where R: IntoRustFunctionResult, A: TryFromValue, B: TryFromValue, C: TryFromValue, D: TryFromValue, E: TryFromValue, F: 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) } }