piccolo_util/
user_methods.rs

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}