Skip to main content

bevy_convars/
reflect.rs

1//! Contains types for reflecting over CVars statically and dynamically.
2
3use std::any::TypeId;
4
5use bevy_ecs::{change_detection::DetectChanges, prelude::Resource, world::Ref};
6use bevy_reflect::{FromType, PartialReflect, Reflect};
7
8use crate::{CVarError, CVarFlags};
9
10/// Static meta information about a cvar, like its contained type and path.
11pub trait CVarMeta:
12    Resource + std::ops::Deref<Target = Self::Inner> + std::ops::DerefMut<Target = Self::Inner>
13{
14    /// The inner type of the CVar.
15    type Inner: std::fmt::Debug + PartialReflect;
16    /// The path of the CVar within the config.
17    const CVAR_PATH: &'static str;
18    /// The flags applied to this CVar.
19    fn flags() -> CVarFlags;
20    /// Returns an instance of the CVar's default value.
21    fn default_inner() -> Self::Inner;
22    /// Sets the CVar to default directly, without modifying other properties.
23    /// This does not reset the CVar's "is default" state in any way and simply modifies the CVar.
24    fn set_to_default(&mut self);
25}
26
27/// Provides bevy reflection metadata for CVars.
28#[derive(Clone)]
29pub struct ReflectCVar {
30    reflect_inner: for<'a> fn(&'a dyn PartialReflect) -> Result<&'a dyn PartialReflect, CVarError>,
31    reflect_inner_mut:
32        for<'a> fn(&'a mut dyn PartialReflect) -> Result<&'a mut dyn PartialReflect, CVarError>,
33    default_inner: fn() -> Box<dyn PartialReflect>,
34    is_default_value: fn(Ref<dyn PartialReflect>) -> bool,
35    inner_type: TypeId,
36    path: &'static str,
37    flags: CVarFlags,
38}
39
40impl ReflectCVar {
41    /// Returns the inner type (i.e. value type) of the CVar.
42    pub fn inner_type(&self) -> TypeId {
43        self.inner_type
44    }
45
46    /// Returns the path of the CVar.
47    pub fn cvar_path(&self) -> &'static str {
48        self.path
49    }
50
51    /// Returns the CVar's flags.
52    pub fn flags(&self) -> CVarFlags {
53        self.flags
54    }
55
56    /// Reflect over the inner value of the CVar, returning a reference to it.
57    pub fn reflect_inner<'a>(
58        &self,
59        cvar: &'a dyn PartialReflect,
60    ) -> Result<&'a dyn PartialReflect, CVarError> {
61        (self.reflect_inner)(cvar)
62    }
63
64    /// Reflect over the inner value of the CVar, returning a mutable reference to it.
65    pub fn reflect_inner_mut<'a>(
66        &self,
67        cvar: &'a mut dyn PartialReflect,
68    ) -> Result<&'a mut dyn PartialReflect, CVarError> {
69        (self.reflect_inner_mut)(cvar)
70    }
71
72    /// Apply a reflected value to the CVar.
73    pub fn reflect_apply(
74        &self,
75        cvar: &mut dyn PartialReflect,
76        value: &dyn PartialReflect,
77    ) -> Result<(), CVarError> {
78        let inner_mut = self.reflect_inner_mut(cvar)?;
79
80        inner_mut.try_apply(value)?;
81        Ok(())
82    }
83
84    /// Returns an instance of the CVar's default value.
85    pub fn default_inner(&self) -> Box<dyn PartialReflect> {
86        (self.default_inner)()
87    }
88
89    /// Returns whether or not the instance is of the default value.
90    pub fn is_default_value<T: Reflect + ?Sized>(&self, r: Ref<T>) -> bool {
91        (self.is_default_value)(r.map(|x| x.as_partial_reflect()))
92    }
93}
94
95impl<T: CVarMeta> FromType<T> for ReflectCVar {
96    fn from_type() -> Self {
97        ReflectCVar {
98            inner_type: std::any::TypeId::of::<T::Inner>(),
99            // TODO: Make these less reflective by adding functions to CVarMeta.
100            reflect_inner: |r| {
101                r.reflect_ref()
102                    .as_tuple_struct()
103                    .map_err(|_| CVarError::BadCVarType)?
104                    .field(0)
105                    .ok_or(CVarError::BadCVarType)
106            },
107
108            reflect_inner_mut: |r| {
109                r.reflect_mut()
110                    .as_tuple_struct()
111                    .map_err(|_| CVarError::BadCVarType)?
112                    .field_mut(0)
113                    .ok_or(CVarError::BadCVarType)
114            },
115            default_inner: || Box::new(T::default_inner()),
116            is_default_value: |r| r.added() == r.last_changed(),
117            path: T::CVAR_PATH,
118            flags: T::flags(),
119        }
120    }
121}