summaryrefslogtreecommitdiff
path: root/platforms/tortuise-script-compiled/src/storage.rs
blob: d028a91a38b306db2503783ef56f251c5e553709 (plain)
use core::ops::{Deref, DerefMut};

use serde::{Serialize, de::DeserializeOwned};
use spin::Mutex;
use tortuise::script::Operation;

use crate::ScriptContext;

pub struct Storage<T> {
	initial_state: fn() -> T,
	data: Mutex<Option<T>>,
}

pub struct Config<T> {
	initial_state: fn() -> T,
	data: Mutex<Option<T>>,
}

pub struct StorageMut<'a, 'cx, T: Serialize + Eq> {
	context: &'cx mut ScriptContext<'a>,
	data: T,
	original: T,
}

pub struct ConfigMut<'a, 'cx, T: Serialize + Eq> {
	context: &'cx mut ScriptContext<'a>,
	data: T,
	original: T,
}

impl<T: Serialize + DeserializeOwned + Clone + Eq> Storage<T> {
	pub const fn new(initial_state: fn() -> T) -> Self {
		Self {
			initial_state,
			data: Mutex::new(None),
		}
	}

	pub fn load(&self, context: &ScriptContext) -> T {
		puffin::profile_function!();
		let mut data = self.data.lock();
		data.get_or_insert_with(|| {
			context
				.0
				.storage
				.data()
				.map(messagepack_serde::from_slice)
				.and_then(|r| r.ok())
				.unwrap_or_else(self.initial_state)
		})
		.clone()
	}

	pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> {
		let data = self.load(context);
		StorageMut {
			original: data.clone(),
			data,
			context,
		}
	}

	pub fn is_saving(&self, context: &ScriptContext) -> bool {
		context.0.storage.is_saving_data()
	}

	pub fn save_failed(&self, context: &ScriptContext) -> bool {
		context.0.storage.saving_data_failed()
	}
}

impl<T: Serialize + DeserializeOwned + Clone + Eq> Config<T> {
	pub const fn new(initial_state: fn() -> T) -> Self {
		Self {
			initial_state,
			data: Mutex::new(None),
		}
	}

	pub fn load(&self, context: &ScriptContext) -> T {
		puffin::profile_function!();
		let mut data = self.data.lock();
		data.get_or_insert_with(|| {
			context
				.0
				.storage
				.config()
				.map(toml::from_slice)
				.and_then(|r| r.ok())
				.unwrap_or_else(self.initial_state)
		})
		.clone()
	}

	pub fn load_mut<'a, 'cx>(&self, context: &'cx mut ScriptContext<'a>) -> StorageMut<'a, 'cx, T> {
		let data = self.load(context);
		StorageMut {
			original: data.clone(),
			data,
			context,
		}
	}

	pub fn is_saving(&self, context: &ScriptContext) -> bool {
		context.0.storage.is_saving_config()
	}

	pub fn save_failed(&self, context: &ScriptContext) -> bool {
		context.0.storage.saving_config_failed()
	}
}

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) {
		puffin::profile_function!();
		if self.data == self.original {
			return;
		}

		let new_save = match messagepack_serde::to_vec(&self.data) {
			Ok(new_save) => new_save.into_boxed_slice(),
			Err(e) => {
				log::error!("{e}");
				return;
			}
		};
		self.context
			.0
			.operation_queue
			.push(Operation::UpdateStorage { new_save });
	}
}

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) {
		puffin::profile_function!();
		if self.data == self.original {
			return;
		}

		let new_save = match toml::to_string(&self.data) {
			Ok(new_save) => new_save.as_bytes().into(),
			Err(e) => {
				log::error!("{e}");
				return;
			}
		};
		self.context
			.0
			.operation_queue
			.push(Operation::UpdateConfig { new_save });
	}
}