primase_core/
clip_identity.rs1use crate::world::Linearity;
3use crate::{Component, Entity, Geometry, JsonError, Linear, World};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(transparent)]
12pub struct ClipKey(pub String);
13
14#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
17#[serde(transparent)]
18pub struct ClipLength(pub f32);
19
20impl Component for ClipKey {
21 const NAME: &'static str = "core.clip_key";
22 const GEOMETRY: Geometry = Geometry::None;
23 fn to_json(&self) -> Result<Option<Value>, JsonError> {
24 crate::json::to_value(self).map(Some)
25 }
26 fn verify(&self, _entity: Entity, _world: &World) -> Result<(), String> {
27 if self.0.trim().is_empty() {
28 return Err("clip key is empty".into());
29 }
30 Ok(())
31 }
32 fn apply_linear(&self, _linear: &Linear) -> Linearity<Self> {
33 Linearity::Invariant
34 }
35}
36
37impl Component for ClipLength {
38 const NAME: &'static str = "core.clip_length";
39 const GEOMETRY: Geometry = Geometry::None;
40 fn to_json(&self) -> Result<Option<Value>, JsonError> {
41 crate::json::to_value(self).map(Some)
42 }
43 fn verify(&self, _entity: Entity, _world: &World) -> Result<(), String> {
44 if !self.0.is_finite() || self.0 < 0.0 {
45 return Err("clip length must be finite and non-negative seconds".into());
46 }
47 Ok(())
48 }
49 fn apply_linear(&self, _linear: &Linear) -> Linearity<Self> {
50 Linearity::Invariant
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 #[test]
58 fn plan_identity_and_known_length_roundtrip_before_loading() {
59 let mut world = World::new();
60 let entity = world.spawn();
61 let key = ClipKey(r#"{"member":"walk"}"#.into());
62 let length = ClipLength(2.5);
63 world.insert(entity, key.clone());
64 world.insert(entity, length);
65 assert!(world.verify_components().is_empty());
66 assert_eq!(
67 serde_json::from_value::<ClipKey>(key.to_json().unwrap().unwrap()).unwrap(),
68 key
69 );
70 assert_eq!(
71 serde_json::from_value::<ClipLength>(length.to_json().unwrap().unwrap()).unwrap(),
72 length
73 );
74 let part = world.subset(&[entity].into());
75 assert_eq!(part.get::<ClipKey>(entity), Some(&key));
76 assert_eq!(part.get::<ClipLength>(entity), Some(&length));
77 assert!(!world.has::<crate::Clip>(entity));
78 assert!(key.remap_primitives(&[1, 0]).is_none());
79 assert!(length.remap_primitives(&[1, 0]).is_none());
80 assert!(matches!(
81 key.apply_linear(&Linear::of(&crate::Transform::IDENTITY)),
82 Linearity::Invariant
83 ));
84 assert!(matches!(
85 length.apply_linear(&Linear::of(&crate::Transform::IDENTITY)),
86 Linearity::Invariant
87 ));
88 }
89 #[test]
90 fn invalid_identity_and_length_refuse() {
91 let mut world = World::new();
92 let e = world.spawn();
93 assert!(ClipKey(" ".into()).verify(e, &world).is_err());
94 for seconds in [f32::NAN, f32::INFINITY, -0.01] {
95 assert!(ClipLength(seconds).verify(e, &world).is_err());
96 }
97 assert!(ClipLength(0.0).verify(e, &world).is_ok());
98 }
99}