use std::any::{Any, TypeId};
use std::fmt;
use serde::de::DeserializeOwned;
use super::codec::Codec;
use super::invalidation::{Invalidate, Invalidation};
pub trait PropertyValue: Any + Send + Sync + Clone + PartialEq + fmt::Debug {}
impl<T: Any + Send + Sync + Clone + PartialEq + fmt::Debug> PropertyValue for T {}
pub struct StyleProperty<T: 'static> {
pub name: &'static str,
pub codec: Codec<T>,
pub invalidate: Invalidate<T>,
}
impl<T: PropertyValue + DeserializeOwned + ts_rs::TS> StyleProperty<T> {
pub const fn new(name: &'static str) -> Self {
Self::with_codec(name, Codec::serde())
}
}
impl<T: PropertyValue> StyleProperty<T> {
pub const fn with_codec(name: &'static str, codec: Codec<T>) -> Self {
Self {
name,
codec,
invalidate: Invalidate::Fixed(Invalidation::NONE),
}
}
}
mod sealed {
pub trait Sealed {}
}
impl<T: PropertyValue> sealed::Sealed for StyleProperty<T> {}
pub trait AnyStyleProperty: sealed::Sealed + Send + Sync + 'static {
fn name(&self) -> &'static str;
fn value_type_id(&self) -> TypeId;
fn value_type_name(&self) -> &'static str;
fn ts_type(&self) -> String;
fn ts_decls(&self, decls: &mut std::collections::BTreeMap<String, String>);
fn keyword_kind(&self) -> Option<&'static str>;
fn computes_invalidation(&self) -> bool;
fn invalidation(
&self,
old: Option<&dyn super::StyleValueDyn>,
new: Option<&dyn super::StyleValueDyn>,
node: &super::NodeCtx<'_>,
) -> Invalidation;
fn stamp(&self, style: &super::Style, ec: &mut bevy::prelude::EntityCommands, fresh: bool);
fn decode_value(
&self,
d: &mut dyn erased_serde::Deserializer<'_>,
) -> Result<Option<super::store::StoredValue>, erased_serde::Error>;
}
impl<T: PropertyValue> AnyStyleProperty for StyleProperty<T> {
fn name(&self) -> &'static str {
self.name
}
fn value_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn value_type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
fn ts_type(&self) -> String {
self.codec.ts_type()
}
fn ts_decls(&self, decls: &mut std::collections::BTreeMap<String, String>) {
self.codec.ts_decls(decls);
}
fn keyword_kind(&self) -> Option<&'static str> {
self.codec.keyword_kind()
}
fn computes_invalidation(&self) -> bool {
matches!(self.invalidate, Invalidate::Computed(_))
}
fn invalidation(
&self,
old: Option<&dyn super::StyleValueDyn>,
new: Option<&dyn super::StyleValueDyn>,
node: &super::NodeCtx<'_>,
) -> Invalidation {
fn typed<T: 'static>(v: Option<&dyn super::StyleValueDyn>) -> Option<&T> {
v.and_then(|v| v.as_any().downcast_ref::<T>())
}
self.invalidate.eval(typed(old), typed(new), node)
}
fn stamp(&self, style: &super::Style, ec: &mut bevy::prelude::EntityCommands, fresh: bool) {
super::stamp::stamp(style.get(self), ec, fresh);
}
fn decode_value(
&self,
d: &mut dyn erased_serde::Deserializer<'_>,
) -> Result<Option<super::store::StoredValue>, erased_serde::Error> {
Ok(self.codec.decode(d)?.map(super::store::stored))
}
}
impl fmt::Debug for dyn AnyStyleProperty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "StyleProperty({:?})", self.name())
}
}