slowshell_plugin_abi/
store.rs1use core::{ffi::c_void, marker::PhantomData};
2
3use abi_stable::{
4 StableAbi, sabi_trait,
5 std_types::{RBox, ROption},
6};
7
8use crate::host::Context;
9
10#[repr(C)]
11#[derive(StableAbi, Clone, Copy, Debug, PartialEq, Eq)]
12pub struct TypeKey {
13 pub name: abi_stable::std_types::RStr<'static>,
14 pub size: usize,
15 pub align: usize,
16}
17
18impl TypeKey {
19 pub fn of<T: 'static>() -> Self {
20 Self {
21 name: abi_stable::std_types::RStr::from_str(std::any::type_name::<T>()),
22 size: core::mem::size_of::<T>(),
23 align: core::mem::align_of::<T>(),
24 }
25 }
26
27 pub fn matches<T: 'static>(&self) -> bool {
28 self == &Self::of::<T>()
29 }
30}
31
32#[repr(C)]
33#[derive(StableAbi)]
34pub struct ErasedValue {
35 pub key: TypeKey,
36 pub ptr: *mut c_void,
37 pub drop_fn: extern "C" fn(*mut c_void),
38}
39
40impl ErasedValue {
41 pub fn new<T: 'static>(value: T) -> Self {
42 extern "C" fn drop_it<T>(ptr: *mut c_void) {
43 unsafe { drop(Box::from_raw(ptr as *mut T)) };
44 }
45 Self {
46 key: TypeKey::of::<T>(),
47 ptr: Box::into_raw(Box::new(value)) as *mut c_void,
48 drop_fn: drop_it::<T>,
49 }
50 }
51
52 pub fn key(&self) -> TypeKey {
53 self.key
54 }
55
56 pub fn take<T: 'static>(mut self) -> Option<T> {
57 if !self.key.matches::<T>() {
58 return None;
59 }
60 let ptr = self.ptr;
61 self.ptr = core::ptr::null_mut();
62 Some(unsafe { *Box::from_raw(ptr as *mut T) })
63 }
64}
65
66impl Drop for ErasedValue {
67 fn drop(&mut self) {
68 if !self.ptr.is_null() {
69 (self.drop_fn)(self.ptr);
70 self.ptr = core::ptr::null_mut();
71 }
72 }
73}
74
75#[repr(C)]
76#[derive(StableAbi, Clone, Copy)]
77pub struct ErasedRef<'a> {
78 key: TypeKey,
79 ptr: *mut c_void,
80 marker: PhantomData<&'a mut ()>,
81}
82
83impl<'a> ErasedRef<'a> {
84 pub fn new(key: TypeKey, ptr: *mut c_void) -> Self {
85 Self {
86 key,
87 ptr,
88 marker: PhantomData,
89 }
90 }
91
92 pub fn key(&self) -> TypeKey {
93 self.key
94 }
95
96 pub fn cast<T: 'static>(&self) -> Option<&T> {
97 if self.key.matches::<T>() {
98 Some(unsafe { &*(self.ptr as *const T) })
99 } else {
100 None
101 }
102 }
103
104 pub fn cast_mut<T: 'static>(&self) -> Option<&mut T> {
105 if self.key.matches::<T>() {
106 Some(unsafe { &mut *(self.ptr as *mut T) })
107 } else {
108 None
109 }
110 }
111}
112
113#[sabi_trait]
114pub trait StoreCallback: Send + Sync + 'static {
115 fn notify(&self, ctx: &Context<'_>);
116}
117
118pub type StoreCallbackBox = StoreCallback_TO<RBox<()>>;
119
120struct CallbackFn<F>(F);
121
122impl<F> StoreCallback for CallbackFn<F>
123where
124 F: Fn(&Context<'_>) + Send + Sync + 'static,
125{
126 fn notify(&self, ctx: &Context<'_>) {
127 (self.0)(ctx)
128 }
129}
130
131#[derive(Clone, Copy)]
132pub struct Store<'a> {
133 ctx: Context<'a>,
134}
135
136impl<'a> Store<'a> {
137 pub fn new(ctx: Context<'a>) -> Self {
138 Self { ctx }
139 }
140
141 pub fn insert<T: 'static>(&self, value: T) -> bool {
142 self
143 .ctx
144 .host()
145 .store_insert(TypeKey::of::<T>(), ErasedValue::new(value))
146 }
147
148 pub fn contains<T: 'static>(&self) -> bool {
149 self.ctx.host().store_contains(TypeKey::of::<T>())
150 }
151
152 pub fn with<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
153 match self.ctx.host().store_get(&self.ctx, TypeKey::of::<T>()) {
154 ROption::RSome(value) => value.cast_mut::<T>().map(f),
155 ROption::RNone => None,
156 }
157 }
158
159 pub fn borrow<T: 'static>(&self) -> Option<StoreBorrow<'a, T>> {
160 match self.ctx.host().store_get(&self.ctx, TypeKey::of::<T>()) {
161 ROption::RSome(value) => value.cast::<T>().map(|ptr| StoreBorrow {
162 ptr: ptr as *const T,
163 marker: PhantomData,
164 }),
165 ROption::RNone => None,
166 }
167 }
168
169 pub fn touch<T: 'static>(&self) -> bool {
170 self.ctx.host().store_touch(TypeKey::of::<T>())
171 }
172
173 pub fn remove<T: 'static>(&self) -> Option<T> {
174 match self.ctx.host().store_remove(TypeKey::of::<T>()) {
175 ROption::RSome(value) => value.take::<T>(),
176 ROption::RNone => None,
177 }
178 }
179
180 pub fn subscribe<T: 'static, F>(&self, callback: F)
181 where
182 F: Fn(&Context<'_>) + Send + Sync + 'static,
183 {
184 self.ctx.host().store_subscribe(
185 TypeKey::of::<T>(),
186 StoreCallbackBox::from_value(CallbackFn(callback), abi_stable::sabi_trait::TD_Opaque),
187 );
188 }
189}
190
191pub struct StoreBorrow<'a, T> {
192 ptr: *const T,
193 marker: PhantomData<&'a T>,
194}
195
196impl<T> core::ops::Deref for StoreBorrow<'_, T> {
197 type Target = T;
198
199 fn deref(&self) -> &T {
200 unsafe { &*self.ptr }
201 }
202}