use crate::object::wrappers::generic::SyncSend;
use crate::{destruct_bind, destruct_eval_bind, Error, Rest, TulispContext, TulispObject};
pub trait TulispCallable<
Args: 'static,
Output: 'static,
const NEEDS_CONTEXT: bool,
const NUM_ARGS: usize,
const NUM_OPTIONAL: usize,
const HAS_PLIST: bool,
const HAS_REST: bool,
const HAS_RETURN: bool,
const FALLIBLE: bool,
>
{
fn add_to_context(self, ctx: &mut TulispContext, name: &str);
}
macro_rules! impl_tulisp_callable {
(
args: $args_count:literal: ($($arg: ident),*),
opts: $opts_count:literal: ($($opt: ident),*) $(,)?
) => {
#[allow(nonstandard_style)]
impl<OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*) , OutT, false, $args_count, $opts_count, false, false, true, false> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>),*) -> OutT + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@deb _ctx, $($arg)* &optional $($opt)*, _args);
let res = (self)($($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*);
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*) , (), false, $args_count, $opts_count, false, false, false, false> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>),*) + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@deb _ctx, $($arg)* &optional $($opt)*, _args);
(self)($($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*);
Ok(TulispObject::nil())
}
);
}
}
#[allow(nonstandard_style)]
impl<OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*) , OutT, true, $args_count, $opts_count, false, false, true,false> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> OutT + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@deb ctx, $($arg)* &optional $($opt)*, _args);
let res = (self)(ctx, $($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*);
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*), (), true, $args_count, $opts_count, false, false, false,false> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@deb ctx, $($arg)* &optional $($opt)*, _args);
(self)(ctx, $($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*);
Ok(TulispObject::nil())
}
);
}
}
#[allow(nonstandard_style)]
impl<OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*) , OutT, false, $args_count, $opts_count, false, false, true, true> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>),*) -> Result<OutT, Error> + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@deb _ctx, $($arg)* &optional $($opt)*, _args);
let res = (self)($($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*)?;
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)*) , OutT, true, $args_count, $opts_count, false, false, true, true> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> Result<OutT, Error> + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@deb ctx, $($arg)* &optional $($opt)*, _args);
let res = (self)(ctx, $($crate::TulispConvertible::from_tulisp(&$arg)?,)* $(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)}),*)?;
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, false, $args_count, $opts_count, false, true, true, false> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>,)* Rest<RestT>) -> OutT + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = _ctx.eval(&$arg)?;)*
$(let $opt = _ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&_ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
let res = (self)(
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
);
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), (), false, $args_count, $opts_count, false, true, false, false> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>,)* Rest<RestT>) + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = _ctx.eval(&$arg)?;)*
$(let $opt = _ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&_ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
(self)(
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
);
Ok(TulispObject::nil())
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, true, $args_count, $opts_count, false, true, true, false> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest<RestT>) -> OutT + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = ctx.eval(&$arg)?;)*
$(let $opt = ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
let res = (self)(
ctx,
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
);
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), (), true, $args_count, $opts_count, false, true, false, false> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest<RestT>) + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = ctx.eval(&$arg)?;)*
$(let $opt = ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
(self)(
ctx,
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
);
Ok(TulispObject::nil())
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, false, $args_count, $opts_count, false, true, true, true> for FnT
where
FnT: Fn($($arg,)* $(Option<$opt>,)* Rest<RestT>) -> Result<OutT, Error> + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |_ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = _ctx.eval(&$arg)?;)*
$(let $opt = _ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&_ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
let res = (self)(
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
)?;
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
#[allow(nonstandard_style)]
impl<RestT, OutT, FnT, $($arg,)* $($opt,)*>
TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, true, $args_count, $opts_count, false, true, true, true> for FnT
where
FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest<RestT>) -> Result<OutT, Error> + 'static + SyncSend,
$($arg: $crate::TulispConvertible + 'static,)*
$($opt: $crate::TulispConvertible + 'static,)*
RestT: $crate::TulispConvertible + 'static,
OutT: $crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(
self,
ctx: &mut TulispContext,
name: &str,
) {
ctx.defspecial(
name,
move |ctx, _args| {
impl_tulisp_callable!(@db $($arg)* &optional $($opt)* &rest rest, _args);
$(let $arg = ctx.eval(&$arg)?;)*
$(let $opt = ctx.eval(&$opt)?;)*
let rest = rest
.base_iter()
.map(|arg| $crate::TulispConvertible::from_tulisp(&ctx.eval(&arg)?))
.collect::<Result<_, _>>()?;
let res = (self)(
ctx,
$($crate::TulispConvertible::from_tulisp(&$arg)?,)*
$(if $opt.null() {None} else {Some($crate::TulispConvertible::from_tulisp(&$opt)?)},)*
rest
)?;
Ok($crate::TulispConvertible::into_tulisp(res))
}
);
}
}
};
(@deb $ctx:ident, $($arg_name:ident)+ &optional, $args:ident) => {
destruct_eval_bind!($ctx, ($($arg_name)+) = $args);
};
(@deb $ctx:ident, &optional $($opt_name:ident)+, $args:ident) => {
destruct_eval_bind!($ctx, (&optional $($opt_name)+) = $args);
};
(@deb $ctx:ident, $($arg_name:ident)+ &optional $($opt_name:ident)+, $args:ident) => {
destruct_eval_bind!($ctx, ($($arg_name)+ &optional $($opt_name)+) = $args);
};
(@deb $ctx:ident, &optional, $args:ident) => {};
(@db $($arg_name:ident)+ &optional &rest $rest:ident, $args:ident) => {
destruct_bind!(($($arg_name)+ &rest $rest) = $args);
};
(@db &optional $($opt_name:ident)+ &rest $rest:ident, $args:ident) => {
destruct_bind!((&optional $($opt_name)+ &rest $rest) = $args);
};
(@db $($arg_name:ident)+ &optional $($opt_name:ident)+ &rest $rest:ident, $args:ident) => {
destruct_bind!(($($arg_name)+ &optional $($opt_name)+ &rest $rest) = $args);
};
(@db &optional &rest $rest:ident, $args:ident) => {
destruct_bind!((&rest $rest) = $args);
};
}
#[cfg(feature = "big_functions")]
mod upto_10_args {
use super::*;
impl_tulisp_callable!(args: 0: (), opts: 10: (A, B, C, D, E, F, G, H, I, J),);
impl_tulisp_callable!(args: 1: (A), opts: 9: (B, C, D, E, F, G, H, I, J),);
impl_tulisp_callable!(args: 2: (A, B), opts: 8: (C, D, E, F, G, H, I, J),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 7: (D, E, F, G, H, I, J),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 6: (E, F, G, H, I, J),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 5: (F, G, H, I, J),);
impl_tulisp_callable!(args: 6: (A, B, C, D, E, F), opts: 4: (G, H, I, J),);
impl_tulisp_callable!(args: 7: (A, B, C, D, E, F, G), opts: 3: (H, I, J),);
impl_tulisp_callable!(args: 8: (A, B, C, D, E, F, G, H), opts: 2: (I, J),);
impl_tulisp_callable!(args: 9: (A, B, C, D, E, F, G, H, I), opts: 1: (J),);
impl_tulisp_callable!(args: 10: (A, B, C, D, E, F, G, H, I, J), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 9: (A, B, C, D, E, F, G, H, I),);
impl_tulisp_callable!(args: 1: (A), opts: 8: (B, C, D, E, F, G, H, I),);
impl_tulisp_callable!(args: 2: (A, B), opts: 7: (C, D, E, F, G, H, I),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 6: (D, E, F, G, H, I),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 5: (E, F, G, H, I),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 4: (F, G, H, I),);
impl_tulisp_callable!(args: 6: (A, B, C, D, E, F), opts: 3: (G, H, I),);
impl_tulisp_callable!(args: 7: (A, B, C, D, E, F, G), opts: 2: (H, I),);
impl_tulisp_callable!(args: 8: (A, B, C, D, E, F, G, H), opts: 1: (I),);
impl_tulisp_callable!(args: 9: (A, B, C, D, E, F, G, H, I), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 8: (A, B, C, D, E, F, G, H),);
impl_tulisp_callable!(args: 1: (A), opts: 7: (B, C, D, E, F, G, H),);
impl_tulisp_callable!(args: 2: (A, B), opts: 6: (C, D, E, F, G, H),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 5: (D, E, F, G, H),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 4: (E, F, G, H),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 3: (F, G, H),);
impl_tulisp_callable!(args: 6: (A, B, C, D, E, F), opts: 2: (G, H),);
impl_tulisp_callable!(args: 7: (A, B, C, D, E, F, G), opts: 1: (H),);
impl_tulisp_callable!(args: 8: (A, B, C, D, E, F, G, H), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 7: (A, B, C, D, E, F, G),);
impl_tulisp_callable!(args: 1: (A), opts: 6: (B, C, D, E, F, G),);
impl_tulisp_callable!(args: 2: (A, B), opts: 5: (C, D, E, F, G),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 4: (D, E, F, G),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 3: (E, F, G),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 2: (F, G),);
impl_tulisp_callable!(args: 6: (A, B, C, D, E, F), opts: 1: (G),);
impl_tulisp_callable!(args: 7: (A, B, C, D, E, F, G), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 6: (A, B, C, D, E, F),);
impl_tulisp_callable!(args: 1: (A), opts: 5: (B, C, D, E, F),);
impl_tulisp_callable!(args: 2: (A, B), opts: 4: (C, D, E, F),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 3: (D, E, F),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 2: (E, F),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 1: (F),);
impl_tulisp_callable!(args: 6: (A, B, C, D, E, F), opts: 0: (),);
}
mod upto_5_args {
use super::*;
impl_tulisp_callable!(args: 0: (), opts: 5: (A, B, C, D, E),);
impl_tulisp_callable!(args: 1: (A), opts: 4: (B, C, D, E),);
impl_tulisp_callable!(args: 2: (A, B), opts: 3: (C, D, E),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 2: (D, E),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 1: (E),);
impl_tulisp_callable!(args: 5: (A, B, C, D, E), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 4: (A, B, C, D),);
impl_tulisp_callable!(args: 1: (A), opts: 3: (B, C, D),);
impl_tulisp_callable!(args: 2: (A, B), opts: 2: (C, D),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 1: (D),);
impl_tulisp_callable!(args: 4: (A, B, C, D), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 3: (A, B, C),);
impl_tulisp_callable!(args: 1: (A), opts: 2: (B, C),);
impl_tulisp_callable!(args: 2: (A, B), opts: 1: (C),);
impl_tulisp_callable!(args: 3: (A, B, C), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 2: (A, B),);
impl_tulisp_callable!(args: 1: (A), opts: 1: (B),);
impl_tulisp_callable!(args: 2: (A, B), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 1: (A),);
impl_tulisp_callable!(args: 1: (A), opts: 0: (),);
impl_tulisp_callable!(args: 0: (), opts: 0: (),);
}
mod plist_args {
use crate::{Plist, Plistable};
use super::*;
#[allow(nonstandard_style)]
impl<PlistT, OutT, FnT> TulispCallable<(PlistT,), OutT, false, 0, 0, true, false, true, false>
for FnT
where
FnT: Fn(Plist<PlistT>) -> OutT + 'static + SyncSend,
PlistT: Plistable + 'static,
OutT: crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
let res = (self)(Plist::new(ctx, rest)?);
Ok(crate::TulispConvertible::into_tulisp(res))
});
}
}
#[allow(nonstandard_style)]
impl<PlistT, FnT> TulispCallable<(PlistT,), (), false, 0, 0, true, false, true, false> for FnT
where
FnT: Fn(Plist<PlistT>) + 'static + SyncSend,
PlistT: Plistable + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
(self)(Plist::new(ctx, rest)?);
Ok(TulispObject::nil())
});
}
}
#[allow(nonstandard_style)]
impl<PlistT, OutT, FnT> TulispCallable<(PlistT,), OutT, true, 0, 0, true, false, true, false>
for FnT
where
FnT: Fn(&mut TulispContext, Plist<PlistT>) -> OutT + 'static + SyncSend,
PlistT: Plistable + 'static,
OutT: crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
let plist = Plist::new(ctx, rest)?;
let res = (self)(ctx, plist);
Ok(crate::TulispConvertible::into_tulisp(res))
});
}
}
#[allow(nonstandard_style)]
impl<PlistT, FnT> TulispCallable<(PlistT,), (), true, 0, 0, true, false, false, false> for FnT
where
FnT: Fn(&mut TulispContext, Plist<PlistT>) + 'static + SyncSend,
PlistT: Plistable + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
let plist = Plist::new(ctx, rest)?;
(self)(ctx, plist);
Ok(TulispObject::nil())
});
}
}
#[allow(nonstandard_style)]
impl<PlistT, OutT, FnT> TulispCallable<(PlistT,), OutT, false, 0, 0, true, false, true, true>
for FnT
where
FnT: Fn(Plist<PlistT>) -> Result<OutT, Error> + 'static + SyncSend,
PlistT: Plistable + 'static,
OutT: crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
let plist = Plist::new(ctx, rest)?;
let res = (self)(plist);
res.map(|x| x.into_tulisp())
});
}
}
#[allow(nonstandard_style)]
impl<PlistT, OutT, FnT> TulispCallable<(PlistT,), OutT, true, 0, 0, true, false, true, true> for FnT
where
FnT: Fn(&mut TulispContext, Plist<PlistT>) -> Result<OutT, Error> + 'static + SyncSend,
PlistT: Plistable + 'static,
OutT: crate::TulispConvertible + 'static,
{
#[track_caller]
fn add_to_context(self, ctx: &mut TulispContext, name: &str) {
ctx.defspecial(name, move |ctx, _args| {
destruct_bind!((&rest rest) = _args);
let plist = Plist::new(ctx, rest)?;
let res = (self)(ctx, plist);
res.map(|x| x.into_tulisp())
});
}
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::{eval_assert_equal, eval_assert_error};
use crate::{Error, Rest, TulispContext, TulispObject};
#[test]
fn test_add_functions_only_rest() -> Result<(), crate::Error> {
let ctx = &mut TulispContext::new();
ctx.defun("sum", |items: Rest<f64>| -> f64 { items.into_iter().sum() });
eval_assert_equal(ctx, "(sum)", "0.0");
eval_assert_equal(ctx, "(sum (- 4 0.5) (+ 3 4) 5 10)", "25.5");
eval_assert_error(
ctx,
r#"(sum "hh" 10)"#,
r#"ERR TypeMismatch: Expected number, got: "hh"
<eval_string>:1.1-1.13: at (sum "hh" 10)
"#,
);
ctx.defun(
"cats",
|items: Rest<String>| -> Result<TulispObject, crate::Error> {
for item in items {
if item == "cat" {
return Ok("meow".into());
}
if item == "stop" {
return Ok(false.into());
}
}
Err(crate::Error::invalid_argument("No cats found"))
},
);
eval_assert_equal(ctx, r#"(let ((a "stop"))(cats "dog" a "cat"))"#, "nil");
eval_assert_error(
ctx,
r#"(cats 1 2 3)"#,
r#"ERR TypeMismatch: Expected string, got: 1
<eval_string>:1.1-1.12: at (cats 1 2 3)
"#,
);
eval_assert_error(
ctx,
r#"(let ((horse "horse"))(cats horse))"#,
r#"ERR InvalidArgument: No cats found
<eval_string>:1.23-1.34: at (cats horse)
<eval_string>:1.1-1.35: at (let ((horse "horse")) (cats horse))
"#,
);
Ok(())
}
#[test]
fn test_add_functions_only_args() -> Result<(), Error> {
let ctx = &mut TulispContext::new();
ctx.defun("add_round", |a: f64, b: f64| -> i64 {
(a + b).round() as i64
});
eval_assert_equal(ctx, "(let ((a 3.5) (b 4.2))(add_round a b))", "8");
eval_assert_error(
ctx,
"(add_round 2)",
r#"ERR MissingArgument: Too few arguments
<eval_string>:1.1-1.13: at (add_round 2)
"#,
);
ctx.defun("greet", |name: String| format!("Hello, {}!", name));
eval_assert_equal(ctx, r#"(greet "Alice")"#, r#""Hello, Alice!""#);
eval_assert_error(
ctx,
r#"(greet "Alice" "Peter")"#,
r#"ERR InvalidArgument: Too many arguments
<eval_string>:1.1-1.23: at (greet "Alice" "Peter")
"#,
);
Ok(())
}
#[test]
fn test_add_functions_args_and_rest() -> Result<(), Error> {
let ctx = &mut TulispContext::new();
ctx.defun(
"make_sentence",
|subject: String, verb: String, objects: Rest<String>| -> String {
let objs = objects.into_iter().collect::<Vec<_>>().join(" ");
format!("{} {} {}", subject, verb, objs)
},
);
eval_assert_equal(
ctx,
r#"(make_sentence "The cat" "chased" "the mouse" "and" "the dog")"#,
r#""The cat chased the mouse and the dog""#,
);
eval_assert_equal(ctx, r#"(make_sentence "Birds" "fly")"#, r#""Birds fly ""#);
Ok(())
}
#[test]
fn test_add_functions_args_and_optional() -> Result<(), Error> {
let ctx = &mut TulispContext::new();
ctx.defun("power", |base: f64, exponent: Option<f64>| -> f64 {
let exp = exponent.unwrap_or(2.0);
base.powf(exp)
});
eval_assert_equal(ctx, "(power 3)", "9.0");
eval_assert_equal(ctx, "(power 2 3)", "8.0");
eval_assert_error(
ctx,
"(power 2 3 4)",
r#"ERR InvalidArgument: Too many arguments
<eval_string>:1.1-1.13: at (power 2 3 4)
"#,
);
eval_assert_error(
ctx,
"(power)",
r#"ERR MissingArgument: Too few arguments
<eval_string>:1.1-1.7: at (power)
"#,
);
Ok(())
}
#[test]
fn test_add_functions_args_optional_and_rest() -> Result<(), Error> {
let ctx = &mut TulispContext::new();
ctx.defun(
"build_url",
|base: String, path: Option<String>, query_params: Rest<String>| -> String {
let mut url = base;
if let Some(p) = path {
url.push('/');
url.push_str(&p);
}
let joined = query_params.into_iter().collect::<Vec<_>>().join("&");
if !joined.is_empty() {
url.push_str("?");
url.push_str(&joined);
}
url
},
);
eval_assert_equal(
ctx,
r#"(build_url "http://example.com" "search" "q=rust" "page=1")"#,
r#""http://example.com/search?q=rust&page=1""#,
);
eval_assert_equal(
ctx,
r#"(build_url "http://example.com")"#,
r#""http://example.com""#,
);
eval_assert_equal(
ctx,
r#"(build_url "http://example.com" "about")"#,
r#""http://example.com/about""#,
);
Ok(())
}
}