lucia_lang/objects/
any.rs1use std::{any::TypeId, fmt};
2
3use gc_arena::{barrier::Write, Collect, Gc, Mutation, Root, Rootable};
4
5#[derive(Collect)]
40#[collect(no_drop)]
41pub struct AnyValue<'gc, M: 'gc>(Gc<'gc, Header<M>>);
42
43impl<'gc, M> fmt::Debug for AnyValue<'gc, M>
44where
45 M: fmt::Debug,
46{
47 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
48 fmt.debug_struct("AnyValue")
49 .field("metadata", self.metadata())
50 .field("type_id", &(self.type_id()))
51 .finish()
52 }
53}
54
55#[derive(Collect)]
56#[collect(no_drop)]
57struct Header<M> {
58 metadata: M,
59 type_id: TypeId,
60}
61
62#[derive(Collect)]
63#[collect(no_drop)]
64#[repr(C)]
65struct Value<M, V> {
66 header: Header<M>,
67 data: V,
68}
69
70impl<'gc, M> Copy for AnyValue<'gc, M> {}
71
72impl<'gc, M> Clone for AnyValue<'gc, M> {
73 fn clone(&self) -> Self {
74 *self
75 }
76}
77
78impl<'gc, M> AnyValue<'gc, M> {
79 pub fn new<R>(mc: &Mutation<'gc>, metadata: M, data: Root<'gc, R>) -> Self
80 where
81 M: Collect,
82 R: for<'a> Rootable<'a>,
83 {
84 let val = Gc::new(
85 mc,
86 Value::<M, Root<'gc, R>> {
87 header: Header {
88 metadata,
89 type_id: TypeId::of::<R>(),
90 },
91 data,
92 },
93 );
94
95 Self(unsafe { Gc::cast::<Header<M>>(val) })
98 }
99
100 pub fn metadata(&self) -> &'gc M {
101 &self.0.as_ref().metadata
102 }
103
104 pub fn write_metadata(&self, mc: &Mutation<'gc>) -> &'gc Write<M> {
105 gc_arena::barrier::field!(Gc::write(mc, self.0), Header, metadata)
106 }
107
108 pub fn as_ptr(&self) -> *const () {
109 Gc::as_ptr(self.0) as *const ()
110 }
111
112 pub fn type_id(&self) -> TypeId {
113 self.0.type_id
114 }
115
116 pub fn is<R>(&self) -> bool
117 where
118 R: for<'b> Rootable<'b>,
119 {
120 TypeId::of::<R>() == self.0.type_id
121 }
122
123 pub fn downcast<R>(&self) -> Option<&'gc Root<'gc, R>>
124 where
125 R: for<'b> Rootable<'b>,
126 {
127 if TypeId::of::<R>() == self.0.type_id {
128 let ptr = unsafe { Gc::cast::<Value<M, Root<'gc, R>>>(self.0) };
129 Some(&ptr.as_ref().data)
130 } else {
131 None
132 }
133 }
134
135 pub fn downcast_write<R>(&self, mc: &Mutation<'gc>) -> Option<&'gc Write<Root<'gc, R>>>
136 where
137 R: for<'b> Rootable<'b>,
138 {
139 let root = self.downcast::<R>()?;
140 Gc::write(mc, self.0);
141 Some(unsafe { Write::assume(root) })
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use gc_arena::rootless_arena;
149
150 use super::*;
151
152 #[test]
153 fn test_any_value() {
154 rootless_arena(|mc| {
155 #[derive(Collect)]
156 #[collect(no_drop)]
157 struct A<'gc>(Gc<'gc, i32>);
158
159 #[derive(Collect)]
160 #[collect(no_drop)]
161 struct B<'gc>(Gc<'gc, i32>);
162
163 #[derive(Collect)]
164 #[collect(no_drop)]
165 struct C<'gc>(Gc<'gc, i32>);
166
167 let any1 = AnyValue::new::<Rootable![A<'_>]>(mc, 1i32, A(Gc::new(mc, 5)));
168 let any2 = AnyValue::new::<Rootable![B<'_>]>(mc, 2i32, B(Gc::new(mc, 6)));
169 let any3 = AnyValue::new::<Rootable![C<'_>]>(mc, 3i32, C(Gc::new(mc, 7)));
170
171 assert!(any1.is::<Rootable![A<'_>]>());
172 assert!(!any1.is::<Rootable![B<'_>]>());
173 assert!(!any1.is::<Rootable![C<'_>]>());
174
175 assert_eq!(*any1.metadata(), 1);
176 assert_eq!(*any1.downcast::<Rootable![A<'_>]>().unwrap().0, 5);
177 assert_eq!(*any2.metadata(), 2);
178 assert_eq!(*any2.downcast::<Rootable![B<'_>]>().unwrap().0, 6);
179 assert_eq!(*any3.metadata(), 3);
180 assert_eq!(*any3.downcast::<Rootable![C<'_>]>().unwrap().0, 7);
181
182 assert!(any1.downcast::<Rootable![B<'_>]>().is_none());
183 assert!(any1.downcast::<Rootable![C<'_>]>().is_none());
184 assert!(any2.downcast::<Rootable![A<'_>]>().is_none());
185 assert!(any2.downcast::<Rootable![C<'_>]>().is_none());
186 assert!(any3.downcast::<Rootable![A<'_>]>().is_none());
187 assert!(any3.downcast::<Rootable![B<'_>]>().is_none());
188 })
189 }
190}