eclisp 0.2.0-beta.0

Bindings for Embeddable Common Lisp
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
    }
}