use serde::de::{self, Deserializer};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct Duration {
pub secs: i64,
pub nanos: f64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct BezierCurve {
pub p0: f32,
pub p1: f32,
pub p2: f32,
pub p3: f32,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum Easing {
String(String),
Bezier {
#[serde(rename = "Bezier")]
bezier: BezierCurve,
},
}
impl Default for Easing {
fn default() -> Self {
Easing::String("Linear".to_string())
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct KeyFrame {
pub value: f32,
pub duration: Duration,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum RepeatType {
String(String),
Repeat {
#[serde(rename = "Repeat")]
repeat: u32,
},
}
impl Default for RepeatType {
fn default() -> Self {
RepeatType::String("NoRepeat".to_string())
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
#[serde(default)]
pub struct SmoothAnimation {
pub duration: Duration,
pub repeat_type: RepeatType,
pub easing: Easing,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
#[serde(default)]
pub struct KeyFrameAnimation {
pub steps: Vec<KeyFrame>,
pub repeat_type: RepeatType,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub struct Animations {
#[serde(rename = "Smooth", skip_serializing_if = "Option::is_none")]
pub smooth: Option<SmoothAnimation>,
#[serde(rename = "KeyFrame", skip_serializing_if = "Option::is_none")]
pub key_frame: Option<KeyFrameAnimation>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum StopType {
None,
ResetToStart,
Complete,
Stop,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct CustomKeyframe {
pub fraction: f32,
#[serde(alias = "value")]
pub value_json: serde_json::Value,
pub easing: Easing,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct CustomTimeline {
#[serde(alias = "targetEasing")]
pub target_easing: Easing,
pub keyframes: Vec<CustomKeyframe>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
#[serde(default)]
pub struct AnimationSpec {
pub initial_delay: Duration,
pub animation: Animations,
pub interrupt_type: Option<StopType>,
pub custom_keyframe_data: std::collections::HashMap<String, CustomTimeline>,
}
#[derive(Serialize, Clone, Debug, PartialEq, Default)]
pub enum AnimationOverrideJson {
#[default]
Default,
Custom(AnimationSpec),
DisableAnimations,
}
impl<'de> Deserialize<'de> for AnimationOverrideJson {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum CustomTimelineRaw {
Typed(CustomTimeline),
Stringified(String),
}
#[derive(Deserialize)]
struct Tmp {
#[serde(rename = "override", default)]
override_type: String,
spec: Option<AnimationSpec>,
#[serde(rename = "customKeyframeData", default)]
custom_keyframe_data_raw: std::collections::HashMap<String, CustomTimelineRaw>,
}
let tmp = Tmp::deserialize(deserializer)?;
if tmp.override_type == "Custom" || (tmp.override_type.is_empty() && tmp.spec.is_some()) {
if let Some(mut spec) = tmp.spec {
let mut custom_keyframe_data = std::collections::HashMap::new();
for (k, v) in tmp.custom_keyframe_data_raw {
match v {
CustomTimelineRaw::Typed(ct) => {
custom_keyframe_data.insert(k, ct);
}
CustomTimelineRaw::Stringified(s) => {
if let Ok(ct) = serde_json::from_str::<CustomTimeline>(&s) {
custom_keyframe_data.insert(k, ct);
}
}
}
}
spec.custom_keyframe_data = custom_keyframe_data;
Ok(AnimationOverrideJson::Custom(spec))
} else {
Err(de::Error::missing_field("spec"))
}
} else if tmp.override_type == "None" {
Ok(AnimationOverrideJson::DisableAnimations)
} else {
Ok(AnimationOverrideJson::Default)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_custom() {
let json = r#"{
"override": "Custom",
"spec": {
"initial_delay": { "secs": 0, "nanos": 0 },
"animation": {
"Smooth": {
"duration": { "secs": 0, "nanos": 1000000000 },
"repeat_type": "NoRepeat",
"easing": "Linear"
}
},
"interrupt_type": "Complete"
}
}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
if let AnimationOverrideJson::Custom(custom_spec) = spec {
assert_eq!(custom_spec.interrupt_type, Some(StopType::Complete));
} else {
panic!("Wrong spec type, expected Custom");
}
}
#[test]
fn test_deserialize_keyframe_custom() {
let json = r#"{"override":"Custom","spec":{"initial_delay":{"secs":0,"nanos":0},"animation":{"KeyFrame":{"steps":[{"value":0,"duration":{"secs":0,"nanos":100000000}},{"value":0.5,"duration":{"secs":0,"nanos":110000000}},{"value":1,"duration":{"secs":0,"nanos":120000000}}],"repeat_type":"NoRepeat"}},"interrupt_type":null}}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
if let AnimationOverrideJson::Custom(custom_spec) = spec {
assert_eq!(custom_spec.interrupt_type, None);
if let Some(key_frame_animation) = custom_spec.animation.key_frame {
assert_eq!(key_frame_animation.steps.len(), 3);
assert_eq!(key_frame_animation.steps[1].value, 0.5);
} else {
panic!("Wrong animation type, expected KeyFrame");
}
} else {
panic!("Wrong spec type, expected Custom");
}
}
#[test]
fn test_deserialize_custom_with_keyframe_data() {
let json = r#"{
"override": "Custom",
"spec": {
"initial_delay": { "secs": 0, "nanos": 0 },
"animation": {
"Smooth": {
"duration": { "secs": 0, "nanos": 1000000000 },
"repeat_type": "NoRepeat",
"easing": "Linear"
}
},
"interrupt_type": "Complete"
},
"customKeyframeData": {
"Right-x": {
"target_easing": "Linear",
"keyframes": [
{ "fraction": 0.275, "value_json": "123", "easing": "Linear" }
]
}
}
}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
if let AnimationOverrideJson::Custom(custom_spec) = spec {
assert_eq!(custom_spec.interrupt_type, Some(StopType::Complete));
assert_eq!(custom_spec.custom_keyframe_data.len(), 1);
assert_eq!(
custom_spec.custom_keyframe_data.get("Right-x").unwrap().keyframes[0].fraction,
0.275
);
} else {
panic!("Wrong spec type, expected Custom");
}
}
#[test]
fn test_deserialize_none() {
let json = r#"{"override":"None","disable":true}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
assert!(matches!(spec, AnimationOverrideJson::DisableAnimations));
}
#[test]
fn test_deserialize_default_no_disable() {
let json = r#"{"override":"Default"}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
assert!(matches!(spec, AnimationOverrideJson::Default));
}
#[test]
fn test_deserialize_default_empty() {
let json = r#"{}"#;
let spec: AnimationOverrideJson = serde_json::from_str(json).unwrap();
assert!(matches!(spec, AnimationOverrideJson::Default));
}
#[test]
fn test_parse_squoosh_json() {
let json_str = r#"{
"spec": {
"initial_delay": { "secs": 0, "nanos": 0 },
"animation": {
"Smooth": {
"duration": { "secs": 0, "nanos": 1000000000 },
"repeat_type": "NoRepeat",
"easing": "Linear"
}
},
"interrupt_type": "None"
},
"customKeyframeData": {
"Right-x": {
"target_easing": "Linear",
"keyframes": []
}
}
}"#;
let anim = serde_json::from_str::<AnimationOverrideJson>(json_str);
assert!(anim.is_ok(), "Failed to parse JSON: {:?}", anim.err());
println!("Successfully parsed: {:?}", anim.unwrap());
}
}