#![warn(missing_docs)]
use std::{
any::{Any, TypeId},
fmt::{self, Debug},
};
pub trait PropertyValue: Any + Debug {
fn as_any(&self) -> &dyn Any;
}
impl<T: Debug + 'static> PropertyValue for T {
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
pub enum CastError {
TypeMismatch {
property_name: String,
expected_type_id: TypeId,
actual_type_id: TypeId,
},
}
pub struct PropertyInfo<'a> {
pub owner_type_id: TypeId,
pub name: &'a str,
pub display_name: &'static str,
pub value: &'a dyn PropertyValue,
pub read_only: bool,
pub min_value: Option<f64>,
pub max_value: Option<f64>,
pub step: Option<f64>,
pub precision: Option<usize>,
pub description: String,
}
impl<'a> PartialEq<Self> for PropertyInfo<'a> {
fn eq(&self, other: &Self) -> bool {
let value_ptr_a = &*self.value as *const _ as *const ();
let value_ptr_b = &*other.value as *const _ as *const ();
self.owner_type_id == other.owner_type_id
&& self.name == other.name
&& self.display_name == other.display_name
&& std::ptr::eq(value_ptr_a, value_ptr_b)
&& self.read_only == other.read_only
&& self.min_value == other.min_value
&& self.max_value == other.max_value
&& self.step == other.step
&& self.precision == other.precision
&& self.description == other.description
}
}
impl<'a> fmt::Debug for PropertyInfo<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PropertyInfo")
.field("owner_type_id", &self.owner_type_id)
.field("name", &self.name)
.field("display_name", &self.display_name)
.field("value", &format_args!("{:?}", self.value as *const _))
.field("read_only", &self.read_only)
.field("min_value", &self.min_value)
.field("max_value", &self.max_value)
.field("step", &self.step)
.field("precision", &self.precision)
.field("description", &self.description)
.finish()
}
}
impl<'a> PropertyInfo<'a> {
pub fn cast_value<T: 'static>(&self) -> Result<&T, CastError> {
match self.value.as_any().downcast_ref::<T>() {
Some(value) => Ok(value),
None => Err(CastError::TypeMismatch {
property_name: self.name.to_string(),
expected_type_id: TypeId::of::<T>(),
actual_type_id: self.value.type_id(),
}),
}
}
}
pub trait Inspect {
fn properties(&self) -> Vec<PropertyInfo<'_>>;
}
impl<T: Inspect> Inspect for Option<T> {
fn properties(&self) -> Vec<PropertyInfo<'_>> {
match self {
Some(v) => v.properties(),
None => vec![],
}
}
}
impl<T: Inspect> Inspect for Box<T> {
fn properties(&self) -> Vec<PropertyInfo<'_>> {
(**self).properties()
}
}
macro_rules! impl_self_inspect {
($ty:ty, $min:expr, $max:expr, $step:expr, $precision:expr) => {
impl Inspect for $ty {
fn properties(&self) -> Vec<PropertyInfo<'_>> {
vec![PropertyInfo {
owner_type_id: TypeId::of::<Self>(),
name: "Value",
display_name: "Value",
value: self,
read_only: false,
min_value: Some($min),
max_value: Some($max),
step: Some($step),
precision: Some($precision),
description: "".to_string(),
}]
}
}
};
}
impl_self_inspect!(f32, f32::MIN as f64, f32::MAX as f64, 1.0, 7);
impl_self_inspect!(f64, f64::MIN, f64::MAX, 1.0, 15);
impl_self_inspect!(i64, i64::MIN as f64, i64::MAX as f64, 1.0, 0);
impl_self_inspect!(u64, u64::MIN as f64, u64::MAX as f64, 1.0, 0);
impl_self_inspect!(i32, i32::MIN as f64, i32::MAX as f64, 1.0, 0);
impl_self_inspect!(u32, u32::MIN as f64, u32::MAX as f64, 1.0, 0);
impl_self_inspect!(i16, i16::MIN as f64, i16::MAX as f64, 1.0, 0);
impl_self_inspect!(u16, u16::MIN as f64, u16::MAX as f64, 1.0, 0);
impl_self_inspect!(i8, i8::MIN as f64, i8::MAX as f64, 1.0, 0);
impl_self_inspect!(u8, u8::MIN as f64, u8::MAX as f64, 1.0, 0);
pub use rg3d_core_derive::Inspect;