use eclisp::{
Lisp, LispContext, LispError,
clos::{Class, ObjectId, ObjectRegistry, Property},
};
struct Transform {
x: f32,
name: String,
}
fn test_property_getters(ctx: &LispContext<'_>) -> Result<(), LispError> {
let transform = Transform {
x: 42.0,
name: "player".into(),
};
let x = Property::new("x").get_owned(|obj: &Transform| obj.x);
let name = Property::new("name").get(|obj: &Transform| &obj.name);
assert_eq!(x.read(&transform, ctx)?.unwrap().try_as_f64()?, 42.0);
assert_eq!(
name.read(&transform, ctx)?.unwrap().try_as_string()?,
"player"
);
Ok(())
}
fn test_property_setters(ctx: &LispContext<'_>) -> Result<(), LispError> {
let property = Property::new("x")
.get(|obj: &Transform| &obj.x)
.set(|obj: &mut Transform, x: f32| obj.x = x);
let mut transform = Transform {
x: 10.0,
name: "player".into(),
};
let value = ctx.float(42.0)?;
assert!(property.write(&mut transform, value.as_ref())?);
assert_eq!(transform.x, 42.0);
let result = property.read(&transform, ctx)?.unwrap();
assert_eq!(result.try_as_f64()?, 42.0);
Ok(())
}
fn test_class(ctx: &LispContext<'_>) -> Result<(), LispError> {
let class = Class::<Transform>::new("transform")
.property_with("x", |p| p.get(|obj| &obj.x).set(|obj, x: f32| obj.x = x))
.property_with("name", |p| p.get(|obj| &obj.name));
let mut transform = Transform {
x: 10.0,
name: "player".into(),
};
let value = class.read("x", &transform, ctx)?.unwrap();
assert_eq!(value.try_as_f64()?, 10.0);
let value = ctx.float(42.0)?;
assert!(class.write("x", &mut transform, value.as_ref())?);
assert_eq!(transform.x, 42.0);
let value = class.read("name", &transform, ctx)?.unwrap();
assert_eq!(value.try_as_string()?, "player");
assert!(class.find_property("missing").is_none());
Ok(())
}
fn test_registry(ctx: &LispContext<'_>) -> Result<(), LispError> {
let class = Class::<Transform>::new("transform")
.property_with("x", |p| p.get(|obj| &obj.x).set(|obj, x: f32| obj.x = x))
.property_with("name", |p| p.get(|obj| &obj.name));
let mut registry = ObjectRegistry::default();
let id = registry.insert(Transform {
x: 10.0,
name: "player".into(),
});
let value = registry.read(id, &class, "x", ctx)?.unwrap();
assert_eq!(value.try_as_f64()?, 10.0);
let value = ctx.float(42.0)?;
assert!(registry.write(id, &class, "x", value.as_ref())?);
assert_eq!(registry.get::<Transform>(id).unwrap().x, 42.0);
Ok(())
}
fn test_expose(ctx: &LispContext<'_>) -> Result<(), LispError> {
let id = ctx.expose(Transform {
x: 10.0,
name: "player".into(),
});
let x = ctx.with_object::<Transform, _>(id, |obj| obj.x);
assert_eq!(x, Some(10.0));
ctx.with_object_mut::<Transform, _>(id, |obj| {
obj.x = 42.0;
});
let x = ctx.with_object::<Transform, _>(id, |obj| obj.x);
assert_eq!(x, Some(42.0));
Ok(())
}
fn test_object_id(ctx: &LispContext<'_>) -> Result<(), LispError> {
let id = ctx.expose(Transform {
x: 10.0,
name: "player".into(),
});
let value = id.to_lisp(ctx)?;
let restored = ObjectId::from_lisp(value.as_ref())?;
assert_eq!(id, restored);
let invalid = ctx.eval("'(1 2 3)")?;
assert!(ObjectId::from_lisp(invalid.as_ref()).is_err());
let invalid = ctx.eval("'(-1 0)")?;
assert!(ObjectId::from_lisp(invalid.as_ref()).is_err());
Ok(())
}
fn test_register_class_manual(ctx: &LispContext<'_>) -> Result<(), LispError> {
let first = ctx.expose(Transform {
x: 10.0,
name: "first".into(),
});
let second = ctx.expose(Transform {
x: 20.0,
name: "second".into(),
});
ctx.defun("transform-x", |ctx, args| {
if args.len() != 1 {
return Err(LispError::RuntimeError("Expected one object".into()));
}
let id = ObjectId::from_lisp(args[0])?;
let x = ctx
.with_object::<Transform, _>(id, |obj| obj.x)
.ok_or_else(|| LispError::RuntimeError("Invalid Transform".into()))?;
ctx.float(x as f64)
})?;
let first = first.to_lisp(ctx)?;
let second = second.to_lisp(ctx)?;
let first_result = ctx.call("transform-x", &[&first])?;
let second_result = ctx.call("transform-x", &[&second])?;
assert_eq!(first_result.try_as_f64()?, 10.0);
assert_eq!(second_result.try_as_f64()?, 20.0);
Ok(())
}
fn run(name: &str, lisp: &LispContext<'_>, test: fn(&LispContext<'_>) -> Result<(), LispError>) {
print!("test {name} ... ");
match test(lisp) {
Ok(()) => println!("ok"),
Err(error) => panic!("{name} failed: {error}"),
}
}
fn main() -> Result<(), LispError> {
let lisp = Lisp::new()?;
lisp.enter(|ctx| {
run("property_getters", &ctx, test_property_getters);
run("property_setters", &ctx, test_property_setters);
run("class", &ctx, test_class);
run("registry", &ctx, test_registry);
run("expose", &ctx, test_expose);
run("object_id", &ctx, test_object_id);
run("register_class_manual", &ctx, test_register_class_manual);
});
Ok(())
}