use paste::paste;
use std::marker::PhantomData;
use crate::{LispContext, LispError, sys};
macro_rules! native_methods {
(
$(
$variant:ident ($arity:literal; $( $arg:ident ),* )
),* $(,)?
) => {
pub enum NativeMethod {
$(
$variant(
unsafe extern "C" fn(
$( $arg: sys::cl_object ),*
) -> sys::cl_object
),
)*
}
impl NativeMethod {
pub fn register(
&self,
ctx: &LispContext<'_>,
name: &str,
) -> Result<(), LispError> {
paste! {
match self {
$(
Self::$variant(f) => {
ctx.[<defun_native_ $arity>](name, *f)
}
),*
}
}
}
pub fn arity(&self) -> usize {
match self {
$(
Self::$variant(_) => $arity,
)*
}
}
}
};
}
native_methods! {
Arity0(0;),
Arity1(1; a),
Arity2(2; a, b),
Arity3(3; a, b, c),
Arity4(4; a, b, c, d),
Arity5(5; a, b, c, d, e),
Arity6(6; a, b, c, d, e, f),
Arity7(7; a, b, c, d, e, f, g),
Arity8(8; a, b, c, d, e, f, g, h),
}
pub enum MethodKind {
Instance,
Static,
}
pub struct Method {
name: String,
native: NativeMethod,
kind: MethodKind,
}
impl Method {
pub fn new(name: impl Into<String>, native: NativeMethod, kind: MethodKind) -> Self {
Self {
name: name.into(),
native,
kind,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> &MethodKind {
&self.kind
}
pub fn native(&self) -> &NativeMethod {
&self.native
}
}
pub struct Methods<T> {
methods: Vec<Method>,
_marker: PhantomData<fn() -> T>,
}
impl<T> Methods<T> {
pub fn new() -> Self {
Self {
methods: Vec::new(),
_marker: PhantomData,
}
}
pub fn method(mut self, method: Method) -> Self {
self.methods.push(method);
self
}
pub fn methods(&self) -> &[Method] {
&self.methods
}
}