#![allow(dead_code)]
use crate::event::{EditType, EdlEvent, TrackType};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct EventList {
events: Vec<EdlEvent>,
}
impl EventList {
#[must_use]
pub fn new() -> Self {
Self { events: Vec::new() }
}
#[must_use]
pub fn from_events(events: Vec<EdlEvent>) -> Self {
Self { events }
}
#[must_use]
pub fn len(&self) -> usize {
self.events.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn push(&mut self, event: EdlEvent) {
self.events.push(event);
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&EdlEvent> {
self.events.get(index)
}
#[must_use]
pub fn find_by_number(&self, number: u32) -> Option<&EdlEvent> {
self.events.iter().find(|e| e.number == number)
}
#[must_use]
pub fn find_by_reel(&self, reel: &str) -> Vec<&EdlEvent> {
self.events.iter().filter(|e| e.reel == reel).collect()
}
#[must_use]
pub fn find_by_edit_type(&self, edit_type: EditType) -> Vec<&EdlEvent> {
self.events
.iter()
.filter(|e| e.edit_type == edit_type)
.collect()
}
#[must_use]
pub fn find_by_track(&self, track: &TrackType) -> Vec<&EdlEvent> {
self.events.iter().filter(|e| &e.track == track).collect()
}
#[must_use]
pub fn slice_by_record_range(&self, start_frames: u64, end_frames: u64) -> Vec<&EdlEvent> {
self.events
.iter()
.filter(|e| {
let f = e.record_in.to_frames();
f >= start_frames && f < end_frames
})
.collect()
}
#[must_use]
pub fn reel_index(&self) -> HashMap<String, Vec<u32>> {
let mut map: HashMap<String, Vec<u32>> = HashMap::new();
for e in &self.events {
map.entry(e.reel.clone()).or_default().push(e.number);
}
map
}
#[must_use]
pub fn unique_reels(&self) -> Vec<String> {
let mut seen = Vec::new();
for e in &self.events {
if !seen.contains(&e.reel) {
seen.push(e.reel.clone());
}
}
seen
}
#[must_use]
pub fn total_duration_frames(&self) -> u64 {
self.events.iter().map(|e| e.duration_frames()).sum()
}
pub fn sort_by_record_in(&mut self) {
self.events.sort_by_key(|e| e.record_in.to_frames());
}
pub fn renumber(&mut self, start: u32) {
for (i, event) in self.events.iter_mut().enumerate() {
event.number = start + i as u32;
}
}
pub fn remove_by_number(&mut self, number: u32) -> Option<EdlEvent> {
if let Some(pos) = self.events.iter().position(|e| e.number == number) {
Some(self.events.remove(pos))
} else {
None
}
}
#[must_use]
pub fn as_slice(&self) -> &[EdlEvent] {
&self.events
}
#[must_use]
pub fn into_inner(self) -> Vec<EdlEvent> {
self.events
}
#[must_use]
pub fn find_overlaps(&self) -> Vec<(u32, u32)> {
let mut overlaps = Vec::new();
for i in 0..self.events.len() {
for j in (i + 1)..self.events.len() {
if self.events[i].overlaps_with(&self.events[j]) {
overlaps.push((self.events[i].number, self.events[j].number));
}
}
}
overlaps
}
}
impl Default for EventList {
fn default() -> Self {
Self::new()
}
}
impl IntoIterator for EventList {
type Item = EdlEvent;
type IntoIter = std::vec::IntoIter<EdlEvent>;
fn into_iter(self) -> Self::IntoIter {
self.events.into_iter()
}
}
impl<'a> IntoIterator for &'a EventList {
type Item = &'a EdlEvent;
type IntoIter = std::slice::Iter<'a, EdlEvent>;
fn into_iter(self) -> Self::IntoIter {
self.events.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{EditType, TrackType};
use crate::timecode::{EdlFrameRate, EdlTimecode};
fn make_event(num: u32, reel: &str, rec_in_sec: u8, rec_out_sec: u8) -> EdlEvent {
let fr = EdlFrameRate::Fps25;
EdlEvent::new(
num,
reel.to_string(),
TrackType::Video,
EditType::Cut,
EdlTimecode::new(1, 0, rec_in_sec, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, rec_out_sec, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, rec_in_sec, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, rec_out_sec, 0, fr).expect("failed to create"),
)
}
#[test]
fn test_new_is_empty() {
let list = EventList::new();
assert!(list.is_empty());
assert_eq!(list.len(), 0);
}
#[test]
fn test_push_and_len() {
let mut list = EventList::new();
list.push(make_event(1, "A001", 0, 5));
assert_eq!(list.len(), 1);
assert!(!list.is_empty());
}
#[test]
fn test_find_by_number() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A002", 5, 10),
]);
assert!(list.find_by_number(1).is_some());
assert!(list.find_by_number(3).is_none());
}
#[test]
fn test_find_by_reel() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A001", 5, 10),
make_event(3, "B001", 10, 15),
]);
assert_eq!(list.find_by_reel("A001").len(), 2);
assert_eq!(list.find_by_reel("B001").len(), 1);
assert_eq!(list.find_by_reel("C001").len(), 0);
}
#[test]
fn test_find_by_edit_type() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A002", 5, 10),
]);
assert_eq!(list.find_by_edit_type(EditType::Cut).len(), 2);
assert_eq!(list.find_by_edit_type(EditType::Dissolve).len(), 0);
}
#[test]
fn test_unique_reels() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "B001", 5, 10),
make_event(3, "A001", 10, 15),
]);
let reels = list.unique_reels();
assert_eq!(reels, vec!["A001", "B001"]);
}
#[test]
fn test_reel_index() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A001", 5, 10),
make_event(3, "B001", 10, 15),
]);
let idx = list.reel_index();
assert_eq!(idx["A001"], vec![1, 2]);
assert_eq!(idx["B001"], vec![3]);
}
#[test]
fn test_sort_by_record_in() {
let mut list = EventList::from_events(vec![
make_event(2, "A002", 5, 10),
make_event(1, "A001", 0, 5),
]);
list.sort_by_record_in();
assert_eq!(list.get(0).expect("failed to get value").number, 1);
assert_eq!(list.get(1).expect("failed to get value").number, 2);
}
#[test]
fn test_renumber() {
let mut list = EventList::from_events(vec![
make_event(10, "A001", 0, 5),
make_event(20, "A002", 5, 10),
]);
list.renumber(1);
assert_eq!(list.get(0).expect("failed to get value").number, 1);
assert_eq!(list.get(1).expect("failed to get value").number, 2);
}
#[test]
fn test_remove_by_number() {
let mut list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A002", 5, 10),
]);
let removed = list.remove_by_number(1);
assert!(removed.is_some());
assert_eq!(list.len(), 1);
assert!(list.remove_by_number(99).is_none());
}
#[test]
fn test_total_duration_frames() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5), make_event(2, "A002", 5, 10), ]);
assert_eq!(list.total_duration_frames(), 250);
}
#[test]
fn test_slice_by_record_range() {
let list = EventList::from_events(vec![
make_event(1, "A001", 0, 5),
make_event(2, "A002", 10, 15),
make_event(3, "A003", 20, 25),
]);
let slice = list.slice_by_record_range(90000, 90300);
assert_eq!(slice.len(), 2);
}
#[test]
fn test_into_iter() {
let list = EventList::from_events(vec![make_event(1, "A001", 0, 5)]);
let collected: Vec<_> = list.into_iter().collect();
assert_eq!(collected.len(), 1);
}
#[test]
fn test_find_overlaps() {
let fr = EdlFrameRate::Fps25;
let e1 = EdlEvent::new(
1,
"A001".to_string(),
TrackType::Video,
EditType::Cut,
EdlTimecode::new(1, 0, 0, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 10, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 0, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 10, 0, fr).expect("failed to create"),
);
let e2 = EdlEvent::new(
2,
"A002".to_string(),
TrackType::Video,
EditType::Cut,
EdlTimecode::new(1, 0, 5, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 15, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 5, 0, fr).expect("failed to create"),
EdlTimecode::new(1, 0, 15, 0, fr).expect("failed to create"),
);
let list = EventList::from_events(vec![e1, e2]);
let overlaps = list.find_overlaps();
assert_eq!(overlaps.len(), 1);
assert_eq!(overlaps[0], (1, 2));
}
#[test]
fn test_default() {
let list = EventList::default();
assert!(list.is_empty());
}
}