timeline_rs 0.1.3

simple timeline library for Rust
Documentation
use std::collections::HashMap;
use std::fs::File;
use std::time::Duration;

use anyhow::Result;
use serde::de::DeserializeOwned;

use crate::{xml_to_json, Timeline, TimelineTrack, TimelineTrackImpl, Track, TrackVariant};
use crate::Keyframe;

pub trait TimelineXMLLoader {
    fn load_xml<T>(&mut self, track_name: &str, xml_path: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>;
    fn load_xml_str<T>(&mut self, track_name: &str, xml: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>;
}
pub trait TimelineJsonLoader {
    fn load_json<T>(&mut self, track_name: &str, json_path: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>;
    fn load_json_str<T>(&mut self, track_name: &str, json: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>;
}

pub trait XMLTrackLoader<T>
where
    TrackVariant: From<Track<T>>,
    T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
{
    fn load_xml(xml: &str) -> Result<Track<T>>;
    fn load_xml_str(xml: &str) -> Result<Track<T>>;
}

pub trait JsonTrackLoader<T>
where
    TrackVariant: From<Track<T>>,
    T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
{
    fn load_json(json_path: &str) -> Result<Track<T>>;
    fn load_json_str(json: &str) -> Result<Track<T>>;
}

impl TimelineXMLLoader for Timeline
{
    fn load_xml<T>(&mut self, track_name: &str, xml_path: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
    {
        let json = xml_to_json::xml_str_to_json(xml_path)?;
        self.load_json::<T>(track_name, &json.to_string())
    }

    fn load_xml_str<T>(&mut self, track_name: &str, xml: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
    {
        let json = xml_to_json::xml_str_to_json(xml)?;
        self.load_json_str::<T>(track_name, &json.to_string())
    }
}

impl TimelineJsonLoader for Timeline {
    fn load_json<T>(&mut self, track_name: &str, json_path: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
    {
        let track = <T as JsonLoaderWrapper<T>>::load_json(json_path)?;
        self.add::<Track<T>>(track_name, track.into());
        Ok(())
    }

    fn load_json_str<T>(&mut self, track_name: &str, json: &str) -> Result<()>
    where
        TrackVariant: From<Track<T>>,
        T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
    {
        let track = <T as JsonLoaderWrapper<T>>::load_json_str(json)?;
        self.add::<Track<T>>(track_name, track.into());
        Ok(())
    }
}

impl<T> XMLTrackLoader<T> for Track<T>
where
    TrackVariant: From<Track<T>>,
T: Copy + DeserializeOwned + JsonLoaderWrapper<T>
{
    fn load_xml(xml_path: &str) -> Result<Track<T>>
    {
        let json = xml_to_json::xml_str_to_json(xml_path)?;
        <T as JsonLoaderWrapper<T>>::load_json(&json.to_string())
    }

    fn load_xml_str(xml: &str) -> Result<Track<T>>
    {
        let json = xml_to_json::xml_str_to_json(xml)?;
        <T as JsonLoaderWrapper<T>>::load_json_str(&json.to_string())
    }
}

trait JsonLoaderWrapper<T>
where 
    T: Copy + DeserializeOwned
{
    fn load_json(json_path: &str) -> Result<Track<T>>;
    fn load_json_str(json: &str) -> Result<Track<T>>;
}
        
macro_rules! impl_json_track_loader {
    ($($t:ty),*) => {
        $(
            impl JsonTrackLoader<$t> for Track<$t>
                where
                    TrackVariant: From<Track<$t>>,
            {
                fn load_json(json: &str) -> Result<Track<$t>>
                {
                    let mut track = Track::<$t>::default();
                    let file = File::open(json)?;
                    let json: KeyframesEntity<$t> = serde_json::from_reader(file)?;
                    for keyframe in json.keyframes.get("key").unwrap() {
                        track.add_keyframe(Keyframe {
                            time: timecode_to_duration(&keyframe.time)?,
                            value: keyframe.value,
                            easing_function: keyframe.easefunc.into(),
                            easing_type: keyframe.easetype.into(),
                        });
                    }
                    Ok(track)
                }

                fn load_json_str(json: &str) -> Result<Track<$t>>
                {
                    let mut track = Track::<$t>::default();
                    let json: KeyframesEntity<$t> = serde_json::from_str(json)?;
                    for keyframe in json.keyframes.get("key").unwrap() {
                        track.add_keyframe(Keyframe {
                            time: timecode_to_duration(&keyframe.time)?,
                            value: keyframe.value,
                            easing_function: keyframe.easefunc.into(),
                            easing_type: keyframe.easetype.into(),
                        });
                    }
                    Ok(track)
                }
            }

            impl JsonLoaderWrapper<$t> for $t {
                fn load_json(json: &str) -> Result<Track<$t>> {
                    Track::<Self>::load_json(json)
                }

                fn load_json_str(json: &str) -> Result<Track<$t>> {
                    Track::<Self>::load_json_str(json)
                }
            }
        )*
    };
}

impl_json_track_loader!(f32, f64, i32, i64);

fn timecode_to_duration(timecode: &str) -> Result<Duration> {
    let parts: Vec<&str> = timecode.split(':').collect();
    if parts.len() != 4 {
        return Err(anyhow::anyhow!("Invalid timecode format"));
    }

    let hours: f64 = parts[0].parse()?;
    let minutes: f64 = parts[1].parse()?;
    let seconds: f64 = parts[2].parse()?;
    let milliseconds: f64 = parts[3].parse()?;

    Ok(Duration::from_secs_f64(hours * 3600.0 + minutes * 60.0 + seconds) + Duration::from_millis(milliseconds as u64))
}

#[derive(serde::Deserialize)]
struct KeyframesEntity<T> {
    keyframes: HashMap<String, Vec<KeyframeEntity<T>>>,
}

// #[derive(serde::Deserialize)]
// struct KeyEntity<T> {
//     key: KeyframeEntity<T>,
// }

#[derive(serde::Deserialize)]
struct KeyframeEntity<T> {
    easefunc: u8,
    easetype: u8,
    time: String,
    value: T,
}

#[cfg(test)]
mod tests {
    use crate::{easing::{EasingFunction, EasingType}, TrackGetter};

    use super::*;

    #[test]
    fn xml_load_test() {
        let xml = r#"
<keyframes>
    <key>
        <easefunc>0</easefunc>
        <easetype>0</easetype>
        <time>00:00:00:643</time>
        <value>0.585546851</value>
    </key>
    <key>
        <easefunc>4</easefunc>
        <easetype>2</easetype>
        <time>00:00:00:826</time>
        <value>0.141503930</value>
    </key>
    <key>
        <easefunc>1</easefunc>
        <easetype>1</easetype>
        <time>00:00:01:594</time>
        <value>0.443359375</value>
    </key>
    <key>
        <easefunc>0</easefunc>
        <easetype>2</easetype>
        <time>00:00:02:033</time>
        <value>0.400390625</value>
    </key>
    <key>
        <easefunc>4</easefunc>
        <easetype>0</easetype>
        <time>00:00:02:260</time>
        <value>0.586718738</value>
    </key>
</keyframes>"#;

        let mut tl = Timeline::new();
        tl.load_xml_str::<f32>("test", xml).unwrap();

        let track = tl.get("test").unwrap().as_float_track();
        assert_eq!(track.keyframes.len(), 5);

        assert_eq!(track.keyframes[0].time, Duration::from_millis(643));
        assert_eq!(track.keyframes[0].value, 0.585546851);
        assert_eq!(track.keyframes[0].easing_function, EasingFunction::Linear);
        assert_eq!(track.keyframes[0].easing_type, EasingType::In);

        assert_eq!(track.keyframes[1].time, Duration::from_millis(826));
        assert_eq!(track.keyframes[1].value, 0.141503930);
        assert_eq!(track.keyframes[1].easing_function, EasingFunction::Cubic);
        assert_eq!(track.keyframes[1].easing_type, EasingType::InOut);

        assert_eq!(track.keyframes[2].time, Duration::from_millis(1594));
        assert_eq!(track.keyframes[2].value, 0.443359375);
        assert_eq!(track.keyframes[2].easing_function, EasingFunction::Sine);
        assert_eq!(track.keyframes[2].easing_type, EasingType::Out);

        assert_eq!(track.keyframes[3].time, Duration::from_millis(2033));
        assert_eq!(track.keyframes[3].value, 0.400390625);
        assert_eq!(track.keyframes[3].easing_function, EasingFunction::Linear);
        assert_eq!(track.keyframes[3].easing_type, EasingType::InOut);
    }
}