summaryrefslogtreecommitdiff
path: root/src/state.rs
blob: 8b4de1831fe63986ae40b4c8702ceb42eda30558 (plain)
use core::{
	alloc::Layout,
	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 state_size(context: u64) -> usize;
		pub unsafe fn load_state(context: u64, offset: *mut u8);
		pub unsafe fn save_state(context: u64, offset: *const u8, size: usize);

	}
}

impl ScriptContext {
	pub fn load_state<T: DeserializeOwned>(&self) -> Option<T> {
		unsafe {
			let size = tortuise::state_size(self.0);
			let layout = Layout::array::<u8>(size).unwrap();
			let ptr = alloc(layout);
			tortuise::load_state(self.0, ptr);
			let data = core::slice::from_raw_parts(ptr, size);
			let data = messagepack_serde::from_reader(data).ok()?;
			dealloc(ptr, layout);
			Some(data)
		}
	}
}

fn save_state<T: Serialize>(context: u64, state: &T) {
	unsafe {
		let data = messagepack_serde::to_vec(state).unwrap();
		tortuise::save_state(context, data.as_ptr(), data.len());
	}
}

pub struct StateMut<T: Serialize> {
	context: u64,
	data: T,
}

impl<T: Serialize + DeserializeOwned> StateMut<T> {
	pub fn load(context: &ScriptContext) -> Option<Self> {
		Some(Self {
			context: context.0,
			data: context.load_state()?,
		})
	}

	pub fn load_or(context: &ScriptContext, default: T) -> Self {
		Self {
			context: context.0,
			data: context.load_state().unwrap_or(default),
		}
	}

	pub fn load_or_else(context: &ScriptContext, default: impl FnOnce() -> T) -> Self {
		Self {
			context: context.0,
			data: context.load_state().unwrap_or_else(default),
		}
	}

	pub fn load_or_default(context: &ScriptContext) -> Self
	where
		T: Default,
	{
		Self {
			context: context.0,
			data: context.load_state().unwrap_or_default(),
		}
	}
}

impl<T: Serialize> Deref for StateMut<T> {
	type Target = T;

	fn deref(&self) -> &Self::Target {
		&self.data
	}
}

impl<T: Serialize> DerefMut for StateMut<T> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.data
	}
}

impl<T: Serialize> Drop for StateMut<T> {
	fn drop(&mut self) {
		save_state(self.context, &self.data);
	}
}