1use std::marker::PhantomData;
2
3use gc_arena::{barrier, Collect, Mutation, Root, Rootable};
4use piccolo::{
5 Callback, CallbackReturn, Context, Error, Execution, FromMultiValue, IntoMultiValue, IntoValue,
6 MetaMethod, Table, UserData, Value,
7};
8
9#[derive(Collect)]
10#[collect(no_drop, bound = "")]
11pub struct UserMethods<'gc, U: for<'a> Rootable<'a>> {
12 table: Table<'gc>,
13 _marker: PhantomData<U>,
14}
15
16impl<'gc, U: for<'a> Rootable<'a>> Copy for UserMethods<'gc, U> {}
17
18impl<'gc, U: for<'a> Rootable<'a>> Clone for UserMethods<'gc, U> {
19 fn clone(&self) -> Self {
20 *self
21 }
22}
23
24impl<'gc, U: for<'a> Rootable<'a>> UserMethods<'gc, U> {
25 pub fn new(mc: &Mutation<'gc>) -> Self {
26 Self {
27 table: Table::new(mc),
28 _marker: PhantomData,
29 }
30 }
31
32 pub fn add<F, A, R>(self, name: &'static str, ctx: Context<'gc>, method: F) -> bool
33 where
34 F: Fn(&Root<'gc, U>, Context<'gc>, Execution<'gc, '_>, A) -> Result<R, Error<'gc>>
35 + 'static,
36 A: FromMultiValue<'gc>,
37 R: IntoMultiValue<'gc>,
38 {
39 let callback = Callback::from_fn(&ctx, move |ctx, exec, mut stack| {
40 let userdata: UserData = stack.from_front(ctx)?;
41 let args: A = stack.consume(ctx)?;
42 let this = userdata.downcast::<U>()?;
43 let ret = method(&this, ctx, exec, args)?;
44 stack.replace(ctx, ret);
45 Ok(CallbackReturn::Return)
46 });
47
48 !self.table.set(ctx, name, callback).unwrap().is_nil()
49 }
50
51 pub fn add_write<F, A, R>(self, name: &'static str, ctx: Context<'gc>, method: F) -> bool
52 where
53 F: Fn(
54 &barrier::Write<Root<'gc, U>>,
55 Context<'gc>,
56 Execution<'gc, '_>,
57 A,
58 ) -> Result<R, Error<'gc>>
59 + 'static,
60 A: FromMultiValue<'gc>,
61 R: IntoMultiValue<'gc>,
62 {
63 let callback = Callback::from_fn(&ctx, move |ctx, exec, mut stack| {
64 let userdata: UserData = stack.from_front(ctx)?;
65 let args: A = stack.consume(ctx)?;
66 let mut this = userdata.downcast_write::<U>(&ctx)?;
67 let ret = method(&mut this, ctx, exec, args)?;
68 stack.replace(ctx, ret);
69 Ok(CallbackReturn::Return)
70 });
71
72 !self.table.set(ctx, name, callback).unwrap().is_nil()
73 }
74
75 pub fn metatable(self, ctx: Context<'gc>) -> Table<'gc> {
76 let metatable = Table::new(&ctx);
77 metatable.set(ctx, MetaMethod::Index, self.table).unwrap();
78 metatable
79 }
80
81 pub fn wrap(self, ctx: Context<'gc>, ud: Root<'gc, U>) -> UserData<'gc> {
82 let ud = UserData::new::<U>(&ctx, ud);
83 ud.set_metatable(&ctx, Some(self.metatable(ctx)));
84 ud
85 }
86}
87
88impl<'gc, U: for<'a> Rootable<'a>> IntoValue<'gc> for UserMethods<'gc, U> {
89 fn into_value(self, _: Context<'gc>) -> Value<'gc> {
90 self.table.into()
91 }
92}
93
94#[derive(Collect)]
95#[collect(no_drop, bound = "")]
96pub struct StaticUserMethods<'gc, U: 'static> {
97 table: Table<'gc>,
98 _marker: PhantomData<U>,
99}
100
101impl<'gc, U: 'static> Copy for StaticUserMethods<'gc, U> {}
102
103impl<'gc, U: 'static> Clone for StaticUserMethods<'gc, U> {
104 fn clone(&self) -> Self {
105 *self
106 }
107}
108
109impl<'gc, U: 'static> StaticUserMethods<'gc, U> {
110 pub fn new(mc: &Mutation<'gc>) -> Self {
111 Self {
112 table: Table::new(mc),
113 _marker: PhantomData,
114 }
115 }
116
117 pub fn add<F, A, R>(self, name: &'static str, ctx: Context<'gc>, method: F) -> bool
118 where
119 F: Fn(&U, Context<'gc>, Execution<'gc, '_>, A) -> Result<R, Error<'gc>> + 'static,
120 A: FromMultiValue<'gc>,
121 R: IntoMultiValue<'gc>,
122 {
123 let callback = Callback::from_fn(&ctx, move |ctx, exec, mut stack| {
124 let userdata: UserData = stack.from_front(ctx)?;
125 let args: A = stack.consume(ctx)?;
126 let this = userdata.downcast_static::<U>()?;
127 let ret = method(&this, ctx, exec, args)?;
128 stack.replace(ctx, ret);
129 Ok(CallbackReturn::Return)
130 });
131
132 !self.table.set(ctx, name, callback).unwrap().is_nil()
133 }
134
135 pub fn metatable(self, ctx: Context<'gc>) -> Table<'gc> {
136 let metatable = Table::new(&ctx);
137 metatable.set(ctx, MetaMethod::Index, self.table).unwrap();
138 metatable
139 }
140
141 pub fn wrap(self, ctx: Context<'gc>, ud: U) -> UserData<'gc> {
142 let ud = UserData::new_static(&ctx, ud);
143 ud.set_metatable(&ctx, Some(self.metatable(ctx)));
144 ud
145 }
146}
147
148impl<'gc, U: 'static> IntoValue<'gc> for StaticUserMethods<'gc, U> {
149 fn into_value(self, _: Context<'gc>) -> Value<'gc> {
150 self.table.into()
151 }
152}