lucia_lang/objects/
userdata.rs

1use std::hash::{Hash, Hasher};
2
3use gc_arena::{barrier, lock, Collect, Mutation, Root, Rootable};
4use thiserror::Error;
5
6use crate::objects::{AnyValue, Table};
7
8#[derive(Debug, Copy, Clone, Error)]
9#[error("UserData type mismatch")]
10pub struct BadUserDataType;
11
12#[derive(Debug, Copy, Clone, Collect)]
13#[collect(no_drop)]
14pub struct AnyUserData<'gc>(AnyValue<'gc, lock::Lock<Option<Table<'gc>>>>);
15
16impl<'gc> PartialEq for AnyUserData<'gc> {
17    fn eq(&self, other: &Self) -> bool {
18        self.0.as_ptr() == other.0.as_ptr()
19    }
20}
21
22impl<'gc> Eq for AnyUserData<'gc> {}
23
24impl<'gc> Hash for AnyUserData<'gc> {
25    fn hash<H: Hasher>(&self, state: &mut H) {
26        self.0.as_ptr().hash(state)
27    }
28}
29
30#[derive(Collect)]
31#[collect(require_static)]
32struct StaticRoot<R> {
33    root: R,
34}
35
36impl<'a, R: 'static> Rootable<'a> for StaticRoot<R> {
37    type Root = StaticRoot<R>;
38}
39
40impl<'gc> AnyUserData<'gc> {
41    pub fn new<R>(mc: &Mutation<'gc>, val: Root<'gc, R>) -> Self
42    where
43        R: for<'a> Rootable<'a>,
44    {
45        AnyUserData(AnyValue::new::<R>(mc, None.into(), val))
46    }
47
48    pub fn new_static<R: 'static>(mc: &Mutation<'gc>, val: R) -> Self {
49        Self::new::<StaticRoot<R>>(mc, StaticRoot { root: val })
50    }
51
52    pub fn is<R>(&self) -> bool
53    where
54        R: for<'a> Rootable<'a>,
55    {
56        self.0.is::<R>()
57    }
58
59    pub fn is_static<R: 'static>(&self) -> bool {
60        self.is::<StaticRoot<R>>()
61    }
62
63    pub fn downcast<'a, R>(&'a self) -> Result<&'gc Root<'gc, R>, BadUserDataType>
64    where
65        R: for<'b> Rootable<'b>,
66    {
67        self.0.downcast::<R>().ok_or(BadUserDataType)
68    }
69
70    pub fn downcast_write<'a, R>(
71        &'a self,
72        mc: &Mutation<'gc>,
73    ) -> Result<&'gc barrier::Write<Root<'gc, R>>, BadUserDataType>
74    where
75        R: for<'b> Rootable<'b>,
76    {
77        self.0.downcast_write::<R>(mc).ok_or(BadUserDataType)
78    }
79
80    pub fn downcast_static<'a, R: 'static>(&'a self) -> Result<&'gc R, BadUserDataType> {
81        self.0
82            .downcast::<StaticRoot<R>>()
83            .map(|r| &r.root)
84            .ok_or(BadUserDataType)
85    }
86
87    pub fn metatable(&self) -> Option<Table<'gc>> {
88        self.0.metadata().get()
89    }
90
91    pub fn set_metatable(
92        &self,
93        mc: &Mutation<'gc>,
94        metatable: Option<Table<'gc>>,
95    ) -> Option<Table<'gc>> {
96        self.0.write_metadata(mc).unlock().replace(metatable)
97    }
98
99    pub fn as_ptr(&self) -> *const () {
100        self.0.as_ptr()
101    }
102}