1#![warn(missing_docs)]
6
7use std::{
8 any::{Any, TypeId},
9 fmt::{self, Debug},
10};
11
12pub trait PropertyValue: Any + Debug {
14 fn as_any(&self) -> &dyn Any;
16}
17
18impl<T: Debug + 'static> PropertyValue for T {
19 fn as_any(&self) -> &dyn Any {
20 self
21 }
22}
23
24#[derive(Debug)]
26pub enum CastError {
27 TypeMismatch {
29 property_name: String,
31
32 expected_type_id: TypeId,
34
35 actual_type_id: TypeId,
37 },
38}
39
40pub struct PropertyInfo<'a> {
42 pub owner_type_id: TypeId,
44
45 pub name: &'a str,
47
48 pub display_name: &'static str,
50
51 pub value: &'a dyn PropertyValue,
53
54 pub read_only: bool,
56
57 pub min_value: Option<f64>,
59
60 pub max_value: Option<f64>,
62
63 pub step: Option<f64>,
65
66 pub precision: Option<usize>,
68
69 pub description: String,
71}
72
73impl<'a> PartialEq<Self> for PropertyInfo<'a> {
74 fn eq(&self, other: &Self) -> bool {
75 let value_ptr_a = &*self.value as *const _ as *const ();
76 let value_ptr_b = &*other.value as *const _ as *const ();
77
78 self.owner_type_id == other.owner_type_id
79 && self.name == other.name
80 && self.display_name == other.display_name
81 && std::ptr::eq(value_ptr_a, value_ptr_b)
82 && self.read_only == other.read_only
83 && self.min_value == other.min_value
84 && self.max_value == other.max_value
85 && self.step == other.step
86 && self.precision == other.precision
87 && self.description == other.description
88 }
89}
90
91impl<'a> fmt::Debug for PropertyInfo<'a> {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 f.debug_struct("PropertyInfo")
94 .field("owner_type_id", &self.owner_type_id)
95 .field("name", &self.name)
96 .field("display_name", &self.display_name)
97 .field("value", &format_args!("{:?}", self.value as *const _))
98 .field("read_only", &self.read_only)
99 .field("min_value", &self.min_value)
100 .field("max_value", &self.max_value)
101 .field("step", &self.step)
102 .field("precision", &self.precision)
103 .field("description", &self.description)
104 .finish()
105 }
106}
107
108impl<'a> PropertyInfo<'a> {
109 pub fn cast_value<T: 'static>(&self) -> Result<&T, CastError> {
111 match self.value.as_any().downcast_ref::<T>() {
112 Some(value) => Ok(value),
113 None => Err(CastError::TypeMismatch {
114 property_name: self.name.to_string(),
115 expected_type_id: TypeId::of::<T>(),
116 actual_type_id: self.value.type_id(),
117 }),
118 }
119 }
120}
121
122pub trait Inspect {
142 fn properties(&self) -> Vec<PropertyInfo<'_>>;
144}
145
146impl<T: Inspect> Inspect for Option<T> {
147 fn properties(&self) -> Vec<PropertyInfo<'_>> {
148 match self {
149 Some(v) => v.properties(),
150 None => vec![],
151 }
152 }
153}
154
155impl<T: Inspect> Inspect for Box<T> {
156 fn properties(&self) -> Vec<PropertyInfo<'_>> {
157 (**self).properties()
158 }
159}
160
161macro_rules! impl_self_inspect {
162 ($ty:ty, $min:expr, $max:expr, $step:expr, $precision:expr) => {
163 impl Inspect for $ty {
164 fn properties(&self) -> Vec<PropertyInfo<'_>> {
165 vec![PropertyInfo {
166 owner_type_id: TypeId::of::<Self>(),
167 name: "Value",
168 display_name: "Value",
169 value: self,
170 read_only: false,
171 min_value: Some($min),
172 max_value: Some($max),
173 step: Some($step),
174 precision: Some($precision),
175 description: "".to_string(),
176 }]
177 }
178 }
179 };
180}
181
182impl_self_inspect!(f32, f32::MIN as f64, f32::MAX as f64, 1.0, 7);
183impl_self_inspect!(f64, f64::MIN, f64::MAX, 1.0, 15);
184impl_self_inspect!(i64, i64::MIN as f64, i64::MAX as f64, 1.0, 0);
185impl_self_inspect!(u64, u64::MIN as f64, u64::MAX as f64, 1.0, 0);
186impl_self_inspect!(i32, i32::MIN as f64, i32::MAX as f64, 1.0, 0);
187impl_self_inspect!(u32, u32::MIN as f64, u32::MAX as f64, 1.0, 0);
188impl_self_inspect!(i16, i16::MIN as f64, i16::MAX as f64, 1.0, 0);
189impl_self_inspect!(u16, u16::MIN as f64, u16::MAX as f64, 1.0, 0);
190impl_self_inspect!(i8, i8::MIN as f64, i8::MAX as f64, 1.0, 0);
191impl_self_inspect!(u8, u8::MIN as f64, u8::MAX as f64, 1.0, 0);
192
193pub use rg3d_core_derive::Inspect;