use super::*;
pub(super) struct WithoutSchema(pub &'static Schema);
pub fn schema_fn(ctx: Context) -> Callback {
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let singletons = ctx.singletons();
let schema_metatable = singletons.get(ctx, schema::metatable);
let schema_name = stack.pop_front();
let Value::String(schema_name) = schema_name else {
return Err(anyhow::format_err!("Type error: expected string schema name").into());
};
let mut matches = SCHEMA_REGISTRY.schemas.iter().filter(|schema| {
schema.name.as_bytes() == schema_name.as_bytes()
|| schema.full_name.as_bytes() == schema_name.as_bytes()
});
if let Some(next_match) = matches.next() {
if matches.next().is_some() {
return Err(anyhow::format_err!("Found multiple schemas matching name.").into());
}
let schema = UserData::new_static(&ctx, next_match);
schema.set_metatable(&ctx, Some(schema_metatable));
stack.push_front(schema.into());
} else {
return Err(anyhow::format_err!("Schema not found: {schema_name}").into());
}
Ok(CallbackReturn::Return)
})
}
pub fn schema_of_fn(ctx: Context) -> Callback {
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let singletons = ctx.singletons();
let schema_metatable = singletons.get(ctx, schema::metatable);
let ecsref: &EcsRef = stack.consume(ctx)?;
let schema = ecsref.borrow().schema_ref()?.schema();
let schema = UserData::new_static(&ctx, schema);
schema.set_metatable(&ctx, Some(schema_metatable));
stack.replace(ctx, schema);
Ok(CallbackReturn::Return)
})
}
pub fn metatable(ctx: Context) -> Table {
let metatable = Table::new(&ctx);
metatable
.set(
ctx,
"__tostring",
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let this: UserData = stack.consume(ctx)?;
let this = this.downcast_static::<&Schema>()?;
let s = piccolo::String::from_slice(&ctx, format!("Schema({})", this.full_name));
stack.push_front(Value::String(s));
Ok(CallbackReturn::Return)
}),
)
.unwrap();
let create_fn = ctx.registry().stash(
&ctx,
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let this: UserData = stack.consume(ctx)?;
let this = this.downcast_static::<&Schema>()?;
let ecsref = EcsRef {
data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::default(this)))),
path: default(),
}
.into_value(ctx);
stack.push_front(ecsref);
Ok(CallbackReturn::Return)
}),
);
let without_fn = ctx.registry().stash(
&ctx,
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let this: UserData = stack.consume(ctx)?;
let this = this.downcast_static::<&Schema>()?;
stack.replace(ctx, UserData::new_static(&ctx, WithoutSchema(this)));
Ok(CallbackReturn::Return)
}),
);
let eq_fn = Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let (this, other): (UserData, UserData) = stack.consume(ctx)?;
let (this, other) = (
this.downcast_static::<&Schema>()?,
other.downcast_static::<&Schema>()?,
);
stack.replace(ctx, this.id() == other.id());
Ok(CallbackReturn::Return)
});
metatable.set(ctx, "__eq", eq_fn).unwrap();
metatable
.set(
ctx,
"__index",
Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
let (this, key): (UserData, lua::String) = stack.consume(ctx)?;
let this = this.downcast_static::<&Schema>()?;
match key.as_bytes() {
b"name" => {
stack.replace(
ctx,
Value::String(piccolo::String::from_static(&ctx, this.name.as_bytes())),
);
}
b"full_name" => {
stack.replace(
ctx,
Value::String(piccolo::String::from_static(
&ctx,
this.full_name.as_bytes(),
)),
);
}
b"create" => stack.replace(ctx, ctx.registry().fetch(&create_fn)),
b"without" => stack.replace(ctx, ctx.registry().fetch(&without_fn)),
_ => (),
}
Ok(CallbackReturn::Return)
}),
)
.unwrap();
metatable
}