talw-timecode 0.1.0

SMPTE timecode arithmetic — parse, format, convert, drop-frame
Documentation
use crate::error::TimecodeError;
use crate::timecode::Timecode;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimeRange {
    start: Timecode,
    duration_frames: i64,
}

impl TimeRange {
    pub fn new(start: Timecode, duration_frames: i64) -> Self {
        Self {
            start,
            duration_frames,
        }
    }

    pub fn from_start_end(start: Timecode, end: Timecode) -> Result<Self, TimecodeError> {
        let diff = end.frame_diff(&start)?;
        Ok(Self {
            start,
            duration_frames: diff,
        })
    }

    pub fn start(&self) -> Timecode {
        self.start
    }

    pub fn duration_frames(&self) -> i64 {
        self.duration_frames
    }

    pub fn end(&self) -> Timecode {
        self.start + self.duration_frames
    }

    pub fn contains(&self, tc: Timecode) -> bool {
        if tc.rate() != self.start.rate() {
            return false;
        }
        let f = tc.total_frames();
        f >= self.start.total_frames() && f < self.start.total_frames() + self.duration_frames
    }

    pub fn overlaps(&self, other: &TimeRange) -> bool {
        if self.start.rate() != other.start.rate() {
            return false;
        }
        let s1 = self.start.total_frames();
        let e1 = s1 + self.duration_frames;
        let s2 = other.start.total_frames();
        let e2 = s2 + other.duration_frames;

        s1 < e2 && s2 < e1
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::framerate::FrameRate;

    #[test]
    fn basic_range() {
        let start = Timecode::from_frames(0, FrameRate::Fps24);
        let r = TimeRange::new(start, 240);
        assert_eq!(r.end().total_frames(), 240);
    }

    #[test]
    fn contains() {
        let start = Timecode::from_frames(100, FrameRate::Fps24);
        let r = TimeRange::new(start, 50);
        assert!(r.contains(Timecode::from_frames(120, FrameRate::Fps24)));
        assert!(!r.contains(Timecode::from_frames(150, FrameRate::Fps24)));
        assert!(!r.contains(Timecode::from_frames(99, FrameRate::Fps24)));
    }

    #[test]
    fn overlaps() {
        let a = TimeRange::new(Timecode::from_frames(0, FrameRate::Fps24), 100);
        let b = TimeRange::new(Timecode::from_frames(50, FrameRate::Fps24), 100);
        let c = TimeRange::new(Timecode::from_frames(100, FrameRate::Fps24), 50);

        assert!(a.overlaps(&b));
        assert!(!a.overlaps(&c));
    }

    #[test]
    fn from_start_end() {
        let start = Timecode::from_frames(100, FrameRate::Fps24);
        let end = Timecode::from_frames(200, FrameRate::Fps24);
        let r = TimeRange::from_start_end(start, end).unwrap();
        assert_eq!(r.duration_frames(), 100);
    }
}