1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use super::*;

pub fn metatable(ctx: Context) -> Table {
    let metatable = Table::new(&ctx);
    metatable
        .set(
            ctx,
            "__tostring",
            Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
                stack.push_front(
                    piccolo::String::from_static(&ctx, "Components { insert, remove, get }").into(),
                );
                Ok(CallbackReturn::Return)
            }),
        )
        .unwrap();
    metatable
        .set(ctx, "__newindex", ctx.singletons().get(ctx, no_newindex))
        .unwrap();

    let get_callback = ctx.registry().stash(
        &ctx,
        Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
            let (world, entity_ecsref, schema): (&WorldRef, &EcsRef, UserData) =
                stack.consume(ctx)?;

            let b = entity_ecsref.borrow();
            let entity = *b.schema_ref()?.try_cast::<Entity>()?;

            let schema = *schema.downcast_static::<&Schema>()?;

            let store = world.with(|world| {
                let store = world.components.get_cell_by_schema(schema);
                Ok::<_, anyhow::Error>(store)
            })?;

            let ecsref = EcsRef {
                data: EcsRefData::Component(ComponentRef { store, entity }),
                path: default(),
            };

            // Return nil if the component is not present
            if ecsref.borrow().schema_ref().is_err() {
                stack.replace(ctx, Value::Nil);
                return Ok(CallbackReturn::Return);
            }

            let ecsref = ecsref.into_value(ctx);
            stack.replace(ctx, ecsref);

            Ok(CallbackReturn::Return)
        }),
    );
    let insert_callback = ctx.registry().stash(
        &ctx,
        Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
            let (world, entity_ecsref, value_ecsref): (&WorldRef, &EcsRef, &EcsRef) =
                stack.consume(ctx)?;

            let b = entity_ecsref.borrow();
            let entity = *b.schema_ref()?.try_cast::<Entity>()?;

            let value = {
                let b = value_ecsref.borrow();
                let value = b.schema_ref()?;
                value.clone_into_box()
            };

            world.with(|world| {
                let store = world.components.get_by_schema(value.schema());
                let mut store = store.borrow_mut();
                store.insert_box(entity, value);
                Ok::<_, anyhow::Error>(())
            })?;

            Ok(CallbackReturn::Return)
        }),
    );
    let remove_callback = ctx.registry().stash(
        &ctx,
        Callback::from_fn(&ctx, |ctx, _fuel, mut stack| {
            let (world, entity_ecsref, schema): (&WorldRef, &EcsRef, UserData) =
                stack.consume(ctx)?;

            let b = entity_ecsref.borrow();
            let entity = *b.schema_ref()?.try_cast::<Entity>()?;

            let schema = *schema.downcast_static::<&Schema>()?;

            world.with(|world| {
                let store = world.components.get_by_schema(schema);
                let mut store = store.borrow_mut();
                store.remove_box(entity);
                Ok::<_, anyhow::Error>(())
            })?;

            Ok(CallbackReturn::Return)
        }),
    );

    metatable
        .set(
            ctx,
            "__index",
            Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| {
                let (_world, key): (&WorldRef, lua::Value) = stack.consume(ctx)?;

                if let Value::String(key) = key {
                    #[allow(clippy::single_match)]
                    match key.as_bytes() {
                        b"get" => {
                            stack.push_front(ctx.registry().fetch(&get_callback).into());
                        }
                        b"insert" => {
                            stack.push_front(ctx.registry().fetch(&insert_callback).into());
                        }
                        b"remove" => {
                            stack.push_front(ctx.registry().fetch(&remove_callback).into());
                        }
                        _ => (),
                    }
                }

                Ok(CallbackReturn::Return)
            }),
        )
        .unwrap();

    metatable
}