use crate::{LispClass, LispContext, LispError, Value, ValueRef, clos};
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)
}
}
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);
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);
impl<T: LispClass> IntoLisp for &mut T {
fn into_lisp<'rt>(self, ctx: &LispContext<'rt>) -> Result<Value<'rt>, LispError> {
clos::scoped_child(ctx, self, T::NAME)
}
}