use crate::types::{Segment, SegmentOp, SegmentOps};
#[derive(Debug, Clone)]
pub struct SegmentSelector<'a> {
segments: Vec<&'a Segment>,
}
impl<'a> SegmentSelector<'a> {
pub(crate) fn new(segments: Vec<&'a Segment>) -> Self {
Self { segments }
}
pub fn count(&self) -> usize {
self.segments.len()
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn segments(&self) -> &[&'a Segment] {
&self.segments
}
pub fn total_duration_ms(&self) -> u64 {
self.segments.iter().map(|s| s.duration_ms() as u64).sum()
}
pub fn total_frames(&self) -> usize {
self.segments.iter().map(|s| s.frame_count()).sum()
}
pub fn longer_than(self, ms: u32) -> Self {
let cs = (ms / 10) as u16;
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.total_duration_cs > cs)
.collect(),
}
}
pub fn shorter_than(self, ms: u32) -> Self {
let cs = (ms / 10) as u16;
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.total_duration_cs < cs)
.collect(),
}
}
pub fn duration_between(self, min_ms: u32, max_ms: u32) -> Self {
let min_cs = (min_ms / 10) as u16;
let max_cs = (max_ms / 10) as u16;
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.total_duration_cs >= min_cs && s.total_duration_cs <= max_cs)
.collect(),
}
}
pub fn frames_gt(self, count: usize) -> Self {
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.frame_count() > count)
.collect(),
}
}
pub fn frames_lt(self, count: usize) -> Self {
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.frame_count() < count)
.collect(),
}
}
pub fn frames_eq(self, count: usize) -> Self {
Self {
segments: self
.segments
.into_iter()
.filter(|s| s.frame_count() == count)
.collect(),
}
}
pub fn filter<F>(self, predicate: F) -> Self
where
F: Fn(&Segment) -> bool,
{
Self {
segments: self.segments.into_iter().filter(|s| predicate(s)).collect(),
}
}
pub fn take(self, n: usize) -> Self {
Self {
segments: self.segments.into_iter().take(n).collect(),
}
}
pub fn skip(self, n: usize) -> Self {
Self {
segments: self.segments.into_iter().skip(n).collect(),
}
}
pub fn first(self) -> Self {
self.take(1)
}
pub fn last(self) -> Self {
Self {
segments: self.segments.into_iter().last().into_iter().collect(),
}
}
pub fn cap(&self, max_ms: u32) -> SegmentOps {
let max_cs = (max_ms / 10) as u16;
let mut ops = SegmentOps::new();
for segment in &self.segments {
if segment.total_duration_cs > max_cs {
ops.insert(segment.id, SegmentOp::Collapse { delay_cs: max_cs });
}
}
ops
}
pub fn collapse(&self, duration_ms: u32) -> SegmentOps {
let delay_cs = (duration_ms / 10) as u16;
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::Collapse { delay_cs });
}
ops
}
pub fn remove(&self) -> SegmentOps {
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::Remove);
}
ops
}
pub fn speed_up(&self, factor: f64) -> SegmentOps {
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(
segment.id,
SegmentOp::Scale {
factor: 1.0 / factor,
},
);
}
ops
}
pub fn slow_down(&self, factor: f64) -> SegmentOps {
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::Scale { factor });
}
ops
}
pub fn set_duration(&self, ms: u32) -> SegmentOps {
let total_cs = (ms / 10) as u16;
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::SetDuration { total_cs });
}
ops
}
pub fn set_frame_delay(&self, ms: u32) -> SegmentOps {
let delay_cs = (ms / 10) as u16;
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::SetFrameDelay { delay_cs });
}
ops
}
pub fn keep(&self) -> SegmentOps {
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::Keep);
}
ops
}
pub fn scale(&self, factor: f64) -> SegmentOps {
let mut ops = SegmentOps::new();
for segment in &self.segments {
ops.insert(segment.id, SegmentOp::Scale { factor });
}
ops
}
}
pub trait SegmentOpsExt {
fn merge(&self, other: &SegmentOps) -> SegmentOps;
fn and(self, other: SegmentOps) -> SegmentOps;
fn merge_all(sets: &[&SegmentOps]) -> SegmentOps;
}
impl SegmentOpsExt for SegmentOps {
fn merge(&self, other: &SegmentOps) -> SegmentOps {
let mut merged = self.clone();
merged.extend(other.iter().map(|(k, v)| (*k, v.clone())));
merged
}
fn and(mut self, other: SegmentOps) -> SegmentOps {
self.extend(other);
self
}
fn merge_all(sets: &[&SegmentOps]) -> SegmentOps {
let mut merged = SegmentOps::new();
for ops in sets {
merged.extend(ops.iter().map(|(k, v)| (*k, v.clone())));
}
merged
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ops::Range;
fn make_segment(id: usize, duration_cs: u16, frames: usize, is_static: bool) -> Segment {
Segment {
id,
frame_range: Range {
start: 0,
end: frames,
},
total_duration_cs: duration_cs,
avg_distance: if is_static { 0.0 } else { 5.0 },
is_static,
}
}
#[test]
fn test_filter_longer_than() {
let segments = [
make_segment(0, 100, 10, true),
make_segment(1, 50, 5, true),
make_segment(2, 200, 20, true),
];
let refs: Vec<_> = segments.iter().collect();
let selector = SegmentSelector::new(refs);
let filtered = selector.longer_than(600); assert_eq!(filtered.count(), 2);
}
#[test]
fn test_cap_operation() {
let segments = [
make_segment(0, 100, 10, true), make_segment(1, 50, 5, true), make_segment(2, 200, 20, true), ];
let refs: Vec<_> = segments.iter().collect();
let selector = SegmentSelector::new(refs);
let ops = selector.cap(800);
assert_eq!(ops.len(), 2);
assert!(matches!(
ops.get(&0),
Some(SegmentOp::Collapse { delay_cs: 80 })
));
assert!(matches!(
ops.get(&2),
Some(SegmentOp::Collapse { delay_cs: 80 })
));
}
#[test]
fn test_merge_ops() {
let mut ops1 = SegmentOps::new();
ops1.insert(0, SegmentOp::Keep);
ops1.insert(1, SegmentOp::Remove);
let mut ops2 = SegmentOps::new();
ops2.insert(1, SegmentOp::Collapse { delay_cs: 50 }); ops2.insert(2, SegmentOp::Remove);
let merged = ops1.merge(&ops2);
assert_eq!(merged.len(), 3);
assert!(matches!(merged.get(&0), Some(SegmentOp::Keep)));
assert!(matches!(
merged.get(&1),
Some(SegmentOp::Collapse { delay_cs: 50 })
));
assert!(matches!(merged.get(&2), Some(SegmentOp::Remove)));
}
}