use crate::{LispContext, LispError, Value, ValueRef, sys};
pub trait IntoLisp {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError>;
}
pub trait ToLisp {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError>;
}
pub trait FromLisp: Sized {
fn from_lisp(value: ValueRef<'_>) -> Result<Self, LispError>;
}
impl<T: ToLisp + ?Sized> IntoLisp for &T {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
self.to_lisp(ctx)
}
}
impl<T: ToLisp + ?Sized> ToLisp for &T {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
(*self).to_lisp(ctx)
}
}
pub trait RawLisp: Sized {
unsafe fn from_raw(value: sys::cl_object) -> Option<Self>;
unsafe fn to_raw(&self) -> sys::cl_object;
}
macro_rules! impl_primitive {
($($ty:ty => $method:ident $(as $cast:ty)? ; $reader:ident),* $(,)?) => {
$(
impl IntoLisp for $ty {
fn into_lisp<'rt>(
self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
ctx.$method(self $(as $cast)?)
}
}
impl ToLisp for $ty {
fn to_lisp<'rt>(
&self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
(*self).into_lisp(ctx)
}
}
impl FromLisp for $ty {
fn from_lisp(value: ValueRef<'_>) -> Result<Self, LispError> {
let value = value.$reader()?;
Self::try_from(value).map_err(|_| {
LispError::RuntimeError(format!(
"Lisp value is out of range for {}",
stringify!($ty)
))
})
}
}
)*
};
}
impl_primitive! {
i8 => int as i64; try_as_i64,
i16 => int as i64; try_as_i64,
i32 => int as i64; try_as_i64,
i64 => int; try_as_i64,
u8 => int as i64; try_as_i64,
u16 => int as i64; try_as_i64,
u32 => int as i64; try_as_i64,
}
macro_rules! impl_float {
($($ty:ty),* $(,)?) => {
$(
impl IntoLisp for $ty {
fn into_lisp<'rt>(
self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
ctx.float(self as f64)
}
}
impl ToLisp for $ty {
fn to_lisp<'rt>(
&self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
(*self).into_lisp(ctx)
}
}
impl FromLisp for $ty {
fn from_lisp(value: ValueRef<'_>) -> Result<Self, LispError> {
Ok(value.try_as_f64()? as Self)
}
}
)*
};
}
impl_float!(f32, f64);
macro_rules! impl_raw_int {
($($ty:ty),* $(,)?) => {
$(
impl RawLisp for $ty {
unsafe fn from_raw(
value: sys::cl_object,
) -> Option<Self> {
if unsafe { sys::eclisp_is_int(value) } == 0 {
return None;
}
let mut result = 0i64;
if unsafe {
sys::eclisp_as_int(value, &mut result)
} == 0 {
return None;
}
<$ty>::try_from(result).ok()
}
unsafe fn to_raw(&self) -> sys::cl_object {
unsafe { sys::eclisp_int_raw(*self as i64) }
}
}
)*
};
}
impl_raw_int!(i8, i16, i32, i64, u8, u16, u32);
impl RawLisp for f32 {
unsafe fn from_raw(value: sys::cl_object) -> Option<Self> {
if unsafe { sys::eclisp_can_be_float(value) } == 0 {
return None;
}
let mut result = 0.0f64;
if unsafe { sys::eclisp_as_float(value, &mut result) } == 0 {
return None;
}
Some(result as f32)
}
unsafe fn to_raw(&self) -> sys::cl_object {
unsafe { sys::ecl_make_single_float(*self) }
}
}
impl RawLisp for bool {
unsafe fn from_raw(value: sys::cl_object) -> Option<Self> {
if unsafe { sys::eclisp_is_nil(value) } != 0 {
Some(false)
} else if unsafe { sys::eclisp_is_t(value) } != 0 {
Some(true)
} else {
None
}
}
unsafe fn to_raw(&self) -> sys::cl_object {
unsafe {
if *self {
sys::eclisp_t_raw()
} else {
sys::eclisp_nil_raw()
}
}
}
}
impl IntoLisp for bool {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.bool(self)
}
}
impl ToLisp for bool {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
(*self).into_lisp(ctx)
}
}
impl FromLisp for bool {
fn from_lisp(value: ValueRef<'_>) -> Result<Self, LispError> {
if value.is_nil() {
Ok(false)
} else if value.is_t() {
Ok(true)
} else {
Err(LispError::RuntimeError(
"Expected Lisp boolean (T or NIL)".into(),
))
}
}
}
impl IntoLisp for String {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.string(self)
}
}
impl ToLisp for String {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.string(self)
}
}
impl FromLisp for String {
fn from_lisp(value: ValueRef<'_>) -> Result<Self, LispError> {
Ok(value.try_as_string()?)
}
}
impl ToLisp for str {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.string(self)
}
}
impl IntoLisp for () {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.nil()
}
}
impl ToLisp for () {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
ctx.nil()
}
}
impl<T: IntoLisp> IntoLisp for Option<T> {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
match self {
Some(value) => value.into_lisp(ctx),
None => ctx.nil(),
}
}
}
impl<T: ToLisp> ToLisp for Option<T> {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
match self {
Some(value) => value.to_lisp(ctx),
None => ctx.nil(),
}
}
}
fn into_list<'rt>(
ctx: &LispContext<'rt>,
values: Vec<Value<'rt>>,
) -> Result<Value<'rt>, LispError> {
let refs: Vec<_> = values.iter().collect();
ctx.list(&refs)
}
impl<T: IntoLisp> IntoLisp for Vec<T> {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
let values = self
.into_iter()
.map(|value| value.into_lisp(ctx))
.collect::<Result<Vec<_>, _>>()?;
into_list(ctx, values)
}
}
impl<T: ToLisp> ToLisp for [T] {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
let values = self
.iter()
.map(|value| value.to_lisp(ctx))
.collect::<Result<Vec<_>, _>>()?;
into_list(ctx, values)
}
}
impl<T: ToLisp> ToLisp for Vec<T> {
fn to_lisp<'rt>(&self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
self.as_slice().to_lisp(ctx)
}
}
macro_rules! impl_tuple {
($($ty:ident : $var:ident),+ $(,)?) => {
impl<$($ty: IntoLisp),+> IntoLisp for ($($ty,)+) {
fn into_lisp<'rt>(
self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
let ($($var,)+) = self;
into_list(ctx, vec![
$($var.into_lisp(ctx)?,)+
])
}
}
impl<$($ty: ToLisp),+> ToLisp for ($($ty,)+) {
fn to_lisp<'rt>(
&self,
ctx: &LispContext<'rt>,
) -> Result<Value<'rt>, LispError> {
let ($($var,)+) = self;
into_list(ctx, vec![
$($var.to_lisp(ctx)?,)+
])
}
}
};
}
impl_tuple!(A: a);
impl_tuple!(A: a, B: b);
impl_tuple!(A: a, B: b, C: c);
impl_tuple!(A: a, B: b, C: c, D: d);
impl_tuple!(A: a, B: b, C: c, D: d, E: e);
impl_tuple!(A: a, B: b, C: c, D: d, E: e, F: f);
impl_tuple!(A: a, B: b, C: c, D: d, E: e, F: f, G: g);
impl_tuple!(A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h);