bevy_react/protocol/
animatable.rs1use serde::Deserialize;
5use serde::de::{self, Deserializer};
6
7use super::decode_warn;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum Animatable<T> {
11 Static(T),
12 Animated {
13 binding: crate::animations::protocol::Binding,
14 seed: Option<T>,
24 },
25}
26
27impl<T> Animatable<T> {
28 pub fn value(&self) -> Option<&T> {
31 match self {
32 Animatable::Static(v) => Some(v),
33 Animatable::Animated { .. } => None,
34 }
35 }
36
37 pub fn binding(&self) -> Option<&crate::animations::protocol::Binding> {
39 match self {
40 Animatable::Static(_) => None,
41 Animatable::Animated { binding, .. } => Some(binding),
42 }
43 }
44
45 pub fn seed(&self) -> Option<&T> {
47 match self {
48 Animatable::Static(_) => None,
49 Animatable::Animated { seed, .. } => seed.as_ref(),
50 }
51 }
52}
53
54pub trait AnimatableField<T> {
57 fn static_val(&self) -> Option<T>
59 where
60 T: Copy;
61 fn static_ref(&self) -> Option<&T>;
63 fn static_or_seed(&self) -> Option<T>
69 where
70 T: Copy;
71 fn binding(&self) -> Option<&crate::animations::protocol::Binding>;
73}
74
75impl<T> AnimatableField<T> for Option<Animatable<T>> {
76 fn static_val(&self) -> Option<T>
77 where
78 T: Copy,
79 {
80 self.static_ref().copied()
81 }
82 fn static_ref(&self) -> Option<&T> {
83 self.as_ref().and_then(Animatable::value)
84 }
85 fn static_or_seed(&self) -> Option<T>
86 where
87 T: Copy,
88 {
89 self.as_ref()
90 .and_then(|a| a.value().or_else(|| a.seed()))
91 .copied()
92 }
93 fn binding(&self) -> Option<&crate::animations::protocol::Binding> {
94 self.as_ref().and_then(Animatable::binding)
95 }
96}
97
98impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Animatable<T> {
99 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
100 let v = serde_json::Value::deserialize(d)?;
101 if let Some(map) = v.as_object()
102 && let Some(inner) = map.get("animated")
103 {
104 let seed = map.get("seed").and_then(|s| match T::deserialize(s) {
105 Ok(seed) => Some(seed),
106 Err(e) => {
107 decode_warn(
108 "styleBinding",
109 &s.to_string(),
110 &format!("invalid seed: {e}"),
111 );
112 None
113 }
114 });
115 return Ok(Animatable::Animated {
116 binding: binding_from_wrapper(inner),
117 seed,
118 });
119 }
120 T::deserialize(v)
121 .map(Animatable::Static)
122 .map_err(de::Error::custom)
123 }
124}
125
126pub(crate) fn binding_from_wrapper(
131 inner: &serde_json::Value,
132) -> crate::animations::protocol::Binding {
133 use crate::animations::protocol::Binding;
134 let inert = Binding::Shared { id: 0 };
135 let Some(map) = inner.as_object() else {
136 decode_warn(
137 "styleBinding",
138 &inner.to_string(),
139 "animated must be a shared value or an interpolate/interpolateColor descriptor",
140 );
141 return inert;
142 };
143 if map.contains_key("type") {
144 match Binding::deserialize(inner) {
145 Ok(b) => b,
146 Err(e) => {
147 decode_warn("styleBinding", &inner.to_string(), &e.to_string());
148 inert
149 }
150 }
151 } else if let Some(id) = map.get("id").and_then(serde_json::Value::as_u64) {
152 Binding::Shared { id: id as u32 }
153 } else {
154 decode_warn(
155 "styleBinding",
156 &inner.to_string(),
157 "animated needs a shared value ({id}) or a descriptor ({type, id, …})",
158 );
159 inert
160 }
161}