use std::any::TypeId;
use bevy_ecs::{change_detection::DetectChanges, prelude::Resource, world::Ref};
use bevy_reflect::{FromType, PartialReflect, Reflect};
use crate::{CVarError, CVarFlags};
pub trait CVarMeta:
Resource + std::ops::Deref<Target = Self::Inner> + std::ops::DerefMut<Target = Self::Inner>
{
type Inner: std::fmt::Debug + PartialReflect;
const CVAR_PATH: &'static str;
fn flags() -> CVarFlags;
fn default_inner() -> Self::Inner;
fn set_to_default(&mut self);
}
#[derive(Clone)]
pub struct ReflectCVar {
reflect_inner: for<'a> fn(&'a dyn PartialReflect) -> Result<&'a dyn PartialReflect, CVarError>,
reflect_inner_mut:
for<'a> fn(&'a mut dyn PartialReflect) -> Result<&'a mut dyn PartialReflect, CVarError>,
default_inner: fn() -> Box<dyn PartialReflect>,
is_default_value: fn(Ref<dyn PartialReflect>) -> bool,
inner_type: TypeId,
path: &'static str,
flags: CVarFlags,
}
impl ReflectCVar {
pub fn inner_type(&self) -> TypeId {
self.inner_type
}
pub fn cvar_path(&self) -> &'static str {
self.path
}
pub fn flags(&self) -> CVarFlags {
self.flags
}
pub fn reflect_inner<'a>(
&self,
cvar: &'a dyn PartialReflect,
) -> Result<&'a dyn PartialReflect, CVarError> {
(self.reflect_inner)(cvar)
}
pub fn reflect_inner_mut<'a>(
&self,
cvar: &'a mut dyn PartialReflect,
) -> Result<&'a mut dyn PartialReflect, CVarError> {
(self.reflect_inner_mut)(cvar)
}
pub fn reflect_apply(
&self,
cvar: &mut dyn PartialReflect,
value: &dyn PartialReflect,
) -> Result<(), CVarError> {
let inner_mut = self.reflect_inner_mut(cvar)?;
inner_mut.try_apply(value)?;
Ok(())
}
pub fn default_inner(&self) -> Box<dyn PartialReflect> {
(self.default_inner)()
}
pub fn is_default_value<T: Reflect + ?Sized>(&self, r: Ref<T>) -> bool {
(self.is_default_value)(r.map(|x| x.as_partial_reflect()))
}
}
impl<T: CVarMeta> FromType<T> for ReflectCVar {
fn from_type() -> Self {
ReflectCVar {
inner_type: std::any::TypeId::of::<T::Inner>(),
reflect_inner: |r| {
r.reflect_ref()
.as_tuple_struct()
.map_err(|_| CVarError::BadCVarType)?
.field(0)
.ok_or(CVarError::BadCVarType)
},
reflect_inner_mut: |r| {
r.reflect_mut()
.as_tuple_struct()
.map_err(|_| CVarError::BadCVarType)?
.field_mut(0)
.ok_or(CVarError::BadCVarType)
},
default_inner: || Box::new(T::default_inner()),
is_default_value: |r| r.added() == r.last_changed(),
path: T::CVAR_PATH,
flags: T::flags(),
}
}
}