summaryrefslogtreecommitdiff
path: root/platforms/tortuise-script-wasm/src/storage.rs
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/tortuise-script-wasm/src/storage.rs')
-rw-r--r--platforms/tortuise-script-wasm/src/storage.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/platforms/tortuise-script-wasm/src/storage.rs b/platforms/tortuise-script-wasm/src/storage.rs
new file mode 100644
index 0000000..2606e2b
--- /dev/null
+++ b/platforms/tortuise-script-wasm/src/storage.rs
@@ -0,0 +1,194 @@
+use core::{
+ alloc::Layout,
+ marker::PhantomData,
+ ops::{Deref, DerefMut},
+};
+
+use alloc::alloc::{alloc, dealloc};
+use serde::{Serialize, de::DeserializeOwned};
+
+use crate::ScriptContext;
+
+mod tortuise {
+ #[link(wasm_import_module = "tortuise")]
+ unsafe extern "C" {
+ pub safe fn storage_size(context: u64) -> usize;
+ pub unsafe fn load_storage(context: u64, ptr: *mut u8);
+ pub unsafe fn save_storage(context: u64, ptr: *const u8, size: usize);
+ pub safe fn is_saving_storage(context: u64) -> usize;
+ pub safe fn saving_storage_failed(context: u64) -> usize;
+ pub safe fn config_size(context: u64) -> usize;
+ pub unsafe fn load_config(context: u64, ptr: *mut u8);
+ pub unsafe fn save_config(context: u64, ptr: *const u8, size: usize);
+ pub safe fn is_saving_config(context: u64) -> usize;
+ pub safe fn saving_config_failed(context: u64) -> usize;
+ }
+}
+
+fn load_storage<T: DeserializeOwned>(context: u64) -> Option<T> {
+ unsafe {
+ let size = tortuise::storage_size(context);
+ let layout = Layout::array::<u8>(size).unwrap();
+ if layout.size() == 0 {
+ return None;
+ }
+ let ptr = alloc(layout);
+ tortuise::load_storage(context, ptr);
+ let data = core::slice::from_raw_parts(ptr, size);
+ let data = messagepack_serde::from_slice(data).ok()?;
+ dealloc(ptr, layout);
+ Some(data)
+ }
+}
+
+fn save_storage<T: Serialize>(context: u64, state: &T) {
+ unsafe {
+ let data = messagepack_serde::to_vec(state).unwrap();
+ tortuise::save_storage(context, data.as_ptr(), data.len());
+ }
+}
+
+fn load_config<T: DeserializeOwned>(context: u64) -> Option<T> {
+ unsafe {
+ let size = tortuise::config_size(context);
+ let layout = Layout::array::<u8>(size).unwrap();
+ if layout.size() == 0 {
+ return None;
+ }
+ let ptr = alloc(layout);
+ tortuise::load_config(context, ptr);
+ let data = core::slice::from_raw_parts(ptr, size);
+ let data = toml::from_slice(data).ok()?;
+ dealloc(ptr, layout);
+ Some(data)
+ }
+}
+
+fn save_config<T: Serialize>(context: u64, state: &T) {
+ unsafe {
+ let data = toml::to_string(state).unwrap();
+ tortuise::save_config(context, data.as_ptr(), data.len());
+ }
+}
+
+pub struct Storage<T> {
+ initial_state: fn() -> T,
+}
+
+pub struct Config<T> {
+ initial_state: fn() -> T,
+}
+
+pub struct StorageMut<'a, 'cx, T: Serialize + Eq> {
+ context: u64,
+ original: T,
+ data: T,
+ _phantom: PhantomData<(&'a (), &'cx ())>,
+}
+
+pub struct ConfigMut<'a, 'cx, T: Serialize + Eq> {
+ context: u64,
+ original: T,
+ data: T,
+ _phantom: PhantomData<(&'a (), &'cx ())>,
+}
+
+impl<T: Serialize + DeserializeOwned + Clone + Eq> Storage<T> {
+ pub const fn new(initial_state: fn() -> T) -> Self {
+ Self { initial_state }
+ }
+
+ pub fn load(&self, context: &ScriptContext) -> T {
+ load_storage(context.0).unwrap_or_else(self.initial_state)
+ }
+
+ pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> {
+ let data = load_storage(context.0).unwrap_or_else(self.initial_state);
+ StorageMut {
+ context: context.0,
+ original: data.clone(),
+ data,
+ _phantom: PhantomData,
+ }
+ }
+
+ pub fn is_saving(&self, context: &ScriptContext) -> bool {
+ tortuise::is_saving_storage(context.0) > 0
+ }
+
+ pub fn save_failed(&self, context: &ScriptContext) -> bool {
+ tortuise::saving_storage_failed(context.0) > 0
+ }
+}
+
+impl<T: Serialize + DeserializeOwned + Clone + Eq> Config<T> {
+ pub const fn new(initial_state: fn() -> T) -> Self {
+ Self { initial_state }
+ }
+
+ pub fn load(&self, context: &ScriptContext) -> T {
+ load_config(context.0).unwrap_or_else(self.initial_state)
+ }
+
+ pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> {
+ let data = load_config(context.0).unwrap_or_else(self.initial_state);
+ StorageMut {
+ context: context.0,
+ original: data.clone(),
+ data,
+ _phantom: PhantomData,
+ }
+ }
+
+ pub fn is_saving(&self, context: &ScriptContext) -> bool {
+ tortuise::is_saving_config(context.0) > 0
+ }
+
+ pub fn save_failed(&self, context: &ScriptContext) -> bool {
+ tortuise::saving_config_failed(context.0) > 0
+ }
+}
+
+impl<T: Serialize + Eq> Deref for StorageMut<'_, '_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.data
+ }
+}
+
+impl<T: Serialize + Eq> DerefMut for StorageMut<'_, '_, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.data
+ }
+}
+
+impl<T: Serialize + Eq> Drop for StorageMut<'_, '_, T> {
+ fn drop(&mut self) {
+ if self.data != self.original {
+ save_storage(self.context, &self.data);
+ }
+ }
+}
+
+impl<T: Serialize + Eq> Deref for ConfigMut<'_, '_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.data
+ }
+}
+
+impl<T: Serialize + Eq> DerefMut for ConfigMut<'_, '_, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.data
+ }
+}
+
+impl<T: Serialize + Eq> Drop for ConfigMut<'_, '_, T> {
+ fn drop(&mut self) {
+ if self.data != self.original {
+ save_config(self.context, &self.data);
+ }
+ }
+}