bones_scripting/lua/bindings/
schema.rs

1use super::*;
2
3/// A wrapper around [`Schema`] that indicates that it should be excluded from, for example,
4/// an `entities:iter_with()` lua call.
5pub(super) struct WithoutSchema(pub &'static Schema);
6
7pub fn schema_fn(ctx: Context) -> Callback {
8    Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
9        let singletons = ctx.singletons();
10        let schema_metatable = singletons.get(ctx, schema::metatable);
11
12        let schema_name = stack.pop_front();
13        let Value::String(schema_name) = schema_name else {
14            return Err(anyhow::format_err!("Type error: expected string schema name").into());
15        };
16        let mut matches = SCHEMA_REGISTRY.schemas.iter().filter(|schema| {
17            schema.name.as_bytes() == schema_name.as_bytes()
18                || schema.full_name.as_bytes() == schema_name.as_bytes()
19        });
20
21        if let Some(next_match) = matches.next() {
22            if matches.next().is_some() {
23                return Err(anyhow::format_err!("Found multiple schemas matching name.").into());
24            }
25
26            // TODO: setup `toString` implementation so that printing schemas gives more information.
27            let schema = UserData::new_static(&ctx, next_match);
28            schema.set_metatable(&ctx, Some(schema_metatable));
29            stack.push_front(schema.into());
30        } else {
31            return Err(anyhow::format_err!("Schema not found: {schema_name}").into());
32        }
33
34        Ok(CallbackReturn::Return)
35    })
36}
37
38pub fn schema_of_fn(ctx: Context) -> Callback {
39    Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
40        let singletons = ctx.singletons();
41        let schema_metatable = singletons.get(ctx, schema::metatable);
42
43        let ecsref: &EcsRef = stack.consume(ctx)?;
44        let schema = ecsref.borrow().schema_ref()?.schema();
45
46        let schema = UserData::new_static(&ctx, schema);
47        schema.set_metatable(&ctx, Some(schema_metatable));
48        stack.replace(ctx, schema);
49
50        Ok(CallbackReturn::Return)
51    })
52}
53
54pub fn metatable(ctx: Context) -> Table {
55    let metatable = Table::new(&ctx);
56    metatable
57        .set(
58            ctx,
59            "__tostring",
60            Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
61                let this: UserData = stack.consume(ctx)?;
62                let this = this.downcast_static::<&Schema>()?;
63                let s = piccolo::String::from_slice(&ctx, format!("Schema({})", this.full_name));
64
65                stack.push_front(Value::String(s));
66                Ok(CallbackReturn::Return)
67            }),
68        )
69        .unwrap();
70    let create_fn = ctx.registry().stash(
71        &ctx,
72        Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
73            let this: UserData = stack.consume(ctx)?;
74            let this = this.downcast_static::<&Schema>()?;
75
76            let ecsref = EcsRef {
77                data: EcsRefData::Free(Rc::new(AtomicCell::new(SchemaBox::default(this)))),
78                path: default(),
79            }
80            .into_value(ctx);
81            stack.push_front(ecsref);
82
83            Ok(CallbackReturn::Return)
84        }),
85    );
86
87    let without_fn = ctx.registry().stash(
88        &ctx,
89        Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
90            let this: UserData = stack.consume(ctx)?;
91            let this = this.downcast_static::<&Schema>()?;
92            stack.replace(ctx, UserData::new_static(&ctx, WithoutSchema(this)));
93            Ok(CallbackReturn::Return)
94        }),
95    );
96
97    let eq_fn = Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
98        let (this, other): (UserData, UserData) = stack.consume(ctx)?;
99        let (this, other) = (
100            this.downcast_static::<&Schema>()?,
101            other.downcast_static::<&Schema>()?,
102        );
103        stack.replace(ctx, this.id() == other.id());
104        Ok(CallbackReturn::Return)
105    });
106
107    metatable.set(ctx, "__eq", eq_fn).unwrap();
108    metatable
109        .set(
110            ctx,
111            "__index",
112            Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
113                let (this, key): (UserData, lua::String) = stack.consume(ctx)?;
114                let this = this.downcast_static::<&Schema>()?;
115
116                match key.as_bytes() {
117                    b"name" => {
118                        stack.replace(
119                            ctx,
120                            Value::String(piccolo::String::from_static(&ctx, this.name.as_bytes())),
121                        );
122                    }
123                    b"full_name" => {
124                        stack.replace(
125                            ctx,
126                            Value::String(piccolo::String::from_static(
127                                &ctx,
128                                this.full_name.as_bytes(),
129                            )),
130                        );
131                    }
132                    b"create" => stack.replace(ctx, ctx.registry().fetch(&create_fn)),
133                    b"without" => stack.replace(ctx, ctx.registry().fetch(&without_fn)),
134                    _ => (),
135                }
136
137                Ok(CallbackReturn::Return)
138            }),
139        )
140        .unwrap();
141
142    metatable
143}