1use std::{
2 any::Any,
3 marker::PhantomData,
4 ops::{Deref, DerefMut},
5 sync::{RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError},
6};
7
8#[allow(unused_imports)]
9use log::*;
10use thiserror::Error;
11
12#[derive(Error, Debug, PartialEq)]
13pub enum CustomValError {
14 #[error("Lock is poisoned")]
15 LockPoisoned,
16 #[error("Lock is not available")]
17 LockNotAvailable,
18 #[error("expected type {expected} but found type {actual}")]
19 WrongType {
20 expected: &'static str,
21 actual: &'static str,
22 },
23}
24
25impl<T> From<TryLockError<T>> for CustomValError {
26 fn from(value: TryLockError<T>) -> Self {
27 match value {
28 TryLockError::Poisoned(_) => CustomValError::LockPoisoned,
31 TryLockError::WouldBlock => CustomValError::LockNotAvailable,
32 }
33 }
34}
35
36#[derive(Debug)]
38pub struct CustomVal(pub(crate) RwLock<Box<dyn CustomTypeSealed>>);
39
40impl CustomVal {
41 pub fn new(val: impl CustomType) -> CustomVal {
43 CustomVal(RwLock::new(Box::new(val)))
44 }
45
46 pub fn get<T>(&self) -> Result<CustomValRef<'_, T>, CustomValError>
51 where
52 T: CustomType,
53 {
54 let guard = self.0.try_read()?;
55 let want_type_id = std::any::TypeId::of::<T>();
56 let have_type_id = guard.as_any().type_id();
57
58 if want_type_id == have_type_id {
59 Ok(CustomValRef {
60 guard,
61 _type: PhantomData,
62 })
63 } else {
64 Err(CustomValError::WrongType {
65 expected: std::any::type_name::<T>(),
66 actual: guard.name(),
67 })
68 }
69 }
70
71 pub fn get_mut<T>(&self) -> Result<CustomValMut<T>, CustomValError>
76 where
77 T: CustomType,
78 {
79 let guard = self.0.try_write()?;
80 let want_type_id = std::any::TypeId::of::<T>();
81 let have_type_id = guard.as_any().type_id();
82 if want_type_id == have_type_id {
83 Ok(CustomValMut {
84 guard,
85 _type: PhantomData,
86 })
87 } else {
88 Err(CustomValError::WrongType {
89 expected: std::any::type_name::<T>(),
90 actual: guard.name(),
91 })
92 }
93 }
94}
95
96impl std::fmt::Display for CustomVal {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self.0.try_read() {
99 Err(_) => write!(f, "<custom-value-read-locked>"),
100 Ok(obj) => obj.deref().fmt(f),
101 }
102 }
103}
104
105#[derive(Debug)]
106pub struct CustomValRef<'a, T> {
107 guard: RwLockReadGuard<'a, Box<dyn CustomTypeSealed>>,
108 _type: PhantomData<T>,
109}
110
111impl<'a, T: 'static> Deref for CustomValRef<'a, T> {
112 type Target = T;
113
114 fn deref(&self) -> &Self::Target {
115 self.guard.as_ref().as_any().downcast_ref::<T>().unwrap()
116 }
117}
118
119#[derive(Debug)]
120pub struct CustomValMut<'a, T> {
121 guard: RwLockWriteGuard<'a, Box<dyn CustomTypeSealed>>,
122 _type: PhantomData<T>,
123}
124
125impl<'a, T: 'static> Deref for CustomValMut<'a, T> {
126 type Target = T;
127
128 fn deref(&self) -> &Self::Target {
129 self.guard.as_ref().as_any().downcast_ref::<T>().unwrap()
130 }
131}
132
133impl<'a, T: 'static> DerefMut for CustomValMut<'a, T> {
134 fn deref_mut(&mut self) -> &mut Self::Target {
135 self.guard
136 .as_mut()
137 .as_any_mut()
138 .downcast_mut::<T>()
139 .unwrap()
140 }
141}
142
143pub(crate) trait CustomTypeSealed:
144 'static + Send + Sync + std::fmt::Display + std::fmt::Debug
145{
146 fn as_any_mut(&mut self) -> &mut dyn Any;
147 fn as_any(&self) -> &dyn Any;
148 fn name(&self) -> &'static str {
149 std::any::type_name_of_val(self)
150 }
151}
152
153pub trait CustomType:
166 'static + Send + Sync + std::fmt::Display + std::fmt::Debug + std::any::Any
167{
168}
169
170impl<T> CustomTypeSealed for T
171where
172 T: CustomType,
173{
174 fn as_any_mut(&mut self) -> &mut dyn Any {
175 self
176 }
177
178 fn as_any(&self) -> &dyn Any {
179 self
180 }
181
182 fn name(&self) -> &'static str {
183 std::any::type_name::<T>()
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use crate::{
191 val::{NativeFunctionContext, UnsafeVal, Val, ValBuilder},
192 Vm, VmResult,
193 };
194
195 #[derive(Debug, PartialEq)]
196 struct MyType {
197 number: i64,
198 }
199
200 impl CustomType for MyType {}
201
202 impl std::fmt::Display for MyType {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 write!(f, "magic number {}", self.number)
205 }
206 }
207
208 #[test]
209 fn custom_type_can_be_printed() {
210 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
211 assert_eq!(
212 vm.eval_str("custom-value").unwrap().to_string(),
213 "magic number 42"
214 );
215 }
216
217 #[test]
218 fn custom_type_can_be_accessed() {
219 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
220 let val = vm.eval_str("custom-value").unwrap();
221 let got = val.try_custom::<MyType>().unwrap();
222 assert_eq!(got.deref(), &MyType { number: 42 });
223 }
224
225 #[test]
226 fn custom_type_can_be_accessed_multiple_times() {
227 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
228 vm.eval_str("custom-value").unwrap().map(|vm, val1| {
229 let got1 = val1.try_custom::<MyType>().unwrap();
230 let val2 = vm.eval_str("custom-value").unwrap();
231 let got2 = val2.try_custom::<MyType>().unwrap();
232 assert_eq!(got1.deref() as *const MyType, got2.deref() as *const MyType);
233 });
234 }
235
236 #[test]
237 fn custom_type_get_after_get_mut_fails() {
238 let mut vm = std::thread::spawn(|| {
239 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
240 let val1 = vm.eval_str("custom-value").unwrap();
241 let get1 = val1.try_custom_mut::<MyType>().unwrap();
242 std::mem::forget(get1);
243 std::mem::forget(val1);
244 vm
245 })
246 .join()
247 .unwrap();
248 let val1 = vm.eval_str("custom-value").unwrap();
249 assert_eq!(
250 val1.try_custom::<MyType>().unwrap_err(),
251 CustomValError::LockNotAvailable
252 );
253 }
254
255 #[test]
256 fn display_on_locked_value_does_not_freeze_or_panic() {
257 let mut vm = std::thread::spawn(|| {
258 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
259 let val1 = vm.eval_str("custom-value").unwrap();
260 let get1 = val1.try_custom_mut::<MyType>().unwrap();
261 std::mem::forget(get1);
262 std::mem::forget(val1);
263 vm
264 })
265 .join()
266 .unwrap();
267 let val1 = vm.eval_str("custom-value").unwrap();
268 assert_eq!(
269 val1.try_custom::<MyType>().unwrap_err(),
270 CustomValError::LockNotAvailable
271 );
272 assert_eq!(val1.to_string(), "<custom-value-read-locked>");
273 }
274
275 #[test]
276 fn custom_type_get_mut_after_read_fails() {
277 let mut vm = std::thread::spawn(|| {
278 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
279 let val1 = vm.eval_str("custom-value").unwrap();
280 let get1 = val1.try_custom::<MyType>().unwrap();
281 let get2 = val1.try_custom::<MyType>().unwrap();
282 std::mem::forget(get1);
283 std::mem::forget(get2);
284 std::mem::forget(val1);
285 vm
286 })
287 .join()
288 .unwrap();
289 let val1 = vm.eval_str("custom-value").unwrap();
290 assert_eq!(
291 val1.try_custom_mut::<MyType>().unwrap_err(),
292 CustomValError::LockNotAvailable
293 );
294 }
295
296 #[test]
297 fn get_mut_can_mutate_value() {
298 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
299 {
300 let mutate_val = vm.eval_str("custom-value").unwrap();
301 let mut mutate_val_ref = mutate_val.try_custom_mut::<MyType>().unwrap();
302 *mutate_val_ref = MyType { number: -42 };
303 assert_eq!(mutate_val_ref.deref(), &MyType { number: -42 });
304 }
305
306 let val = vm.eval_str("custom-value").unwrap();
307 assert_eq!(
308 val.try_custom::<MyType>().unwrap().deref(),
309 &MyType { number: -42 }
310 );
311 }
312
313 #[derive(Debug, PartialEq)]
314 struct OtherType {
315 string: &'static str,
316 }
317
318 impl CustomType for OtherType {}
319
320 impl std::fmt::Display for OtherType {
321 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322 write!(f, "magic string {}", self.string)
323 }
324 }
325
326 #[test]
327 fn custom_type_get_and_get_mut_with_wrong_custom_type_fails() {
328 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
329 let val = vm.eval_str("custom-value").unwrap();
330 assert_eq!(
331 val.try_custom::<OtherType>().unwrap_err(),
332 CustomValError::WrongType {
333 expected: OtherType { string: "" }.name(),
334 actual: MyType { number: 0 }.name(),
335 }
336 );
337 assert_eq!(
338 val.try_custom_mut::<OtherType>().unwrap_err(),
339 CustomValError::WrongType {
340 expected: OtherType { string: "" }.name(),
341 actual: MyType { number: 0 }.name(),
342 }
343 );
344 }
345
346 #[test]
347 fn custom_type_get_and_get_mut_with_wrong_val_type_fails() {
348 let mut vm = Vm::default().with_custom_value("custom-value", MyType { number: 42 });
349 let val = vm.eval_str("42.0").unwrap();
350 assert_eq!(
351 val.try_custom::<OtherType>().unwrap_err(),
352 CustomValError::WrongType {
353 expected: OtherType { string: "" }.name(),
354 actual: UnsafeVal::FLOAT_TYPE_NAME,
355 }
356 );
357 assert_eq!(
358 val.try_custom_mut::<OtherType>().unwrap_err(),
359 CustomValError::WrongType {
360 expected: OtherType { string: "" }.name(),
361 actual: UnsafeVal::FLOAT_TYPE_NAME,
362 }
363 );
364 }
365
366 #[test]
367 fn custom_type_can_be_made_from_native_function() {
368 fn custom_function<'a>(
369 ctx: NativeFunctionContext<'a>,
370 args: &[Val],
371 ) -> VmResult<ValBuilder<'a>> {
372 let number = args[0].try_int().unwrap();
373 let v = MyType { number };
374 Ok(ctx.new_custom(v))
375 }
376 let mut vm = Vm::default().with_native_function("custom-function", custom_function);
377 assert_eq!(
378 vm.eval_str("(custom-function 6)")
379 .unwrap()
380 .try_custom::<MyType>()
381 .unwrap()
382 .deref(),
383 &MyType { number: 6 }
384 );
385 }
386
387 #[test]
388 fn custom_type_has_name() {
389 assert_eq!(
390 MyType { number: 0 }.name(),
391 "spore_vm::val::custom::tests::MyType"
392 );
393 assert_eq!(
394 OtherType { string: "" }.name(),
395 "spore_vm::val::custom::tests::OtherType"
396 );
397 }
398
399 #[test]
400 fn hacks_for_code_coverage() {
401 let mut other_type = OtherType { string: "" };
403 other_type.as_any();
404 other_type.as_any_mut();
405 other_type.to_string();
406 }
407}