use core::any::{Any, TypeId};
use core::fmt;
use core::ops::{Deref, DerefMut};
use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
use std::collections::HashMap;
use super::{Lua, LuaRef};
#[derive(Default)]
pub(crate) struct AppData {
values: RefCell<HashMap<TypeId, Box<dyn Any>>>,
}
pub struct AppDataRef<'lua, T: ?Sized> {
value: Ref<'lua, T>,
}
pub struct AppDataRefMut<'lua, T: ?Sized> {
value: RefMut<'lua, T>,
}
impl Lua {
#[track_caller]
pub fn set_app_data<T: 'static>(&self, data: T) -> Option<T> {
self.lua_ref().set_app_data(data)
}
pub fn try_set_app_data<T: 'static>(&self, data: T) -> Result<Option<T>, T> {
self.lua_ref().try_set_app_data(data)
}
#[track_caller]
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'_, T>> {
self.lua_ref().app_data_ref()
}
pub fn try_app_data_ref<T: 'static>(&self) -> Result<Option<AppDataRef<'_, T>>, BorrowError> {
self.lua_ref().try_app_data_ref()
}
#[track_caller]
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'_, T>> {
self.lua_ref().app_data_mut()
}
pub fn try_app_data_mut<T: 'static>(
&self,
) -> Result<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
self.lua_ref().try_app_data_mut()
}
#[track_caller]
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
self.lua_ref().remove_app_data()
}
pub fn try_remove_app_data<T: 'static>(&self) -> Result<Option<T>, BorrowMutError> {
self.lua_ref().try_remove_app_data()
}
}
impl<'lua> LuaRef<'lua> {
#[track_caller]
pub fn set_app_data<T: 'static>(&self, data: T) -> Option<T> {
match self.try_set_app_data(data) {
Ok(previous) => previous,
Err(_) => panic!("cannot mutably borrow application data"),
}
}
pub fn try_set_app_data<T: 'static>(&self, data: T) -> Result<Option<T>, T> {
let Ok(mut values) = self.runtime().app_data().values.try_borrow_mut() else {
return Err(data);
};
let previous = values
.insert(TypeId::of::<T>(), Box::new(data))
.map(|previous| {
*previous
.downcast::<T>()
.expect("application data TypeId must match its stored value")
});
self.runtime().invalidate_managed_safe_env();
Ok(previous)
}
#[track_caller]
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'lua, T>> {
match self.try_app_data_ref() {
Ok(value) => value,
Err(error) => panic!("application data is already mutably borrowed: {error}"),
}
}
pub fn try_app_data_ref<T: 'static>(&self) -> Result<Option<AppDataRef<'lua, T>>, BorrowError> {
let values = self.runtime().app_data().values.try_borrow()?;
Ok(Ref::filter_map(values, |values| {
values
.get(&TypeId::of::<T>())
.and_then(|value| value.downcast_ref())
})
.ok()
.map(|value| AppDataRef { value }))
}
#[track_caller]
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'lua, T>> {
match self.try_app_data_mut() {
Ok(value) => value,
Err(error) => panic!("application data is already borrowed: {error}"),
}
}
pub fn try_app_data_mut<T: 'static>(
&self,
) -> Result<Option<AppDataRefMut<'lua, T>>, BorrowMutError> {
let values = self.runtime().app_data().values.try_borrow_mut()?;
let value = RefMut::filter_map(values, |values| {
values
.get_mut(&TypeId::of::<T>())
.and_then(|value| value.downcast_mut())
})
.ok()
.map(|value| AppDataRefMut { value });
if value.is_some() {
self.runtime().invalidate_managed_safe_env();
}
Ok(value)
}
#[track_caller]
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
self.try_remove_app_data()
.unwrap_or_else(|error| panic!("application data is already borrowed: {error}"))
}
pub fn try_remove_app_data<T: 'static>(&self) -> Result<Option<T>, BorrowMutError> {
let mut values = self.runtime().app_data().values.try_borrow_mut()?;
let value = values.remove(&TypeId::of::<T>()).map(|value| {
*value
.downcast::<T>()
.expect("application data TypeId must match its stored value")
});
if value.is_some() {
self.runtime().invalidate_managed_safe_env();
}
Ok(value)
}
}
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for AppDataRef<'_, T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, formatter)
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for AppDataRefMut<'_, T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, formatter)
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for AppDataRef<'_, T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, formatter)
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for AppDataRefMut<'_, T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, formatter)
}
}