Skip to main content

slowshell_plugin_abi/
store.rs

1use 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}