use serde::Deserialize;
use serde::de::{self, Deserializer};
use super::decode_warn;
#[derive(Debug, Clone, PartialEq)]
pub enum Animatable<T> {
Static(T),
Animated(Box<AnimatedSlot<T>>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct AnimatedSlot<T> {
pub binding: crate::animations::protocol::Binding,
pub seed: Option<T>,
}
impl<T> Animatable<T> {
pub fn animated(binding: crate::animations::protocol::Binding, seed: Option<T>) -> Self {
Animatable::Animated(Box::new(AnimatedSlot { binding, seed }))
}
pub fn value(&self) -> Option<&T> {
match self {
Animatable::Static(v) => Some(v),
Animatable::Animated(_) => None,
}
}
pub fn binding(&self) -> Option<&crate::animations::protocol::Binding> {
match self {
Animatable::Static(_) => None,
Animatable::Animated(a) => Some(&a.binding),
}
}
pub fn seed(&self) -> Option<&T> {
match self {
Animatable::Static(_) => None,
Animatable::Animated(a) => a.seed.as_ref(),
}
}
pub fn seed_mut(&mut self) -> Option<&mut Option<T>> {
match self {
Animatable::Static(_) => None,
Animatable::Animated(a) => Some(&mut a.seed),
}
}
}
pub trait AnimatableField<'a, T: 'a> {
fn static_val(self) -> Option<T>
where
T: Copy;
fn static_ref(self) -> Option<&'a T>;
fn static_or_seed(self) -> Option<T>
where
T: Copy;
fn binding(self) -> Option<&'a crate::animations::protocol::Binding>;
}
impl<'a, T: 'a> AnimatableField<'a, T> for Option<&'a Animatable<T>> {
fn static_val(self) -> Option<T>
where
T: Copy,
{
self.static_ref().copied()
}
fn static_ref(self) -> Option<&'a T> {
self.and_then(Animatable::value)
}
fn static_or_seed(self) -> Option<T>
where
T: Copy,
{
self.and_then(|a| a.value().or_else(|| a.seed())).copied()
}
fn binding(self) -> Option<&'a crate::animations::protocol::Binding> {
self.and_then(Animatable::binding)
}
}
impl<'a, T: 'a> AnimatableField<'a, T> for &'a Option<Animatable<T>> {
fn static_val(self) -> Option<T>
where
T: Copy,
{
self.as_ref().static_val()
}
fn static_ref(self) -> Option<&'a T> {
self.as_ref().static_ref()
}
fn static_or_seed(self) -> Option<T>
where
T: Copy,
{
self.as_ref().static_or_seed()
}
fn binding(self) -> Option<&'a crate::animations::protocol::Binding> {
self.as_ref().binding()
}
}
impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Animatable<T> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let v = serde_json::Value::deserialize(d)?;
if let Some(map) = v.as_object()
&& let Some(inner) = map.get("animated")
{
let seed = map.get("seed").and_then(|s| match T::deserialize(s) {
Ok(seed) => Some(seed),
Err(e) => {
decode_warn(
"styleBinding",
&s.to_string(),
&format!("invalid seed: {e}"),
);
None
}
});
return Ok(Animatable::animated(binding_from_wrapper(inner), seed));
}
T::deserialize(v)
.map(Animatable::Static)
.map_err(de::Error::custom)
}
}
pub(crate) fn binding_from_wrapper(
inner: &serde_json::Value,
) -> crate::animations::protocol::Binding {
use crate::animations::protocol::Binding;
let inert = Binding::Shared { id: 0 };
let Some(map) = inner.as_object() else {
decode_warn(
"styleBinding",
&inner.to_string(),
"animated must be a shared value or an interpolate/interpolateColor descriptor",
);
return inert;
};
if map.contains_key("type") {
match Binding::deserialize(inner) {
Ok(b) => b,
Err(e) => {
decode_warn("styleBinding", &inner.to_string(), &e.to_string());
inert
}
}
} else if let Some(id) = map.get("id").and_then(serde_json::Value::as_u64) {
Binding::Shared { id: id as u32 }
} else {
decode_warn(
"styleBinding",
&inner.to_string(),
"animated needs a shared value ({id}) or a descriptor ({type, id, …})",
);
inert
}
}