use std::any::TypeId;
use std::collections::BTreeMap;
use std::fmt;
use serde::de::DeserializeOwned;
use crate::animations::protocol::Binding;
use crate::protocol::animatable::Animatable;
use crate::style::{Codec, Invalidation, PropertyValue, StoredValue, StyleValueDyn};
pub struct AttrBinding<T: 'static> {
pub domain: &'static str,
pub binding: fn(&T) -> Option<Binding>,
}
pub struct Attribute<T: 'static> {
pub name: &'static str,
pub codec: Codec<T>,
pub event: bool,
pub invalidate: Invalidation,
pub typed: bool,
pub animated: Option<AttrBinding<T>>,
}
impl<T: PropertyValue + DeserializeOwned + ts_rs::TS> Attribute<T> {
pub const fn new(name: &'static str) -> Self {
Self::with_codec(name, Codec::serde())
}
}
impl<T: PropertyValue> Attribute<T> {
pub const fn with_codec(name: &'static str, codec: Codec<T>) -> Self {
Self {
name,
codec,
event: false,
invalidate: Invalidation::NONE,
typed: true,
animated: None,
}
}
}
pub fn animatable_binding<T>(value: &Animatable<T>) -> Option<Binding> {
match value {
Animatable::Animated(slot) => Some(slot.binding.clone()),
Animatable::Static(_) => None,
}
}
mod sealed {
pub trait Sealed {}
}
impl<T: PropertyValue> sealed::Sealed for Attribute<T> {}
pub trait AnyAttribute: sealed::Sealed + Send + Sync + 'static {
fn name(&self) -> &'static str;
fn is_event(&self) -> bool;
fn invalidation(&self) -> Invalidation;
fn is_typed(&self) -> bool;
fn value_type_id(&self) -> TypeId;
fn ts_type(&self) -> String;
fn ts_decls(&self, decls: &mut BTreeMap<String, String>);
fn keyword_kind(&self) -> Option<&'static str>;
fn decode_value(
&self,
d: &mut dyn erased_serde::Deserializer<'_>,
) -> Result<Option<StoredValue>, erased_serde::Error>;
fn binding(&self, value: &dyn StyleValueDyn) -> Option<(&'static str, Binding)>;
}
impl<T: PropertyValue> AnyAttribute for Attribute<T> {
fn name(&self) -> &'static str {
self.name
}
fn is_event(&self) -> bool {
self.event
}
fn invalidation(&self) -> Invalidation {
self.invalidate
}
fn is_typed(&self) -> bool {
self.typed
}
fn value_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn ts_type(&self) -> String {
self.codec.ts_type()
}
fn ts_decls(&self, decls: &mut BTreeMap<String, String>) {
self.codec.ts_decls(decls);
}
fn keyword_kind(&self) -> Option<&'static str> {
self.codec.keyword_kind()
}
fn decode_value(
&self,
d: &mut dyn erased_serde::Deserializer<'_>,
) -> Result<Option<StoredValue>, erased_serde::Error> {
Ok(self.codec.decode(d)?.map(crate::style::stored_value))
}
fn binding(&self, value: &dyn StyleValueDyn) -> Option<(&'static str, Binding)> {
let animated = self.animated.as_ref()?;
let value = value.as_any().downcast_ref::<T>()?;
(animated.binding)(value).map(|b| (animated.domain, b))
}
}
impl fmt::Debug for dyn AnyAttribute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Attribute({:?})", self.name())
}
}
pub(crate) fn is_attr<T: 'static>(registered: &dyn AnyAttribute, attribute: &Attribute<T>) -> bool {
std::ptr::addr_eq(
registered as *const dyn AnyAttribute,
attribute as *const Attribute<T>,
)
}
pub(crate) fn same_attr(a: &dyn AnyAttribute, b: &dyn AnyAttribute) -> bool {
std::ptr::addr_eq(a as *const dyn AnyAttribute, b as *const dyn AnyAttribute)
}