1use std::{
2 ffi::CStr,
3 num::NonZero,
4 ops::{Deref, DerefMut},
5 os::raw::{c_char, c_long, c_void},
6 slice, str,
7 sync::atomic::Ordering,
8};
9
10use crate::{api::sapi, bindings::*, Result, Value};
11
12#[repr(C)]
13#[derive(Debug, Copy, Clone)]
14pub struct Atom(NonZero<som_atom_t>);
15
16impl Atom {
17 pub fn new(name: impl AsRef<CStr>) -> Result<Self> {
18 sapi()?
19 .atom_value(name.as_ref())
20 .map(|v| Self(unsafe { NonZero::new_unchecked(v) }))
21 }
22
23 pub fn name(&self) -> Result<String> {
24 let mut target = String::new();
25 let done =
26 sapi()?.atom_name_cb(self.0.get(), Some(str_thunk), &mut target as *mut _ as _)?;
27 if done {
28 Ok(target)
29 } else {
30 Err(crate::Error::InvalidAtom(self.0.get()))
31 }
32 }
33}
34
35impl From<Atom> for som_atom_t {
36 fn from(value: Atom) -> Self {
37 value.0.get()
38 }
39}
40
41unsafe extern "C" fn str_thunk(data: LPCSTR, len: UINT, target_ptr: LPVOID) {
42 let data = slice::from_raw_parts(data as _, len as _);
43 let data = str::from_utf8_unchecked(data);
44 let target = target_ptr as *mut String;
45 *target = data.to_string();
46}
47
48#[repr(transparent)]
49struct RawAssetObj(som_asset_t);
50
51#[allow(dead_code)]
52impl RawAssetObj {
53 pub(crate) fn new(class_data: som_asset_class_t) -> Self {
54 let isa = Box::new(class_data);
55 Self(som_asset_t {
56 isa: Box::into_raw(isa),
57 })
58 }
59
60 pub(crate) fn vtable(&self) -> &som_asset_class_t {
61 unsafe { &*self.0.isa }
62 }
63
64 pub(crate) fn add_ref(&self) -> c_long {
65 unsafe {
66 let Some(f) = self.vtable().asset_add_ref else {
67 return -1;
68 };
69
70 f(core::mem::transmute_copy(&self))
71 }
72 }
73
74 pub(crate) fn release(&self) -> c_long {
75 unsafe {
76 let Some(f) = self.vtable().asset_release else {
77 return -1;
78 };
79
80 f(core::mem::transmute_copy(&self))
81 }
82 }
83
84 pub fn passport(&self) -> Option<&som_passport_t> {
85 unsafe {
86 self.vtable()
87 .asset_get_passport
88 .map(|f| &*f(core::mem::transmute_copy(&self)))
89 }
90 }
91}
92
93pub type Passport = crate::bindings::som_passport_t;
94pub trait HasPassport {
95 fn passport(&self) -> Result<&'static Passport>;
96}
97
98pub trait ItemGetter: HasPassport {
99 fn get_item(&self, key: &Value) -> Result<Option<Value>>;
100}
101pub trait HasItemGetter {
102 fn has_item_getter(&self) -> bool;
103 fn do_get_item(&self, key: &Value) -> Result<Option<Value>>;
104}
105
106impl<T> HasItemGetter for &&T {
107 #[inline(always)]
108 fn has_item_getter(&self) -> bool {
109 false
110 }
111
112 fn do_get_item(&self, _key: &Value) -> Result<Option<Value>> {
113 Ok(None)
114 }
115}
116
117impl<T: ItemGetter> HasItemGetter for &mut &&T {
118 #[inline(always)]
119 fn has_item_getter(&self) -> bool {
120 true
121 }
122
123 fn do_get_item(&self, key: &Value) -> Result<Option<Value>> {
124 self.get_item(key)
125 }
126}
127
128pub trait ItemSetter: HasPassport {
129 fn set_item(&self, key: &Value, value: &Value) -> Result<()>;
130}
131
132pub trait HasItemSetter {
133 fn has_item_setter(&self) -> bool;
134 fn do_set_item(&self, key: &Value, value: &Value) -> Result<()>;
135}
136
137impl<T> HasItemSetter for &&T {
138 #[inline(always)]
139 fn has_item_setter(&self) -> bool {
140 false
141 }
142
143 fn do_set_item(&self, _key: &Value, _value: &Value) -> Result<()> {
144 Ok(())
145 }
146}
147
148impl<T: ItemSetter> HasItemSetter for &mut &&T {
149 #[inline(always)]
150 fn has_item_setter(&self) -> bool {
151 true
152 }
153
154 fn do_set_item(&self, key: &Value, value: &Value) -> Result<()> {
155 self.set_item(key, value)
156 }
157}
158
159#[macro_export]
160macro_rules! impl_item_getter {
161 ($type:ty) => {
162 impl_item_getter!($type, item_getter)
163 };
164 ($type:ty, $name:ident) => {
165 unsafe extern "C" fn $name(
166 thing: *mut ::rsciter::bindings::som_asset_t,
167 p_key: *const ::rsciter::bindings::SCITER_VALUE,
168 p_value: *mut ::rsciter::bindings::SCITER_VALUE,
169 ) -> ::rsciter::bindings::SBOOL {
170 use rsciter::AsValueRef;
171 let key = p_key.as_value_ref();
172 let asset_ref = ::rsciter::som::AssetRef::<$type>::new(thing);
173 let Ok(Some(res)) = (&mut &asset_ref.data()).do_get_item(key) else {
174 return 0;
175 };
176
177 *p_value = res.take();
178 return 1;
179 }
180 };
181}
182
183#[macro_export]
184macro_rules! impl_item_setter {
185 ($type:ty) => {
186 impl_item_setter!($type, item_setter)
187 };
188 ($type:ty, $name:ident) => {
189 unsafe extern "C" fn $name(
190 thing: *mut ::rsciter::bindings::som_asset_t,
191 p_key: *const ::rsciter::bindings::SCITER_VALUE,
192 p_value: *const ::rsciter::bindings::SCITER_VALUE,
193 ) -> ::rsciter::bindings::SBOOL {
194 use rsciter::AsValueRef;
195 let key = p_key.as_value_ref();
196 let value = p_value.as_value_ref();
197 let asset_ref = ::rsciter::som::AssetRef::<$type>::new(thing);
198 let Ok(_) = (&mut &asset_ref.data()).do_set_item(key, value) else {
199 return 0;
200 };
201
202 return 1;
203 }
204 };
205}
206
207pub use impl_item_getter;
208pub use impl_item_setter;
209
210pub type PropertyDef = crate::bindings::som_property_def_t;
211pub type PropertyAccessorDef = crate::bindings::som_property_def_t__bindgen_ty_1;
212pub type PropertyAccessors = crate::bindings::som_property_def_t__bindgen_ty_1__bindgen_ty_1;
213unsafe impl Sync for PropertyDef {}
214unsafe impl Send for PropertyDef {}
215
216#[macro_export]
217macro_rules! impl_prop {
218 ($type:ident :: $name:ident) => {
219 impl_prop!($type :: $name : true true)
220 };
221 ($type:ident :: $name:ident get) => {
222 impl_prop!($type :: $name : true false)
223 };
224 ($type:ident :: $name:ident set) => {
225 impl_prop!($type :: $name : false true)
226 };
227 ($type:ident :: $name:ident get set) => {
228 impl_prop!($type :: $name : true true)
229 };
230 ($type:ident :: $name:ident set get) => {
231 impl_prop!($type :: $name : true true)
232 };
233
234 ($type:ident :: $name:ident : $has_getter:literal $has_setter:literal) => {{
235 use ::rsciter::*;
236
237 unsafe extern "C" fn getter(
238 thing: *mut bindings::som_asset_t,
239 p_value: *mut bindings::SCITER_VALUE,
240 ) -> bindings::SBOOL {
241 let asset_ref = som::AssetRef::<$type>::new(thing);
242 let Ok(value) = conv::ToValue::to_value(&asset_ref.$name) else {
243 return 0;
244 };
245
246 *p_value = value.take();
247
248 1
249 }
250
251 unsafe extern "C" fn setter(
252 thing: *mut bindings::som_asset_t,
253 p_value: *mut bindings::SCITER_VALUE,
254 ) -> bindings::SBOOL {
255 let mut asset_mut = som::AssetRefMut::<$type>::new(thing);
256 let value = p_value.as_value_ref();
257 let Ok(_) = ::rsciter::conv::FromValue::from_value(value)
258 .map(|v| asset_mut.$name = v)
259 else {
260 return 0;
261 };
262
263 1
264 }
265
266 som::Atom::new(::rsciter::cstr!($name)).map(|name| som::PropertyDef {
267 type_: bindings::SOM_PROP_TYPE::SOM_PROP_ACCSESSOR.0 as _,
268 name: name.into(),
269 u: som::PropertyAccessorDef {
270 accs: som::PropertyAccessors {
271 getter: if $has_getter { Some(getter) } else { None },
272 setter: if $has_setter { Some(setter) } else { None },
273 },
274 },
275 })
276
277
278 }};
279}
280pub use impl_prop;
281
282pub trait Fields: HasPassport {
320 fn fields() -> &'static [Result<PropertyDef>];
321}
322
323pub trait VirtualProperties: HasPassport {
325 fn properties() -> &'static [Result<PropertyDef>];
326}
327
328pub trait HasFields {
329 fn enum_fields(&self) -> &'static [Result<PropertyDef>];
330}
331
332impl<T> HasFields for &&T {
333 fn enum_fields(&self) -> &'static [Result<PropertyDef>] {
334 &[]
335 }
336}
337
338impl<T: Fields> HasFields for &mut &&T {
339 fn enum_fields(&self) -> &'static [Result<PropertyDef>] {
340 T::fields()
341 }
342}
343
344pub trait HasVirtualProperties {
345 fn enum_properties(&self) -> &'static [Result<PropertyDef>];
346}
347
348impl<T> HasVirtualProperties for &&T {
349 fn enum_properties(&self) -> &'static [Result<PropertyDef>] {
350 &[]
351 }
352}
353
354impl<T: VirtualProperties> HasVirtualProperties for &mut &&T {
355 fn enum_properties(&self) -> &'static [Result<PropertyDef>] {
356 T::properties()
357 }
358}
359
360pub type MethodDef = som_method_def_t;
361unsafe impl Send for MethodDef {}
362unsafe impl Sync for MethodDef {}
363pub trait Methods: HasPassport {
364 fn methods() -> &'static [Result<MethodDef>];
365}
366
367pub trait HasMethods {
368 fn enum_methods(&self) -> &'static [Result<MethodDef>];
369}
370
371impl<T> HasMethods for &&T {
372 fn enum_methods(&self) -> &'static [Result<MethodDef>] {
373 &[]
374 }
375}
376
377impl<T: Methods> HasMethods for &mut &&T {
378 fn enum_methods(&self) -> &'static [Result<MethodDef>] {
379 T::methods()
380 }
381}
382
383trait IAsset {
384 fn class() -> som_asset_class_t
385 where
386 Self: Sized;
387}
388
389pub struct GlobalAsset<T: HasPassport> {
390 ptr: *mut AssetData<T>,
391}
392
393impl<T: HasPassport> IAsset for GlobalAsset<T> {
394 fn class() -> som_asset_class_t {
395 unsafe extern "C" fn ref_count_stub(_thing: *mut som_asset_t) -> c_long {
397 return 1;
398 }
399
400 unsafe extern "C" fn asset_get_interface(
401 _thing: *mut som_asset_t,
402 _name: *const c_char,
403 _out: *mut *mut c_void,
404 ) -> c_long {
405 return 0;
407 }
408
409 unsafe extern "C" fn asset_get_passport<T: HasPassport>(
410 thing: *mut som_asset_t,
411 ) -> *mut som_passport_t {
412 let asset_ref = AssetRef::<T>::new(thing);
413 let Ok(passport) = asset_ref.passport() else {
414 return std::ptr::null_mut();
415 };
416 passport as *const _ as *mut _
417 }
418
419 som_asset_class_t {
420 asset_add_ref: Some(ref_count_stub),
421 asset_release: Some(ref_count_stub),
422 asset_get_interface: Some(asset_get_interface),
423 asset_get_passport: Some(asset_get_passport::<T>),
424 }
425 }
426}
427
428impl<T: HasPassport> Drop for GlobalAsset<T> {
429 fn drop(&mut self) {
430 let ptr: *mut som_asset_t = self.ptr.cast();
431 let _res = sapi().and_then(|api| api.release_global_asset(ptr));
432 debug_assert!(_res.is_ok());
433 }
434}
435
436impl<T: HasPassport> GlobalAsset<T> {
437 pub fn new(data: T) -> Result<Self> {
438 let obj = RawAssetObj::new(Self::class());
439 let res = AssetData::new(obj, data);
440 let boxed = Box::new(res);
441 let ptr = Box::into_raw(boxed);
442
443 let _res = sapi()?.set_global_asset(ptr as _)?;
446 debug_assert!(_res);
447
448 Ok(Self { ptr })
449 }
450
451 pub fn as_ref(&self) -> AssetRef<T> {
452 unsafe { AssetRef::new(self.ptr.cast()) }
453 }
454}
455
456#[macro_export]
457macro_rules! impl_passport {
458 ($self:ident, $type:ident) => {{
459 static PASSPORT: std::sync::OnceLock<::rsciter::Result<::rsciter::bindings::som_passport_t>> =
460 std::sync::OnceLock::new();
461
462 let res = PASSPORT.get_or_init(|| {
463 let mut passport =
464 ::rsciter::bindings::som_passport_t::new(::rsciter::cstr!($type))?;
465 use ::rsciter::som::{
466 self, HasFields, HasItemGetter, HasItemSetter, HasMethods, HasVirtualProperties
467 };
468
469 let autoref_trick = &mut &$self;
470
471 if autoref_trick.has_item_getter() {
472 som::impl_item_getter!($type);
473 passport.item_getter = Some(item_getter);
474 }
475
476 if autoref_trick.has_item_setter() {
477 som::impl_item_setter!($type);
478 passport.item_setter = Some(item_setter);
479 }
480
481 let mut properties = Vec::new();
482 for f in autoref_trick.enum_fields() {
483 match f {
484 Ok(v) => properties.push(v.clone()),
485 Err(e) => return Err(e.clone()),
486 }
487 }
488 for p in autoref_trick.enum_properties() {
489 match p {
490 Ok(v) => properties.push(v.clone()),
491 Err(e) => return Err(e.clone()),
492 }
493 }
494
495 let mut methods = Vec::new();
496 for m in autoref_trick.enum_methods() {
497 match m {
498 Ok(v) => methods.push(v.clone()),
499 Err(e) => return Err(e.clone()),
500 }
501 }
502
503 let boxed_props = properties.into_boxed_slice();
504 passport.n_properties = boxed_props.len();
505 if passport.n_properties > 0 {
506 passport.properties = Box::into_raw(boxed_props) as *const _; }
508
509 let boxed_methods = methods.into_boxed_slice();
510 passport.n_methods = boxed_methods.len();
511 if passport.n_methods > 0 {
512 passport.methods = Box::into_raw(boxed_methods) as *const _; }
514
515 Ok(passport)
516 });
517
518 match res {
519 Ok(p) => Ok(p),
520 Err(e) => Err(e.clone()),
521 }
522 }};
523}
524pub use impl_passport;
525
526#[repr(C)]
527struct AssetData<T> {
528 obj: RawAssetObj,
529 pub data: T,
530}
531
532impl<T> AssetData<T> {
533 fn new(obj: RawAssetObj, data: T) -> Self {
534 Self { obj, data }
535 }
536}
537
538pub struct AssetRef<'a, T> {
542 this: &'a AssetData<T>,
543}
544
545impl<'a, T> AssetRef<'a, T> {
546 pub unsafe fn new(thing: *const som_asset_t) -> Self {
547 let this = thing as *const AssetData<T>;
548 let this = unsafe { &*this };
549 Self { this }
550 }
551
552 pub fn data(&self) -> &T {
553 &self.this.data
554 }
555}
556
557impl<T> Deref for AssetRef<'_, T> {
558 type Target = T;
559
560 fn deref(&self) -> &Self::Target {
561 &self.data()
562 }
563}
564
565pub struct AssetRefMut<'a, T> {
566 this: &'a mut AssetData<T>,
567}
568
569impl<'a, T> AssetRefMut<'a, T> {
570 pub unsafe fn new(thing: *mut som_asset_t) -> Self {
571 let this = thing as *mut AssetData<T>;
572 let this = unsafe { &mut *this };
573 Self { this }
574 }
575
576 pub fn data(&self) -> &T {
577 &self.this.data
578 }
579
580 pub fn data_mut(&mut self) -> &mut T {
581 &mut self.this.data
582 }
583}
584
585impl<T> Deref for AssetRefMut<'_, T> {
586 type Target = T;
587
588 fn deref(&self) -> &Self::Target {
589 &self.data()
590 }
591}
592
593impl<T> DerefMut for AssetRefMut<'_, T> {
594 fn deref_mut(&mut self) -> &mut Self::Target {
595 self.data_mut()
596 }
597}
598
599pub struct Asset<T: HasPassport> {
600 boxed: Box<AssetDataWithCounter<T>>,
601}
602
603#[repr(C)]
604struct AssetDataWithCounter<T> {
605 data: AssetData<T>,
606 counter: std::sync::atomic::AtomicI32,
607}
608
609impl<T> AssetDataWithCounter<T> {
610 unsafe fn get_mut_ref<'c>(thing: *mut som_asset_t) -> &'c mut Self {
611 let this = thing as *mut AssetDataWithCounter<T>;
612 &mut *this
613 }
614}
615
616impl<T: HasPassport> IAsset for Asset<T> {
617 fn class() -> som_asset_class_t {
618 unsafe extern "C" fn asset_add_ref<TT>(thing: *mut som_asset_t) -> c_long {
619 let this = AssetDataWithCounter::<TT>::get_mut_ref(thing);
620 let refc = this.counter.fetch_add(1, Ordering::SeqCst) + 1;
621 return refc;
622 }
623
624 unsafe extern "C" fn asset_release<TT>(thing: *mut som_asset_t) -> c_long {
625 let this = AssetDataWithCounter::<TT>::get_mut_ref(thing);
626 let refc = this.counter.fetch_sub(1, Ordering::SeqCst) - 1;
627 if refc == 0 {
628 let _ = std::panic::catch_unwind(|| {
630 let _asset_to_drop = Box::from_raw(thing as *mut AssetDataWithCounter<TT>);
631 });
632 }
633 return refc;
634 }
635
636 unsafe extern "C" fn asset_get_interface(
637 _thing: *mut som_asset_t,
638 _name: *const c_char,
639 _out: *mut *mut c_void,
640 ) -> c_long {
641 return 0;
643 }
644
645 unsafe extern "C" fn asset_get_passport<TT: HasPassport>(
646 thing: *mut som_asset_t,
647 ) -> *mut som_passport_t {
648 let asset_ref = AssetRef::<TT>::new(thing);
649 let Ok(passport) = asset_ref.passport() else {
650 return std::ptr::null_mut();
651 };
652 passport as *const _ as *mut _
653 }
654
655 som_asset_class_t {
656 asset_add_ref: Some(asset_add_ref::<T>),
657 asset_release: Some(asset_release::<T>),
658 asset_get_interface: Some(asset_get_interface),
659 asset_get_passport: Some(asset_get_passport::<T>),
660 }
661 }
662}
663
664impl<T: HasPassport> Asset<T> {
665 pub fn new(data: T) -> Self {
666 let obj = RawAssetObj::new(Self::class());
667 Self {
668 boxed: Box::new(AssetDataWithCounter {
669 data: AssetData::new(obj, data),
670 counter: Default::default(),
671 }),
672 }
673 }
674
675 pub(crate) fn to_raw_ptr(self) -> *const som_asset_t {
676 let ptr = Box::into_raw(self.boxed);
677 ptr.cast()
678 }
679
680 pub fn as_ref(&self) -> AssetRef<T> {
681 let ptr = &self.boxed.as_ref().data as *const AssetData<T>;
682 unsafe { AssetRef::new(ptr.cast()) }
683 }
684}
685
686#[cfg(test)]
687mod tests {
688 use super::*;
689
690 #[test]
691 fn test_atom() {
692 let atom = Atom::new(c"name").unwrap();
693 let name = atom.name().unwrap();
694 assert_eq!(name, "name");
695 }
696}