use std::collections::BTreeMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::schema::animation::EasingType;
use crate::schema::time::TimePoint;
pub type VarSet = BTreeMap<String, VarDef>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct VarDef {
pub default: f64,
#[serde(default)]
pub animation: Vec<VarKeyframe>,
}
impl VarDef {
pub fn is_static(&self) -> bool {
self.animation.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct VarKeyframe {
pub at: TimePoint,
pub to: f64,
#[serde(default = "default_duration")]
pub duration: TimePoint,
#[serde(default)]
pub easing: EasingType,
}
fn default_duration() -> TimePoint {
TimePoint::Seconds(0.0)
}
pub fn dynamic_names(vars: &VarSet) -> impl Iterator<Item = &str> {
vars.iter()
.filter(|(_, def)| !def.is_static())
.map(|(name, _)| name.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn var_def_without_animation_is_static() {
let def = VarDef {
default: 42.0,
animation: Vec::new(),
};
assert!(def.is_static());
}
#[test]
fn var_def_with_animation_is_not_static() {
let def = VarDef {
default: 0.0,
animation: vec![VarKeyframe {
at: TimePoint::Spec("@1s".to_string()),
to: 1.0,
duration: TimePoint::Seconds(1.0),
easing: EasingType::Linear,
}],
};
assert!(!def.is_static());
}
#[test]
fn dynamic_names_only_reports_animated_variables() {
let mut vars = VarSet::new();
vars.insert(
"constant".to_string(),
VarDef {
default: 1.0,
animation: Vec::new(),
},
);
vars.insert(
"moving".to_string(),
VarDef {
default: 0.0,
animation: vec![VarKeyframe {
at: TimePoint::Seconds(0.0),
to: 1.0,
duration: TimePoint::Seconds(1.0),
easing: EasingType::Linear,
}],
},
);
let names: Vec<&str> = dynamic_names(&vars).collect();
assert_eq!(names, vec!["moving"]);
}
#[test]
fn keyframe_deserializes_from_the_issue_example() {
let json = r#"{
"default": 0,
"animation": [{ "at": "@4.85s", "to": 1, "duration": "3b", "easing": "ease_in_out" }]
}"#;
let def: VarDef = serde_json::from_str(json).unwrap();
assert_eq!(def.default, 0.0);
assert_eq!(def.animation.len(), 1);
assert_eq!(def.animation[0].at, TimePoint::Spec("@4.85s".to_string()));
assert_eq!(def.animation[0].to, 1.0);
assert_eq!(def.animation[0].duration, TimePoint::Spec("3b".to_string()));
assert_eq!(def.animation[0].easing, EasingType::EaseInOut);
}
#[test]
fn keyframe_duration_defaults_to_an_instant_snap() {
let json = r#"{ "default": 0, "animation": [{ "at": "1s", "to": 1 }] }"#;
let def: VarDef = serde_json::from_str(json).unwrap();
assert_eq!(def.animation[0].duration, TimePoint::Seconds(0.0));
assert_eq!(def.animation[0].easing, EasingType::Linear);
}
#[test]
fn unknown_field_is_rejected() {
let json = r#"{ "default": 0, "typo": 1 }"#;
assert!(serde_json::from_str::<VarDef>(json).is_err());
}
}