1use rydit_core::{RyditModule, ModuleResult, ModuleError};
9use serde_json::{Value, json};
10use std::collections::HashMap;
11
12pub struct AnimModule;
14
15impl RyditModule for AnimModule {
16 fn name(&self) -> &'static str {
17 "anim"
18 }
19
20 fn version(&self) -> &'static str {
21 "0.7.3"
22 }
23
24 fn register(&self) -> HashMap<&'static str, &'static str> {
25 let mut cmds = HashMap::new();
26 cmds.insert("ease_in", "Easing In - comienza lento, acelera");
27 cmds.insert("ease_out", "Easing Out - comienza rápido, frena");
28 cmds.insert("ease_in_out", "Easing In-Out - combina ambos");
29 cmds.insert("squash", "Squash - aplasta (mantiene área)");
30 cmds.insert("stretch", "Stretch - estira (mantiene área)");
31 cmds.insert("anticipate", "Anticipation - retrocede antes de avanzar");
32 cmds
33 }
34
35 fn execute(&self, command: &str, params: Value) -> ModuleResult {
36 match command {
37 "ease_in" => self.ease_in(params),
38 "ease_out" => self.ease_out(params),
39 "ease_in_out" => self.ease_in_out(params),
40 "squash" => self.squash(params),
41 "stretch" => self.stretch(params),
42 "anticipate" => self.anticipate(params),
43 _ => Err(ModuleError {
44 code: "UNKNOWN_COMMAND".to_string(),
45 message: format!("Comando desconocido: {}", command),
46 }),
47 }
48 }
49}
50
51impl AnimModule {
52 fn ease_in(&self, params: Value) -> ModuleResult {
55 let arr = params.as_array().ok_or_else(|| ModuleError {
56 code: "INVALID_PARAMS".to_string(),
57 message: "Params must be an array".to_string(),
58 })?;
59
60 if arr.len() != 1 {
61 return Err(ModuleError {
62 code: "INVALID_PARAMS".to_string(),
63 message: "ease_in requires 1 param: t (0.0-1.0)".to_string(),
64 });
65 }
66
67 let t = arr[0].as_f64().unwrap_or(0.0).max(0.0).min(1.0);
68 Ok(json!(t * t))
69 }
70
71 fn ease_out(&self, params: Value) -> ModuleResult {
74 let arr = params.as_array().ok_or_else(|| ModuleError {
75 code: "INVALID_PARAMS".to_string(),
76 message: "Params must be an array".to_string(),
77 })?;
78
79 if arr.len() != 1 {
80 return Err(ModuleError {
81 code: "INVALID_PARAMS".to_string(),
82 message: "ease_out requires 1 param: t (0.0-1.0)".to_string(),
83 });
84 }
85
86 let t = arr[0].as_f64().unwrap_or(0.0).max(0.0).min(1.0);
87 Ok(json!(t * (2.0 - t)))
88 }
89
90 fn ease_in_out(&self, params: Value) -> ModuleResult {
93 let arr = params.as_array().ok_or_else(|| ModuleError {
94 code: "INVALID_PARAMS".to_string(),
95 message: "Params must be an array".to_string(),
96 })?;
97
98 if arr.len() != 1 {
99 return Err(ModuleError {
100 code: "INVALID_PARAMS".to_string(),
101 message: "ease_in_out requires 1 param: t (0.0-1.0)".to_string(),
102 });
103 }
104
105 let t = arr[0].as_f64().unwrap_or(0.0).max(0.0).min(1.0);
106 let result = if t < 0.5 {
107 2.0 * t * t
108 } else {
109 1.0 - 2.0 * (1.0 - t) * (1.0 - t)
110 };
111 Ok(json!(result))
112 }
113
114 fn squash(&self, params: Value) -> ModuleResult {
117 let arr = params.as_array().ok_or_else(|| ModuleError {
118 code: "INVALID_PARAMS".to_string(),
119 message: "Params must be an array".to_string(),
120 })?;
121
122 if arr.len() != 1 {
123 return Err(ModuleError {
124 code: "INVALID_PARAMS".to_string(),
125 message: "squash requires 1 param: factor (0.5-2.0)".to_string(),
126 });
127 }
128
129 let factor = arr[0].as_f64().unwrap_or(1.0).max(0.5).min(2.0);
130 Ok(json!([factor, 1.0 / factor]))
131 }
132
133 fn stretch(&self, params: Value) -> ModuleResult {
136 let arr = params.as_array().ok_or_else(|| ModuleError {
137 code: "INVALID_PARAMS".to_string(),
138 message: "Params must be an array".to_string(),
139 })?;
140
141 if arr.len() != 1 {
142 return Err(ModuleError {
143 code: "INVALID_PARAMS".to_string(),
144 message: "stretch requires 1 param: factor (0.5-2.0)".to_string(),
145 });
146 }
147
148 let factor = arr[0].as_f64().unwrap_or(1.0).max(0.5).min(2.0);
149 Ok(json!([1.0 / factor, factor]))
150 }
151
152 fn anticipate(&self, params: Value) -> ModuleResult {
155 let arr = params.as_array().ok_or_else(|| ModuleError {
156 code: "INVALID_PARAMS".to_string(),
157 message: "Params must be an array".to_string(),
158 })?;
159
160 if arr.len() != 3 {
161 return Err(ModuleError {
162 code: "INVALID_PARAMS".to_string(),
163 message: "anticipate requires 3 params: pos, target, amount".to_string(),
164 });
165 }
166
167 let pos = arr[0].as_f64().unwrap_or(0.0);
168 let target = arr[1].as_f64().unwrap_or(0.0);
169 let amount = arr[2].as_f64().unwrap_or(0.0);
170
171 let dir = if target > pos { -1.0 } else { 1.0 };
172 Ok(json!(pos + dir * amount))
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_anim_module_name() {
182 let module = AnimModule;
183 assert_eq!(module.name(), "anim");
184 assert_eq!(module.version(), "0.7.3");
185 }
186
187 #[test]
188 fn test_anim_register() {
189 let module = AnimModule;
190 let cmds = module.register();
191
192 assert!(cmds.contains_key("ease_in"));
193 assert!(cmds.contains_key("ease_out"));
194 assert!(cmds.contains_key("squash"));
195 assert!(cmds.contains_key("anticipate"));
196 }
197
198 #[test]
199 fn test_ease_in() {
200 let module = AnimModule;
201 let params = json!([0.5]);
203 let result = module.execute("ease_in", params).unwrap();
204 assert!((result.as_f64().unwrap() - 0.25).abs() < 0.001);
205 }
206
207 #[test]
208 fn test_ease_out() {
209 let module = AnimModule;
210 let params = json!([0.5]);
212 let result = module.execute("ease_out", params).unwrap();
213 assert!((result.as_f64().unwrap() - 0.75).abs() < 0.001);
214 }
215
216 #[test]
217 fn test_ease_in_out() {
218 let module = AnimModule;
219 let params = json!([0.5]);
221 let result = module.execute("ease_in_out", params).unwrap();
222 assert!((result.as_f64().unwrap() - 0.5).abs() < 0.001);
223 }
224
225 #[test]
226 fn test_squash() {
227 let module = AnimModule;
228 let params = json!([2.0]);
230 let result = module.execute("squash", params).unwrap();
231 let arr = result.as_array().unwrap();
232 assert!((arr[0].as_f64().unwrap() - 2.0).abs() < 0.001);
233 assert!((arr[1].as_f64().unwrap() - 0.5).abs() < 0.001);
234 }
235
236 #[test]
237 fn test_stretch() {
238 let module = AnimModule;
239 let params = json!([2.0]);
241 let result = module.execute("stretch", params).unwrap();
242 let arr = result.as_array().unwrap();
243 assert!((arr[0].as_f64().unwrap() - 0.5).abs() < 0.001);
244 assert!((arr[1].as_f64().unwrap() - 2.0).abs() < 0.001);
245 }
246
247 #[test]
248 fn test_anticipate() {
249 let module = AnimModule;
250 let params = json!([100.0, 200.0, 20.0]);
252 let result = module.execute("anticipate", params).unwrap();
253 assert!((result.as_f64().unwrap() - 80.0).abs() < 0.001);
254 }
255
256 #[test]
257 fn test_unknown_command() {
258 let module = AnimModule;
259 let result = module.execute("unknown", json!([]));
260
261 assert!(result.is_err());
262 let err = result.unwrap_err();
263 assert_eq!(err.code, "UNKNOWN_COMMAND");
264 }
265}